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.

feat: add due date reminders scheduler

Andrej aeb345ee b313a15c

+3566 -8
+12
apps/api/drizzle/0027_chilly_namorita.sql
··· 1 + CREATE TABLE "task_reminder_sent" ( 2 + "id" text PRIMARY KEY NOT NULL, 3 + "task_id" text NOT NULL, 4 + "reminder_type" text NOT NULL, 5 + "created_at" timestamp DEFAULT now() NOT NULL, 6 + "updated_at" timestamp DEFAULT now() NOT NULL, 7 + CONSTRAINT "task_reminder_sent_task_type_unique" UNIQUE("task_id","reminder_type") 8 + ); 9 + --> statement-breakpoint 10 + ALTER TABLE "task_reminder_sent" ADD CONSTRAINT "task_reminder_sent_task_id_task_id_fk" FOREIGN KEY ("task_id") REFERENCES "public"."task"("id") ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint 11 + CREATE INDEX "task_reminder_sent_taskId_idx" ON "task_reminder_sent" USING btree ("task_id");--> statement-breakpoint 12 + CREATE INDEX "task_dueDate_idx" ON "task" USING btree ("due_date");
+3284
apps/api/drizzle/meta/0027_snapshot.json
··· 1 + { 2 + "id": "99696a36-1f3d-4361-92ef-e3bd8d9c6297", 3 + "prevId": "0e05bf62-b50b-4a4e-9d73-3b4cdb696930", 4 + "version": "7", 5 + "dialect": "postgresql", 6 + "tables": { 7 + "public.account": { 8 + "name": "account", 9 + "schema": "", 10 + "columns": { 11 + "id": { 12 + "name": "id", 13 + "type": "text", 14 + "primaryKey": true, 15 + "notNull": true 16 + }, 17 + "account_id": { 18 + "name": "account_id", 19 + "type": "text", 20 + "primaryKey": false, 21 + "notNull": true 22 + }, 23 + "provider_id": { 24 + "name": "provider_id", 25 + "type": "text", 26 + "primaryKey": false, 27 + "notNull": true 28 + }, 29 + "user_id": { 30 + "name": "user_id", 31 + "type": "text", 32 + "primaryKey": false, 33 + "notNull": true 34 + }, 35 + "access_token": { 36 + "name": "access_token", 37 + "type": "text", 38 + "primaryKey": false, 39 + "notNull": false 40 + }, 41 + "refresh_token": { 42 + "name": "refresh_token", 43 + "type": "text", 44 + "primaryKey": false, 45 + "notNull": false 46 + }, 47 + "id_token": { 48 + "name": "id_token", 49 + "type": "text", 50 + "primaryKey": false, 51 + "notNull": false 52 + }, 53 + "access_token_expires_at": { 54 + "name": "access_token_expires_at", 55 + "type": "timestamp", 56 + "primaryKey": false, 57 + "notNull": false 58 + }, 59 + "refresh_token_expires_at": { 60 + "name": "refresh_token_expires_at", 61 + "type": "timestamp", 62 + "primaryKey": false, 63 + "notNull": false 64 + }, 65 + "scope": { 66 + "name": "scope", 67 + "type": "text", 68 + "primaryKey": false, 69 + "notNull": false 70 + }, 71 + "password": { 72 + "name": "password", 73 + "type": "text", 74 + "primaryKey": false, 75 + "notNull": false 76 + }, 77 + "created_at": { 78 + "name": "created_at", 79 + "type": "timestamp", 80 + "primaryKey": false, 81 + "notNull": true, 82 + "default": "now()" 83 + }, 84 + "updated_at": { 85 + "name": "updated_at", 86 + "type": "timestamp", 87 + "primaryKey": false, 88 + "notNull": true 89 + } 90 + }, 91 + "indexes": { 92 + "account_userId_idx": { 93 + "name": "account_userId_idx", 94 + "columns": [ 95 + { 96 + "expression": "user_id", 97 + "isExpression": false, 98 + "asc": true, 99 + "nulls": "last" 100 + } 101 + ], 102 + "isUnique": false, 103 + "concurrently": false, 104 + "method": "btree", 105 + "with": {} 106 + } 107 + }, 108 + "foreignKeys": { 109 + "account_user_id_user_id_fk": { 110 + "name": "account_user_id_user_id_fk", 111 + "tableFrom": "account", 112 + "tableTo": "user", 113 + "columnsFrom": ["user_id"], 114 + "columnsTo": ["id"], 115 + "onDelete": "cascade", 116 + "onUpdate": "no action" 117 + } 118 + }, 119 + "compositePrimaryKeys": {}, 120 + "uniqueConstraints": {}, 121 + "policies": {}, 122 + "checkConstraints": {}, 123 + "isRLSEnabled": false 124 + }, 125 + "public.activity": { 126 + "name": "activity", 127 + "schema": "", 128 + "columns": { 129 + "id": { 130 + "name": "id", 131 + "type": "text", 132 + "primaryKey": true, 133 + "notNull": true 134 + }, 135 + "task_id": { 136 + "name": "task_id", 137 + "type": "text", 138 + "primaryKey": false, 139 + "notNull": true 140 + }, 141 + "type": { 142 + "name": "type", 143 + "type": "text", 144 + "primaryKey": false, 145 + "notNull": true 146 + }, 147 + "created_at": { 148 + "name": "created_at", 149 + "type": "timestamp", 150 + "primaryKey": false, 151 + "notNull": true, 152 + "default": "now()" 153 + }, 154 + "updated_at": { 155 + "name": "updated_at", 156 + "type": "timestamp", 157 + "primaryKey": false, 158 + "notNull": true, 159 + "default": "now()" 160 + }, 161 + "user_id": { 162 + "name": "user_id", 163 + "type": "text", 164 + "primaryKey": false, 165 + "notNull": false 166 + }, 167 + "content": { 168 + "name": "content", 169 + "type": "text", 170 + "primaryKey": false, 171 + "notNull": false 172 + }, 173 + "event_data": { 174 + "name": "event_data", 175 + "type": "jsonb", 176 + "primaryKey": false, 177 + "notNull": false 178 + }, 179 + "external_user_name": { 180 + "name": "external_user_name", 181 + "type": "text", 182 + "primaryKey": false, 183 + "notNull": false 184 + }, 185 + "external_user_avatar": { 186 + "name": "external_user_avatar", 187 + "type": "text", 188 + "primaryKey": false, 189 + "notNull": false 190 + }, 191 + "external_source": { 192 + "name": "external_source", 193 + "type": "text", 194 + "primaryKey": false, 195 + "notNull": false 196 + }, 197 + "external_url": { 198 + "name": "external_url", 199 + "type": "text", 200 + "primaryKey": false, 201 + "notNull": false 202 + } 203 + }, 204 + "indexes": { 205 + "activity_task_id_idx": { 206 + "name": "activity_task_id_idx", 207 + "columns": [ 208 + { 209 + "expression": "task_id", 210 + "isExpression": false, 211 + "asc": true, 212 + "nulls": "last" 213 + } 214 + ], 215 + "isUnique": false, 216 + "concurrently": false, 217 + "method": "btree", 218 + "with": {} 219 + } 220 + }, 221 + "foreignKeys": { 222 + "activity_task_id_task_id_fk": { 223 + "name": "activity_task_id_task_id_fk", 224 + "tableFrom": "activity", 225 + "tableTo": "task", 226 + "columnsFrom": ["task_id"], 227 + "columnsTo": ["id"], 228 + "onDelete": "cascade", 229 + "onUpdate": "cascade" 230 + }, 231 + "activity_user_id_user_id_fk": { 232 + "name": "activity_user_id_user_id_fk", 233 + "tableFrom": "activity", 234 + "tableTo": "user", 235 + "columnsFrom": ["user_id"], 236 + "columnsTo": ["id"], 237 + "onDelete": "cascade", 238 + "onUpdate": "cascade" 239 + } 240 + }, 241 + "compositePrimaryKeys": {}, 242 + "uniqueConstraints": { 243 + "activity_task_external_source_external_url_unique": { 244 + "name": "activity_task_external_source_external_url_unique", 245 + "nullsNotDistinct": false, 246 + "columns": ["task_id", "external_source", "external_url"] 247 + } 248 + }, 249 + "policies": {}, 250 + "checkConstraints": {}, 251 + "isRLSEnabled": false 252 + }, 253 + "public.apikey": { 254 + "name": "apikey", 255 + "schema": "", 256 + "columns": { 257 + "id": { 258 + "name": "id", 259 + "type": "text", 260 + "primaryKey": true, 261 + "notNull": true 262 + }, 263 + "config_id": { 264 + "name": "config_id", 265 + "type": "text", 266 + "primaryKey": false, 267 + "notNull": true, 268 + "default": "'default'" 269 + }, 270 + "name": { 271 + "name": "name", 272 + "type": "text", 273 + "primaryKey": false, 274 + "notNull": false 275 + }, 276 + "start": { 277 + "name": "start", 278 + "type": "text", 279 + "primaryKey": false, 280 + "notNull": false 281 + }, 282 + "reference_id": { 283 + "name": "reference_id", 284 + "type": "text", 285 + "primaryKey": false, 286 + "notNull": true 287 + }, 288 + "prefix": { 289 + "name": "prefix", 290 + "type": "text", 291 + "primaryKey": false, 292 + "notNull": false 293 + }, 294 + "key": { 295 + "name": "key", 296 + "type": "text", 297 + "primaryKey": false, 298 + "notNull": true 299 + }, 300 + "user_id": { 301 + "name": "user_id", 302 + "type": "text", 303 + "primaryKey": false, 304 + "notNull": false 305 + }, 306 + "refill_interval": { 307 + "name": "refill_interval", 308 + "type": "integer", 309 + "primaryKey": false, 310 + "notNull": false 311 + }, 312 + "refill_amount": { 313 + "name": "refill_amount", 314 + "type": "integer", 315 + "primaryKey": false, 316 + "notNull": false 317 + }, 318 + "last_refill_at": { 319 + "name": "last_refill_at", 320 + "type": "timestamp", 321 + "primaryKey": false, 322 + "notNull": false 323 + }, 324 + "enabled": { 325 + "name": "enabled", 326 + "type": "boolean", 327 + "primaryKey": false, 328 + "notNull": false, 329 + "default": true 330 + }, 331 + "rate_limit_enabled": { 332 + "name": "rate_limit_enabled", 333 + "type": "boolean", 334 + "primaryKey": false, 335 + "notNull": false, 336 + "default": true 337 + }, 338 + "rate_limit_time_window": { 339 + "name": "rate_limit_time_window", 340 + "type": "integer", 341 + "primaryKey": false, 342 + "notNull": false, 343 + "default": 86400000 344 + }, 345 + "rate_limit_max": { 346 + "name": "rate_limit_max", 347 + "type": "integer", 348 + "primaryKey": false, 349 + "notNull": false, 350 + "default": 10 351 + }, 352 + "request_count": { 353 + "name": "request_count", 354 + "type": "integer", 355 + "primaryKey": false, 356 + "notNull": false, 357 + "default": 0 358 + }, 359 + "remaining": { 360 + "name": "remaining", 361 + "type": "integer", 362 + "primaryKey": false, 363 + "notNull": false 364 + }, 365 + "last_request": { 366 + "name": "last_request", 367 + "type": "timestamp", 368 + "primaryKey": false, 369 + "notNull": false 370 + }, 371 + "expires_at": { 372 + "name": "expires_at", 373 + "type": "timestamp", 374 + "primaryKey": false, 375 + "notNull": false 376 + }, 377 + "created_at": { 378 + "name": "created_at", 379 + "type": "timestamp", 380 + "primaryKey": false, 381 + "notNull": true 382 + }, 383 + "updated_at": { 384 + "name": "updated_at", 385 + "type": "timestamp", 386 + "primaryKey": false, 387 + "notNull": true 388 + }, 389 + "permissions": { 390 + "name": "permissions", 391 + "type": "text", 392 + "primaryKey": false, 393 + "notNull": false 394 + }, 395 + "metadata": { 396 + "name": "metadata", 397 + "type": "text", 398 + "primaryKey": false, 399 + "notNull": false 400 + } 401 + }, 402 + "indexes": { 403 + "apikey_configId_idx": { 404 + "name": "apikey_configId_idx", 405 + "columns": [ 406 + { 407 + "expression": "config_id", 408 + "isExpression": false, 409 + "asc": true, 410 + "nulls": "last" 411 + } 412 + ], 413 + "isUnique": false, 414 + "concurrently": false, 415 + "method": "btree", 416 + "with": {} 417 + }, 418 + "apikey_key_idx": { 419 + "name": "apikey_key_idx", 420 + "columns": [ 421 + { 422 + "expression": "key", 423 + "isExpression": false, 424 + "asc": true, 425 + "nulls": "last" 426 + } 427 + ], 428 + "isUnique": false, 429 + "concurrently": false, 430 + "method": "btree", 431 + "with": {} 432 + }, 433 + "apikey_referenceId_idx": { 434 + "name": "apikey_referenceId_idx", 435 + "columns": [ 436 + { 437 + "expression": "reference_id", 438 + "isExpression": false, 439 + "asc": true, 440 + "nulls": "last" 441 + } 442 + ], 443 + "isUnique": false, 444 + "concurrently": false, 445 + "method": "btree", 446 + "with": {} 447 + }, 448 + "apikey_userId_idx": { 449 + "name": "apikey_userId_idx", 450 + "columns": [ 451 + { 452 + "expression": "user_id", 453 + "isExpression": false, 454 + "asc": true, 455 + "nulls": "last" 456 + } 457 + ], 458 + "isUnique": false, 459 + "concurrently": false, 460 + "method": "btree", 461 + "with": {} 462 + } 463 + }, 464 + "foreignKeys": { 465 + "apikey_reference_id_user_id_fk": { 466 + "name": "apikey_reference_id_user_id_fk", 467 + "tableFrom": "apikey", 468 + "tableTo": "user", 469 + "columnsFrom": ["reference_id"], 470 + "columnsTo": ["id"], 471 + "onDelete": "cascade", 472 + "onUpdate": "no action" 473 + }, 474 + "apikey_user_id_user_id_fk": { 475 + "name": "apikey_user_id_user_id_fk", 476 + "tableFrom": "apikey", 477 + "tableTo": "user", 478 + "columnsFrom": ["user_id"], 479 + "columnsTo": ["id"], 480 + "onDelete": "cascade", 481 + "onUpdate": "no action" 482 + } 483 + }, 484 + "compositePrimaryKeys": {}, 485 + "uniqueConstraints": {}, 486 + "policies": {}, 487 + "checkConstraints": {}, 488 + "isRLSEnabled": false 489 + }, 490 + "public.asset": { 491 + "name": "asset", 492 + "schema": "", 493 + "columns": { 494 + "id": { 495 + "name": "id", 496 + "type": "text", 497 + "primaryKey": true, 498 + "notNull": true 499 + }, 500 + "workspace_id": { 501 + "name": "workspace_id", 502 + "type": "text", 503 + "primaryKey": false, 504 + "notNull": true 505 + }, 506 + "project_id": { 507 + "name": "project_id", 508 + "type": "text", 509 + "primaryKey": false, 510 + "notNull": true 511 + }, 512 + "task_id": { 513 + "name": "task_id", 514 + "type": "text", 515 + "primaryKey": false, 516 + "notNull": false 517 + }, 518 + "activity_id": { 519 + "name": "activity_id", 520 + "type": "text", 521 + "primaryKey": false, 522 + "notNull": false 523 + }, 524 + "object_key": { 525 + "name": "object_key", 526 + "type": "text", 527 + "primaryKey": false, 528 + "notNull": true 529 + }, 530 + "filename": { 531 + "name": "filename", 532 + "type": "text", 533 + "primaryKey": false, 534 + "notNull": true 535 + }, 536 + "mime_type": { 537 + "name": "mime_type", 538 + "type": "text", 539 + "primaryKey": false, 540 + "notNull": true 541 + }, 542 + "size": { 543 + "name": "size", 544 + "type": "integer", 545 + "primaryKey": false, 546 + "notNull": true 547 + }, 548 + "kind": { 549 + "name": "kind", 550 + "type": "text", 551 + "primaryKey": false, 552 + "notNull": true, 553 + "default": "'image'" 554 + }, 555 + "surface": { 556 + "name": "surface", 557 + "type": "text", 558 + "primaryKey": false, 559 + "notNull": true, 560 + "default": "'description'" 561 + }, 562 + "created_by": { 563 + "name": "created_by", 564 + "type": "text", 565 + "primaryKey": false, 566 + "notNull": false 567 + }, 568 + "created_at": { 569 + "name": "created_at", 570 + "type": "timestamp", 571 + "primaryKey": false, 572 + "notNull": true, 573 + "default": "now()" 574 + } 575 + }, 576 + "indexes": { 577 + "asset_workspaceId_idx": { 578 + "name": "asset_workspaceId_idx", 579 + "columns": [ 580 + { 581 + "expression": "workspace_id", 582 + "isExpression": false, 583 + "asc": true, 584 + "nulls": "last" 585 + } 586 + ], 587 + "isUnique": false, 588 + "concurrently": false, 589 + "method": "btree", 590 + "with": {} 591 + }, 592 + "asset_projectId_idx": { 593 + "name": "asset_projectId_idx", 594 + "columns": [ 595 + { 596 + "expression": "project_id", 597 + "isExpression": false, 598 + "asc": true, 599 + "nulls": "last" 600 + } 601 + ], 602 + "isUnique": false, 603 + "concurrently": false, 604 + "method": "btree", 605 + "with": {} 606 + }, 607 + "asset_taskId_idx": { 608 + "name": "asset_taskId_idx", 609 + "columns": [ 610 + { 611 + "expression": "task_id", 612 + "isExpression": false, 613 + "asc": true, 614 + "nulls": "last" 615 + } 616 + ], 617 + "isUnique": false, 618 + "concurrently": false, 619 + "method": "btree", 620 + "with": {} 621 + }, 622 + "asset_activityId_idx": { 623 + "name": "asset_activityId_idx", 624 + "columns": [ 625 + { 626 + "expression": "activity_id", 627 + "isExpression": false, 628 + "asc": true, 629 + "nulls": "last" 630 + } 631 + ], 632 + "isUnique": false, 633 + "concurrently": false, 634 + "method": "btree", 635 + "with": {} 636 + } 637 + }, 638 + "foreignKeys": { 639 + "asset_workspace_id_workspace_id_fk": { 640 + "name": "asset_workspace_id_workspace_id_fk", 641 + "tableFrom": "asset", 642 + "tableTo": "workspace", 643 + "columnsFrom": ["workspace_id"], 644 + "columnsTo": ["id"], 645 + "onDelete": "cascade", 646 + "onUpdate": "cascade" 647 + }, 648 + "asset_project_id_project_id_fk": { 649 + "name": "asset_project_id_project_id_fk", 650 + "tableFrom": "asset", 651 + "tableTo": "project", 652 + "columnsFrom": ["project_id"], 653 + "columnsTo": ["id"], 654 + "onDelete": "cascade", 655 + "onUpdate": "cascade" 656 + }, 657 + "asset_task_id_task_id_fk": { 658 + "name": "asset_task_id_task_id_fk", 659 + "tableFrom": "asset", 660 + "tableTo": "task", 661 + "columnsFrom": ["task_id"], 662 + "columnsTo": ["id"], 663 + "onDelete": "cascade", 664 + "onUpdate": "cascade" 665 + }, 666 + "asset_activity_id_activity_id_fk": { 667 + "name": "asset_activity_id_activity_id_fk", 668 + "tableFrom": "asset", 669 + "tableTo": "activity", 670 + "columnsFrom": ["activity_id"], 671 + "columnsTo": ["id"], 672 + "onDelete": "cascade", 673 + "onUpdate": "cascade" 674 + }, 675 + "asset_created_by_user_id_fk": { 676 + "name": "asset_created_by_user_id_fk", 677 + "tableFrom": "asset", 678 + "tableTo": "user", 679 + "columnsFrom": ["created_by"], 680 + "columnsTo": ["id"], 681 + "onDelete": "set null", 682 + "onUpdate": "cascade" 683 + } 684 + }, 685 + "compositePrimaryKeys": {}, 686 + "uniqueConstraints": { 687 + "asset_object_key_unique": { 688 + "name": "asset_object_key_unique", 689 + "nullsNotDistinct": false, 690 + "columns": ["object_key"] 691 + } 692 + }, 693 + "policies": {}, 694 + "checkConstraints": {}, 695 + "isRLSEnabled": false 696 + }, 697 + "public.column": { 698 + "name": "column", 699 + "schema": "", 700 + "columns": { 701 + "id": { 702 + "name": "id", 703 + "type": "text", 704 + "primaryKey": true, 705 + "notNull": true 706 + }, 707 + "project_id": { 708 + "name": "project_id", 709 + "type": "text", 710 + "primaryKey": false, 711 + "notNull": true 712 + }, 713 + "name": { 714 + "name": "name", 715 + "type": "text", 716 + "primaryKey": false, 717 + "notNull": true 718 + }, 719 + "slug": { 720 + "name": "slug", 721 + "type": "text", 722 + "primaryKey": false, 723 + "notNull": true 724 + }, 725 + "position": { 726 + "name": "position", 727 + "type": "integer", 728 + "primaryKey": false, 729 + "notNull": true, 730 + "default": 0 731 + }, 732 + "icon": { 733 + "name": "icon", 734 + "type": "text", 735 + "primaryKey": false, 736 + "notNull": false 737 + }, 738 + "color": { 739 + "name": "color", 740 + "type": "text", 741 + "primaryKey": false, 742 + "notNull": false 743 + }, 744 + "is_final": { 745 + "name": "is_final", 746 + "type": "boolean", 747 + "primaryKey": false, 748 + "notNull": true, 749 + "default": false 750 + }, 751 + "created_at": { 752 + "name": "created_at", 753 + "type": "timestamp", 754 + "primaryKey": false, 755 + "notNull": true, 756 + "default": "now()" 757 + }, 758 + "updated_at": { 759 + "name": "updated_at", 760 + "type": "timestamp", 761 + "primaryKey": false, 762 + "notNull": true, 763 + "default": "now()" 764 + } 765 + }, 766 + "indexes": { 767 + "column_projectId_idx": { 768 + "name": "column_projectId_idx", 769 + "columns": [ 770 + { 771 + "expression": "project_id", 772 + "isExpression": false, 773 + "asc": true, 774 + "nulls": "last" 775 + } 776 + ], 777 + "isUnique": false, 778 + "concurrently": false, 779 + "method": "btree", 780 + "with": {} 781 + } 782 + }, 783 + "foreignKeys": { 784 + "column_project_id_project_id_fk": { 785 + "name": "column_project_id_project_id_fk", 786 + "tableFrom": "column", 787 + "tableTo": "project", 788 + "columnsFrom": ["project_id"], 789 + "columnsTo": ["id"], 790 + "onDelete": "cascade", 791 + "onUpdate": "cascade" 792 + } 793 + }, 794 + "compositePrimaryKeys": {}, 795 + "uniqueConstraints": {}, 796 + "policies": {}, 797 + "checkConstraints": {}, 798 + "isRLSEnabled": false 799 + }, 800 + "public.comment": { 801 + "name": "comment", 802 + "schema": "", 803 + "columns": { 804 + "id": { 805 + "name": "id", 806 + "type": "text", 807 + "primaryKey": true, 808 + "notNull": true 809 + }, 810 + "task_id": { 811 + "name": "task_id", 812 + "type": "text", 813 + "primaryKey": false, 814 + "notNull": true 815 + }, 816 + "user_id": { 817 + "name": "user_id", 818 + "type": "text", 819 + "primaryKey": false, 820 + "notNull": true 821 + }, 822 + "content": { 823 + "name": "content", 824 + "type": "text", 825 + "primaryKey": false, 826 + "notNull": true 827 + }, 828 + "created_at": { 829 + "name": "created_at", 830 + "type": "timestamp", 831 + "primaryKey": false, 832 + "notNull": true, 833 + "default": "now()" 834 + }, 835 + "updated_at": { 836 + "name": "updated_at", 837 + "type": "timestamp", 838 + "primaryKey": false, 839 + "notNull": true, 840 + "default": "now()" 841 + } 842 + }, 843 + "indexes": { 844 + "comment_task_idx": { 845 + "name": "comment_task_idx", 846 + "columns": [ 847 + { 848 + "expression": "task_id", 849 + "isExpression": false, 850 + "asc": true, 851 + "nulls": "last" 852 + } 853 + ], 854 + "isUnique": false, 855 + "concurrently": false, 856 + "method": "btree", 857 + "with": {} 858 + }, 859 + "comment_user_idx": { 860 + "name": "comment_user_idx", 861 + "columns": [ 862 + { 863 + "expression": "user_id", 864 + "isExpression": false, 865 + "asc": true, 866 + "nulls": "last" 867 + } 868 + ], 869 + "isUnique": false, 870 + "concurrently": false, 871 + "method": "btree", 872 + "with": {} 873 + } 874 + }, 875 + "foreignKeys": { 876 + "comment_task_id_task_id_fk": { 877 + "name": "comment_task_id_task_id_fk", 878 + "tableFrom": "comment", 879 + "tableTo": "task", 880 + "columnsFrom": ["task_id"], 881 + "columnsTo": ["id"], 882 + "onDelete": "cascade", 883 + "onUpdate": "cascade" 884 + }, 885 + "comment_user_id_user_id_fk": { 886 + "name": "comment_user_id_user_id_fk", 887 + "tableFrom": "comment", 888 + "tableTo": "user", 889 + "columnsFrom": ["user_id"], 890 + "columnsTo": ["id"], 891 + "onDelete": "cascade", 892 + "onUpdate": "cascade" 893 + } 894 + }, 895 + "compositePrimaryKeys": {}, 896 + "uniqueConstraints": {}, 897 + "policies": {}, 898 + "checkConstraints": {}, 899 + "isRLSEnabled": false 900 + }, 901 + "public.device_code": { 902 + "name": "device_code", 903 + "schema": "", 904 + "columns": { 905 + "id": { 906 + "name": "id", 907 + "type": "text", 908 + "primaryKey": true, 909 + "notNull": true 910 + }, 911 + "device_code": { 912 + "name": "device_code", 913 + "type": "text", 914 + "primaryKey": false, 915 + "notNull": true 916 + }, 917 + "user_code": { 918 + "name": "user_code", 919 + "type": "text", 920 + "primaryKey": false, 921 + "notNull": true 922 + }, 923 + "user_id": { 924 + "name": "user_id", 925 + "type": "text", 926 + "primaryKey": false, 927 + "notNull": false 928 + }, 929 + "created_at": { 930 + "name": "created_at", 931 + "type": "timestamp", 932 + "primaryKey": false, 933 + "notNull": true, 934 + "default": "now()" 935 + }, 936 + "updated_at": { 937 + "name": "updated_at", 938 + "type": "timestamp", 939 + "primaryKey": false, 940 + "notNull": true, 941 + "default": "now()" 942 + }, 943 + "expires_at": { 944 + "name": "expires_at", 945 + "type": "timestamp", 946 + "primaryKey": false, 947 + "notNull": true 948 + }, 949 + "status": { 950 + "name": "status", 951 + "type": "text", 952 + "primaryKey": false, 953 + "notNull": true 954 + }, 955 + "last_polled_at": { 956 + "name": "last_polled_at", 957 + "type": "timestamp", 958 + "primaryKey": false, 959 + "notNull": false 960 + }, 961 + "polling_interval": { 962 + "name": "polling_interval", 963 + "type": "integer", 964 + "primaryKey": false, 965 + "notNull": false 966 + }, 967 + "client_id": { 968 + "name": "client_id", 969 + "type": "text", 970 + "primaryKey": false, 971 + "notNull": false 972 + }, 973 + "scope": { 974 + "name": "scope", 975 + "type": "text", 976 + "primaryKey": false, 977 + "notNull": false 978 + } 979 + }, 980 + "indexes": { 981 + "device_code_device_code_uidx": { 982 + "name": "device_code_device_code_uidx", 983 + "columns": [ 984 + { 985 + "expression": "device_code", 986 + "isExpression": false, 987 + "asc": true, 988 + "nulls": "last" 989 + } 990 + ], 991 + "isUnique": true, 992 + "concurrently": false, 993 + "method": "btree", 994 + "with": {} 995 + }, 996 + "device_code_user_code_uidx": { 997 + "name": "device_code_user_code_uidx", 998 + "columns": [ 999 + { 1000 + "expression": "user_code", 1001 + "isExpression": false, 1002 + "asc": true, 1003 + "nulls": "last" 1004 + } 1005 + ], 1006 + "isUnique": true, 1007 + "concurrently": false, 1008 + "method": "btree", 1009 + "with": {} 1010 + }, 1011 + "device_code_user_id_idx": { 1012 + "name": "device_code_user_id_idx", 1013 + "columns": [ 1014 + { 1015 + "expression": "user_id", 1016 + "isExpression": false, 1017 + "asc": true, 1018 + "nulls": "last" 1019 + } 1020 + ], 1021 + "isUnique": false, 1022 + "concurrently": false, 1023 + "method": "btree", 1024 + "with": {} 1025 + } 1026 + }, 1027 + "foreignKeys": { 1028 + "device_code_user_id_user_id_fk": { 1029 + "name": "device_code_user_id_user_id_fk", 1030 + "tableFrom": "device_code", 1031 + "tableTo": "user", 1032 + "columnsFrom": ["user_id"], 1033 + "columnsTo": ["id"], 1034 + "onDelete": "cascade", 1035 + "onUpdate": "cascade" 1036 + } 1037 + }, 1038 + "compositePrimaryKeys": {}, 1039 + "uniqueConstraints": {}, 1040 + "policies": {}, 1041 + "checkConstraints": {}, 1042 + "isRLSEnabled": false 1043 + }, 1044 + "public.external_link": { 1045 + "name": "external_link", 1046 + "schema": "", 1047 + "columns": { 1048 + "id": { 1049 + "name": "id", 1050 + "type": "text", 1051 + "primaryKey": true, 1052 + "notNull": true 1053 + }, 1054 + "task_id": { 1055 + "name": "task_id", 1056 + "type": "text", 1057 + "primaryKey": false, 1058 + "notNull": true 1059 + }, 1060 + "integration_id": { 1061 + "name": "integration_id", 1062 + "type": "text", 1063 + "primaryKey": false, 1064 + "notNull": true 1065 + }, 1066 + "resource_type": { 1067 + "name": "resource_type", 1068 + "type": "text", 1069 + "primaryKey": false, 1070 + "notNull": true 1071 + }, 1072 + "external_id": { 1073 + "name": "external_id", 1074 + "type": "text", 1075 + "primaryKey": false, 1076 + "notNull": true 1077 + }, 1078 + "url": { 1079 + "name": "url", 1080 + "type": "text", 1081 + "primaryKey": false, 1082 + "notNull": true 1083 + }, 1084 + "title": { 1085 + "name": "title", 1086 + "type": "text", 1087 + "primaryKey": false, 1088 + "notNull": false 1089 + }, 1090 + "metadata": { 1091 + "name": "metadata", 1092 + "type": "text", 1093 + "primaryKey": false, 1094 + "notNull": false 1095 + }, 1096 + "created_at": { 1097 + "name": "created_at", 1098 + "type": "timestamp", 1099 + "primaryKey": false, 1100 + "notNull": true, 1101 + "default": "now()" 1102 + }, 1103 + "updated_at": { 1104 + "name": "updated_at", 1105 + "type": "timestamp", 1106 + "primaryKey": false, 1107 + "notNull": true, 1108 + "default": "now()" 1109 + } 1110 + }, 1111 + "indexes": { 1112 + "external_link_taskId_idx": { 1113 + "name": "external_link_taskId_idx", 1114 + "columns": [ 1115 + { 1116 + "expression": "task_id", 1117 + "isExpression": false, 1118 + "asc": true, 1119 + "nulls": "last" 1120 + } 1121 + ], 1122 + "isUnique": false, 1123 + "concurrently": false, 1124 + "method": "btree", 1125 + "with": {} 1126 + }, 1127 + "external_link_integrationId_idx": { 1128 + "name": "external_link_integrationId_idx", 1129 + "columns": [ 1130 + { 1131 + "expression": "integration_id", 1132 + "isExpression": false, 1133 + "asc": true, 1134 + "nulls": "last" 1135 + } 1136 + ], 1137 + "isUnique": false, 1138 + "concurrently": false, 1139 + "method": "btree", 1140 + "with": {} 1141 + }, 1142 + "external_link_externalId_idx": { 1143 + "name": "external_link_externalId_idx", 1144 + "columns": [ 1145 + { 1146 + "expression": "external_id", 1147 + "isExpression": false, 1148 + "asc": true, 1149 + "nulls": "last" 1150 + } 1151 + ], 1152 + "isUnique": false, 1153 + "concurrently": false, 1154 + "method": "btree", 1155 + "with": {} 1156 + }, 1157 + "external_link_resourceType_idx": { 1158 + "name": "external_link_resourceType_idx", 1159 + "columns": [ 1160 + { 1161 + "expression": "resource_type", 1162 + "isExpression": false, 1163 + "asc": true, 1164 + "nulls": "last" 1165 + } 1166 + ], 1167 + "isUnique": false, 1168 + "concurrently": false, 1169 + "method": "btree", 1170 + "with": {} 1171 + } 1172 + }, 1173 + "foreignKeys": { 1174 + "external_link_task_id_task_id_fk": { 1175 + "name": "external_link_task_id_task_id_fk", 1176 + "tableFrom": "external_link", 1177 + "tableTo": "task", 1178 + "columnsFrom": ["task_id"], 1179 + "columnsTo": ["id"], 1180 + "onDelete": "cascade", 1181 + "onUpdate": "cascade" 1182 + }, 1183 + "external_link_integration_id_integration_id_fk": { 1184 + "name": "external_link_integration_id_integration_id_fk", 1185 + "tableFrom": "external_link", 1186 + "tableTo": "integration", 1187 + "columnsFrom": ["integration_id"], 1188 + "columnsTo": ["id"], 1189 + "onDelete": "cascade", 1190 + "onUpdate": "cascade" 1191 + } 1192 + }, 1193 + "compositePrimaryKeys": {}, 1194 + "uniqueConstraints": {}, 1195 + "policies": {}, 1196 + "checkConstraints": {}, 1197 + "isRLSEnabled": false 1198 + }, 1199 + "public.github_integration": { 1200 + "name": "github_integration", 1201 + "schema": "", 1202 + "columns": { 1203 + "id": { 1204 + "name": "id", 1205 + "type": "text", 1206 + "primaryKey": true, 1207 + "notNull": true 1208 + }, 1209 + "project_id": { 1210 + "name": "project_id", 1211 + "type": "text", 1212 + "primaryKey": false, 1213 + "notNull": true 1214 + }, 1215 + "repository_owner": { 1216 + "name": "repository_owner", 1217 + "type": "text", 1218 + "primaryKey": false, 1219 + "notNull": true 1220 + }, 1221 + "repository_name": { 1222 + "name": "repository_name", 1223 + "type": "text", 1224 + "primaryKey": false, 1225 + "notNull": true 1226 + }, 1227 + "installation_id": { 1228 + "name": "installation_id", 1229 + "type": "integer", 1230 + "primaryKey": false, 1231 + "notNull": false 1232 + }, 1233 + "is_active": { 1234 + "name": "is_active", 1235 + "type": "boolean", 1236 + "primaryKey": false, 1237 + "notNull": false, 1238 + "default": true 1239 + }, 1240 + "created_at": { 1241 + "name": "created_at", 1242 + "type": "timestamp", 1243 + "primaryKey": false, 1244 + "notNull": true, 1245 + "default": "now()" 1246 + }, 1247 + "updated_at": { 1248 + "name": "updated_at", 1249 + "type": "timestamp", 1250 + "primaryKey": false, 1251 + "notNull": true, 1252 + "default": "now()" 1253 + } 1254 + }, 1255 + "indexes": {}, 1256 + "foreignKeys": { 1257 + "github_integration_project_id_project_id_fk": { 1258 + "name": "github_integration_project_id_project_id_fk", 1259 + "tableFrom": "github_integration", 1260 + "tableTo": "project", 1261 + "columnsFrom": ["project_id"], 1262 + "columnsTo": ["id"], 1263 + "onDelete": "cascade", 1264 + "onUpdate": "cascade" 1265 + } 1266 + }, 1267 + "compositePrimaryKeys": {}, 1268 + "uniqueConstraints": { 1269 + "github_integration_project_id_unique": { 1270 + "name": "github_integration_project_id_unique", 1271 + "nullsNotDistinct": false, 1272 + "columns": ["project_id"] 1273 + } 1274 + }, 1275 + "policies": {}, 1276 + "checkConstraints": {}, 1277 + "isRLSEnabled": false 1278 + }, 1279 + "public.integration": { 1280 + "name": "integration", 1281 + "schema": "", 1282 + "columns": { 1283 + "id": { 1284 + "name": "id", 1285 + "type": "text", 1286 + "primaryKey": true, 1287 + "notNull": true 1288 + }, 1289 + "project_id": { 1290 + "name": "project_id", 1291 + "type": "text", 1292 + "primaryKey": false, 1293 + "notNull": true 1294 + }, 1295 + "type": { 1296 + "name": "type", 1297 + "type": "text", 1298 + "primaryKey": false, 1299 + "notNull": true 1300 + }, 1301 + "config": { 1302 + "name": "config", 1303 + "type": "text", 1304 + "primaryKey": false, 1305 + "notNull": true 1306 + }, 1307 + "is_active": { 1308 + "name": "is_active", 1309 + "type": "boolean", 1310 + "primaryKey": false, 1311 + "notNull": false, 1312 + "default": true 1313 + }, 1314 + "created_at": { 1315 + "name": "created_at", 1316 + "type": "timestamp", 1317 + "primaryKey": false, 1318 + "notNull": true, 1319 + "default": "now()" 1320 + }, 1321 + "updated_at": { 1322 + "name": "updated_at", 1323 + "type": "timestamp", 1324 + "primaryKey": false, 1325 + "notNull": true, 1326 + "default": "now()" 1327 + } 1328 + }, 1329 + "indexes": { 1330 + "integration_projectId_idx": { 1331 + "name": "integration_projectId_idx", 1332 + "columns": [ 1333 + { 1334 + "expression": "project_id", 1335 + "isExpression": false, 1336 + "asc": true, 1337 + "nulls": "last" 1338 + } 1339 + ], 1340 + "isUnique": false, 1341 + "concurrently": false, 1342 + "method": "btree", 1343 + "with": {} 1344 + }, 1345 + "integration_type_idx": { 1346 + "name": "integration_type_idx", 1347 + "columns": [ 1348 + { 1349 + "expression": "type", 1350 + "isExpression": false, 1351 + "asc": true, 1352 + "nulls": "last" 1353 + } 1354 + ], 1355 + "isUnique": false, 1356 + "concurrently": false, 1357 + "method": "btree", 1358 + "with": {} 1359 + } 1360 + }, 1361 + "foreignKeys": { 1362 + "integration_project_id_project_id_fk": { 1363 + "name": "integration_project_id_project_id_fk", 1364 + "tableFrom": "integration", 1365 + "tableTo": "project", 1366 + "columnsFrom": ["project_id"], 1367 + "columnsTo": ["id"], 1368 + "onDelete": "cascade", 1369 + "onUpdate": "cascade" 1370 + } 1371 + }, 1372 + "compositePrimaryKeys": {}, 1373 + "uniqueConstraints": { 1374 + "integration_project_type_unique": { 1375 + "name": "integration_project_type_unique", 1376 + "nullsNotDistinct": false, 1377 + "columns": ["project_id", "type"] 1378 + } 1379 + }, 1380 + "policies": {}, 1381 + "checkConstraints": {}, 1382 + "isRLSEnabled": false 1383 + }, 1384 + "public.invitation": { 1385 + "name": "invitation", 1386 + "schema": "", 1387 + "columns": { 1388 + "id": { 1389 + "name": "id", 1390 + "type": "text", 1391 + "primaryKey": true, 1392 + "notNull": true 1393 + }, 1394 + "workspace_id": { 1395 + "name": "workspace_id", 1396 + "type": "text", 1397 + "primaryKey": false, 1398 + "notNull": true 1399 + }, 1400 + "email": { 1401 + "name": "email", 1402 + "type": "text", 1403 + "primaryKey": false, 1404 + "notNull": true 1405 + }, 1406 + "role": { 1407 + "name": "role", 1408 + "type": "text", 1409 + "primaryKey": false, 1410 + "notNull": false 1411 + }, 1412 + "team_id": { 1413 + "name": "team_id", 1414 + "type": "text", 1415 + "primaryKey": false, 1416 + "notNull": false 1417 + }, 1418 + "status": { 1419 + "name": "status", 1420 + "type": "text", 1421 + "primaryKey": false, 1422 + "notNull": true, 1423 + "default": "'pending'" 1424 + }, 1425 + "expires_at": { 1426 + "name": "expires_at", 1427 + "type": "timestamp", 1428 + "primaryKey": false, 1429 + "notNull": true 1430 + }, 1431 + "created_at": { 1432 + "name": "created_at", 1433 + "type": "timestamp", 1434 + "primaryKey": false, 1435 + "notNull": true, 1436 + "default": "now()" 1437 + }, 1438 + "inviter_id": { 1439 + "name": "inviter_id", 1440 + "type": "text", 1441 + "primaryKey": false, 1442 + "notNull": true 1443 + } 1444 + }, 1445 + "indexes": { 1446 + "invitation_workspaceId_idx": { 1447 + "name": "invitation_workspaceId_idx", 1448 + "columns": [ 1449 + { 1450 + "expression": "workspace_id", 1451 + "isExpression": false, 1452 + "asc": true, 1453 + "nulls": "last" 1454 + } 1455 + ], 1456 + "isUnique": false, 1457 + "concurrently": false, 1458 + "method": "btree", 1459 + "with": {} 1460 + }, 1461 + "invitation_email_idx": { 1462 + "name": "invitation_email_idx", 1463 + "columns": [ 1464 + { 1465 + "expression": "email", 1466 + "isExpression": false, 1467 + "asc": true, 1468 + "nulls": "last" 1469 + } 1470 + ], 1471 + "isUnique": false, 1472 + "concurrently": false, 1473 + "method": "btree", 1474 + "with": {} 1475 + } 1476 + }, 1477 + "foreignKeys": { 1478 + "invitation_workspace_id_workspace_id_fk": { 1479 + "name": "invitation_workspace_id_workspace_id_fk", 1480 + "tableFrom": "invitation", 1481 + "tableTo": "workspace", 1482 + "columnsFrom": ["workspace_id"], 1483 + "columnsTo": ["id"], 1484 + "onDelete": "cascade", 1485 + "onUpdate": "no action" 1486 + }, 1487 + "invitation_inviter_id_user_id_fk": { 1488 + "name": "invitation_inviter_id_user_id_fk", 1489 + "tableFrom": "invitation", 1490 + "tableTo": "user", 1491 + "columnsFrom": ["inviter_id"], 1492 + "columnsTo": ["id"], 1493 + "onDelete": "cascade", 1494 + "onUpdate": "no action" 1495 + } 1496 + }, 1497 + "compositePrimaryKeys": {}, 1498 + "uniqueConstraints": {}, 1499 + "policies": {}, 1500 + "checkConstraints": {}, 1501 + "isRLSEnabled": false 1502 + }, 1503 + "public.label": { 1504 + "name": "label", 1505 + "schema": "", 1506 + "columns": { 1507 + "id": { 1508 + "name": "id", 1509 + "type": "text", 1510 + "primaryKey": true, 1511 + "notNull": true 1512 + }, 1513 + "name": { 1514 + "name": "name", 1515 + "type": "text", 1516 + "primaryKey": false, 1517 + "notNull": true 1518 + }, 1519 + "color": { 1520 + "name": "color", 1521 + "type": "text", 1522 + "primaryKey": false, 1523 + "notNull": true 1524 + }, 1525 + "created_at": { 1526 + "name": "created_at", 1527 + "type": "timestamp", 1528 + "primaryKey": false, 1529 + "notNull": true, 1530 + "default": "now()" 1531 + }, 1532 + "updated_at": { 1533 + "name": "updated_at", 1534 + "type": "timestamp", 1535 + "primaryKey": false, 1536 + "notNull": true, 1537 + "default": "now()" 1538 + }, 1539 + "task_id": { 1540 + "name": "task_id", 1541 + "type": "text", 1542 + "primaryKey": false, 1543 + "notNull": false 1544 + }, 1545 + "workspace_id": { 1546 + "name": "workspace_id", 1547 + "type": "text", 1548 + "primaryKey": false, 1549 + "notNull": false 1550 + } 1551 + }, 1552 + "indexes": { 1553 + "label_task_id_idx": { 1554 + "name": "label_task_id_idx", 1555 + "columns": [ 1556 + { 1557 + "expression": "task_id", 1558 + "isExpression": false, 1559 + "asc": true, 1560 + "nulls": "last" 1561 + } 1562 + ], 1563 + "isUnique": false, 1564 + "concurrently": false, 1565 + "method": "btree", 1566 + "with": {} 1567 + }, 1568 + "label_workspace_id_idx": { 1569 + "name": "label_workspace_id_idx", 1570 + "columns": [ 1571 + { 1572 + "expression": "workspace_id", 1573 + "isExpression": false, 1574 + "asc": true, 1575 + "nulls": "last" 1576 + } 1577 + ], 1578 + "isUnique": false, 1579 + "concurrently": false, 1580 + "method": "btree", 1581 + "with": {} 1582 + }, 1583 + "label_workspace_name_unique": { 1584 + "name": "label_workspace_name_unique", 1585 + "columns": [ 1586 + { 1587 + "expression": "workspace_id", 1588 + "isExpression": false, 1589 + "asc": true, 1590 + "nulls": "last" 1591 + }, 1592 + { 1593 + "expression": "name", 1594 + "isExpression": false, 1595 + "asc": true, 1596 + "nulls": "last" 1597 + } 1598 + ], 1599 + "isUnique": true, 1600 + "where": "\"label\".\"task_id\" is null", 1601 + "concurrently": false, 1602 + "method": "btree", 1603 + "with": {} 1604 + } 1605 + }, 1606 + "foreignKeys": { 1607 + "label_task_id_task_id_fk": { 1608 + "name": "label_task_id_task_id_fk", 1609 + "tableFrom": "label", 1610 + "tableTo": "task", 1611 + "columnsFrom": ["task_id"], 1612 + "columnsTo": ["id"], 1613 + "onDelete": "cascade", 1614 + "onUpdate": "cascade" 1615 + }, 1616 + "label_workspace_id_workspace_id_fk": { 1617 + "name": "label_workspace_id_workspace_id_fk", 1618 + "tableFrom": "label", 1619 + "tableTo": "workspace", 1620 + "columnsFrom": ["workspace_id"], 1621 + "columnsTo": ["id"], 1622 + "onDelete": "cascade", 1623 + "onUpdate": "cascade" 1624 + } 1625 + }, 1626 + "compositePrimaryKeys": {}, 1627 + "uniqueConstraints": { 1628 + "label_task_name_unique": { 1629 + "name": "label_task_name_unique", 1630 + "nullsNotDistinct": false, 1631 + "columns": ["task_id", "name"] 1632 + } 1633 + }, 1634 + "policies": {}, 1635 + "checkConstraints": {}, 1636 + "isRLSEnabled": false 1637 + }, 1638 + "public.notification": { 1639 + "name": "notification", 1640 + "schema": "", 1641 + "columns": { 1642 + "id": { 1643 + "name": "id", 1644 + "type": "text", 1645 + "primaryKey": true, 1646 + "notNull": true 1647 + }, 1648 + "user_id": { 1649 + "name": "user_id", 1650 + "type": "text", 1651 + "primaryKey": false, 1652 + "notNull": true 1653 + }, 1654 + "title": { 1655 + "name": "title", 1656 + "type": "text", 1657 + "primaryKey": false, 1658 + "notNull": false 1659 + }, 1660 + "content": { 1661 + "name": "content", 1662 + "type": "text", 1663 + "primaryKey": false, 1664 + "notNull": false 1665 + }, 1666 + "type": { 1667 + "name": "type", 1668 + "type": "text", 1669 + "primaryKey": false, 1670 + "notNull": true, 1671 + "default": "'info'" 1672 + }, 1673 + "event_data": { 1674 + "name": "event_data", 1675 + "type": "jsonb", 1676 + "primaryKey": false, 1677 + "notNull": false 1678 + }, 1679 + "is_read": { 1680 + "name": "is_read", 1681 + "type": "boolean", 1682 + "primaryKey": false, 1683 + "notNull": false, 1684 + "default": false 1685 + }, 1686 + "resource_id": { 1687 + "name": "resource_id", 1688 + "type": "text", 1689 + "primaryKey": false, 1690 + "notNull": false 1691 + }, 1692 + "resource_type": { 1693 + "name": "resource_type", 1694 + "type": "text", 1695 + "primaryKey": false, 1696 + "notNull": false 1697 + }, 1698 + "created_at": { 1699 + "name": "created_at", 1700 + "type": "timestamp with time zone", 1701 + "primaryKey": false, 1702 + "notNull": true, 1703 + "default": "now()" 1704 + } 1705 + }, 1706 + "indexes": {}, 1707 + "foreignKeys": { 1708 + "notification_user_id_user_id_fk": { 1709 + "name": "notification_user_id_user_id_fk", 1710 + "tableFrom": "notification", 1711 + "tableTo": "user", 1712 + "columnsFrom": ["user_id"], 1713 + "columnsTo": ["id"], 1714 + "onDelete": "cascade", 1715 + "onUpdate": "cascade" 1716 + } 1717 + }, 1718 + "compositePrimaryKeys": {}, 1719 + "uniqueConstraints": {}, 1720 + "policies": {}, 1721 + "checkConstraints": {}, 1722 + "isRLSEnabled": false 1723 + }, 1724 + "public.project": { 1725 + "name": "project", 1726 + "schema": "", 1727 + "columns": { 1728 + "id": { 1729 + "name": "id", 1730 + "type": "text", 1731 + "primaryKey": true, 1732 + "notNull": true 1733 + }, 1734 + "workspace_id": { 1735 + "name": "workspace_id", 1736 + "type": "text", 1737 + "primaryKey": false, 1738 + "notNull": true 1739 + }, 1740 + "slug": { 1741 + "name": "slug", 1742 + "type": "text", 1743 + "primaryKey": false, 1744 + "notNull": true 1745 + }, 1746 + "icon": { 1747 + "name": "icon", 1748 + "type": "text", 1749 + "primaryKey": false, 1750 + "notNull": false, 1751 + "default": "'Layout'" 1752 + }, 1753 + "name": { 1754 + "name": "name", 1755 + "type": "text", 1756 + "primaryKey": false, 1757 + "notNull": true 1758 + }, 1759 + "description": { 1760 + "name": "description", 1761 + "type": "text", 1762 + "primaryKey": false, 1763 + "notNull": false 1764 + }, 1765 + "created_at": { 1766 + "name": "created_at", 1767 + "type": "timestamp", 1768 + "primaryKey": false, 1769 + "notNull": true, 1770 + "default": "now()" 1771 + }, 1772 + "is_public": { 1773 + "name": "is_public", 1774 + "type": "boolean", 1775 + "primaryKey": false, 1776 + "notNull": false, 1777 + "default": false 1778 + }, 1779 + "archived_at": { 1780 + "name": "archived_at", 1781 + "type": "timestamp", 1782 + "primaryKey": false, 1783 + "notNull": false 1784 + } 1785 + }, 1786 + "indexes": {}, 1787 + "foreignKeys": { 1788 + "project_workspace_id_workspace_id_fk": { 1789 + "name": "project_workspace_id_workspace_id_fk", 1790 + "tableFrom": "project", 1791 + "tableTo": "workspace", 1792 + "columnsFrom": ["workspace_id"], 1793 + "columnsTo": ["id"], 1794 + "onDelete": "cascade", 1795 + "onUpdate": "cascade" 1796 + } 1797 + }, 1798 + "compositePrimaryKeys": {}, 1799 + "uniqueConstraints": { 1800 + "project_workspace_id_id_unique": { 1801 + "name": "project_workspace_id_id_unique", 1802 + "nullsNotDistinct": false, 1803 + "columns": ["workspace_id", "id"] 1804 + } 1805 + }, 1806 + "policies": {}, 1807 + "checkConstraints": {}, 1808 + "isRLSEnabled": false 1809 + }, 1810 + "public.session": { 1811 + "name": "session", 1812 + "schema": "", 1813 + "columns": { 1814 + "id": { 1815 + "name": "id", 1816 + "type": "text", 1817 + "primaryKey": true, 1818 + "notNull": true 1819 + }, 1820 + "expires_at": { 1821 + "name": "expires_at", 1822 + "type": "timestamp", 1823 + "primaryKey": false, 1824 + "notNull": true 1825 + }, 1826 + "token": { 1827 + "name": "token", 1828 + "type": "text", 1829 + "primaryKey": false, 1830 + "notNull": true 1831 + }, 1832 + "created_at": { 1833 + "name": "created_at", 1834 + "type": "timestamp", 1835 + "primaryKey": false, 1836 + "notNull": true, 1837 + "default": "now()" 1838 + }, 1839 + "updated_at": { 1840 + "name": "updated_at", 1841 + "type": "timestamp", 1842 + "primaryKey": false, 1843 + "notNull": true 1844 + }, 1845 + "ip_address": { 1846 + "name": "ip_address", 1847 + "type": "text", 1848 + "primaryKey": false, 1849 + "notNull": false 1850 + }, 1851 + "user_agent": { 1852 + "name": "user_agent", 1853 + "type": "text", 1854 + "primaryKey": false, 1855 + "notNull": false 1856 + }, 1857 + "user_id": { 1858 + "name": "user_id", 1859 + "type": "text", 1860 + "primaryKey": false, 1861 + "notNull": true 1862 + }, 1863 + "active_organization_id": { 1864 + "name": "active_organization_id", 1865 + "type": "text", 1866 + "primaryKey": false, 1867 + "notNull": false 1868 + }, 1869 + "active_team_id": { 1870 + "name": "active_team_id", 1871 + "type": "text", 1872 + "primaryKey": false, 1873 + "notNull": false 1874 + } 1875 + }, 1876 + "indexes": { 1877 + "session_userId_idx": { 1878 + "name": "session_userId_idx", 1879 + "columns": [ 1880 + { 1881 + "expression": "user_id", 1882 + "isExpression": false, 1883 + "asc": true, 1884 + "nulls": "last" 1885 + } 1886 + ], 1887 + "isUnique": false, 1888 + "concurrently": false, 1889 + "method": "btree", 1890 + "with": {} 1891 + } 1892 + }, 1893 + "foreignKeys": { 1894 + "session_user_id_user_id_fk": { 1895 + "name": "session_user_id_user_id_fk", 1896 + "tableFrom": "session", 1897 + "tableTo": "user", 1898 + "columnsFrom": ["user_id"], 1899 + "columnsTo": ["id"], 1900 + "onDelete": "cascade", 1901 + "onUpdate": "no action" 1902 + } 1903 + }, 1904 + "compositePrimaryKeys": {}, 1905 + "uniqueConstraints": { 1906 + "session_token_unique": { 1907 + "name": "session_token_unique", 1908 + "nullsNotDistinct": false, 1909 + "columns": ["token"] 1910 + } 1911 + }, 1912 + "policies": {}, 1913 + "checkConstraints": {}, 1914 + "isRLSEnabled": false 1915 + }, 1916 + "public.task_relation": { 1917 + "name": "task_relation", 1918 + "schema": "", 1919 + "columns": { 1920 + "id": { 1921 + "name": "id", 1922 + "type": "text", 1923 + "primaryKey": true, 1924 + "notNull": true 1925 + }, 1926 + "source_task_id": { 1927 + "name": "source_task_id", 1928 + "type": "text", 1929 + "primaryKey": false, 1930 + "notNull": true 1931 + }, 1932 + "target_task_id": { 1933 + "name": "target_task_id", 1934 + "type": "text", 1935 + "primaryKey": false, 1936 + "notNull": true 1937 + }, 1938 + "relation_type": { 1939 + "name": "relation_type", 1940 + "type": "text", 1941 + "primaryKey": false, 1942 + "notNull": true 1943 + }, 1944 + "created_at": { 1945 + "name": "created_at", 1946 + "type": "timestamp", 1947 + "primaryKey": false, 1948 + "notNull": true, 1949 + "default": "now()" 1950 + } 1951 + }, 1952 + "indexes": { 1953 + "task_relation_source_idx": { 1954 + "name": "task_relation_source_idx", 1955 + "columns": [ 1956 + { 1957 + "expression": "source_task_id", 1958 + "isExpression": false, 1959 + "asc": true, 1960 + "nulls": "last" 1961 + } 1962 + ], 1963 + "isUnique": false, 1964 + "concurrently": false, 1965 + "method": "btree", 1966 + "with": {} 1967 + }, 1968 + "task_relation_target_idx": { 1969 + "name": "task_relation_target_idx", 1970 + "columns": [ 1971 + { 1972 + "expression": "target_task_id", 1973 + "isExpression": false, 1974 + "asc": true, 1975 + "nulls": "last" 1976 + } 1977 + ], 1978 + "isUnique": false, 1979 + "concurrently": false, 1980 + "method": "btree", 1981 + "with": {} 1982 + } 1983 + }, 1984 + "foreignKeys": { 1985 + "task_relation_source_task_id_task_id_fk": { 1986 + "name": "task_relation_source_task_id_task_id_fk", 1987 + "tableFrom": "task_relation", 1988 + "tableTo": "task", 1989 + "columnsFrom": ["source_task_id"], 1990 + "columnsTo": ["id"], 1991 + "onDelete": "cascade", 1992 + "onUpdate": "cascade" 1993 + }, 1994 + "task_relation_target_task_id_task_id_fk": { 1995 + "name": "task_relation_target_task_id_task_id_fk", 1996 + "tableFrom": "task_relation", 1997 + "tableTo": "task", 1998 + "columnsFrom": ["target_task_id"], 1999 + "columnsTo": ["id"], 2000 + "onDelete": "cascade", 2001 + "onUpdate": "cascade" 2002 + } 2003 + }, 2004 + "compositePrimaryKeys": {}, 2005 + "uniqueConstraints": {}, 2006 + "policies": {}, 2007 + "checkConstraints": {}, 2008 + "isRLSEnabled": false 2009 + }, 2010 + "public.task_reminder_sent": { 2011 + "name": "task_reminder_sent", 2012 + "schema": "", 2013 + "columns": { 2014 + "id": { 2015 + "name": "id", 2016 + "type": "text", 2017 + "primaryKey": true, 2018 + "notNull": true 2019 + }, 2020 + "task_id": { 2021 + "name": "task_id", 2022 + "type": "text", 2023 + "primaryKey": false, 2024 + "notNull": true 2025 + }, 2026 + "reminder_type": { 2027 + "name": "reminder_type", 2028 + "type": "text", 2029 + "primaryKey": false, 2030 + "notNull": true 2031 + }, 2032 + "created_at": { 2033 + "name": "created_at", 2034 + "type": "timestamp", 2035 + "primaryKey": false, 2036 + "notNull": true, 2037 + "default": "now()" 2038 + }, 2039 + "updated_at": { 2040 + "name": "updated_at", 2041 + "type": "timestamp", 2042 + "primaryKey": false, 2043 + "notNull": true, 2044 + "default": "now()" 2045 + } 2046 + }, 2047 + "indexes": { 2048 + "task_reminder_sent_taskId_idx": { 2049 + "name": "task_reminder_sent_taskId_idx", 2050 + "columns": [ 2051 + { 2052 + "expression": "task_id", 2053 + "isExpression": false, 2054 + "asc": true, 2055 + "nulls": "last" 2056 + } 2057 + ], 2058 + "isUnique": false, 2059 + "concurrently": false, 2060 + "method": "btree", 2061 + "with": {} 2062 + } 2063 + }, 2064 + "foreignKeys": { 2065 + "task_reminder_sent_task_id_task_id_fk": { 2066 + "name": "task_reminder_sent_task_id_task_id_fk", 2067 + "tableFrom": "task_reminder_sent", 2068 + "tableTo": "task", 2069 + "columnsFrom": ["task_id"], 2070 + "columnsTo": ["id"], 2071 + "onDelete": "cascade", 2072 + "onUpdate": "cascade" 2073 + } 2074 + }, 2075 + "compositePrimaryKeys": {}, 2076 + "uniqueConstraints": { 2077 + "task_reminder_sent_task_type_unique": { 2078 + "name": "task_reminder_sent_task_type_unique", 2079 + "nullsNotDistinct": false, 2080 + "columns": ["task_id", "reminder_type"] 2081 + } 2082 + }, 2083 + "policies": {}, 2084 + "checkConstraints": {}, 2085 + "isRLSEnabled": false 2086 + }, 2087 + "public.task": { 2088 + "name": "task", 2089 + "schema": "", 2090 + "columns": { 2091 + "id": { 2092 + "name": "id", 2093 + "type": "text", 2094 + "primaryKey": true, 2095 + "notNull": true 2096 + }, 2097 + "project_id": { 2098 + "name": "project_id", 2099 + "type": "text", 2100 + "primaryKey": false, 2101 + "notNull": true 2102 + }, 2103 + "position": { 2104 + "name": "position", 2105 + "type": "integer", 2106 + "primaryKey": false, 2107 + "notNull": false, 2108 + "default": 0 2109 + }, 2110 + "number": { 2111 + "name": "number", 2112 + "type": "integer", 2113 + "primaryKey": false, 2114 + "notNull": false, 2115 + "default": 1 2116 + }, 2117 + "assignee_id": { 2118 + "name": "assignee_id", 2119 + "type": "text", 2120 + "primaryKey": false, 2121 + "notNull": false 2122 + }, 2123 + "title": { 2124 + "name": "title", 2125 + "type": "text", 2126 + "primaryKey": false, 2127 + "notNull": true 2128 + }, 2129 + "description": { 2130 + "name": "description", 2131 + "type": "text", 2132 + "primaryKey": false, 2133 + "notNull": false 2134 + }, 2135 + "status": { 2136 + "name": "status", 2137 + "type": "text", 2138 + "primaryKey": false, 2139 + "notNull": true, 2140 + "default": "'to-do'" 2141 + }, 2142 + "column_id": { 2143 + "name": "column_id", 2144 + "type": "text", 2145 + "primaryKey": false, 2146 + "notNull": false 2147 + }, 2148 + "priority": { 2149 + "name": "priority", 2150 + "type": "text", 2151 + "primaryKey": false, 2152 + "notNull": false, 2153 + "default": "'low'" 2154 + }, 2155 + "start_date": { 2156 + "name": "start_date", 2157 + "type": "timestamp", 2158 + "primaryKey": false, 2159 + "notNull": false 2160 + }, 2161 + "due_date": { 2162 + "name": "due_date", 2163 + "type": "timestamp", 2164 + "primaryKey": false, 2165 + "notNull": false 2166 + }, 2167 + "created_at": { 2168 + "name": "created_at", 2169 + "type": "timestamp", 2170 + "primaryKey": false, 2171 + "notNull": true, 2172 + "default": "now()" 2173 + }, 2174 + "updated_at": { 2175 + "name": "updated_at", 2176 + "type": "timestamp", 2177 + "primaryKey": false, 2178 + "notNull": true, 2179 + "default": "now()" 2180 + } 2181 + }, 2182 + "indexes": { 2183 + "task_projectId_idx": { 2184 + "name": "task_projectId_idx", 2185 + "columns": [ 2186 + { 2187 + "expression": "project_id", 2188 + "isExpression": false, 2189 + "asc": true, 2190 + "nulls": "last" 2191 + } 2192 + ], 2193 + "isUnique": false, 2194 + "concurrently": false, 2195 + "method": "btree", 2196 + "with": {} 2197 + }, 2198 + "task_dueDate_idx": { 2199 + "name": "task_dueDate_idx", 2200 + "columns": [ 2201 + { 2202 + "expression": "due_date", 2203 + "isExpression": false, 2204 + "asc": true, 2205 + "nulls": "last" 2206 + } 2207 + ], 2208 + "isUnique": false, 2209 + "concurrently": false, 2210 + "method": "btree", 2211 + "with": {} 2212 + } 2213 + }, 2214 + "foreignKeys": { 2215 + "task_project_id_project_id_fk": { 2216 + "name": "task_project_id_project_id_fk", 2217 + "tableFrom": "task", 2218 + "tableTo": "project", 2219 + "columnsFrom": ["project_id"], 2220 + "columnsTo": ["id"], 2221 + "onDelete": "cascade", 2222 + "onUpdate": "cascade" 2223 + }, 2224 + "task_assignee_id_user_id_fk": { 2225 + "name": "task_assignee_id_user_id_fk", 2226 + "tableFrom": "task", 2227 + "tableTo": "user", 2228 + "columnsFrom": ["assignee_id"], 2229 + "columnsTo": ["id"], 2230 + "onDelete": "cascade", 2231 + "onUpdate": "cascade" 2232 + }, 2233 + "task_column_id_column_id_fk": { 2234 + "name": "task_column_id_column_id_fk", 2235 + "tableFrom": "task", 2236 + "tableTo": "column", 2237 + "columnsFrom": ["column_id"], 2238 + "columnsTo": ["id"], 2239 + "onDelete": "set null", 2240 + "onUpdate": "cascade" 2241 + } 2242 + }, 2243 + "compositePrimaryKeys": {}, 2244 + "uniqueConstraints": { 2245 + "task_project_number_unique": { 2246 + "name": "task_project_number_unique", 2247 + "nullsNotDistinct": false, 2248 + "columns": ["project_id", "number"] 2249 + } 2250 + }, 2251 + "policies": {}, 2252 + "checkConstraints": {}, 2253 + "isRLSEnabled": false 2254 + }, 2255 + "public.team": { 2256 + "name": "team", 2257 + "schema": "", 2258 + "columns": { 2259 + "id": { 2260 + "name": "id", 2261 + "type": "text", 2262 + "primaryKey": true, 2263 + "notNull": true 2264 + }, 2265 + "name": { 2266 + "name": "name", 2267 + "type": "text", 2268 + "primaryKey": false, 2269 + "notNull": true 2270 + }, 2271 + "workspace_id": { 2272 + "name": "workspace_id", 2273 + "type": "text", 2274 + "primaryKey": false, 2275 + "notNull": true 2276 + }, 2277 + "created_at": { 2278 + "name": "created_at", 2279 + "type": "timestamp", 2280 + "primaryKey": false, 2281 + "notNull": true 2282 + }, 2283 + "updated_at": { 2284 + "name": "updated_at", 2285 + "type": "timestamp", 2286 + "primaryKey": false, 2287 + "notNull": false 2288 + } 2289 + }, 2290 + "indexes": { 2291 + "team_workspaceId_idx": { 2292 + "name": "team_workspaceId_idx", 2293 + "columns": [ 2294 + { 2295 + "expression": "workspace_id", 2296 + "isExpression": false, 2297 + "asc": true, 2298 + "nulls": "last" 2299 + } 2300 + ], 2301 + "isUnique": false, 2302 + "concurrently": false, 2303 + "method": "btree", 2304 + "with": {} 2305 + } 2306 + }, 2307 + "foreignKeys": { 2308 + "team_workspace_id_workspace_id_fk": { 2309 + "name": "team_workspace_id_workspace_id_fk", 2310 + "tableFrom": "team", 2311 + "tableTo": "workspace", 2312 + "columnsFrom": ["workspace_id"], 2313 + "columnsTo": ["id"], 2314 + "onDelete": "cascade", 2315 + "onUpdate": "no action" 2316 + } 2317 + }, 2318 + "compositePrimaryKeys": {}, 2319 + "uniqueConstraints": {}, 2320 + "policies": {}, 2321 + "checkConstraints": {}, 2322 + "isRLSEnabled": false 2323 + }, 2324 + "public.team_member": { 2325 + "name": "team_member", 2326 + "schema": "", 2327 + "columns": { 2328 + "id": { 2329 + "name": "id", 2330 + "type": "text", 2331 + "primaryKey": true, 2332 + "notNull": true 2333 + }, 2334 + "team_id": { 2335 + "name": "team_id", 2336 + "type": "text", 2337 + "primaryKey": false, 2338 + "notNull": true 2339 + }, 2340 + "user_id": { 2341 + "name": "user_id", 2342 + "type": "text", 2343 + "primaryKey": false, 2344 + "notNull": true 2345 + }, 2346 + "created_at": { 2347 + "name": "created_at", 2348 + "type": "timestamp", 2349 + "primaryKey": false, 2350 + "notNull": false 2351 + } 2352 + }, 2353 + "indexes": { 2354 + "teamMember_teamId_idx": { 2355 + "name": "teamMember_teamId_idx", 2356 + "columns": [ 2357 + { 2358 + "expression": "team_id", 2359 + "isExpression": false, 2360 + "asc": true, 2361 + "nulls": "last" 2362 + } 2363 + ], 2364 + "isUnique": false, 2365 + "concurrently": false, 2366 + "method": "btree", 2367 + "with": {} 2368 + }, 2369 + "teamMember_userId_idx": { 2370 + "name": "teamMember_userId_idx", 2371 + "columns": [ 2372 + { 2373 + "expression": "user_id", 2374 + "isExpression": false, 2375 + "asc": true, 2376 + "nulls": "last" 2377 + } 2378 + ], 2379 + "isUnique": false, 2380 + "concurrently": false, 2381 + "method": "btree", 2382 + "with": {} 2383 + } 2384 + }, 2385 + "foreignKeys": { 2386 + "team_member_team_id_team_id_fk": { 2387 + "name": "team_member_team_id_team_id_fk", 2388 + "tableFrom": "team_member", 2389 + "tableTo": "team", 2390 + "columnsFrom": ["team_id"], 2391 + "columnsTo": ["id"], 2392 + "onDelete": "cascade", 2393 + "onUpdate": "no action" 2394 + }, 2395 + "team_member_user_id_user_id_fk": { 2396 + "name": "team_member_user_id_user_id_fk", 2397 + "tableFrom": "team_member", 2398 + "tableTo": "user", 2399 + "columnsFrom": ["user_id"], 2400 + "columnsTo": ["id"], 2401 + "onDelete": "cascade", 2402 + "onUpdate": "no action" 2403 + } 2404 + }, 2405 + "compositePrimaryKeys": {}, 2406 + "uniqueConstraints": {}, 2407 + "policies": {}, 2408 + "checkConstraints": {}, 2409 + "isRLSEnabled": false 2410 + }, 2411 + "public.time_entry": { 2412 + "name": "time_entry", 2413 + "schema": "", 2414 + "columns": { 2415 + "id": { 2416 + "name": "id", 2417 + "type": "text", 2418 + "primaryKey": true, 2419 + "notNull": true 2420 + }, 2421 + "task_id": { 2422 + "name": "task_id", 2423 + "type": "text", 2424 + "primaryKey": false, 2425 + "notNull": true 2426 + }, 2427 + "user_id": { 2428 + "name": "user_id", 2429 + "type": "text", 2430 + "primaryKey": false, 2431 + "notNull": false 2432 + }, 2433 + "description": { 2434 + "name": "description", 2435 + "type": "text", 2436 + "primaryKey": false, 2437 + "notNull": false 2438 + }, 2439 + "start_time": { 2440 + "name": "start_time", 2441 + "type": "timestamp", 2442 + "primaryKey": false, 2443 + "notNull": true 2444 + }, 2445 + "end_time": { 2446 + "name": "end_time", 2447 + "type": "timestamp", 2448 + "primaryKey": false, 2449 + "notNull": false 2450 + }, 2451 + "duration": { 2452 + "name": "duration", 2453 + "type": "integer", 2454 + "primaryKey": false, 2455 + "notNull": false, 2456 + "default": 0 2457 + }, 2458 + "created_at": { 2459 + "name": "created_at", 2460 + "type": "timestamp", 2461 + "primaryKey": false, 2462 + "notNull": true, 2463 + "default": "now()" 2464 + } 2465 + }, 2466 + "indexes": {}, 2467 + "foreignKeys": { 2468 + "time_entry_task_id_task_id_fk": { 2469 + "name": "time_entry_task_id_task_id_fk", 2470 + "tableFrom": "time_entry", 2471 + "tableTo": "task", 2472 + "columnsFrom": ["task_id"], 2473 + "columnsTo": ["id"], 2474 + "onDelete": "cascade", 2475 + "onUpdate": "cascade" 2476 + }, 2477 + "time_entry_user_id_user_id_fk": { 2478 + "name": "time_entry_user_id_user_id_fk", 2479 + "tableFrom": "time_entry", 2480 + "tableTo": "user", 2481 + "columnsFrom": ["user_id"], 2482 + "columnsTo": ["id"], 2483 + "onDelete": "cascade", 2484 + "onUpdate": "cascade" 2485 + } 2486 + }, 2487 + "compositePrimaryKeys": {}, 2488 + "uniqueConstraints": {}, 2489 + "policies": {}, 2490 + "checkConstraints": {}, 2491 + "isRLSEnabled": false 2492 + }, 2493 + "public.user": { 2494 + "name": "user", 2495 + "schema": "", 2496 + "columns": { 2497 + "id": { 2498 + "name": "id", 2499 + "type": "text", 2500 + "primaryKey": true, 2501 + "notNull": true 2502 + }, 2503 + "name": { 2504 + "name": "name", 2505 + "type": "text", 2506 + "primaryKey": false, 2507 + "notNull": true 2508 + }, 2509 + "email": { 2510 + "name": "email", 2511 + "type": "text", 2512 + "primaryKey": false, 2513 + "notNull": true 2514 + }, 2515 + "email_verified": { 2516 + "name": "email_verified", 2517 + "type": "boolean", 2518 + "primaryKey": false, 2519 + "notNull": true 2520 + }, 2521 + "image": { 2522 + "name": "image", 2523 + "type": "text", 2524 + "primaryKey": false, 2525 + "notNull": false 2526 + }, 2527 + "locale": { 2528 + "name": "locale", 2529 + "type": "text", 2530 + "primaryKey": false, 2531 + "notNull": false 2532 + }, 2533 + "created_at": { 2534 + "name": "created_at", 2535 + "type": "timestamp", 2536 + "primaryKey": false, 2537 + "notNull": true, 2538 + "default": "now()" 2539 + }, 2540 + "updated_at": { 2541 + "name": "updated_at", 2542 + "type": "timestamp", 2543 + "primaryKey": false, 2544 + "notNull": true, 2545 + "default": "now()" 2546 + }, 2547 + "is_anonymous": { 2548 + "name": "is_anonymous", 2549 + "type": "boolean", 2550 + "primaryKey": false, 2551 + "notNull": false, 2552 + "default": false 2553 + } 2554 + }, 2555 + "indexes": {}, 2556 + "foreignKeys": {}, 2557 + "compositePrimaryKeys": {}, 2558 + "uniqueConstraints": { 2559 + "user_email_unique": { 2560 + "name": "user_email_unique", 2561 + "nullsNotDistinct": false, 2562 + "columns": ["email"] 2563 + } 2564 + }, 2565 + "policies": {}, 2566 + "checkConstraints": {}, 2567 + "isRLSEnabled": false 2568 + }, 2569 + "public.user_notification_preference": { 2570 + "name": "user_notification_preference", 2571 + "schema": "", 2572 + "columns": { 2573 + "id": { 2574 + "name": "id", 2575 + "type": "text", 2576 + "primaryKey": true, 2577 + "notNull": true 2578 + }, 2579 + "user_id": { 2580 + "name": "user_id", 2581 + "type": "text", 2582 + "primaryKey": false, 2583 + "notNull": true 2584 + }, 2585 + "email_enabled": { 2586 + "name": "email_enabled", 2587 + "type": "boolean", 2588 + "primaryKey": false, 2589 + "notNull": true, 2590 + "default": false 2591 + }, 2592 + "ntfy_enabled": { 2593 + "name": "ntfy_enabled", 2594 + "type": "boolean", 2595 + "primaryKey": false, 2596 + "notNull": true, 2597 + "default": false 2598 + }, 2599 + "ntfy_server_url": { 2600 + "name": "ntfy_server_url", 2601 + "type": "text", 2602 + "primaryKey": false, 2603 + "notNull": false 2604 + }, 2605 + "ntfy_topic": { 2606 + "name": "ntfy_topic", 2607 + "type": "text", 2608 + "primaryKey": false, 2609 + "notNull": false 2610 + }, 2611 + "ntfy_token": { 2612 + "name": "ntfy_token", 2613 + "type": "text", 2614 + "primaryKey": false, 2615 + "notNull": false 2616 + }, 2617 + "gotify_enabled": { 2618 + "name": "gotify_enabled", 2619 + "type": "boolean", 2620 + "primaryKey": false, 2621 + "notNull": true, 2622 + "default": false 2623 + }, 2624 + "gotify_server_url": { 2625 + "name": "gotify_server_url", 2626 + "type": "text", 2627 + "primaryKey": false, 2628 + "notNull": false 2629 + }, 2630 + "gotify_token": { 2631 + "name": "gotify_token", 2632 + "type": "text", 2633 + "primaryKey": false, 2634 + "notNull": false 2635 + }, 2636 + "webhook_enabled": { 2637 + "name": "webhook_enabled", 2638 + "type": "boolean", 2639 + "primaryKey": false, 2640 + "notNull": true, 2641 + "default": false 2642 + }, 2643 + "webhook_url": { 2644 + "name": "webhook_url", 2645 + "type": "text", 2646 + "primaryKey": false, 2647 + "notNull": false 2648 + }, 2649 + "webhook_secret": { 2650 + "name": "webhook_secret", 2651 + "type": "text", 2652 + "primaryKey": false, 2653 + "notNull": false 2654 + }, 2655 + "created_at": { 2656 + "name": "created_at", 2657 + "type": "timestamp", 2658 + "primaryKey": false, 2659 + "notNull": true, 2660 + "default": "now()" 2661 + }, 2662 + "updated_at": { 2663 + "name": "updated_at", 2664 + "type": "timestamp", 2665 + "primaryKey": false, 2666 + "notNull": true, 2667 + "default": "now()" 2668 + } 2669 + }, 2670 + "indexes": {}, 2671 + "foreignKeys": { 2672 + "user_notification_preference_user_id_user_id_fk": { 2673 + "name": "user_notification_preference_user_id_user_id_fk", 2674 + "tableFrom": "user_notification_preference", 2675 + "tableTo": "user", 2676 + "columnsFrom": ["user_id"], 2677 + "columnsTo": ["id"], 2678 + "onDelete": "cascade", 2679 + "onUpdate": "cascade" 2680 + } 2681 + }, 2682 + "compositePrimaryKeys": {}, 2683 + "uniqueConstraints": { 2684 + "user_notification_preference_user_id_unique": { 2685 + "name": "user_notification_preference_user_id_unique", 2686 + "nullsNotDistinct": false, 2687 + "columns": ["user_id"] 2688 + } 2689 + }, 2690 + "policies": {}, 2691 + "checkConstraints": {}, 2692 + "isRLSEnabled": false 2693 + }, 2694 + "public.user_notification_workspace_project": { 2695 + "name": "user_notification_workspace_project", 2696 + "schema": "", 2697 + "columns": { 2698 + "id": { 2699 + "name": "id", 2700 + "type": "text", 2701 + "primaryKey": true, 2702 + "notNull": true 2703 + }, 2704 + "workspace_id": { 2705 + "name": "workspace_id", 2706 + "type": "text", 2707 + "primaryKey": false, 2708 + "notNull": true 2709 + }, 2710 + "workspace_rule_id": { 2711 + "name": "workspace_rule_id", 2712 + "type": "text", 2713 + "primaryKey": false, 2714 + "notNull": true 2715 + }, 2716 + "project_id": { 2717 + "name": "project_id", 2718 + "type": "text", 2719 + "primaryKey": false, 2720 + "notNull": true 2721 + }, 2722 + "created_at": { 2723 + "name": "created_at", 2724 + "type": "timestamp", 2725 + "primaryKey": false, 2726 + "notNull": true, 2727 + "default": "now()" 2728 + }, 2729 + "updated_at": { 2730 + "name": "updated_at", 2731 + "type": "timestamp", 2732 + "primaryKey": false, 2733 + "notNull": true, 2734 + "default": "now()" 2735 + } 2736 + }, 2737 + "indexes": { 2738 + "user_notification_workspace_project_ruleId_idx": { 2739 + "name": "user_notification_workspace_project_ruleId_idx", 2740 + "columns": [ 2741 + { 2742 + "expression": "workspace_rule_id", 2743 + "isExpression": false, 2744 + "asc": true, 2745 + "nulls": "last" 2746 + } 2747 + ], 2748 + "isUnique": false, 2749 + "concurrently": false, 2750 + "method": "btree", 2751 + "with": {} 2752 + }, 2753 + "user_notification_workspace_project_projectId_idx": { 2754 + "name": "user_notification_workspace_project_projectId_idx", 2755 + "columns": [ 2756 + { 2757 + "expression": "project_id", 2758 + "isExpression": false, 2759 + "asc": true, 2760 + "nulls": "last" 2761 + } 2762 + ], 2763 + "isUnique": false, 2764 + "concurrently": false, 2765 + "method": "btree", 2766 + "with": {} 2767 + } 2768 + }, 2769 + "foreignKeys": { 2770 + "user_notification_workspace_project_workspace_id_workspace_id_fk": { 2771 + "name": "user_notification_workspace_project_workspace_id_workspace_id_fk", 2772 + "tableFrom": "user_notification_workspace_project", 2773 + "tableTo": "workspace", 2774 + "columnsFrom": ["workspace_id"], 2775 + "columnsTo": ["id"], 2776 + "onDelete": "cascade", 2777 + "onUpdate": "cascade" 2778 + }, 2779 + "user_notification_workspace_project_workspace_id_workspace_rule_id_user_notification_workspace_rule_workspace_id_id_fk": { 2780 + "name": "user_notification_workspace_project_workspace_id_workspace_rule_id_user_notification_workspace_rule_workspace_id_id_fk", 2781 + "tableFrom": "user_notification_workspace_project", 2782 + "tableTo": "user_notification_workspace_rule", 2783 + "columnsFrom": ["workspace_id", "workspace_rule_id"], 2784 + "columnsTo": ["workspace_id", "id"], 2785 + "onDelete": "cascade", 2786 + "onUpdate": "cascade" 2787 + }, 2788 + "user_notification_workspace_project_workspace_id_project_id_project_workspace_id_id_fk": { 2789 + "name": "user_notification_workspace_project_workspace_id_project_id_project_workspace_id_id_fk", 2790 + "tableFrom": "user_notification_workspace_project", 2791 + "tableTo": "project", 2792 + "columnsFrom": ["workspace_id", "project_id"], 2793 + "columnsTo": ["workspace_id", "id"], 2794 + "onDelete": "cascade", 2795 + "onUpdate": "cascade" 2796 + } 2797 + }, 2798 + "compositePrimaryKeys": {}, 2799 + "uniqueConstraints": { 2800 + "user_notification_workspace_project_rule_project_unique": { 2801 + "name": "user_notification_workspace_project_rule_project_unique", 2802 + "nullsNotDistinct": false, 2803 + "columns": ["workspace_rule_id", "project_id"] 2804 + } 2805 + }, 2806 + "policies": {}, 2807 + "checkConstraints": {}, 2808 + "isRLSEnabled": false 2809 + }, 2810 + "public.user_notification_workspace_rule": { 2811 + "name": "user_notification_workspace_rule", 2812 + "schema": "", 2813 + "columns": { 2814 + "id": { 2815 + "name": "id", 2816 + "type": "text", 2817 + "primaryKey": true, 2818 + "notNull": true 2819 + }, 2820 + "user_id": { 2821 + "name": "user_id", 2822 + "type": "text", 2823 + "primaryKey": false, 2824 + "notNull": true 2825 + }, 2826 + "workspace_id": { 2827 + "name": "workspace_id", 2828 + "type": "text", 2829 + "primaryKey": false, 2830 + "notNull": true 2831 + }, 2832 + "is_active": { 2833 + "name": "is_active", 2834 + "type": "boolean", 2835 + "primaryKey": false, 2836 + "notNull": true, 2837 + "default": true 2838 + }, 2839 + "email_enabled": { 2840 + "name": "email_enabled", 2841 + "type": "boolean", 2842 + "primaryKey": false, 2843 + "notNull": true, 2844 + "default": false 2845 + }, 2846 + "ntfy_enabled": { 2847 + "name": "ntfy_enabled", 2848 + "type": "boolean", 2849 + "primaryKey": false, 2850 + "notNull": true, 2851 + "default": false 2852 + }, 2853 + "gotify_enabled": { 2854 + "name": "gotify_enabled", 2855 + "type": "boolean", 2856 + "primaryKey": false, 2857 + "notNull": true, 2858 + "default": false 2859 + }, 2860 + "webhook_enabled": { 2861 + "name": "webhook_enabled", 2862 + "type": "boolean", 2863 + "primaryKey": false, 2864 + "notNull": true, 2865 + "default": false 2866 + }, 2867 + "project_mode": { 2868 + "name": "project_mode", 2869 + "type": "text", 2870 + "primaryKey": false, 2871 + "notNull": true, 2872 + "default": "'all'" 2873 + }, 2874 + "created_at": { 2875 + "name": "created_at", 2876 + "type": "timestamp", 2877 + "primaryKey": false, 2878 + "notNull": true, 2879 + "default": "now()" 2880 + }, 2881 + "updated_at": { 2882 + "name": "updated_at", 2883 + "type": "timestamp", 2884 + "primaryKey": false, 2885 + "notNull": true, 2886 + "default": "now()" 2887 + } 2888 + }, 2889 + "indexes": { 2890 + "user_notification_workspace_rule_userId_idx": { 2891 + "name": "user_notification_workspace_rule_userId_idx", 2892 + "columns": [ 2893 + { 2894 + "expression": "user_id", 2895 + "isExpression": false, 2896 + "asc": true, 2897 + "nulls": "last" 2898 + } 2899 + ], 2900 + "isUnique": false, 2901 + "concurrently": false, 2902 + "method": "btree", 2903 + "with": {} 2904 + }, 2905 + "user_notification_workspace_rule_workspaceId_idx": { 2906 + "name": "user_notification_workspace_rule_workspaceId_idx", 2907 + "columns": [ 2908 + { 2909 + "expression": "workspace_id", 2910 + "isExpression": false, 2911 + "asc": true, 2912 + "nulls": "last" 2913 + } 2914 + ], 2915 + "isUnique": false, 2916 + "concurrently": false, 2917 + "method": "btree", 2918 + "with": {} 2919 + } 2920 + }, 2921 + "foreignKeys": { 2922 + "user_notification_workspace_rule_user_id_user_id_fk": { 2923 + "name": "user_notification_workspace_rule_user_id_user_id_fk", 2924 + "tableFrom": "user_notification_workspace_rule", 2925 + "tableTo": "user", 2926 + "columnsFrom": ["user_id"], 2927 + "columnsTo": ["id"], 2928 + "onDelete": "cascade", 2929 + "onUpdate": "cascade" 2930 + }, 2931 + "user_notification_workspace_rule_workspace_id_workspace_id_fk": { 2932 + "name": "user_notification_workspace_rule_workspace_id_workspace_id_fk", 2933 + "tableFrom": "user_notification_workspace_rule", 2934 + "tableTo": "workspace", 2935 + "columnsFrom": ["workspace_id"], 2936 + "columnsTo": ["id"], 2937 + "onDelete": "cascade", 2938 + "onUpdate": "cascade" 2939 + } 2940 + }, 2941 + "compositePrimaryKeys": {}, 2942 + "uniqueConstraints": { 2943 + "user_notification_workspace_rule_user_workspace_unique": { 2944 + "name": "user_notification_workspace_rule_user_workspace_unique", 2945 + "nullsNotDistinct": false, 2946 + "columns": ["user_id", "workspace_id"] 2947 + }, 2948 + "user_notification_workspace_rule_workspace_id_id_unique": { 2949 + "name": "user_notification_workspace_rule_workspace_id_id_unique", 2950 + "nullsNotDistinct": false, 2951 + "columns": ["workspace_id", "id"] 2952 + } 2953 + }, 2954 + "policies": {}, 2955 + "checkConstraints": {}, 2956 + "isRLSEnabled": false 2957 + }, 2958 + "public.verification": { 2959 + "name": "verification", 2960 + "schema": "", 2961 + "columns": { 2962 + "id": { 2963 + "name": "id", 2964 + "type": "text", 2965 + "primaryKey": true, 2966 + "notNull": true 2967 + }, 2968 + "identifier": { 2969 + "name": "identifier", 2970 + "type": "text", 2971 + "primaryKey": false, 2972 + "notNull": true 2973 + }, 2974 + "value": { 2975 + "name": "value", 2976 + "type": "text", 2977 + "primaryKey": false, 2978 + "notNull": true 2979 + }, 2980 + "expires_at": { 2981 + "name": "expires_at", 2982 + "type": "timestamp", 2983 + "primaryKey": false, 2984 + "notNull": true 2985 + }, 2986 + "created_at": { 2987 + "name": "created_at", 2988 + "type": "timestamp", 2989 + "primaryKey": false, 2990 + "notNull": true, 2991 + "default": "now()" 2992 + }, 2993 + "updated_at": { 2994 + "name": "updated_at", 2995 + "type": "timestamp", 2996 + "primaryKey": false, 2997 + "notNull": true, 2998 + "default": "now()" 2999 + } 3000 + }, 3001 + "indexes": { 3002 + "verification_identifier_idx": { 3003 + "name": "verification_identifier_idx", 3004 + "columns": [ 3005 + { 3006 + "expression": "identifier", 3007 + "isExpression": false, 3008 + "asc": true, 3009 + "nulls": "last" 3010 + } 3011 + ], 3012 + "isUnique": false, 3013 + "concurrently": false, 3014 + "method": "btree", 3015 + "with": {} 3016 + } 3017 + }, 3018 + "foreignKeys": {}, 3019 + "compositePrimaryKeys": {}, 3020 + "uniqueConstraints": {}, 3021 + "policies": {}, 3022 + "checkConstraints": {}, 3023 + "isRLSEnabled": false 3024 + }, 3025 + "public.workflow_rule": { 3026 + "name": "workflow_rule", 3027 + "schema": "", 3028 + "columns": { 3029 + "id": { 3030 + "name": "id", 3031 + "type": "text", 3032 + "primaryKey": true, 3033 + "notNull": true 3034 + }, 3035 + "project_id": { 3036 + "name": "project_id", 3037 + "type": "text", 3038 + "primaryKey": false, 3039 + "notNull": true 3040 + }, 3041 + "integration_type": { 3042 + "name": "integration_type", 3043 + "type": "text", 3044 + "primaryKey": false, 3045 + "notNull": true 3046 + }, 3047 + "event_type": { 3048 + "name": "event_type", 3049 + "type": "text", 3050 + "primaryKey": false, 3051 + "notNull": true 3052 + }, 3053 + "column_id": { 3054 + "name": "column_id", 3055 + "type": "text", 3056 + "primaryKey": false, 3057 + "notNull": true 3058 + }, 3059 + "created_at": { 3060 + "name": "created_at", 3061 + "type": "timestamp", 3062 + "primaryKey": false, 3063 + "notNull": true, 3064 + "default": "now()" 3065 + }, 3066 + "updated_at": { 3067 + "name": "updated_at", 3068 + "type": "timestamp", 3069 + "primaryKey": false, 3070 + "notNull": true, 3071 + "default": "now()" 3072 + } 3073 + }, 3074 + "indexes": { 3075 + "workflow_rule_projectId_idx": { 3076 + "name": "workflow_rule_projectId_idx", 3077 + "columns": [ 3078 + { 3079 + "expression": "project_id", 3080 + "isExpression": false, 3081 + "asc": true, 3082 + "nulls": "last" 3083 + } 3084 + ], 3085 + "isUnique": false, 3086 + "concurrently": false, 3087 + "method": "btree", 3088 + "with": {} 3089 + } 3090 + }, 3091 + "foreignKeys": { 3092 + "workflow_rule_project_id_project_id_fk": { 3093 + "name": "workflow_rule_project_id_project_id_fk", 3094 + "tableFrom": "workflow_rule", 3095 + "tableTo": "project", 3096 + "columnsFrom": ["project_id"], 3097 + "columnsTo": ["id"], 3098 + "onDelete": "cascade", 3099 + "onUpdate": "cascade" 3100 + }, 3101 + "workflow_rule_column_id_column_id_fk": { 3102 + "name": "workflow_rule_column_id_column_id_fk", 3103 + "tableFrom": "workflow_rule", 3104 + "tableTo": "column", 3105 + "columnsFrom": ["column_id"], 3106 + "columnsTo": ["id"], 3107 + "onDelete": "cascade", 3108 + "onUpdate": "cascade" 3109 + } 3110 + }, 3111 + "compositePrimaryKeys": {}, 3112 + "uniqueConstraints": {}, 3113 + "policies": {}, 3114 + "checkConstraints": {}, 3115 + "isRLSEnabled": false 3116 + }, 3117 + "public.workspace": { 3118 + "name": "workspace", 3119 + "schema": "", 3120 + "columns": { 3121 + "id": { 3122 + "name": "id", 3123 + "type": "text", 3124 + "primaryKey": true, 3125 + "notNull": true 3126 + }, 3127 + "name": { 3128 + "name": "name", 3129 + "type": "text", 3130 + "primaryKey": false, 3131 + "notNull": true 3132 + }, 3133 + "slug": { 3134 + "name": "slug", 3135 + "type": "text", 3136 + "primaryKey": false, 3137 + "notNull": true 3138 + }, 3139 + "logo": { 3140 + "name": "logo", 3141 + "type": "text", 3142 + "primaryKey": false, 3143 + "notNull": false 3144 + }, 3145 + "metadata": { 3146 + "name": "metadata", 3147 + "type": "text", 3148 + "primaryKey": false, 3149 + "notNull": false 3150 + }, 3151 + "description": { 3152 + "name": "description", 3153 + "type": "text", 3154 + "primaryKey": false, 3155 + "notNull": false 3156 + }, 3157 + "created_at": { 3158 + "name": "created_at", 3159 + "type": "timestamp", 3160 + "primaryKey": false, 3161 + "notNull": true 3162 + } 3163 + }, 3164 + "indexes": {}, 3165 + "foreignKeys": {}, 3166 + "compositePrimaryKeys": {}, 3167 + "uniqueConstraints": { 3168 + "workspace_slug_unique": { 3169 + "name": "workspace_slug_unique", 3170 + "nullsNotDistinct": false, 3171 + "columns": ["slug"] 3172 + } 3173 + }, 3174 + "policies": {}, 3175 + "checkConstraints": {}, 3176 + "isRLSEnabled": false 3177 + }, 3178 + "public.workspace_member": { 3179 + "name": "workspace_member", 3180 + "schema": "", 3181 + "columns": { 3182 + "id": { 3183 + "name": "id", 3184 + "type": "text", 3185 + "primaryKey": true, 3186 + "notNull": true 3187 + }, 3188 + "workspace_id": { 3189 + "name": "workspace_id", 3190 + "type": "text", 3191 + "primaryKey": false, 3192 + "notNull": true 3193 + }, 3194 + "user_id": { 3195 + "name": "user_id", 3196 + "type": "text", 3197 + "primaryKey": false, 3198 + "notNull": true 3199 + }, 3200 + "role": { 3201 + "name": "role", 3202 + "type": "text", 3203 + "primaryKey": false, 3204 + "notNull": true, 3205 + "default": "'member'" 3206 + }, 3207 + "joined_at": { 3208 + "name": "joined_at", 3209 + "type": "timestamp", 3210 + "primaryKey": false, 3211 + "notNull": true 3212 + } 3213 + }, 3214 + "indexes": { 3215 + "workspace_member_workspaceId_idx": { 3216 + "name": "workspace_member_workspaceId_idx", 3217 + "columns": [ 3218 + { 3219 + "expression": "workspace_id", 3220 + "isExpression": false, 3221 + "asc": true, 3222 + "nulls": "last" 3223 + } 3224 + ], 3225 + "isUnique": false, 3226 + "concurrently": false, 3227 + "method": "btree", 3228 + "with": {} 3229 + }, 3230 + "workspace_member_userId_idx": { 3231 + "name": "workspace_member_userId_idx", 3232 + "columns": [ 3233 + { 3234 + "expression": "user_id", 3235 + "isExpression": false, 3236 + "asc": true, 3237 + "nulls": "last" 3238 + } 3239 + ], 3240 + "isUnique": false, 3241 + "concurrently": false, 3242 + "method": "btree", 3243 + "with": {} 3244 + } 3245 + }, 3246 + "foreignKeys": { 3247 + "workspace_member_workspace_id_workspace_id_fk": { 3248 + "name": "workspace_member_workspace_id_workspace_id_fk", 3249 + "tableFrom": "workspace_member", 3250 + "tableTo": "workspace", 3251 + "columnsFrom": ["workspace_id"], 3252 + "columnsTo": ["id"], 3253 + "onDelete": "cascade", 3254 + "onUpdate": "no action" 3255 + }, 3256 + "workspace_member_user_id_user_id_fk": { 3257 + "name": "workspace_member_user_id_user_id_fk", 3258 + "tableFrom": "workspace_member", 3259 + "tableTo": "user", 3260 + "columnsFrom": ["user_id"], 3261 + "columnsTo": ["id"], 3262 + "onDelete": "cascade", 3263 + "onUpdate": "no action" 3264 + } 3265 + }, 3266 + "compositePrimaryKeys": {}, 3267 + "uniqueConstraints": {}, 3268 + "policies": {}, 3269 + "checkConstraints": {}, 3270 + "isRLSEnabled": false 3271 + } 3272 + }, 3273 + "enums": {}, 3274 + "schemas": {}, 3275 + "sequences": {}, 3276 + "roles": {}, 3277 + "policies": {}, 3278 + "views": {}, 3279 + "_meta": { 3280 + "columns": {}, 3281 + "schemas": {}, 3282 + "tables": {} 3283 + } 3284 + }
+7
apps/api/drizzle/meta/_journal.json
··· 190 190 "when": 1775102400000, 191 191 "tag": "0026_encrypt_notification_preference_secrets", 192 192 "breakpoints": true 193 + }, 194 + { 195 + "idx": 27, 196 + "version": "7", 197 + "when": 1775245262351, 198 + "tag": "0027_chilly_namorita", 199 + "breakpoints": true 193 200 } 194 201 ] 195 202 }
+12
apps/api/src/database/relations.ts
··· 15 15 projectTable, 16 16 sessionTable, 17 17 taskRelationTable, 18 + taskReminderSentTable, 18 19 taskTable, 19 20 teamMemberTable, 20 21 teamTable, ··· 153 154 externalLinks: many(externalLinkTable), 154 155 sourceRelations: many(taskRelationTable, { relationName: "sourceTask" }), 155 156 targetRelations: many(taskRelationTable, { relationName: "targetTask" }), 157 + remindersSent: many(taskReminderSentTable), 156 158 })); 157 159 158 160 export const timeEntryTableRelations = relations(timeEntryTable, ({ one }) => ({ ··· 355 357 integration: one(integrationTable, { 356 358 fields: [externalLinkTable.integrationId], 357 359 references: [integrationTable.id], 360 + }), 361 + }), 362 + ); 363 + 364 + export const taskReminderSentTableRelations = relations( 365 + taskReminderSentTable, 366 + ({ one }) => ({ 367 + task: one(taskTable, { 368 + fields: [taskReminderSentTable.taskId], 369 + references: [taskTable.id], 358 370 }), 359 371 }), 360 372 );
+29
apps/api/src/database/schema.ts
··· 314 314 }, 315 315 (table) => [ 316 316 index("task_projectId_idx").on(table.projectId), 317 + index("task_dueDate_idx").on(table.dueDate), 317 318 unique("task_project_number_unique").on(table.projectId, table.number), 319 + ], 320 + ); 321 + 322 + export 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 + ), 318 347 ], 319 348 ); 320 349
+6
apps/api/src/index.ts
··· 36 36 import { migrateGitHubIntegration } from "./plugins/github/migration"; 37 37 import project from "./project"; 38 38 import { getPublicProject } from "./project/controllers/get-public-project"; 39 + import { initializeScheduler, shutdownScheduler } from "./scheduler"; 39 40 import search from "./search"; 40 41 import slackIntegration from "./slack-integration"; 41 42 import { getPrivateObject } from "./storage/s3"; ··· 517 518 await migrateColumns(); 518 519 519 520 initializePlugins(); 521 + initializeScheduler(); 520 522 } 521 523 522 524 export async function startServer(port = 1337) { ··· 526 528 console.error("❌ Database migration failed!", error); 527 529 process.exit(1); 528 530 } 531 + 532 + process.on("SIGTERM", () => { 533 + shutdownScheduler(); 534 + }); 529 535 530 536 serve( 531 537 {
+24
apps/api/src/notification-preferences/delivery.ts
··· 124 124 : "A time entry was created in Kaneo.", 125 125 }; 126 126 } 127 + case "due_date_reminder": { 128 + const taskTitle = getStringValue(notification.eventData, "taskTitle"); 129 + const reminderType = getStringValue( 130 + notification.eventData, 131 + "reminderType", 132 + ); 133 + const label = 134 + reminderType === "one_hour_before" ? "in 1 hour" : "in 1 day"; 135 + return { 136 + title: "Task due soon", 137 + body: taskTitle 138 + ? `"${taskTitle}" is due ${label}.` 139 + : `A task is due ${label}.`, 140 + }; 141 + } 142 + case "task_overdue": { 143 + const taskTitle = getStringValue(notification.eventData, "taskTitle"); 144 + return { 145 + title: "Task overdue", 146 + body: taskTitle 147 + ? `"${taskTitle}" is past its due date.` 148 + : "A task is past its due date.", 149 + }; 150 + } 127 151 default: 128 152 return { 129 153 title: notification.title ?? "New Kaneo notification",
+153
apps/api/src/scheduler/due-date-reminders.ts
··· 1 + import { and, between, eq, isNotNull, isNull, or } from "drizzle-orm"; 2 + import db from "../database"; 3 + import { 4 + columnTable, 5 + taskReminderSentTable, 6 + taskTable, 7 + } from "../database/schema"; 8 + import createNotification from "../notification/controllers/create-notification"; 9 + 10 + type ReminderType = "one_day_before" | "one_hour_before" | "overdue"; 11 + 12 + const HOUR_MS = 60 * 60 * 1000; 13 + const MINUTE_MS = 60 * 1000; 14 + 15 + function buildWindows(now: Date) { 16 + const nowMs = now.getTime(); 17 + 18 + return { 19 + oneDay: { 20 + start: new Date(nowMs + 23 * HOUR_MS + 50 * MINUTE_MS), 21 + end: new Date(nowMs + 24 * HOUR_MS + 10 * MINUTE_MS), 22 + type: "one_day_before" as ReminderType, 23 + notificationType: "due_date_reminder" as const, 24 + }, 25 + oneHour: { 26 + start: new Date(nowMs + 50 * MINUTE_MS), 27 + end: new Date(nowMs + 70 * MINUTE_MS), 28 + type: "one_hour_before" as ReminderType, 29 + notificationType: "due_date_reminder" as const, 30 + }, 31 + overdue: { 32 + end: now, 33 + start: new Date(nowMs - 10 * MINUTE_MS), 34 + type: "overdue" as ReminderType, 35 + notificationType: "task_overdue" as const, 36 + }, 37 + }; 38 + } 39 + 40 + async function getTasksNeedingReminder( 41 + windowStart: Date, 42 + windowEnd: Date, 43 + reminderType: ReminderType, 44 + ) { 45 + const results = await db 46 + .select({ 47 + id: taskTable.id, 48 + title: taskTable.title, 49 + userId: taskTable.userId, 50 + dueDate: taskTable.dueDate, 51 + projectId: taskTable.projectId, 52 + }) 53 + .from(taskTable) 54 + .leftJoin(columnTable, eq(taskTable.columnId, columnTable.id)) 55 + .leftJoin( 56 + taskReminderSentTable, 57 + and( 58 + eq(taskReminderSentTable.taskId, taskTable.id), 59 + eq(taskReminderSentTable.reminderType, reminderType), 60 + ), 61 + ) 62 + .where( 63 + and( 64 + isNotNull(taskTable.userId), 65 + isNotNull(taskTable.dueDate), 66 + between(taskTable.dueDate, windowStart, windowEnd), 67 + isNull(taskReminderSentTable.id), 68 + // Exclude tasks in final columns (completed); include tasks with no column 69 + or(isNull(columnTable.isFinal), eq(columnTable.isFinal, false)), 70 + ), 71 + ); 72 + 73 + return results; 74 + } 75 + 76 + async function processReminder( 77 + task: { 78 + id: string; 79 + title: string; 80 + userId: string | null; 81 + dueDate: Date | null; 82 + projectId: string; 83 + }, 84 + reminderType: ReminderType, 85 + notificationType: "due_date_reminder" | "task_overdue", 86 + ) { 87 + if (!task.userId) return; 88 + 89 + // Insert sent record first — if it already exists, skip notification 90 + try { 91 + const [inserted] = await db 92 + .insert(taskReminderSentTable) 93 + .values({ 94 + taskId: task.id, 95 + reminderType, 96 + }) 97 + .onConflictDoNothing({ 98 + target: [ 99 + taskReminderSentTable.taskId, 100 + taskReminderSentTable.reminderType, 101 + ], 102 + }) 103 + .returning(); 104 + 105 + if (!inserted) return; 106 + } catch { 107 + return; 108 + } 109 + 110 + await createNotification({ 111 + userId: task.userId, 112 + type: notificationType, 113 + eventData: { 114 + taskTitle: task.title, 115 + reminderType, 116 + dueDate: task.dueDate?.toISOString() ?? null, 117 + }, 118 + resourceId: task.id, 119 + resourceType: "task", 120 + }); 121 + } 122 + 123 + export async function checkDueDateReminders(): Promise<void> { 124 + const now = new Date(); 125 + const windows = buildWindows(now); 126 + 127 + for (const window of Object.values(windows)) { 128 + try { 129 + const tasks = await getTasksNeedingReminder( 130 + window.start, 131 + window.end, 132 + window.type, 133 + ); 134 + 135 + for (const task of tasks) { 136 + try { 137 + await processReminder(task, window.type, window.notificationType); 138 + } catch (error) { 139 + console.error("Failed to process due date reminder", { 140 + taskId: task.id, 141 + reminderType: window.type, 142 + error, 143 + }); 144 + } 145 + } 146 + } catch (error) { 147 + console.error("Failed to query tasks for due date reminders", { 148 + reminderType: window.type, 149 + error, 150 + }); 151 + } 152 + } 153 + }
+16
apps/api/src/scheduler/index.ts
··· 1 + import { Cron } from "croner"; 2 + import { checkDueDateReminders } from "./due-date-reminders"; 3 + 4 + const jobs: Cron[] = []; 5 + 6 + export function initializeScheduler(): void { 7 + jobs.push(new Cron("*/5 * * * *", checkDueDateReminders)); 8 + console.log("⏰ Scheduler started (due date reminders every 5 minutes)"); 9 + } 10 + 11 + export function shutdownScheduler(): void { 12 + for (const job of jobs) { 13 + job.stop(); 14 + } 15 + jobs.length = 0; 16 + }
+2
apps/api/src/schemas.ts
··· 90 90 "task_status_changed", 91 91 "task_assignee_changed", 92 92 "time_entry_created", 93 + "due_date_reminder", 94 + "task_overdue", 93 95 ] as const), 94 96 eventData: v.nullable(v.record(v.string(), v.unknown())), 95 97 isRead: v.optional(v.boolean()),
+6 -1
apps/api/src/task/controllers/update-task-due-date.ts
··· 1 1 import { eq } from "drizzle-orm"; 2 2 import { HTTPException } from "hono/http-exception"; 3 3 import db from "../../database"; 4 - import { taskTable } from "../../database/schema"; 4 + import { taskReminderSentTable, taskTable } from "../../database/schema"; 5 5 6 6 async function updateTaskDueDate({ 7 7 id, ··· 19 19 message: "Task not found", 20 20 }); 21 21 } 22 + 23 + // Clear sent reminders so new due date triggers fresh notifications 24 + await db 25 + .delete(taskReminderSentTable) 26 + .where(eq(taskReminderSentTable.taskId, id)); 22 27 23 28 const [updatedTask] = await db 24 29 .update(taskTable)
+15 -7
apps/web/src/components/shared/modals/create-task-modal.tsx
··· 42 42 import { useUpdateTask } from "@/hooks/mutations/task/use-update-task"; 43 43 import useGetLabelsByWorkspace from "@/hooks/queries/label/use-get-labels-by-workspace"; 44 44 import useActiveWorkspace from "@/hooks/queries/workspace/use-active-workspace"; 45 + import { useGetActiveWorkspaceUsers } from "@/hooks/queries/workspace-users/use-get-active-workspace-users"; 45 46 import { cn } from "@/lib/cn"; 46 47 import { formatDateMedium } from "@/lib/format"; 47 48 import { getPriorityIcon } from "@/lib/priority"; ··· 166 167 ); 167 168 const location = useLocation(); 168 169 const { data: workspace } = useActiveWorkspace(); 170 + const { data: workspaceUsers } = useGetActiveWorkspaceUsers( 171 + workspace?.id || "", 172 + ); 169 173 const { mutateAsync: createLabel } = useCreateLabel(); 170 174 const { data: workspaceLabels = [] } = useGetLabelsByWorkspace( 171 175 workspace?.id || "", ··· 283 287 ...task, 284 288 assigneeId: task.userId, 285 289 assigneeName: 286 - workspace?.members?.find((member) => member.userId === task.userId) 287 - ?.user?.name ?? 290 + workspaceUsers?.members?.find( 291 + (member) => member.userId === task.userId, 292 + )?.user?.name ?? 288 293 existingTask?.assigneeName ?? 289 294 null, 290 295 assigneeImage: 291 - workspace?.members?.find((member) => member.userId === task.userId) 292 - ?.user?.image ?? 296 + workspaceUsers?.members?.find( 297 + (member) => member.userId === task.userId, 298 + )?.user?.image ?? 293 299 existingTask?.assigneeImage ?? 294 300 null, 295 301 position: task.position ?? 0, ··· 298 304 299 305 setProject(updatedProject); 300 306 }, 301 - [project, setProject, workspace?.members], 307 + [project, setProject, workspaceUsers?.members], 302 308 ); 303 309 304 310 const ensureDraftTask = useCallback(async () => { ··· 460 466 } 461 467 return t("tasks:status.in-progress"); 462 468 }, [status, t]); 463 - const selectedUser = workspace?.members?.find((u) => u.userId === assigneeId); 469 + const selectedUser = workspaceUsers?.members?.find( 470 + (u) => u.userId === assigneeId, 471 + ); 464 472 465 473 useEffect(() => { 466 474 if (labelsOpen && labelsStep === "select" && searchInputRef.current) { ··· 790 798 </span> 791 799 {!assigneeId && <Check className="ml-auto h-4 w-4" />} 792 800 </button> 793 - {workspace?.members?.map((member) => ( 801 + {workspaceUsers?.members?.map((member) => ( 794 802 <button 795 803 key={member.userId} 796 804 type="button"