
- Complete NestJS TypeScript implementation with WebSocket support - Direct messaging (DM) and group chat functionality - End-to-end encryption with AES encryption and key pairs - Media file support (images, videos, audio, documents) up to 100MB - Push notifications with Firebase Cloud Messaging integration - Mention alerts and real-time typing indicators - User authentication with JWT and Passport - SQLite database with TypeORM entities and relationships - Comprehensive API documentation with Swagger/OpenAPI - File upload handling with secure access control - Online/offline status tracking and presence management - Message editing, deletion, and reply functionality - Notification management with automatic cleanup - Health check endpoint for monitoring - CORS configuration for cross-origin requests - Environment-based configuration management - Structured for Flutter SDK integration Features implemented: ✅ Real-time messaging with Socket.IO ✅ User registration and authentication ✅ Direct messages and group chats ✅ Media file uploads and management ✅ End-to-end encryption ✅ Push notifications ✅ Mention alerts ✅ Typing indicators ✅ Message read receipts ✅ Online status tracking ✅ File access control ✅ Comprehensive API documentation Ready for Flutter SDK development and production deployment.
59 lines
2.2 KiB
JavaScript
59 lines
2.2 KiB
JavaScript
/**
|
|
* This plugin creates a summary tag, if missing, from the first sentence in the description.
|
|
*
|
|
* @module plugins/summarize
|
|
*/
|
|
exports.handlers = {
|
|
/**
|
|
* Autogenerate summaries, if missing, from the description, if present.
|
|
*/
|
|
newDoclet({doclet}) {
|
|
let endTag;
|
|
let tags;
|
|
let stack;
|
|
|
|
// If the summary is missing, grab the first sentence from the description
|
|
// and use that.
|
|
if (doclet && !doclet.summary && doclet.description) {
|
|
// The summary may end with `.$`, `. `, or `.<` (a period followed by an HTML tag).
|
|
doclet.summary = doclet.description.split(/\.$|\.\s|\.</)[0];
|
|
// Append `.` as it was removed in both cases, or is possibly missing.
|
|
doclet.summary += '.';
|
|
|
|
// This is an excerpt of something that is possibly HTML.
|
|
// Balance it using a stack. Assume it was initially balanced.
|
|
tags = doclet.summary.match(/<[^>]+>/g) || [];
|
|
stack = [];
|
|
|
|
tags.forEach(tag => {
|
|
const idx = tag.indexOf('/');
|
|
|
|
if (idx === -1) {
|
|
// start tag -- push onto the stack
|
|
stack.push(tag);
|
|
} else if (idx === 1) {
|
|
// end tag -- pop off of the stack
|
|
stack.pop();
|
|
}
|
|
|
|
// otherwise, it's a self-closing tag; don't modify the stack
|
|
});
|
|
|
|
// stack should now contain only the start tags that lack end tags,
|
|
// with the most deeply nested start tag at the top
|
|
while (stack.length > 0) {
|
|
// pop the unmatched tag off the stack
|
|
endTag = stack.pop();
|
|
// get just the tag name
|
|
endTag = endTag.substring(1, endTag.search(/[ >]/));
|
|
// append the end tag
|
|
doclet.summary += `</${endTag}>`;
|
|
}
|
|
|
|
// and, finally, if the summary starts and ends with a <p> tag, remove it; let the
|
|
// template decide whether to wrap the summary in a <p> tag
|
|
doclet.summary = doclet.summary.replace(/^<p>(.*)<\/p>$/i, '$1');
|
|
}
|
|
}
|
|
};
|