kaneo (minimalist kanban) fork to experiment adding a tangled integration github.com/usekaneo/kaneo
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 39e2dfae265f26c8d6d888a560f50ab2d5d58b3f 938 lines 29 kB view raw
1import { createId } from "@paralleldrive/cuid2"; 2import { relations, sql } from "drizzle-orm"; 3import { 4 boolean, 5 foreignKey, 6 index, 7 integer, 8 jsonb, 9 pgTable, 10 text, 11 timestamp, 12 unique, 13 uniqueIndex, 14} from "drizzle-orm/pg-core"; 15 16export const userTable = pgTable("user", { 17 id: text("id") 18 .$defaultFn(() => createId()) 19 .primaryKey(), 20 name: text("name").notNull(), 21 email: text("email").notNull().unique(), 22 emailVerified: boolean("email_verified") 23 .$defaultFn(() => false) 24 .notNull(), 25 image: text("image"), 26 locale: text("locale"), 27 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 28 updatedAt: timestamp("updated_at", { mode: "date" }) 29 .defaultNow() 30 .$onUpdate(() => /* @__PURE__ */ new Date()) 31 .notNull(), 32 isAnonymous: boolean("is_anonymous").default(false), 33}); 34 35export const sessionTable = pgTable( 36 "session", 37 { 38 id: text("id").primaryKey(), 39 expiresAt: timestamp("expires_at", { mode: "date" }).notNull(), 40 token: text("token").notNull().unique(), 41 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 42 updatedAt: timestamp("updated_at", { mode: "date" }) 43 .$onUpdate(() => /* @__PURE__ */ new Date()) 44 .notNull(), 45 ipAddress: text("ip_address"), 46 userAgent: text("user_agent"), 47 userId: text("user_id") 48 .notNull() 49 .references(() => userTable.id, { onDelete: "cascade" }), 50 activeOrganizationId: text("active_organization_id"), 51 activeTeamId: text("active_team_id"), 52 }, 53 (table) => [index("session_userId_idx").on(table.userId)], 54); 55 56export const accountTable = pgTable( 57 "account", 58 { 59 id: text("id") 60 .$defaultFn(() => createId()) 61 .primaryKey(), 62 accountId: text("account_id").notNull(), 63 providerId: text("provider_id").notNull(), 64 userId: text("user_id") 65 .notNull() 66 .references(() => userTable.id, { onDelete: "cascade" }), 67 accessToken: text("access_token"), 68 refreshToken: text("refresh_token"), 69 idToken: text("id_token"), 70 accessTokenExpiresAt: timestamp("access_token_expires_at", { 71 mode: "date", 72 }), 73 refreshTokenExpiresAt: timestamp("refresh_token_expires_at", { 74 mode: "date", 75 }), 76 scope: text("scope"), 77 password: text("password"), 78 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 79 updatedAt: timestamp("updated_at", { mode: "date" }) 80 .$onUpdate(() => /* @__PURE__ */ new Date()) 81 .notNull(), 82 }, 83 (table) => [index("account_userId_idx").on(table.userId)], 84); 85 86export const verificationTable = pgTable( 87 "verification", 88 { 89 id: text("id") 90 .$defaultFn(() => createId()) 91 .primaryKey(), 92 identifier: text("identifier").notNull(), 93 value: text("value").notNull(), 94 expiresAt: timestamp("expires_at", { mode: "date" }).notNull(), 95 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 96 updatedAt: timestamp("updated_at", { mode: "date" }) 97 .defaultNow() 98 .$onUpdate(() => /* @__PURE__ */ new Date()) 99 .notNull(), 100 }, 101 (table) => [index("verification_identifier_idx").on(table.identifier)], 102); 103 104export const workspaceTable = pgTable("workspace", { 105 id: text("id") 106 .$defaultFn(() => createId()) 107 .primaryKey(), 108 name: text("name").notNull(), 109 slug: text("slug").notNull().unique(), 110 logo: text("logo"), 111 metadata: text("metadata"), 112 description: text("description"), 113 createdAt: timestamp("created_at", { mode: "date" }).notNull(), 114}); 115 116export const workspaceUserTable = pgTable( 117 "workspace_member", 118 { 119 id: text("id") 120 .$defaultFn(() => createId()) 121 .primaryKey(), 122 workspaceId: text("workspace_id") 123 .notNull() 124 .references(() => workspaceTable.id, { 125 onDelete: "cascade", 126 }), 127 userId: text("user_id") 128 .notNull() 129 .references(() => userTable.id, { 130 onDelete: "cascade", 131 }), 132 role: text("role").default("member").notNull(), 133 joinedAt: timestamp("joined_at", { mode: "date" }).notNull(), 134 }, 135 (table) => [ 136 index("workspace_member_workspaceId_idx").on(table.workspaceId), 137 index("workspace_member_userId_idx").on(table.userId), 138 ], 139); 140 141export const teamTable = pgTable( 142 "team", 143 { 144 id: text("id").primaryKey(), 145 name: text("name").notNull(), 146 workspaceId: text("workspace_id") 147 .notNull() 148 .references(() => workspaceTable.id, { onDelete: "cascade" }), 149 createdAt: timestamp("created_at").notNull(), 150 updatedAt: timestamp("updated_at").$onUpdate( 151 () => /* @__PURE__ */ new Date(), 152 ), 153 }, 154 (table) => [index("team_workspaceId_idx").on(table.workspaceId)], 155); 156 157export const teamMemberTable = pgTable( 158 "team_member", 159 { 160 id: text("id").primaryKey(), 161 teamId: text("team_id") 162 .notNull() 163 .references(() => teamTable.id, { onDelete: "cascade" }), 164 userId: text("user_id") 165 .notNull() 166 .references(() => userTable.id, { onDelete: "cascade" }), 167 createdAt: timestamp("created_at"), 168 }, 169 (table) => [ 170 index("teamMember_teamId_idx").on(table.teamId), 171 index("teamMember_userId_idx").on(table.userId), 172 ], 173); 174 175export const invitationTable = pgTable( 176 "invitation", 177 { 178 id: text("id") 179 .$defaultFn(() => createId()) 180 .primaryKey(), 181 workspaceId: text("workspace_id") 182 .notNull() 183 .references(() => workspaceTable.id, { onDelete: "cascade" }), 184 email: text("email").notNull(), 185 role: text("role"), 186 teamId: text("team_id"), 187 status: text("status").default("pending").notNull(), 188 expiresAt: timestamp("expires_at").notNull(), 189 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 190 inviterId: text("inviter_id") 191 .notNull() 192 .references(() => userTable.id, { onDelete: "cascade" }), 193 }, 194 (table) => [ 195 index("invitation_workspaceId_idx").on(table.workspaceId), 196 index("invitation_email_idx").on(table.email), 197 ], 198); 199 200export const projectTable = pgTable( 201 "project", 202 { 203 id: text("id") 204 .$defaultFn(() => createId()) 205 .primaryKey(), 206 workspaceId: text("workspace_id") 207 .notNull() 208 .references(() => workspaceTable.id, { 209 onDelete: "cascade", 210 onUpdate: "cascade", 211 }), 212 slug: text("slug").notNull(), 213 icon: text("icon").default("Layout"), 214 name: text("name").notNull(), 215 description: text("description"), 216 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 217 isPublic: boolean("is_public").default(false), 218 archivedAt: timestamp("archived_at", { mode: "date" }), 219 }, 220 (table) => [ 221 unique("project_workspace_id_id_unique").on(table.workspaceId, table.id), 222 ], 223); 224 225export const columnTable = pgTable( 226 "column", 227 { 228 id: text("id") 229 .$defaultFn(() => createId()) 230 .primaryKey(), 231 projectId: text("project_id") 232 .notNull() 233 .references(() => projectTable.id, { 234 onDelete: "cascade", 235 onUpdate: "cascade", 236 }), 237 name: text("name").notNull(), 238 slug: text("slug").notNull(), 239 position: integer("position").notNull().default(0), 240 icon: text("icon"), 241 color: text("color"), 242 isFinal: boolean("is_final").default(false).notNull(), 243 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 244 updatedAt: timestamp("updated_at", { mode: "date" }) 245 .defaultNow() 246 .$onUpdate(() => new Date()) 247 .notNull(), 248 }, 249 (table) => [index("column_projectId_idx").on(table.projectId)], 250); 251 252export const workflowRuleTable = pgTable( 253 "workflow_rule", 254 { 255 id: text("id") 256 .$defaultFn(() => createId()) 257 .primaryKey(), 258 projectId: text("project_id") 259 .notNull() 260 .references(() => projectTable.id, { 261 onDelete: "cascade", 262 onUpdate: "cascade", 263 }), 264 integrationType: text("integration_type").notNull(), 265 eventType: text("event_type").notNull(), 266 columnId: text("column_id") 267 .notNull() 268 .references(() => columnTable.id, { 269 onDelete: "cascade", 270 onUpdate: "cascade", 271 }), 272 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 273 updatedAt: timestamp("updated_at", { mode: "date" }) 274 .defaultNow() 275 .$onUpdate(() => new Date()) 276 .notNull(), 277 }, 278 (table) => [index("workflow_rule_projectId_idx").on(table.projectId)], 279); 280 281export const taskTable = pgTable( 282 "task", 283 { 284 id: text("id") 285 .$defaultFn(() => createId()) 286 .primaryKey(), 287 projectId: text("project_id") 288 .notNull() 289 .references(() => projectTable.id, { 290 onDelete: "cascade", 291 onUpdate: "cascade", 292 }), 293 position: integer("position").default(0), 294 number: integer("number").default(1), 295 userId: text("assignee_id").references(() => userTable.id, { 296 onDelete: "cascade", 297 onUpdate: "cascade", 298 }), 299 title: text("title").notNull(), 300 description: text("description"), 301 status: text("status").notNull().default("to-do"), 302 columnId: text("column_id").references(() => columnTable.id, { 303 onDelete: "set null", 304 onUpdate: "cascade", 305 }), 306 priority: text("priority").default("low"), 307 startDate: timestamp("start_date", { mode: "date" }), 308 dueDate: timestamp("due_date", { mode: "date" }), 309 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 310 updatedAt: timestamp("updated_at", { mode: "date" }) 311 .defaultNow() 312 .$onUpdate(() => new Date()) 313 .notNull(), 314 }, 315 (table) => [ 316 index("task_projectId_idx").on(table.projectId), 317 index("task_dueDate_idx").on(table.dueDate), 318 unique("task_project_number_unique").on(table.projectId, table.number), 319 ], 320); 321 322export const taskReminderSentTable = pgTable( 323 "task_reminder_sent", 324 { 325 id: text("id") 326 .$defaultFn(() => createId()) 327 .primaryKey(), 328 taskId: text("task_id") 329 .notNull() 330 .references(() => taskTable.id, { 331 onDelete: "cascade", 332 onUpdate: "cascade", 333 }), 334 reminderType: text("reminder_type").notNull(), 335 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 336 updatedAt: timestamp("updated_at", { mode: "date" }) 337 .defaultNow() 338 .$onUpdate(() => new Date()) 339 .notNull(), 340 }, 341 (table) => [ 342 index("task_reminder_sent_taskId_idx").on(table.taskId), 343 unique("task_reminder_sent_task_type_unique").on( 344 table.taskId, 345 table.reminderType, 346 ), 347 ], 348); 349 350export const timeEntryTable = pgTable("time_entry", { 351 id: text("id") 352 .$defaultFn(() => createId()) 353 .primaryKey(), 354 taskId: text("task_id") 355 .notNull() 356 .references(() => taskTable.id, { 357 onDelete: "cascade", 358 onUpdate: "cascade", 359 }), 360 userId: text("user_id").references(() => userTable.id, { 361 onDelete: "cascade", 362 onUpdate: "cascade", 363 }), 364 description: text("description"), 365 startTime: timestamp("start_time", { mode: "date" }).notNull(), 366 endTime: timestamp("end_time", { mode: "date" }), 367 duration: integer("duration").default(0), 368 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 369}); 370 371export const activityTable = pgTable( 372 "activity", 373 { 374 id: text("id") 375 .$defaultFn(() => createId()) 376 .primaryKey(), 377 taskId: text("task_id") 378 .notNull() 379 .references(() => taskTable.id, { 380 onDelete: "cascade", 381 onUpdate: "cascade", 382 }), 383 type: text("type").notNull(), 384 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 385 updatedAt: timestamp("updated_at", { mode: "date" }) 386 .defaultNow() 387 .$onUpdate(() => new Date()) 388 .notNull(), 389 userId: text("user_id").references(() => userTable.id, { 390 onDelete: "cascade", 391 onUpdate: "cascade", 392 }), 393 content: text("content"), 394 eventData: jsonb("event_data"), 395 externalUserName: text("external_user_name"), 396 externalUserAvatar: text("external_user_avatar"), 397 externalSource: text("external_source"), 398 externalUrl: text("external_url"), 399 }, 400 (table) => [ 401 index("activity_task_id_idx").on(table.taskId), 402 unique("activity_task_external_source_external_url_unique").on( 403 table.taskId, 404 table.externalSource, 405 table.externalUrl, 406 ), 407 ], 408); 409 410export const assetTable = pgTable( 411 "asset", 412 { 413 id: text("id") 414 .$defaultFn(() => createId()) 415 .primaryKey(), 416 workspaceId: text("workspace_id") 417 .notNull() 418 .references(() => workspaceTable.id, { 419 onDelete: "cascade", 420 onUpdate: "cascade", 421 }), 422 projectId: text("project_id") 423 .notNull() 424 .references(() => projectTable.id, { 425 onDelete: "cascade", 426 onUpdate: "cascade", 427 }), 428 taskId: text("task_id").references(() => taskTable.id, { 429 onDelete: "cascade", 430 onUpdate: "cascade", 431 }), 432 activityId: text("activity_id").references(() => activityTable.id, { 433 onDelete: "cascade", 434 onUpdate: "cascade", 435 }), 436 objectKey: text("object_key").notNull().unique(), 437 filename: text("filename").notNull(), 438 mimeType: text("mime_type").notNull(), 439 size: integer("size").notNull(), 440 kind: text("kind").notNull().default("image"), 441 surface: text("surface").notNull().default("description"), 442 createdBy: text("created_by").references(() => userTable.id, { 443 onDelete: "set null", 444 onUpdate: "cascade", 445 }), 446 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 447 }, 448 (table) => [ 449 index("asset_workspaceId_idx").on(table.workspaceId), 450 index("asset_projectId_idx").on(table.projectId), 451 index("asset_taskId_idx").on(table.taskId), 452 index("asset_activityId_idx").on(table.activityId), 453 ], 454); 455 456export const labelTable = pgTable( 457 "label", 458 { 459 id: text("id") 460 .$defaultFn(() => createId()) 461 .primaryKey(), 462 name: text("name").notNull(), 463 color: text("color").notNull(), 464 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 465 updatedAt: timestamp("updated_at", { mode: "date" }) 466 .defaultNow() 467 .$onUpdate(() => new Date()) 468 .notNull(), 469 taskId: text("task_id").references(() => taskTable.id, { 470 onDelete: "cascade", 471 onUpdate: "cascade", 472 }), 473 workspaceId: text("workspace_id").references(() => workspaceTable.id, { 474 onDelete: "cascade", 475 onUpdate: "cascade", 476 }), 477 }, 478 (table) => [ 479 index("label_task_id_idx").on(table.taskId), 480 index("label_workspace_id_idx").on(table.workspaceId), 481 unique("label_task_name_unique").on(table.taskId, table.name), 482 uniqueIndex("label_workspace_name_unique") 483 .on(table.workspaceId, table.name) 484 .where(sql`${table.taskId} is null`), 485 ], 486); 487 488export const notificationTable = pgTable("notification", { 489 id: text("id") 490 .$defaultFn(() => createId()) 491 .primaryKey(), 492 userId: text("user_id") 493 .notNull() 494 .references(() => userTable.id, { 495 onDelete: "cascade", 496 onUpdate: "cascade", 497 }), 498 title: text("title"), 499 content: text("content"), 500 type: text("type").notNull().default("info"), 501 eventData: jsonb("event_data"), 502 isRead: boolean("is_read").default(false), 503 resourceId: text("resource_id"), 504 resourceType: text("resource_type"), 505 createdAt: timestamp("created_at", { mode: "date", withTimezone: true }) 506 .defaultNow() 507 .notNull(), 508}); 509 510export const userNotificationPreferenceTable = pgTable( 511 "user_notification_preference", 512 { 513 id: text("id") 514 .$defaultFn(() => createId()) 515 .primaryKey(), 516 userId: text("user_id") 517 .notNull() 518 .unique() 519 .references(() => userTable.id, { 520 onDelete: "cascade", 521 onUpdate: "cascade", 522 }), 523 emailEnabled: boolean("email_enabled").default(false).notNull(), 524 ntfyEnabled: boolean("ntfy_enabled").default(false).notNull(), 525 ntfyServerUrl: text("ntfy_server_url"), 526 ntfyTopic: text("ntfy_topic"), 527 ntfyToken: text("ntfy_token"), 528 gotifyEnabled: boolean("gotify_enabled").default(false).notNull(), 529 gotifyServerUrl: text("gotify_server_url"), 530 gotifyToken: text("gotify_token"), 531 webhookEnabled: boolean("webhook_enabled").default(false).notNull(), 532 webhookUrl: text("webhook_url"), 533 webhookSecret: text("webhook_secret"), 534 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 535 updatedAt: timestamp("updated_at", { mode: "date" }) 536 .defaultNow() 537 .$onUpdate(() => new Date()) 538 .notNull(), 539 }, 540); 541 542export const userNotificationWorkspaceRuleTable = pgTable( 543 "user_notification_workspace_rule", 544 { 545 id: text("id") 546 .$defaultFn(() => createId()) 547 .primaryKey(), 548 userId: text("user_id") 549 .notNull() 550 .references(() => userTable.id, { 551 onDelete: "cascade", 552 onUpdate: "cascade", 553 }), 554 workspaceId: text("workspace_id") 555 .notNull() 556 .references(() => workspaceTable.id, { 557 onDelete: "cascade", 558 onUpdate: "cascade", 559 }), 560 isActive: boolean("is_active").default(true).notNull(), 561 emailEnabled: boolean("email_enabled").default(false).notNull(), 562 ntfyEnabled: boolean("ntfy_enabled").default(false).notNull(), 563 gotifyEnabled: boolean("gotify_enabled").default(false).notNull(), 564 webhookEnabled: boolean("webhook_enabled").default(false).notNull(), 565 projectMode: text("project_mode").default("all").notNull(), 566 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 567 updatedAt: timestamp("updated_at", { mode: "date" }) 568 .defaultNow() 569 .$onUpdate(() => new Date()) 570 .notNull(), 571 }, 572 (table) => [ 573 index("user_notification_workspace_rule_userId_idx").on(table.userId), 574 index("user_notification_workspace_rule_workspaceId_idx").on( 575 table.workspaceId, 576 ), 577 unique("user_notification_workspace_rule_user_workspace_unique").on( 578 table.userId, 579 table.workspaceId, 580 ), 581 unique("user_notification_workspace_rule_workspace_id_id_unique").on( 582 table.workspaceId, 583 table.id, 584 ), 585 ], 586); 587 588export const userNotificationWorkspaceProjectTable = pgTable( 589 "user_notification_workspace_project", 590 { 591 id: text("id") 592 .$defaultFn(() => createId()) 593 .primaryKey(), 594 workspaceId: text("workspace_id") 595 .notNull() 596 .references(() => workspaceTable.id, { 597 onDelete: "cascade", 598 onUpdate: "cascade", 599 }), 600 workspaceRuleId: text("workspace_rule_id").notNull(), 601 projectId: text("project_id").notNull(), 602 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 603 updatedAt: timestamp("updated_at", { mode: "date" }) 604 .defaultNow() 605 .$onUpdate(() => new Date()) 606 .notNull(), 607 }, 608 (table) => [ 609 foreignKey({ 610 columns: [table.workspaceId, table.workspaceRuleId], 611 foreignColumns: [ 612 userNotificationWorkspaceRuleTable.workspaceId, 613 userNotificationWorkspaceRuleTable.id, 614 ], 615 }) 616 .onDelete("cascade") 617 .onUpdate("cascade"), 618 foreignKey({ 619 columns: [table.workspaceId, table.projectId], 620 foreignColumns: [projectTable.workspaceId, projectTable.id], 621 }) 622 .onDelete("cascade") 623 .onUpdate("cascade"), 624 index("user_notification_workspace_project_ruleId_idx").on( 625 table.workspaceRuleId, 626 ), 627 index("user_notification_workspace_project_projectId_idx").on( 628 table.projectId, 629 ), 630 unique("user_notification_workspace_project_rule_project_unique").on( 631 table.workspaceRuleId, 632 table.projectId, 633 ), 634 ], 635); 636 637export const githubIntegrationTable = pgTable("github_integration", { 638 id: text("id") 639 .$defaultFn(() => createId()) 640 .primaryKey(), 641 projectId: text("project_id") 642 .notNull() 643 .references(() => projectTable.id, { 644 onDelete: "cascade", 645 onUpdate: "cascade", 646 }) 647 .unique(), 648 repositoryOwner: text("repository_owner").notNull(), 649 repositoryName: text("repository_name").notNull(), 650 installationId: integer("installation_id"), 651 isActive: boolean("is_active").default(true), 652 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 653 updatedAt: timestamp("updated_at", { mode: "date" }) 654 .defaultNow() 655 .$onUpdate(() => new Date()) 656 .notNull(), 657}); 658 659export const integrationTable = pgTable( 660 "integration", 661 { 662 id: text("id") 663 .$defaultFn(() => createId()) 664 .primaryKey(), 665 projectId: text("project_id") 666 .notNull() 667 .references(() => projectTable.id, { 668 onDelete: "cascade", 669 onUpdate: "cascade", 670 }), 671 type: text("type").notNull(), 672 config: text("config").notNull(), 673 isActive: boolean("is_active").default(true), 674 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 675 updatedAt: timestamp("updated_at", { mode: "date" }) 676 .defaultNow() 677 .$onUpdate(() => new Date()) 678 .notNull(), 679 }, 680 (table) => [ 681 index("integration_projectId_idx").on(table.projectId), 682 index("integration_type_idx").on(table.type), 683 unique("integration_project_type_unique").on(table.projectId, table.type), 684 ], 685); 686 687export const externalLinkTable = pgTable( 688 "external_link", 689 { 690 id: text("id") 691 .$defaultFn(() => createId()) 692 .primaryKey(), 693 taskId: text("task_id") 694 .notNull() 695 .references(() => taskTable.id, { 696 onDelete: "cascade", 697 onUpdate: "cascade", 698 }), 699 integrationId: text("integration_id") 700 .notNull() 701 .references(() => integrationTable.id, { 702 onDelete: "cascade", 703 onUpdate: "cascade", 704 }), 705 resourceType: text("resource_type").notNull(), 706 externalId: text("external_id").notNull(), 707 url: text("url").notNull(), 708 title: text("title"), 709 metadata: text("metadata"), 710 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 711 updatedAt: timestamp("updated_at", { mode: "date" }) 712 .defaultNow() 713 .$onUpdate(() => new Date()) 714 .notNull(), 715 }, 716 (table) => [ 717 index("external_link_taskId_idx").on(table.taskId), 718 index("external_link_integrationId_idx").on(table.integrationId), 719 index("external_link_externalId_idx").on(table.externalId), 720 index("external_link_resourceType_idx").on(table.resourceType), 721 ], 722); 723 724export const commentTable = pgTable( 725 "comment", 726 { 727 id: text("id") 728 .$defaultFn(() => createId()) 729 .primaryKey(), 730 taskId: text("task_id") 731 .notNull() 732 .references(() => taskTable.id, { 733 onDelete: "cascade", 734 onUpdate: "cascade", 735 }), 736 userId: text("user_id") 737 .notNull() 738 .references(() => userTable.id, { 739 onDelete: "cascade", 740 onUpdate: "cascade", 741 }), 742 content: text("content").notNull(), 743 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 744 updatedAt: timestamp("updated_at", { mode: "date" }) 745 .defaultNow() 746 .$onUpdate(() => new Date()) 747 .notNull(), 748 }, 749 (table) => [ 750 index("comment_task_idx").on(table.taskId), 751 index("comment_user_idx").on(table.userId), 752 ], 753); 754 755export const taskRelationTable = pgTable( 756 "task_relation", 757 { 758 id: text("id") 759 .$defaultFn(() => createId()) 760 .primaryKey(), 761 sourceTaskId: text("source_task_id") 762 .notNull() 763 .references(() => taskTable.id, { 764 onDelete: "cascade", 765 onUpdate: "cascade", 766 }), 767 targetTaskId: text("target_task_id") 768 .notNull() 769 .references(() => taskTable.id, { 770 onDelete: "cascade", 771 onUpdate: "cascade", 772 }), 773 relationType: text("relation_type").notNull(), 774 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 775 }, 776 (table) => [ 777 index("task_relation_source_idx").on(table.sourceTaskId), 778 index("task_relation_target_idx").on(table.targetTaskId), 779 ], 780); 781 782export const apikeyTable = pgTable( 783 "apikey", 784 { 785 id: text("id") 786 .$defaultFn(() => createId()) 787 .primaryKey(), 788 configId: text("config_id").default("default").notNull(), 789 name: text("name"), 790 start: text("start"), 791 referenceId: text("reference_id") 792 .notNull() 793 .references(() => userTable.id, { onDelete: "cascade" }), 794 prefix: text("prefix"), 795 key: text("key").notNull(), 796 userId: text("user_id").references(() => userTable.id, { 797 onDelete: "cascade", 798 }), 799 refillInterval: integer("refill_interval"), 800 refillAmount: integer("refill_amount"), 801 lastRefillAt: timestamp("last_refill_at", { mode: "date" }), 802 enabled: boolean("enabled").default(true), 803 rateLimitEnabled: boolean("rate_limit_enabled").default(true), 804 rateLimitTimeWindow: integer("rate_limit_time_window").default(86400000), 805 rateLimitMax: integer("rate_limit_max").default(10), 806 requestCount: integer("request_count").default(0), 807 remaining: integer("remaining"), 808 lastRequest: timestamp("last_request", { mode: "date" }), 809 expiresAt: timestamp("expires_at", { mode: "date" }), 810 createdAt: timestamp("created_at", { mode: "date" }).notNull(), 811 updatedAt: timestamp("updated_at", { mode: "date" }).notNull(), 812 permissions: text("permissions"), 813 metadata: text("metadata"), 814 }, 815 (table) => [ 816 index("apikey_configId_idx").on(table.configId), 817 index("apikey_key_idx").on(table.key), 818 index("apikey_referenceId_idx").on(table.referenceId), 819 index("apikey_userId_idx").on(table.userId), 820 ], 821); 822 823export const deviceCodeTable = pgTable( 824 "device_code", 825 { 826 id: text("id") 827 .$defaultFn(() => createId()) 828 .primaryKey(), 829 deviceCode: text("device_code").notNull(), 830 userCode: text("user_code").notNull(), 831 userId: text("user_id").references(() => userTable.id, { 832 onDelete: "cascade", 833 onUpdate: "cascade", 834 }), 835 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), 836 updatedAt: timestamp("updated_at", { mode: "date" }) 837 .defaultNow() 838 .$onUpdate(() => new Date()) 839 .notNull(), 840 expiresAt: timestamp("expires_at", { mode: "date" }).notNull(), 841 status: text("status").notNull(), 842 lastPolledAt: timestamp("last_polled_at", { mode: "date" }), 843 pollingInterval: integer("polling_interval"), 844 clientId: text("client_id"), 845 scope: text("scope"), 846 }, 847 (table) => [ 848 uniqueIndex("device_code_device_code_uidx").on(table.deviceCode), 849 uniqueIndex("device_code_user_code_uidx").on(table.userCode), 850 index("device_code_user_id_idx").on(table.userId), 851 ], 852); 853 854// Auth-schema compatible aliases in schema.ts 855export const user = userTable; 856export const session = sessionTable; 857export const account = accountTable; 858export const verification = verificationTable; 859export const workspace = workspaceTable; 860export const team = teamTable; 861export const teamMember = teamMemberTable; 862export const workspace_member = workspaceUserTable; 863export const invitation = invitationTable; 864export const apikey = apikeyTable; 865export const deviceCode = deviceCodeTable; 866 867// Auth-schema compatible relation exports in schema.ts 868export const userRelations = relations(user, ({ many }) => ({ 869 sessions: many(session), 870 accounts: many(account), 871 teamMembers: many(teamMember), 872 workspace_members: many(workspace_member), 873 invitations: many(invitation), 874})); 875 876export const sessionRelations = relations(session, ({ one }) => ({ 877 user: one(user, { 878 fields: [session.userId], 879 references: [user.id], 880 }), 881})); 882 883export const accountRelations = relations(account, ({ one }) => ({ 884 user: one(user, { 885 fields: [account.userId], 886 references: [user.id], 887 }), 888})); 889 890export const workspaceRelations = relations(workspace, ({ many }) => ({ 891 teams: many(team), 892 workspace_members: many(workspace_member), 893 invitations: many(invitation), 894})); 895 896export const teamRelations = relations(team, ({ one, many }) => ({ 897 workspace: one(workspace, { 898 fields: [team.workspaceId], 899 references: [workspace.id], 900 }), 901 teamMembers: many(teamMember), 902})); 903 904export const teamMemberRelations = relations(teamMember, ({ one }) => ({ 905 team: one(team, { 906 fields: [teamMember.teamId], 907 references: [team.id], 908 }), 909 user: one(user, { 910 fields: [teamMember.userId], 911 references: [user.id], 912 }), 913})); 914 915export const workspace_memberRelations = relations( 916 workspace_member, 917 ({ one }) => ({ 918 workspace: one(workspace, { 919 fields: [workspace_member.workspaceId], 920 references: [workspace.id], 921 }), 922 user: one(user, { 923 fields: [workspace_member.userId], 924 references: [user.id], 925 }), 926 }), 927); 928 929export const invitationRelations = relations(invitation, ({ one }) => ({ 930 workspace: one(workspace, { 931 fields: [invitation.workspaceId], 932 references: [workspace.id], 933 }), 934 user: one(user, { 935 fields: [invitation.inviterId], 936 references: [user.id], 937 }), 938}));