134 lines · 3.8 KB
1 /**
2 * REST API: User settings and token management endpoints.
3 */
4
5 import { requireAuth } from '../../middleware/auth.js';
6 import { RepoService } from '../../services/repo-service.js';
7 import { ValidationError } from '@gitfastr/shared/utils/errors.js';
8 import type { HandlerContext } from '../../router.js';
9
10 /** GET /api/user — Current user profile */
11 export async function handleApiGetUser(
12 request: Request,
13 hctx: HandlerContext
14 ): Promise<Response> {
15 const userId = await requireAuth(request, hctx.env.DB);
16
17 const user = await hctx.env.DB
18 .prepare('SELECT id, username, email, display_name, bio, is_admin, created_at FROM users WHERE id = ?')
19 .bind(userId)
20 .first();
21
22 return Response.json(user);
23 }
24
25 /** GET /api/user/tokens — List user's PATs */
26 export async function handleApiListTokens(
27 request: Request,
28 hctx: HandlerContext
29 ): Promise<Response> {
30 const userId = await requireAuth(request, hctx.env.DB);
31
32 const result = await hctx.env.DB
33 .prepare(
34 'SELECT id, name, token_prefix, scopes, expires_at, last_used_at, created_at FROM access_tokens WHERE user_id = ? ORDER BY created_at DESC'
35 )
36 .bind(userId)
37 .all();
38
39 return Response.json(result.results);
40 }
41
42 /** DELETE /api/user/tokens/:id — Revoke a PAT */
43 export async function handleApiRevokeToken(
44 request: Request,
45 hctx: HandlerContext
46 ): Promise<Response> {
47 const userId = await requireAuth(request, hctx.env.DB);
48
49 await hctx.env.DB
50 .prepare('DELETE FROM access_tokens WHERE id = ? AND user_id = ?')
51 .bind(hctx.params.tokenId, userId)
52 .run();
53
54 return new Response(null, { status: 204 });
55 }
56
57 /** PUT /api/repos/:owner/:repo/settings — Update repo settings */
58 export async function handleApiUpdateRepo(
59 request: Request,
60 hctx: HandlerContext
61 ): Promise<Response> {
62 const userId = await requireAuth(request, hctx.env.DB);
63 const { owner, repo: repoName } = hctx.params;
64
65 const repoService = new RepoService(hctx.env.DB);
66 const { repo } = await repoService.getByOwnerAndName(owner, repoName);
67
68 const hasAccess = await repoService.hasWriteAccess(repo.id, userId);
69 if (!hasAccess) throw new ValidationError('Write access required');
70
71 const body = await request.json<{
72 description?: string;
73 is_private?: boolean;
74 default_branch?: string;
75 }>();
76
77 const updates: string[] = [];
78 const values: any[] = [];
79
80 if (body.description !== undefined) {
81 updates.push('description = ?');
82 values.push(body.description);
83 }
84 if (body.is_private !== undefined) {
85 updates.push('is_private = ?');
86 values.push(body.is_private ? 1 : 0);
87 }
88 if (body.default_branch !== undefined) {
89 updates.push('default_branch = ?');
90 values.push(body.default_branch);
91 }
92
93 if (updates.length === 0) {
94 return Response.json(repo);
95 }
96
97 updates.push("updated_at = datetime('now')");
98 values.push(repo.id);
99
100 const result = await hctx.env.DB
101 .prepare(`UPDATE repositories SET ${updates.join(', ')} WHERE id = ? RETURNING *`)
102 .bind(...values)
103 .first();
104
105 return Response.json(result);
106 }
107
108 /** DELETE /api/repos/:owner/:repo — Delete repo */
109 export async function handleApiDeleteRepo(
110 request: Request,
111 hctx: HandlerContext
112 ): Promise<Response> {
113 const userId = await requireAuth(request, hctx.env.DB);
114 const { owner, repo: repoName } = hctx.params;
115
116 const repoService = new RepoService(hctx.env.DB);
117 const { repo } = await repoService.getByOwnerAndName(owner, repoName);
118
119 // Only the owner can delete
120 if (repo.owner_id !== userId) {
121 throw new ValidationError('Only the repository owner can delete it');
122 }
123
124 // Delete from D1 (cascade will remove refs, MRs, etc.)
125 await hctx.env.DB
126 .prepare('DELETE FROM repositories WHERE id = ?')
127 .bind(repo.id)
128 .run();
129
130 // Note: R2 objects are not cleaned up here. A separate cleanup job could handle that.
131
132 return new Response(null, { status: 204 });
133 }
134