Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesPull RequestsActionsSecurityInsightsSettings
✨ AI
More
Blame · Line-by-line history

notify.ts

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

notify.tsBlame105 lines · 1 contributor
3ef4c9dClaude1/**
2 * Notifications + audit log helpers.
3 * Swallows DB failures so notifications never break the primary request path.
4 */
5
6import { db } from "../db";
7import { notifications, auditLog } from "../db/schema";
8
9export type NotificationKind =
10 | "mention"
11 | "review_requested"
12 | "pr_opened"
13 | "pr_merged"
14 | "pr_closed"
15 | "issue_opened"
16 | "issue_closed"
17 | "assigned"
18 | "ai_review"
19 | "gate_failed"
20 | "gate_repaired"
21 | "gate_passed"
22 | "security_alert"
23 | "deploy_success"
24 | "deploy_failed"
25 | "release_published"
26 | "repo_archived";
27
28export async function notify(
29 userId: string,
30 opts: {
31 kind: NotificationKind;
32 title: string;
33 body?: string;
34 url?: string;
35 repositoryId?: string;
36 }
37): Promise<void> {
38 try {
39 await db.insert(notifications).values({
40 userId,
41 kind: opts.kind,
42 title: opts.title,
43 body: opts.body,
44 url: opts.url,
45 repositoryId: opts.repositoryId,
46 });
47 } catch (err) {
48 console.error("[notify] failed:", err);
49 }
50}
51
52export async function notifyMany(
53 userIds: string[],
54 opts: {
55 kind: NotificationKind;
56 title: string;
57 body?: string;
58 url?: string;
59 repositoryId?: string;
60 }
61): Promise<void> {
62 const unique = Array.from(new Set(userIds));
63 if (unique.length === 0) return;
64 try {
65 await db.insert(notifications).values(
66 unique.map((userId) => ({
67 userId,
68 kind: opts.kind,
69 title: opts.title,
70 body: opts.body,
71 url: opts.url,
72 repositoryId: opts.repositoryId,
73 }))
74 );
75 } catch (err) {
76 console.error("[notify] batch failed:", err);
77 }
78}
79
80export async function audit(opts: {
81 userId?: string | null;
82 repositoryId?: string | null;
83 action: string;
84 targetType?: string;
85 targetId?: string;
86 ip?: string;
87 userAgent?: string;
88 metadata?: Record<string, unknown>;
89}): Promise<void> {
90 try {
91 await db.insert(auditLog).values({
92 userId: opts.userId ?? null,
93 repositoryId: opts.repositoryId ?? null,
94 action: opts.action,
95 targetType: opts.targetType,
96 targetId: opts.targetId,
97 ip: opts.ip,
98 userAgent: opts.userAgent,
99 metadata: opts.metadata ? JSON.stringify(opts.metadata) : null,
100 });
101 } catch (err) {
102 // Audit must never break the primary flow
103 console.error("[audit] failed:", err);
104 }
105}