86 lines · 2.8 KB
1 /**
2 * REST API: Webhook management endpoints.
3 */
4
5 import { requireAuth } from '../../middleware/auth.js';
6 import { RepoService } from '../../services/repo-service.js';
7 import { ValidationError, NotFoundError } from '@gitfastr/shared/utils/errors.js';
8 import type { HandlerContext } from '../../router.js';
9
10 /** POST /api/repos/:owner/:repo/webhooks — Create webhook */
11 export async function handleApiCreateWebhook(
12 request: Request,
13 hctx: HandlerContext
14 ): Promise<Response> {
15 const userId = await requireAuth(request, hctx.env.DB);
16 const { owner, repo: repoName } = hctx.params;
17
18 const repoService = new RepoService(hctx.env.DB);
19 const { repo } = await repoService.getByOwnerAndName(owner, repoName);
20
21 const hasAccess = await repoService.hasWriteAccess(repo.id, userId);
22 if (!hasAccess) throw new ValidationError('Write access required');
23
24 const body = await request.json<{
25 url: string;
26 secret?: string;
27 events?: string;
28 }>();
29
30 if (!body.url) throw new ValidationError('URL is required');
31
32 const result = await hctx.env.DB.prepare(
33 `INSERT INTO webhooks (repo_id, url, secret, events)
34 VALUES (?, ?, ?, ?)
35 RETURNING id, repo_id, url, events, is_active, created_at`
36 )
37 .bind(repo.id, body.url, body.secret ?? null, body.events ?? 'push')
38 .first();
39
40 return Response.json(result, { status: 201 });
41 }
42
43 /** GET /api/repos/:owner/:repo/webhooks — List webhooks */
44 export async function handleApiListWebhooks(
45 request: Request,
46 hctx: HandlerContext
47 ): Promise<Response> {
48 const userId = await requireAuth(request, hctx.env.DB);
49 const { owner, repo: repoName } = hctx.params;
50
51 const repoService = new RepoService(hctx.env.DB);
52 const { repo } = await repoService.getByOwnerAndName(owner, repoName);
53
54 const hasAccess = await repoService.hasWriteAccess(repo.id, userId);
55 if (!hasAccess) throw new ValidationError('Write access required');
56
57 const result = await hctx.env.DB.prepare(
58 'SELECT id, repo_id, url, events, is_active, created_at FROM webhooks WHERE repo_id = ?'
59 )
60 .bind(repo.id)
61 .all();
62
63 return Response.json(result.results);
64 }
65
66 /** DELETE /api/repos/:owner/:repo/webhooks/:id — Delete webhook */
67 export async function handleApiDeleteWebhook(
68 request: Request,
69 hctx: HandlerContext
70 ): Promise<Response> {
71 const userId = await requireAuth(request, hctx.env.DB);
72 const { owner, repo: repoName } = hctx.params;
73
74 const repoService = new RepoService(hctx.env.DB);
75 const { repo } = await repoService.getByOwnerAndName(owner, repoName);
76
77 const hasAccess = await repoService.hasWriteAccess(repo.id, userId);
78 if (!hasAccess) throw new ValidationError('Write access required');
79
80 await hctx.env.DB.prepare('DELETE FROM webhooks WHERE id = ? AND repo_id = ?')
81 .bind(hctx.params.webhookId, repo.id)
82 .run();
83
84 return new Response(null, { status: 204 });
85 }
86