Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
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.tsBlame640 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(),
3ef4c9dClaude45 isArchived: boolean("is_archived").default(false).notNull(),
fc1817aClaude46 defaultBranch: text("default_branch").default("main").notNull(),
47 diskPath: text("disk_path").notNull(),
c81ab7aClaude48 forkedFromId: uuid("forked_from_id").references(() => repositories.id, {
49 onDelete: "set null",
50 }),
fc1817aClaude51 createdAt: timestamp("created_at").defaultNow().notNull(),
52 updatedAt: timestamp("updated_at").defaultNow().notNull(),
53 pushedAt: timestamp("pushed_at"),
54 starCount: integer("star_count").default(0).notNull(),
55 forkCount: integer("fork_count").default(0).notNull(),
79136bbClaude56 issueCount: integer("issue_count").default(0).notNull(),
fc1817aClaude57 },
58 (table) => [uniqueIndex("repos_owner_name").on(table.ownerId, table.name)]
59);
60
3ef4c9dClaude61/**
62 * Per-repository gate + auto-repair configuration.
63 * Every new repo is created with all gates ENABLED by default —
64 * the "full green ecosystem" default. Owners can manually opt-out per setting.
65 */
66export const repoSettings = pgTable("repo_settings", {
67 id: uuid("id").primaryKey().defaultRandom(),
68 repositoryId: uuid("repository_id")
69 .notNull()
70 .unique()
71 .references(() => repositories.id, { onDelete: "cascade" }),
72 // Gates
73 gateTestEnabled: boolean("gate_test_enabled").default(true).notNull(),
74 aiReviewEnabled: boolean("ai_review_enabled").default(true).notNull(),
75 secretScanEnabled: boolean("secret_scan_enabled").default(true).notNull(),
76 securityScanEnabled: boolean("security_scan_enabled").default(true).notNull(),
77 dependencyScanEnabled: boolean("dependency_scan_enabled").default(true).notNull(),
78 lintEnabled: boolean("lint_enabled").default(true).notNull(),
79 typeCheckEnabled: boolean("type_check_enabled").default(true).notNull(),
80 testEnabled: boolean("test_enabled").default(true).notNull(),
81 // Auto-repair
82 autoFixEnabled: boolean("auto_fix_enabled").default(true).notNull(),
83 autoMergeResolveEnabled: boolean("auto_merge_resolve_enabled").default(true).notNull(),
84 autoFormatEnabled: boolean("auto_format_enabled").default(true).notNull(),
85 // AI features
86 aiCommitMessagesEnabled: boolean("ai_commit_messages_enabled").default(true).notNull(),
87 aiPrSummaryEnabled: boolean("ai_pr_summary_enabled").default(true).notNull(),
88 aiChangelogEnabled: boolean("ai_changelog_enabled").default(true).notNull(),
89 // Deploy
90 autoDeployEnabled: boolean("auto_deploy_enabled").default(true).notNull(),
91 deployRequireAllGreen: boolean("deploy_require_all_green").default(true).notNull(),
92 createdAt: timestamp("created_at").defaultNow().notNull(),
93 updatedAt: timestamp("updated_at").defaultNow().notNull(),
94});
95
96/**
97 * Branch protection rules — enforced on push and merge.
98 * Every repo's default branch gets a protection rule on creation.
99 */
100export const branchProtection = pgTable(
101 "branch_protection",
102 {
103 id: uuid("id").primaryKey().defaultRandom(),
104 repositoryId: uuid("repository_id")
105 .notNull()
106 .references(() => repositories.id, { onDelete: "cascade" }),
107 pattern: text("pattern").notNull(), // branch name or glob (e.g. "main", "release/*")
108 requirePullRequest: boolean("require_pull_request").default(true).notNull(),
109 requireGreenGates: boolean("require_green_gates").default(true).notNull(),
110 requireAiApproval: boolean("require_ai_approval").default(true).notNull(),
111 requireHumanReview: boolean("require_human_review").default(false).notNull(),
112 requiredApprovals: integer("required_approvals").default(0).notNull(),
113 allowForcePush: boolean("allow_force_push").default(false).notNull(),
114 allowDeletion: boolean("allow_deletion").default(false).notNull(),
115 dismissStaleReviews: boolean("dismiss_stale_reviews").default(true).notNull(),
116 createdAt: timestamp("created_at").defaultNow().notNull(),
117 updatedAt: timestamp("updated_at").defaultNow().notNull(),
118 },
119 (table) => [
120 uniqueIndex("branch_protection_repo_pattern").on(
121 table.repositoryId,
122 table.pattern
123 ),
124 ]
125);
126
127/**
128 * Gate run history. Every push + every PR creates gate_runs entries —
129 * one per configured gate. Serves as the source of truth for "is this green?".
130 */
131export const gateRuns = pgTable(
132 "gate_runs",
133 {
134 id: uuid("id").primaryKey().defaultRandom(),
135 repositoryId: uuid("repository_id")
136 .notNull()
137 .references(() => repositories.id, { onDelete: "cascade" }),
138 pullRequestId: uuid("pull_request_id").references(() => pullRequests.id, {
139 onDelete: "cascade",
140 }),
141 commitSha: text("commit_sha").notNull(),
142 ref: text("ref").notNull(),
143 gateName: text("gate_name").notNull(), // e.g. "GateTest", "AI Review", "Secret Scan", "Type Check"
144 status: text("status").notNull(), // pending, running, passed, failed, skipped, repaired
145 summary: text("summary"),
146 details: text("details"), // JSON: per-check output, affected files, etc
147 repairAttempted: boolean("repair_attempted").default(false).notNull(),
148 repairSucceeded: boolean("repair_succeeded").default(false).notNull(),
149 repairCommitSha: text("repair_commit_sha"),
150 durationMs: integer("duration_ms"),
151 createdAt: timestamp("created_at").defaultNow().notNull(),
152 completedAt: timestamp("completed_at"),
153 },
154 (table) => [
155 index("gate_runs_repo_sha").on(table.repositoryId, table.commitSha),
156 index("gate_runs_pr").on(table.pullRequestId),
157 index("gate_runs_created").on(table.createdAt),
158 ]
159);
160
161/**
162 * In-app notifications. Powered by the activity feed + explicit mentions.
163 */
164export const notifications = pgTable(
165 "notifications",
166 {
167 id: uuid("id").primaryKey().defaultRandom(),
168 userId: uuid("user_id")
169 .notNull()
170 .references(() => users.id, { onDelete: "cascade" }),
171 repositoryId: uuid("repository_id").references(() => repositories.id, {
172 onDelete: "cascade",
173 }),
174 kind: text("kind").notNull(), // mention, review_requested, pr_merged, pr_closed, gate_failed, gate_repaired, ai_review, assigned, security_alert, deploy_success, deploy_failed
175 title: text("title").notNull(),
176 body: text("body"),
177 url: text("url"), // link to the relevant page
178 readAt: timestamp("read_at"),
179 createdAt: timestamp("created_at").defaultNow().notNull(),
180 },
181 (table) => [
182 index("notifications_user_unread").on(table.userId, table.readAt),
183 index("notifications_user_created").on(table.userId, table.createdAt),
184 ]
185);
186
187/**
188 * Releases — named snapshots of a repo at a tag/commit.
189 * AI-generated changelogs bundled in notes field.
190 */
191export const releases = pgTable(
192 "releases",
193 {
194 id: uuid("id").primaryKey().defaultRandom(),
195 repositoryId: uuid("repository_id")
196 .notNull()
197 .references(() => repositories.id, { onDelete: "cascade" }),
198 authorId: uuid("author_id")
199 .notNull()
200 .references(() => users.id),
201 tag: text("tag").notNull(),
202 name: text("name").notNull(),
203 body: text("body"), // AI-generated release notes + changelog
204 targetCommit: text("target_commit").notNull(),
205 isDraft: boolean("is_draft").default(false).notNull(),
206 isPrerelease: boolean("is_prerelease").default(false).notNull(),
207 createdAt: timestamp("created_at").defaultNow().notNull(),
208 publishedAt: timestamp("published_at"),
209 },
210 (table) => [
211 uniqueIndex("releases_repo_tag").on(table.repositoryId, table.tag),
212 ]
213);
214
215/**
216 * Milestones — group issues + PRs toward a shared goal.
217 */
218export const milestones = pgTable(
219 "milestones",
220 {
221 id: uuid("id").primaryKey().defaultRandom(),
222 repositoryId: uuid("repository_id")
223 .notNull()
224 .references(() => repositories.id, { onDelete: "cascade" }),
225 title: text("title").notNull(),
226 description: text("description"),
227 state: text("state").notNull().default("open"), // open, closed
228 dueDate: timestamp("due_date"),
229 createdAt: timestamp("created_at").defaultNow().notNull(),
230 closedAt: timestamp("closed_at"),
231 },
232 (table) => [index("milestones_repo_state").on(table.repositoryId, table.state)]
233);
234
235/**
236 * Reactions on issues, PRs, and comments. Universal target pointer.
237 */
238export const reactions = pgTable(
239 "reactions",
240 {
241 id: uuid("id").primaryKey().defaultRandom(),
242 userId: uuid("user_id")
243 .notNull()
244 .references(() => users.id, { onDelete: "cascade" }),
245 targetType: text("target_type").notNull(), // issue, pr, issue_comment, pr_comment
246 targetId: uuid("target_id").notNull(),
247 emoji: text("emoji").notNull(), // thumbs_up, thumbs_down, rocket, heart, eyes, laugh, hooray, confused
248 createdAt: timestamp("created_at").defaultNow().notNull(),
249 },
250 (table) => [
251 uniqueIndex("reactions_unique").on(
252 table.userId,
253 table.targetType,
254 table.targetId,
255 table.emoji
256 ),
257 index("reactions_target").on(table.targetType, table.targetId),
258 ]
259);
260
261/**
262 * PR reviews (formal approve/request-changes).
263 * Separate from inline comments in pr_comments.
264 */
265export const prReviews = pgTable(
266 "pr_reviews",
267 {
268 id: uuid("id").primaryKey().defaultRandom(),
269 pullRequestId: uuid("pull_request_id")
270 .notNull()
271 .references(() => pullRequests.id, { onDelete: "cascade" }),
272 reviewerId: uuid("reviewer_id")
273 .notNull()
274 .references(() => users.id),
275 state: text("state").notNull(), // approved, changes_requested, commented
276 body: text("body"),
277 isAi: boolean("is_ai").default(false).notNull(),
278 createdAt: timestamp("created_at").defaultNow().notNull(),
279 },
280 (table) => [index("pr_reviews_pr").on(table.pullRequestId)]
281);
282
283/**
284 * Code owners — who owns which paths (auto-request review on PR).
285 * Parsed from a CODEOWNERS file at the root of the default branch.
286 */
287export const codeOwners = pgTable(
288 "code_owners",
289 {
290 id: uuid("id").primaryKey().defaultRandom(),
291 repositoryId: uuid("repository_id")
292 .notNull()
293 .references(() => repositories.id, { onDelete: "cascade" }),
294 pathPattern: text("path_pattern").notNull(),
295 ownerUsernames: text("owner_usernames").notNull(), // comma-separated
296 createdAt: timestamp("created_at").defaultNow().notNull(),
297 },
298 (table) => [index("code_owners_repo").on(table.repositoryId)]
299);
300
301/**
302 * Per-repo AI chat sessions — conversational repo assistant.
303 */
304export const aiChats = pgTable(
305 "ai_chats",
306 {
307 id: uuid("id").primaryKey().defaultRandom(),
308 userId: uuid("user_id")
309 .notNull()
310 .references(() => users.id, { onDelete: "cascade" }),
311 repositoryId: uuid("repository_id").references(() => repositories.id, {
312 onDelete: "cascade",
313 }),
314 title: text("title"),
315 messages: text("messages").notNull().default("[]"), // JSON array of {role, content}
316 createdAt: timestamp("created_at").defaultNow().notNull(),
317 updatedAt: timestamp("updated_at").defaultNow().notNull(),
318 },
319 (table) => [
320 index("ai_chats_user").on(table.userId),
321 index("ai_chats_repo").on(table.repositoryId),
322 ]
323);
324
325/**
326 * Audit log — every sensitive action. Who did what, when, from where.
327 */
328export const auditLog = pgTable(
329 "audit_log",
330 {
331 id: uuid("id").primaryKey().defaultRandom(),
332 userId: uuid("user_id").references(() => users.id, { onDelete: "set null" }),
333 repositoryId: uuid("repository_id").references(() => repositories.id, {
334 onDelete: "set null",
335 }),
336 action: text("action").notNull(), // repo.create, repo.delete, repo.transfer, token.create, token.revoke, merge, force_push, branch_protection.update, deploy, ...
337 targetType: text("target_type"),
338 targetId: text("target_id"),
339 ip: text("ip"),
340 userAgent: text("user_agent"),
341 metadata: text("metadata"), // JSON for extra context
342 createdAt: timestamp("created_at").defaultNow().notNull(),
343 },
344 (table) => [
345 index("audit_log_user").on(table.userId),
346 index("audit_log_repo").on(table.repositoryId),
347 index("audit_log_created").on(table.createdAt),
348 ]
349);
350
351/**
352 * Deployments — tracks every deploy to downstream systems (Crontech, etc).
353 * Each deploy is gated on ALL green gates passing.
354 */
355export const deployments = pgTable(
356 "deployments",
357 {
358 id: uuid("id").primaryKey().defaultRandom(),
359 repositoryId: uuid("repository_id")
360 .notNull()
361 .references(() => repositories.id, { onDelete: "cascade" }),
362 environment: text("environment").notNull().default("production"),
363 commitSha: text("commit_sha").notNull(),
364 ref: text("ref").notNull(),
365 status: text("status").notNull(), // pending, running, success, failed, blocked
366 blockedReason: text("blocked_reason"),
367 target: text("target"), // e.g. "crontech", "fly.io"
368 triggeredBy: uuid("triggered_by").references(() => users.id),
369 createdAt: timestamp("created_at").defaultNow().notNull(),
370 completedAt: timestamp("completed_at"),
371 },
372 (table) => [
373 index("deployments_repo").on(table.repositoryId),
374 index("deployments_created").on(table.createdAt),
375 ]
376);
377
378/**
379 * Rate-limit buckets — in-memory or persisted counter per IP / token / route.
380 */
381export const rateLimitBuckets = pgTable(
382 "rate_limit_buckets",
383 {
384 id: uuid("id").primaryKey().defaultRandom(),
385 bucketKey: text("bucket_key").notNull().unique(), // "ip:1.2.3.4:api" or "token:abc:api"
386 count: integer("count").default(0).notNull(),
387 windowStart: timestamp("window_start").defaultNow().notNull(),
388 expiresAt: timestamp("expires_at").notNull(),
389 },
390 (table) => [index("rate_limit_expires").on(table.expiresAt)]
391);
392
06d5ffeClaude393export const stars = pgTable(
394 "stars",
395 {
396 id: uuid("id").primaryKey().defaultRandom(),
397 userId: uuid("user_id")
398 .notNull()
399 .references(() => users.id, { onDelete: "cascade" }),
400 repositoryId: uuid("repository_id")
401 .notNull()
402 .references(() => repositories.id, { onDelete: "cascade" }),
403 createdAt: timestamp("created_at").defaultNow().notNull(),
404 },
79136bbClaude405 (table) => [
406 uniqueIndex("stars_user_repo").on(table.userId, table.repositoryId),
407 ]
408);
409
410export const issues = pgTable(
411 "issues",
412 {
413 id: uuid("id").primaryKey().defaultRandom(),
414 number: serial("number"),
415 repositoryId: uuid("repository_id")
416 .notNull()
417 .references(() => repositories.id, { onDelete: "cascade" }),
418 authorId: uuid("author_id")
419 .notNull()
420 .references(() => users.id),
421 title: text("title").notNull(),
422 body: text("body"),
423 state: text("state").notNull().default("open"), // open, closed
424 createdAt: timestamp("created_at").defaultNow().notNull(),
425 updatedAt: timestamp("updated_at").defaultNow().notNull(),
426 closedAt: timestamp("closed_at"),
427 },
428 (table) => [
429 index("issues_repo_state").on(table.repositoryId, table.state),
430 index("issues_repo_number").on(table.repositoryId, table.number),
431 ]
432);
433
434export const issueComments = pgTable(
435 "issue_comments",
436 {
437 id: uuid("id").primaryKey().defaultRandom(),
438 issueId: uuid("issue_id")
439 .notNull()
440 .references(() => issues.id, { onDelete: "cascade" }),
441 authorId: uuid("author_id")
442 .notNull()
443 .references(() => users.id),
444 body: text("body").notNull(),
445 createdAt: timestamp("created_at").defaultNow().notNull(),
446 updatedAt: timestamp("updated_at").defaultNow().notNull(),
447 },
448 (table) => [index("comments_issue").on(table.issueId)]
449);
450
451export const labels = pgTable(
452 "labels",
453 {
454 id: uuid("id").primaryKey().defaultRandom(),
455 repositoryId: uuid("repository_id")
456 .notNull()
457 .references(() => repositories.id, { onDelete: "cascade" }),
458 name: text("name").notNull(),
459 color: text("color").notNull().default("#8b949e"),
460 description: text("description"),
461 },
462 (table) => [
463 uniqueIndex("labels_repo_name").on(table.repositoryId, table.name),
464 ]
465);
466
467export const issueLabels = pgTable(
468 "issue_labels",
469 {
470 id: uuid("id").primaryKey().defaultRandom(),
471 issueId: uuid("issue_id")
472 .notNull()
473 .references(() => issues.id, { onDelete: "cascade" }),
474 labelId: uuid("label_id")
475 .notNull()
476 .references(() => labels.id, { onDelete: "cascade" }),
477 },
478 (table) => [
479 uniqueIndex("issue_labels_unique").on(table.issueId, table.labelId),
480 ]
06d5ffeClaude481);
482
0074234Claude483export const pullRequests = pgTable(
484 "pull_requests",
485 {
486 id: uuid("id").primaryKey().defaultRandom(),
487 number: serial("number"),
488 repositoryId: uuid("repository_id")
489 .notNull()
490 .references(() => repositories.id, { onDelete: "cascade" }),
491 authorId: uuid("author_id")
492 .notNull()
493 .references(() => users.id),
494 title: text("title").notNull(),
495 body: text("body"),
496 state: text("state").notNull().default("open"), // open, closed, merged
497 baseBranch: text("base_branch").notNull(),
498 headBranch: text("head_branch").notNull(),
3ef4c9dClaude499 isDraft: boolean("is_draft").default(false).notNull(),
500 mergeStrategy: text("merge_strategy").default("merge").notNull(), // merge, squash, rebase
501 milestoneId: uuid("milestone_id"),
0074234Claude502 mergedAt: timestamp("merged_at"),
503 mergedBy: uuid("merged_by").references(() => users.id),
504 createdAt: timestamp("created_at").defaultNow().notNull(),
505 updatedAt: timestamp("updated_at").defaultNow().notNull(),
506 closedAt: timestamp("closed_at"),
507 },
508 (table) => [
509 index("prs_repo_state").on(table.repositoryId, table.state),
510 index("prs_repo_number").on(table.repositoryId, table.number),
511 ]
512);
513
514export const prComments = pgTable(
515 "pr_comments",
516 {
517 id: uuid("id").primaryKey().defaultRandom(),
518 pullRequestId: uuid("pull_request_id")
519 .notNull()
520 .references(() => pullRequests.id, { onDelete: "cascade" }),
521 authorId: uuid("author_id")
522 .notNull()
523 .references(() => users.id),
524 body: text("body").notNull(),
525 isAiReview: boolean("is_ai_review").default(false).notNull(),
526 filePath: text("file_path"),
527 lineNumber: integer("line_number"),
528 createdAt: timestamp("created_at").defaultNow().notNull(),
529 updatedAt: timestamp("updated_at").defaultNow().notNull(),
530 },
531 (table) => [index("pr_comments_pr").on(table.pullRequestId)]
532);
533
534export const activityFeed = pgTable(
535 "activity_feed",
536 {
537 id: uuid("id").primaryKey().defaultRandom(),
538 repositoryId: uuid("repository_id")
539 .notNull()
540 .references(() => repositories.id, { onDelete: "cascade" }),
541 userId: uuid("user_id").references(() => users.id),
542 action: text("action").notNull(), // push, issue_open, issue_close, pr_open, pr_merge, star, comment
543 targetType: text("target_type"), // issue, pr, commit
544 targetId: text("target_id"),
545 metadata: text("metadata"), // JSON string for extra data
546 createdAt: timestamp("created_at").defaultNow().notNull(),
547 },
548 (table) => [
549 index("activity_repo").on(table.repositoryId),
550 index("activity_user").on(table.userId),
551 ]
552);
553
c81ab7aClaude554export const webhooks = pgTable(
555 "webhooks",
556 {
557 id: uuid("id").primaryKey().defaultRandom(),
558 repositoryId: uuid("repository_id")
559 .notNull()
560 .references(() => repositories.id, { onDelete: "cascade" }),
561 url: text("url").notNull(),
562 secret: text("secret"),
563 events: text("events").notNull().default("push"), // comma-separated: push,issue,pr
564 isActive: boolean("is_active").default(true).notNull(),
565 lastDeliveredAt: timestamp("last_delivered_at"),
566 lastStatus: integer("last_status"),
567 createdAt: timestamp("created_at").defaultNow().notNull(),
568 },
569 (table) => [index("webhooks_repo").on(table.repositoryId)]
570);
571
572export const apiTokens = pgTable("api_tokens", {
573 id: uuid("id").primaryKey().defaultRandom(),
574 userId: uuid("user_id")
575 .notNull()
576 .references(() => users.id, { onDelete: "cascade" }),
577 name: text("name").notNull(),
578 tokenHash: text("token_hash").notNull(),
579 tokenPrefix: text("token_prefix").notNull(), // first 8 chars for display
580 scopes: text("scopes").notNull().default("repo"), // comma-separated
581 lastUsedAt: timestamp("last_used_at"),
582 expiresAt: timestamp("expires_at"),
583 createdAt: timestamp("created_at").defaultNow().notNull(),
584});
585
586export const repoTopics = pgTable(
587 "repo_topics",
588 {
589 id: uuid("id").primaryKey().defaultRandom(),
590 repositoryId: uuid("repository_id")
591 .notNull()
592 .references(() => repositories.id, { onDelete: "cascade" }),
593 topic: text("topic").notNull(),
594 },
595 (table) => [
596 uniqueIndex("repo_topics_unique").on(table.repositoryId, table.topic),
597 index("topics_name").on(table.topic),
598 ]
599);
600
fc1817aClaude601export const sshKeys = pgTable("ssh_keys", {
602 id: uuid("id").primaryKey().defaultRandom(),
603 userId: uuid("user_id")
604 .notNull()
06d5ffeClaude605 .references(() => users.id, { onDelete: "cascade" }),
fc1817aClaude606 title: text("title").notNull(),
607 fingerprint: text("fingerprint").notNull(),
608 publicKey: text("public_key").notNull(),
609 lastUsedAt: timestamp("last_used_at"),
610 createdAt: timestamp("created_at").defaultNow().notNull(),
611});
612
613export type User = typeof users.$inferSelect;
614export type NewUser = typeof users.$inferInsert;
615export type Repository = typeof repositories.$inferSelect;
616export type NewRepository = typeof repositories.$inferInsert;
06d5ffeClaude617export type Session = typeof sessions.$inferSelect;
618export type Star = typeof stars.$inferSelect;
619export type SshKey = typeof sshKeys.$inferSelect;
79136bbClaude620export type Issue = typeof issues.$inferSelect;
621export type IssueComment = typeof issueComments.$inferSelect;
622export type Label = typeof labels.$inferSelect;
0074234Claude623export type PullRequest = typeof pullRequests.$inferSelect;
624export type PrComment = typeof prComments.$inferSelect;
625export type ActivityEntry = typeof activityFeed.$inferSelect;
c81ab7aClaude626export type Webhook = typeof webhooks.$inferSelect;
627export type ApiToken = typeof apiTokens.$inferSelect;
628export type RepoTopic = typeof repoTopics.$inferSelect;
3ef4c9dClaude629export type RepoSettings = typeof repoSettings.$inferSelect;
630export type BranchProtection = typeof branchProtection.$inferSelect;
631export type GateRun = typeof gateRuns.$inferSelect;
632export type Notification = typeof notifications.$inferSelect;
633export type Release = typeof releases.$inferSelect;
634export type Milestone = typeof milestones.$inferSelect;
635export type Reaction = typeof reactions.$inferSelect;
636export type PrReview = typeof prReviews.$inferSelect;
637export type CodeOwner = typeof codeOwners.$inferSelect;
638export type AiChat = typeof aiChats.$inferSelect;
639export type AuditLogEntry = typeof auditLog.$inferSelect;
640export type Deployment = typeof deployments.$inferSelect;