feat(notifications): add real-time notification system

Add database tables for notifications and WebSocket tickets
Implement REST API and WebSocket server for real-time delivery
Add NotificationBell component with dropdown and unread badge
Trigger alerts for comments, reactions, and AI moderation results
This commit is contained in:
2026-05-04 10:40:14 +08:00
parent 579d092020
commit a25f1661b5
12 changed files with 1811 additions and 0 deletions

View File

@@ -134,6 +134,13 @@ import {
saveEntityImageUpload,
uploadRoot
} from './uploads.ts';
import {
createNotificationWebSocketTicket,
listNotifications,
markAllNotificationsRead,
markNotificationRead,
setupNotificationWebSocketServer
} from './notifications.ts';
const app = Fastify({
logger: true,
@@ -1041,6 +1048,32 @@ app.get('/api/auth/referral', async (request, reply) => {
return { referral: await getReferralSummary(user.id) };
});
app.get('/api/notifications', async (request, reply) => {
const user = await requireVerifiedUser(request, reply);
return user ? listNotifications(user.id, request.query as Record<string, string | string[] | undefined>) : undefined;
});
app.post('/api/notifications/ws-ticket', async (request, reply) => {
const user = await requireVerifiedUser(request, reply);
return user ? createNotificationWebSocketTicket(user.id) : undefined;
});
app.post('/api/notifications/read-all', async (request, reply) => {
const user = await requireVerifiedUser(request, reply);
return user ? markAllNotificationsRead(user.id) : undefined;
});
app.post('/api/notifications/:id/read', async (request, reply) => {
const user = await requireVerifiedUser(request, reply);
if (!user) {
return;
}
const { id } = request.params as { id: string };
const result = await markNotificationRead(Number(id), user.id);
return result.notification ? result : notFound(reply, request);
});
app.post('/api/auth/logout', async (request, reply) => {
const token = getBearerToken(request.headers.authorization);
if (token) {
@@ -2004,6 +2037,7 @@ try {
await initializeDatabase();
await syncSystemWordingCatalog();
await startAiModerationWorker(app.log);
setupNotificationWebSocketServer(app.server, app.log);
await app.listen({ host: '0.0.0.0', port });
} catch (error) {
app.log.error(error);