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