76 lines · 2.0 KB
1 /**
2 * Activity logging service.
3 * Records events for the activity feed.
4 */
5
6 export type ActivityAction = 'push' | 'create_repo' | 'create_mr' | 'merge_mr' | 'close_mr' | 'comment';
7
8 export interface ActivityDetails {
9 ref?: string;
10 commits?: number;
11 mr_number?: number;
12 mr_title?: string;
13 [key: string]: any;
14 }
15
16 export class ActivityService {
17 constructor(private db: D1Database) {}
18
19 /** Log an activity event. */
20 async log(
21 repoId: string | null,
22 userId: string | null,
23 action: ActivityAction,
24 details?: ActivityDetails
25 ): Promise<void> {
26 await this.db
27 .prepare(
28 `INSERT INTO activity_log (repo_id, user_id, action, details)
29 VALUES (?, ?, ?, ?)`
30 )
31 .bind(repoId, userId, action, details ? JSON.stringify(details) : null)
32 .run();
33 }
34
35 /** Get activity feed for a repo. */
36 async getRepoActivity(repoId: string, limit: number = 20): Promise<any[]> {
37 const result = await this.db
38 .prepare(
39 `SELECT al.*, u.username
40 FROM activity_log al
41 LEFT JOIN users u ON al.user_id = u.id
42 WHERE al.repo_id = ?
43 ORDER BY al.created_at DESC
44 LIMIT ?`
45 )
46 .bind(repoId, limit)
47 .all();
48
49 return result.results.map((row: any) => ({
50 ...row,
51 details: row.details ? JSON.parse(row.details) : null,
52 }));
53 }
54
55 /** Get global activity feed (across all repos). */
56 async getGlobalActivity(limit: number = 20): Promise<any[]> {
57 const result = await this.db
58 .prepare(
59 `SELECT al.*, u.username, r.name as repo_name,
60 (SELECT username FROM users WHERE id = r.owner_id) as repo_owner
61 FROM activity_log al
62 LEFT JOIN users u ON al.user_id = u.id
63 LEFT JOIN repositories r ON al.repo_id = r.id
64 ORDER BY al.created_at DESC
65 LIMIT ?`
66 )
67 .bind(limit)
68 .all();
69
70 return result.results.map((row: any) => ({
71 ...row,
72 details: row.details ? JSON.parse(row.details) : null,
73 }));
74 }
75 }
76