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

api.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.

api.tsBlame161 lines · 1 contributor
fc1817aClaude1/**
2 * REST API routes for repository management.
3 */
4
5import { Hono } from "hono";
6import { eq, and } from "drizzle-orm";
7import { db } from "../db";
8import { users, repositories } from "../db/schema";
9import { initBareRepo, repoExists } from "../git/repository";
06d5ffeClaude10import { hashPassword } from "../lib/auth";
fc1817aClaude11
12const api = new Hono().basePath("/api");
13
14// Create repository
15api.post("/repos", async (c) => {
16 const body = await c.req.json<{
17 name: string;
18 owner: string;
19 description?: string;
20 isPrivate?: boolean;
21 }>();
22
23 if (!body.name || !body.owner) {
24 return c.json({ error: "name and owner are required" }, 400);
25 }
26
27 // Validate repo name
28 if (!/^[a-zA-Z0-9._-]+$/.test(body.name)) {
29 return c.json({ error: "Invalid repository name" }, 400);
30 }
31
32 // Find owner
33 const [owner] = await db
34 .select()
35 .from(users)
36 .where(eq(users.username, body.owner));
37
38 if (!owner) {
39 return c.json({ error: "Owner not found" }, 404);
40 }
41
42 // Check duplicate
43 if (await repoExists(body.owner, body.name)) {
44 return c.json({ error: "Repository already exists" }, 409);
45 }
46
47 // Init bare repo on disk
48 const diskPath = await initBareRepo(body.owner, body.name);
49
50 // Insert into DB
51 const [repo] = await db
52 .insert(repositories)
53 .values({
54 name: body.name,
55 ownerId: owner.id,
56 description: body.description || null,
57 isPrivate: body.isPrivate || false,
58 diskPath,
59 })
60 .returning();
61
3ef4c9dClaude62 // Green-ecosystem bootstrap: settings, protection, labels, welcome issue
63 if (repo) {
64 const { bootstrapRepository } = await import("../lib/repo-bootstrap");
65 await bootstrapRepository({
66 repositoryId: repo.id,
67 ownerUserId: owner.id,
68 });
69 }
70
fc1817aClaude71 return c.json(repo, 201);
72});
73
74// List user's repositories
75api.get("/users/:username/repos", async (c) => {
76 const { username } = c.req.param();
77 const [owner] = await db
78 .select()
79 .from(users)
80 .where(eq(users.username, username));
81 if (!owner) return c.json({ error: "User not found" }, 404);
82
83 const repos = await db
84 .select()
85 .from(repositories)
86 .where(eq(repositories.ownerId, owner.id));
87
88 return c.json(repos);
89});
90
91// Get single repository
92api.get("/repos/:owner/:name", async (c) => {
93 const { owner: ownerName, name } = c.req.param();
94 const [owner] = await db
95 .select()
96 .from(users)
97 .where(eq(users.username, ownerName));
98 if (!owner) return c.json({ error: "Not found" }, 404);
99
100 const [repo] = await db
101 .select()
102 .from(repositories)
103 .where(
104 and(eq(repositories.ownerId, owner.id), eq(repositories.name, name))
105 );
106 if (!repo) return c.json({ error: "Not found" }, 404);
107
108 return c.json(repo);
109});
110
111// Quick-setup: create user + repo in one call (dev convenience)
112api.post("/setup", async (c) => {
113 const body = await c.req.json<{
114 username: string;
115 email: string;
116 repoName: string;
117 description?: string;
118 }>();
119
120 // Upsert user
121 let [user] = await db
122 .select()
123 .from(users)
124 .where(eq(users.username, body.username));
125
126 if (!user) {
127 [user] = await db
128 .insert(users)
129 .values({
130 username: body.username,
131 email: body.email,
06d5ffeClaude132 passwordHash: await hashPassword("changeme"),
fc1817aClaude133 })
134 .returning();
135 }
136
137 // Create repo if not exists
138 if (!(await repoExists(body.username, body.repoName))) {
139 const diskPath = await initBareRepo(body.username, body.repoName);
3ef4c9dClaude140 const [repo] = await db
141 .insert(repositories)
142 .values({
143 name: body.repoName,
144 ownerId: user.id,
145 description: body.description || null,
146 diskPath,
147 })
148 .returning();
149 if (repo) {
150 const { bootstrapRepository } = await import("../lib/repo-bootstrap");
151 await bootstrapRepository({
152 repositoryId: repo.id,
153 ownerUserId: user.id,
154 });
155 }
fc1817aClaude156 }
157
158 return c.json({ user: user.username, repo: body.repoName, status: "ready" });
159});
160
161export default api;