System Architecture
Core Features
Data Management
Frontend Components
Extensibility
The following files were used as context for generating this wiki page:
The notification system provides mechanisms for alerting users about significant events within the application, such as mentions in comments, changes in task status, or new findings. It leverages both email and in-app notifications to ensure timely communication. The system is designed to be flexible, allowing for different notification types and user-specific preferences, including unsubscribe options.
At its core, the notification architecture integrates with external services like Novu for in-app notifications and Resend for email delivery, while utilizing React-based templates for consistent and branded email content.
The notification system is composed of several key services and components that work together to deliver alerts to users. This includes a dedicated service for handling in-app notifications via Novu, a utility for sending emails via Resend, and various React components for rendering email templates. A primary example of its usage is the , which orchestrates the process of notifying users when they are mentioned in a comment.
CommentMentionNotifierServiceSources: apps/api/src/comments/comment-mention-notifier.service.ts, apps/api/src/notifications/novu.service.ts, apps/api/src/email/resend.ts
The NovuService is responsible for sending in-app notifications by interacting with the Novu API. It provides a trigger method that allows other services to initiate notification workflows.
The NovuService is an injectable NestJS service that encapsulates the logic for calling the Novu API.
@Injectable()
export class NovuService {
private readonly logger = new Logger(NovuService.name);
async trigger(params: {
workflowId: string;
subscriberId: string;
email: string;
payload: Record<string, unknown>;
}): Promise<void> {
// ... implementation ...
}
}The trigger method sends a POST request to the Novu API endpoint /v1/events/trigger. It requires a workflowId, subscriberId, email, and a payload containing dynamic data for the notification. The NOVU_API_KEY environment variable is essential for authentication. If it's not configured, a warning is logged, and the notification is skipped.
The NOVU_API_KEY environment variable must be configured for Novu in-app notifications to function. Without it, the NovuService will log a warning and skip triggering any workflows.
Email notifications are handled through the sendEmail utility, which integrates with the Resend email service. This utility allows for sending rich HTML emails using React components as templates.
The resend client is initialized using the RESEND_API_KEY environment variable. If this key is not present, the resend object will be null, and any attempt to send an email will result in an error.
export const resend = process.env.RESEND_API_KEY
? new Resend(process.env.RESEND_API_KEY)
: null;Sources: apps/api/src/email/resend.ts
sendEmail FunctionThe sendEmail function is a central utility for dispatching emails. It takes various parameters, including the recipient, subject, and a React component for the email body. It also supports different from addresses based on whether the email is marketing or system related.
| Parameter | Type | Description Sources: apps/api/src/notifications/novu.service.ts, apps/api/src/email/resend.ts, apps/api/src/comments/comment-mention-notifier.service.ts, apps/api/src/email/templates/comment-mentioned.tsx, apps/api/src/email/templates/finding-notification.tsx, apps/api/src/email/templates/task-status-changed.tsx, apps/api/src/email/components/footer.tsx, apps/api/src/email/components/logo.tsx
The system uses React components to define email templates, ensuring a consistent look and feel across different notification types. These templates leverage @react-email/components for structuring the email content and Tailwind CSS for styling.
All email templates utilize shared components for branding and consistency:
Logo: Displays the Comp AI logo at the top of the email.Footer: Contains standard footer information, including a link to Comp AI and unsubscribe options.CommentMentionedEmailThis template is used when a user is mentioned in a comment. It displays who mentioned the user, the entity where the mention occurred, and a snippet of the comment content. It also provides a direct link to view the comment.
interface Props {
toName: string;
toEmail: string;
commentContent: string;
mentionedByName: string;
entityName: string;
entityRoutePath: string;
entityId: string;
organizationId: string;
commentUrl: string;
}The CommentMentionedEmail component includes a getPlainText utility to extract readable text from TipTap JSON content, which is then used for the email preview and display.
Sources: apps/api/src/email/templates/comment-mentioned.tsx
FindingNotificationEmailThis template is used for notifications related to findings, such as new findings or status updates. It includes details like the organization, task title, finding type, content, and a link to view the finding.
interface Props {
toName: string;
toEmail: string;
heading: string;
message: string;
taskTitle: string;
organizationName: string;
findingType: string;
findingContent: string;
newStatus?: string;
findingUrl: string;
}TaskStatusChangedEmailThis template informs users when the status of a task has changed. It specifies the old and new statuses, who made the change, and provides a link to the task.
interface Props {
toName: string;
toEmail: string;
taskTitle: string;
oldStatus: string;
newStatus: string;
changedByName: string;
organizationName: string;
taskUrl: string;
}The CommentMentionNotifierService is a concrete implementation of the notification system, specifically designed to handle mentions within comments. It ensures that mentioned users receive both email and in-app notifications, provided they have not unsubscribed.
notifyMentionedUsers MethodThis method orchestrates the entire process of notifying users mentioned in a comment.
The extractMentionedUserIds function parses the comment content (expected to be in a JSON format, potentially from a rich text editor like TipTap) to identify all unique user IDs that have been mentioned.
It fetches details for the user who made the mention (mentionedByUserId) and all the mentioned users (mentionedUserIds) from the database.
The system attempts to normalize the provided contextUrl to ensure it's a valid and safe URL within the application's allowed origins and organization. If no valid contextUrl is provided or it's invalid, buildFallbackCommentContext generates a default URL based on the comment's entityType and entityId (e.g., task, vendor, risk, policy). This ensures notifications always link to a relevant page.
Before sending any notification, the system checks if the recipient user has unsubscribed from "task mentions" (currently used as the preference for comment mentions). If unsubscribed, the notification is skipped for that user.
For each eligible mentioned user, an email is sent using the sendEmail utility with the CommentMentionedEmail React component. The email includes details about the mention, the entity, and a direct link to the comment.
Concurrently, an in-app notification is triggered via the NovuService for each eligible mentioned user. This notification uses the comment-mentioned workflow and includes relevant payload data.
The notification system includes robust logic to construct valid and secure URLs for linking to specific entities within the application.
getAppBaseUrl(): Retrieves the base URL for the application, prioritizing NEXT_PUBLIC_APP_URL, then BETTER_AUTH_URL, and falling back to https://app.trycomp.ai.getAllowedOrigins(): Gathers a list of allowed origins from environment variables to validate incoming contextUrls.tryNormalizeContextUrl(): Validates a provided contextUrl against allowed origins and ensures it belongs to the correct organization to prevent malicious deep-linking.buildFallbackCommentContext(): If a contextUrl is not provided or invalid, this function constructs a URL based on the entityType (e.g., task, vendor, risk, policy) and entityId, querying the database to retrieve relevant entity names and routes.The tryNormalizeContextUrl and buildFallbackCommentContext functions are critical for security, ensuring that notification links point to valid, internal application pages and prevent potential path traversal or phishing attempts by validating against allowed origins and organization IDs.
The system integrates with an unsubscribe mechanism (isUserUnsubscribed from @trycompai/email) to respect user preferences. When sending notifications, it checks if a user has opted out of specific notification types. For comment mentions, the taskMentions preference is currently used.
Sources: apps/api/src/comments/comment-mention-notifier.service.ts