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
|
import { Hono } from "hono";
import { and, eq } from "drizzle-orm";
import { db } from "../db";
import { activityFeed, repositories, users } from "../db/schema";
import { softAuth, requireAuth } from "../middleware/auth";
import type { AuthEnv } from "../middleware/auth";
import { getRepoPath, repoExists } from "../git/repository";
import { config } from "../lib/config";
import { join } from "path";
const templates = new Hono<AuthEnv>();
templates.use("*", softAuth);
templates.post("/:owner/:repo/use-template", requireAuth, async (c) => {
const { owner: ownerName, repo: repoName } = c.req.param();
const user = c.get("user")!;
const body = await c.req.parseBody();
const newName = String(body.name || "").trim();
if (!newName) {
return c.redirect(`/${ownerName}/${repoName}?error=Name+required`);
}
if (!/^[a-zA-Z0-9._-]+$/.test(newName)) {
return c.redirect(`/${ownerName}/${repoName}?error=Invalid+name`);
}
if (!(await repoExists(ownerName, repoName))) {
return c.redirect(`/${ownerName}/${repoName}`);
}
const [sourceOwner] = await db
.select()
.from(users)
.where(eq(users.username, ownerName))
.limit(1);
if (!sourceOwner) return c.redirect(`/${ownerName}/${repoName}`);
const [sourceRepo] = await db
.select()
.from(repositories)
.where(
and(
eq(repositories.ownerId, sourceOwner.id),
eq(repositories.name, repoName)
)
)
.limit(1);
if (!sourceRepo) return c.redirect(`/${ownerName}/${repoName}`);
if (!sourceRepo.isTemplate) {
return c.redirect(`/${ownerName}/${repoName}?error=Not+a+template`);
}
if (await repoExists(user.username, newName)) {
return c.redirect(`/${user.username}/${newName}`);
}
const sourcePath = getRepoPath(ownerName, repoName);
const destPath = join(config.gitReposPath, user.username, `${newName}.git`);
const proc = Bun.spawn(["git", "clone", "--bare", sourcePath, destPath], {
stdout: "pipe",
stderr: "pipe",
});
await proc.exited;
const [newRepo] = await db
.insert(repositories)
.values({
name: newName,
ownerId: user.id,
description: sourceRepo.description
? `Seeded from ${ownerName}/${repoName} template`
: `Seeded from ${ownerName}/${repoName} template`,
isPrivate: false,
defaultBranch: sourceRepo.defaultBranch,
diskPath: destPath,
})
.returning();
if (newRepo) {
try {
const { bootstrapRepository } = await import("../lib/repo-bootstrap");
await bootstrapRepository({
repositoryId: newRepo.id,
ownerUserId: user.id,
defaultBranch: sourceRepo.defaultBranch,
skipWelcomeIssue: true,
});
} catch {
}
try {
await db.insert(activityFeed).values({
repositoryId: newRepo.id,
userId: user.id,
action: "created",
metadata: JSON.stringify({
fromTemplate: `${ownerName}/${repoName}`,
}),
});
} catch {
}
}
return c.redirect(`/${user.username}/${newName}`);
});
export default templates;
|