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.tsBlame168 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
fc1817aClaude147export const sshKeys = pgTable("ssh_keys", {
148 id: uuid("id").primaryKey().defaultRandom(),
149 userId: uuid("user_id")
150 .notNull()
06d5ffeClaude151 .references(() => users.id, { onDelete: "cascade" }),
fc1817aClaude152 title: text("title").notNull(),
153 fingerprint: text("fingerprint").notNull(),
154 publicKey: text("public_key").notNull(),
155 lastUsedAt: timestamp("last_used_at"),
156 createdAt: timestamp("created_at").defaultNow().notNull(),
157});
158
159export type User = typeof users.$inferSelect;
160export type NewUser = typeof users.$inferInsert;
161export type Repository = typeof repositories.$inferSelect;
162export type NewRepository = typeof repositories.$inferInsert;
06d5ffeClaude163export type Session = typeof sessions.$inferSelect;
164export type Star = typeof stars.$inferSelect;
165export type SshKey = typeof sshKeys.$inferSelect;
79136bbClaude166export type Issue = typeof issues.$inferSelect;
167export type IssueComment = typeof issueComments.$inferSelect;
168export type Label = typeof labels.$inferSelect;