Files
ai-code-app/etc/servers/work-server/src/routes/notification.ts

165 lines
5.3 KiB
TypeScript

import type { FastifyInstance } from 'fastify';
import { z } from 'zod';
import {
listWebPushSubscriptions,
getAutomationNotificationPreference,
getWebPushConfig,
registerAutomationNotificationPreferenceSchema,
registerWebPushSubscription,
registerWebPushSubscriptionSchema,
sendNotifications,
sendIosNotificationSchema,
setupNotificationTables,
upsertAutomationNotificationPreference,
unregisterWebPushSubscription,
unregisterWebPushSubscriptionSchema,
} from '../services/notification-service.js';
import {
createNotificationMessage,
deleteNotificationMessage,
getNotificationMessage,
listNotificationMessages,
notificationMessageListQuerySchema,
notificationMessagePayloadSchema,
notificationMessageReadPayloadSchema,
updateNotificationMessageReadState,
} from '../services/notification-message-service.js';
const automationNotificationPreferenceQuerySchema = z.object({
targetKind: z.enum(['client', 'web-endpoint']).optional(),
targetId: z.string().trim().min(1).max(1000).optional(),
});
function getClientIdHeader(request: { headers: Record<string, string | string[] | undefined> }) {
const rawClientId = request.headers['x-client-id'];
const clientId = Array.isArray(rawClientId) ? rawClientId[0] : rawClientId;
return clientId?.trim() ?? '';
}
export async function registerNotificationRoutes(app: FastifyInstance) {
app.post('/api/notifications/setup', async () => setupNotificationTables());
app.get('/api/notifications/subscriptions/web', async () => ({
items: await listWebPushSubscriptions(),
}));
app.get('/api/notifications/webpush/config', async () => getWebPushConfig());
app.get('/api/notifications/messages', async (request) => {
const query = notificationMessageListQuerySchema.parse(request.query ?? {});
const clientId = getClientIdHeader(request);
return {
ok: true,
...(await listNotificationMessages(query, clientId)),
};
});
app.get('/api/notifications/messages/:id', async (request, reply) => {
const id = z.coerce.number().int().positive().parse((request.params as { id: string }).id);
const item = await getNotificationMessage(id);
if (!item) {
return reply.code(404).send({
message: '알림 메시지를 찾을 수 없습니다.',
});
}
return {
ok: true,
item,
};
});
app.post('/api/notifications/messages', async (request) => {
const item = await createNotificationMessage(notificationMessagePayloadSchema.parse(request.body ?? {}));
return {
ok: true,
item,
};
});
app.patch('/api/notifications/messages/:id', async (request, reply) => {
const id = z.coerce.number().int().positive().parse((request.params as { id: string }).id);
const item = await updateNotificationMessageReadState(id, notificationMessageReadPayloadSchema.parse(request.body ?? {}));
if (!item) {
return reply.code(404).send({
message: '상태를 변경할 알림 메시지를 찾을 수 없습니다.',
});
}
return {
ok: true,
item,
};
});
app.delete('/api/notifications/messages/:id', async (request, reply) => {
const id = z.coerce.number().int().positive().parse((request.params as { id: string }).id);
const deleted = await deleteNotificationMessage(id);
if (!deleted) {
return reply.code(404).send({
message: '삭제할 알림 메시지를 찾을 수 없습니다.',
});
}
return {
ok: true,
deleted: true,
};
});
app.get('/api/notifications/preferences/automation', async (request) => {
const query = automationNotificationPreferenceQuerySchema.parse(request.query ?? {});
const targetId = query.targetId || getClientIdHeader(request);
const targetKind = query.targetId ? query.targetKind ?? 'client' : 'client';
return {
ok: true,
automation: await getAutomationNotificationPreference(targetId, targetKind),
};
});
app.put('/api/notifications/preferences/automation', async (request, reply) => {
try {
const payload = registerAutomationNotificationPreferenceSchema.parse(request.body ?? {});
const targetId = payload.targetId || getClientIdHeader(request);
if (!targetId) {
throw new Error('알림 설정을 저장할 클라이언트 ID가 없습니다.');
}
return upsertAutomationNotificationPreference({
...payload,
targetId,
});
} catch (error) {
return reply.code(409).send({
message: error instanceof Error ? error.message : '알림 설정 저장에 실패했습니다.',
});
}
});
app.put('/api/notifications/subscriptions/web', async (request) => {
const payload = registerWebPushSubscriptionSchema.parse(request.body ?? {});
return registerWebPushSubscription(payload);
});
app.delete('/api/notifications/subscriptions/web', async (request) => {
const payload = unregisterWebPushSubscriptionSchema.parse(request.body ?? {});
return unregisterWebPushSubscription(payload.endpoint);
});
app.post('/api/notifications/send', async (request) => {
const payload = sendIosNotificationSchema.parse(request.body ?? {});
return sendNotifications(payload);
});
app.post('/api/notifications/send-test', async (request) => {
const payload = sendIosNotificationSchema.parse(request.body ?? {});
return sendNotifications(payload);
});
}