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

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

schema.tsBlame239 lines · 1 contributor
fc1817aClaude1import {
2 pgTable,
3 text,
4 timestamp,
5 uuid,
6 boolean,
7 integer,
8 uniqueIndex,
79136bbClaude9 index,
10 serial,
fc1817aClaude11} from "drizzle-orm/pg-core";
12
13export const users = pgTable("users", {
14 id: uuid("id").primaryKey().defaultRandom(),
15 username: text("username").notNull().unique(),
16 email: text("email").notNull().unique(),
17 displayName: text("display_name"),
18 passwordHash: text("password_hash").notNull(),
19 avatarUrl: text("avatar_url"),
20 bio: text("bio"),
21 createdAt: timestamp("created_at").defaultNow().notNull(),
22 updatedAt: timestamp("updated_at").defaultNow().notNull(),
23});
24
06d5ffeClaude25export const sessions = pgTable("sessions", {
26 id: uuid("id").primaryKey().defaultRandom(),
27 userId: uuid("user_id")
28 .notNull()
29 .references(() => users.id, { onDelete: "cascade" }),
30 token: text("token").notNull().unique(),
31 expiresAt: timestamp("expires_at").notNull(),
32 createdAt: timestamp("created_at").defaultNow().notNull(),
33});
34
fc1817aClaude35export const repositories = pgTable(
36 "repositories",
37 {
38 id: uuid("id").primaryKey().defaultRandom(),
39 name: text("name").notNull(),
40 ownerId: uuid("owner_id")
41 .notNull()
42 .references(() => users.id),
43 description: text("description"),
44 isPrivate: boolean("is_private").default(false).notNull(),
45 defaultBranch: text("default_branch").default("main").notNull(),
46 diskPath: text("disk_path").notNull(),
47 createdAt: timestamp("created_at").defaultNow().notNull(),
48 updatedAt: timestamp("updated_at").defaultNow().notNull(),
49 pushedAt: timestamp("pushed_at"),
50 starCount: integer("star_count").default(0).notNull(),
51 forkCount: integer("fork_count").default(0).notNull(),
79136bbClaude52 issueCount: integer("issue_count").default(0).notNull(),
fc1817aClaude53 },
54 (table) => [uniqueIndex("repos_owner_name").on(table.ownerId, table.name)]
55);
56
06d5ffeClaude57export const stars = pgTable(
58 "stars",
59 {
60 id: uuid("id").primaryKey().defaultRandom(),
61 userId: uuid("user_id")
62 .notNull()
63 .references(() => users.id, { onDelete: "cascade" }),
64 repositoryId: uuid("repository_id")
65 .notNull()
66 .references(() => repositories.id, { onDelete: "cascade" }),
67 createdAt: timestamp("created_at").defaultNow().notNull(),
68 },
79136bbClaude69 (table) => [
70 uniqueIndex("stars_user_repo").on(table.userId, table.repositoryId),
71 ]
72);
73
74export const issues = pgTable(
75 "issues",
76 {
77 id: uuid("id").primaryKey().defaultRandom(),
78 number: serial("number"),
79 repositoryId: uuid("repository_id")
80 .notNull()
81 .references(() => repositories.id, { onDelete: "cascade" }),
82 authorId: uuid("author_id")
83 .notNull()
84 .references(() => users.id),
85 title: text("title").notNull(),
86 body: text("body"),
87 state: text("state").notNull().default("open"), // open, closed
88 createdAt: timestamp("created_at").defaultNow().notNull(),
89 updatedAt: timestamp("updated_at").defaultNow().notNull(),
90 closedAt: timestamp("closed_at"),
91 },
92 (table) => [
93 index("issues_repo_state").on(table.repositoryId, table.state),
94 index("issues_repo_number").on(table.repositoryId, table.number),
95 ]
96);
97
98export const issueComments = pgTable(
99 "issue_comments",
100 {
101 id: uuid("id").primaryKey().defaultRandom(),
102 issueId: uuid("issue_id")
103 .notNull()
104 .references(() => issues.id, { onDelete: "cascade" }),
105 authorId: uuid("author_id")
106 .notNull()
107 .references(() => users.id),
108 body: text("body").notNull(),
109 createdAt: timestamp("created_at").defaultNow().notNull(),
110 updatedAt: timestamp("updated_at").defaultNow().notNull(),
111 },
112 (table) => [index("comments_issue").on(table.issueId)]
113);
114
115export const labels = pgTable(
116 "labels",
117 {
118 id: uuid("id").primaryKey().defaultRandom(),
119 repositoryId: uuid("repository_id")
120 .notNull()
121 .references(() => repositories.id, { onDelete: "cascade" }),
122 name: text("name").notNull(),
123 color: text("color").notNull().default("#8b949e"),
124 description: text("description"),
125 },
126 (table) => [
127 uniqueIndex("labels_repo_name").on(table.repositoryId, table.name),
128 ]
129);
130
131export const issueLabels = pgTable(
132 "issue_labels",
133 {
134 id: uuid("id").primaryKey().defaultRandom(),
135 issueId: uuid("issue_id")
136 .notNull()
137 .references(() => issues.id, { onDelete: "cascade" }),
138 labelId: uuid("label_id")
139 .notNull()
140 .references(() => labels.id, { onDelete: "cascade" }),
141 },
142 (table) => [
143 uniqueIndex("issue_labels_unique").on(table.issueId, table.labelId),
144 ]
06d5ffeClaude145);
146
0074234Claude147export const pullRequests = pgTable(
148 "pull_requests",
149 {
150 id: uuid("id").primaryKey().defaultRandom(),
151 number: serial("number"),
152 repositoryId: uuid("repository_id")
153 .notNull()
154 .references(() => repositories.id, { onDelete: "cascade" }),
155 authorId: uuid("author_id")
156 .notNull()
157 .references(() => users.id),
158 title: text("title").notNull(),
159 body: text("body"),
160 state: text("state").notNull().default("open"), // open, closed, merged
161 baseBranch: text("base_branch").notNull(),
162 headBranch: text("head_branch").notNull(),
163 mergedAt: timestamp("merged_at"),
164 mergedBy: uuid("merged_by").references(() => users.id),
165 createdAt: timestamp("created_at").defaultNow().notNull(),
166 updatedAt: timestamp("updated_at").defaultNow().notNull(),
167 closedAt: timestamp("closed_at"),
168 },
169 (table) => [
170 index("prs_repo_state").on(table.repositoryId, table.state),
171 index("prs_repo_number").on(table.repositoryId, table.number),
172 ]
173);
174
175export const prComments = pgTable(
176 "pr_comments",
177 {
178 id: uuid("id").primaryKey().defaultRandom(),
179 pullRequestId: uuid("pull_request_id")
180 .notNull()
181 .references(() => pullRequests.id, { onDelete: "cascade" }),
182 authorId: uuid("author_id")
183 .notNull()
184 .references(() => users.id),
185 body: text("body").notNull(),
186 isAiReview: boolean("is_ai_review").default(false).notNull(),
187 filePath: text("file_path"),
188 lineNumber: integer("line_number"),
189 createdAt: timestamp("created_at").defaultNow().notNull(),
190 updatedAt: timestamp("updated_at").defaultNow().notNull(),
191 },
192 (table) => [index("pr_comments_pr").on(table.pullRequestId)]
193);
194
195export const activityFeed = pgTable(
196 "activity_feed",
197 {
198 id: uuid("id").primaryKey().defaultRandom(),
199 repositoryId: uuid("repository_id")
200 .notNull()
201 .references(() => repositories.id, { onDelete: "cascade" }),
202 userId: uuid("user_id").references(() => users.id),
203 action: text("action").notNull(), // push, issue_open, issue_close, pr_open, pr_merge, star, comment
204 targetType: text("target_type"), // issue, pr, commit
205 targetId: text("target_id"),
206 metadata: text("metadata"), // JSON string for extra data
207 createdAt: timestamp("created_at").defaultNow().notNull(),
208 },
209 (table) => [
210 index("activity_repo").on(table.repositoryId),
211 index("activity_user").on(table.userId),
212 ]
213);
214
fc1817aClaude215export const sshKeys = pgTable("ssh_keys", {
216 id: uuid("id").primaryKey().defaultRandom(),
217 userId: uuid("user_id")
218 .notNull()
06d5ffeClaude219 .references(() => users.id, { onDelete: "cascade" }),
fc1817aClaude220 title: text("title").notNull(),
221 fingerprint: text("fingerprint").notNull(),
222 publicKey: text("public_key").notNull(),
223 lastUsedAt: timestamp("last_used_at"),
224 createdAt: timestamp("created_at").defaultNow().notNull(),
225});
226
227export type User = typeof users.$inferSelect;
228export type NewUser = typeof users.$inferInsert;
229export type Repository = typeof repositories.$inferSelect;
230export type NewRepository = typeof repositories.$inferInsert;
06d5ffeClaude231export type Session = typeof sessions.$inferSelect;
232export type Star = typeof stars.$inferSelect;
233export type SshKey = typeof sshKeys.$inferSelect;
79136bbClaude234export type Issue = typeof issues.$inferSelect;
235export type IssueComment = typeof issueComments.$inferSelect;
236export type Label = typeof labels.$inferSelect;
0074234Claude237export type PullRequest = typeof pullRequests.$inferSelect;
238export type PrComment = typeof prComments.$inferSelect;
239export type ActivityEntry = typeof activityFeed.$inferSelect;