Files

20 lines
2.5 KiB
JavaScript

/**
* portal/notifications/api.mjs — 알림 센터 화면·API(§24.1). 현재 그룹의 알림(그룹 알림 + 나에게 온 개인 알림)만 보인다.
* 분류 필터·읽지 않음 필터·읽음 처리·사건 반복 횟수·필수 표시·조치 화면 링크.
*/
import { requireGroup } from '../auth/guard.mjs';
import { listNotifications, markRead, unreadCount } from '../../lib/notify.mjs';
import { t } from '../../lib/i18n.mjs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const HERE = dirname(fileURLToPath(import.meta.url));
export const CATEGORIES = ['billing', 'site', 'domain', 'mail', 'usage', 'backup', 'relocation', 'security', 'incident', 'membership'];
export async function register(app, deps) {
const { store } = deps;
app.static('/ui/notifications', join(HERE, 'public'));
app.get('/notifications', (ctx) => { requireGroup(ctx); return ctx.html(deps.shell(ctx, { title: t('notifications.title'), nav: null, crumbs: [{ label: t('notifications.title') }], scripts: ['/ui/notifications/notifications.js'], body: `<div class="tabs" id="ncats"><a class="on" href="#" data-cat="">${t('notifications.all')}</a>${CATEGORIES.map((c) => `<a href="#" data-cat="${c}">${t('notifications.categories.' + c)}</a>`).join('')}<a href="#" data-unread="1">${t('notifications.unread')}</a></div><div id="nlist">${deps.statePanel('loading')}</div>` })); });
app.get('/api/notifications', (ctx) => { requireGroup(ctx); const list = listNotifications(store, { groupId: ctx.group.id, userId: ctx.user.id, category: ctx.query.category || undefined, unreadOnly: ctx.query.unread === '1', limit: Number(ctx.query.limit || 100) }); return { ok: true, unread: unreadCount(store, ctx.group.id), items: list.map((n) => ({ id: n.id, category: n.category, categoryLabel: t('notifications.categories.' + n.category), severity: n.severity, mandatory: !!n.mandatory, title: n.title, body: n.body, link: n.link, actionRequired: n.action_required, state: n.state, count: n.count, firstAt: n.first_at, lastAt: n.last_at, readAt: n.read_at, emailSentAt: n.email_sent_at })) }; });
app.post('/api/notifications/:id/read', (ctx) => { requireGroup(ctx); return { ok: true, changed: markRead(store, ctx.params.id, ctx.group.id) }; });
app.post('/api/notifications/read-all', (ctx) => { requireGroup(ctx); const n = store.run('UPDATE notifications SET read_at=? WHERE (group_id=? OR (group_id IS NULL AND user_id=?)) AND read_at IS NULL', new Date().toISOString(), ctx.group.id, ctx.user.id).changes; return { ok: true, changed: n }; });
}