Skip to content
File

Blob: src/worker/db/d1/repositories/projects.ts

typescript108 lines
1import { and, desc, eq, inArray } from "drizzle-orm";
2 
3import { type D1DbExecutor, projectIndex, runIndex } from "@/worker/db/d1";
4import type { ProjectId } from "@/contracts";
5 
6export type ProjectIndexRow = typeof projectIndex.$inferSelect;
7export type ProjectRow = ProjectIndexRow;
8export type NewProjectIndexRow = typeof projectIndex.$inferInsert;
9export type NewProjectRow = NewProjectIndexRow;
10 
11export interface ProjectIndexReplicaValues {
12 name: string;
13 repoUrl: string;
14 defaultBranch: string;
15 configPath: string;
16 updatedAt: number;
17}
18 
19export const listProjectsByOwnerUserId = async (db: D1DbExecutor, ownerUserId: string): Promise<ProjectIndexRow[]> =>
20 db.select().from(projectIndex).where(eq(projectIndex.ownerUserId, ownerUserId)).orderBy(desc(projectIndex.updatedAt));
21 
22export const findProjectById = async (db: D1DbExecutor, projectId: ProjectId): Promise<ProjectIndexRow | undefined> => {
23 const rows = await db.select().from(projectIndex).where(eq(projectIndex.id, projectId)).limit(1);
24 return rows[0];
25};
26 
27export const findOwnedProjectById = async (
28 db: D1DbExecutor,
29 ownerUserId: string,
30 projectId: ProjectId,
31): Promise<ProjectIndexRow | undefined> => {
32 const rows = await db
33 .select()
34 .from(projectIndex)
35 .where(and(eq(projectIndex.ownerUserId, ownerUserId), eq(projectIndex.id, projectId)))
36 .limit(1);
37 
38 return rows[0];
39};
40 
41export const findProjectBySlugs = async (
42 db: D1DbExecutor,
43 ownerSlug: string,
44 projectSlug: string,
45): Promise<ProjectIndexRow | undefined> => {
46 const rows = await db
47 .select()
48 .from(projectIndex)
49 .where(and(eq(projectIndex.ownerSlug, ownerSlug), eq(projectIndex.projectSlug, projectSlug)))
50 .limit(1);
51 
52 return rows[0];
53};
54 
55export const insertProjectIndex = async (db: D1DbExecutor, row: NewProjectIndexRow): Promise<void> => {
56 await db.insert(projectIndex).values(row);
57};
58 
59export const insertProject = insertProjectIndex;
60 
61export const updateProjectIndexReplicaById = async (
62 db: D1DbExecutor,
63 projectId: string,
64 values: ProjectIndexReplicaValues,
65): Promise<boolean> => {
66 const rows = await db
67 .update(projectIndex)
68 .set(values)
69 .where(eq(projectIndex.id, projectId))
70 .returning({ id: projectIndex.id });
71 
72 return rows.length === 1;
73};
74 
75export const deleteProjectIndexById = async (db: D1DbExecutor, projectId: ProjectId): Promise<boolean> => {
76 const rows = await db.delete(projectIndex).where(eq(projectIndex.id, projectId)).returning({ id: projectIndex.id });
77 
78 return rows.length === 1;
79};
80 
81export const listLatestRunStatusByProjectIds = async (
82 db: D1DbExecutor,
83 projectIds: string[],
84): Promise<Map<string, string>> => {
85 if (projectIds.length === 0) {
86 return new Map();
87 }
88 
89 const rows = await db
90 .select({
91 projectId: runIndex.projectId,
92 status: runIndex.status,
93 })
94 .from(runIndex)
95 .where(inArray(runIndex.projectId, projectIds))
96 .orderBy(desc(runIndex.queuedAt), desc(runIndex.startedAt));
97 
98 const latestByProjectId = new Map<string, string>();
99 
100 for (const row of rows) {
101 if (!latestByProjectId.has(row.projectId)) {
102 latestByProjectId.set(row.projectId, row.status);
103 }
104 }
105 
106 return latestByProjectId;
107};