+
+ setValue("comment", value)}
+ workspaceMembers={workspaceMembers}
+ enableYouTubeEmbed={false}
+ placeholder={t`Add comment... (type '/' to open commands or '@' to mention)`}
+ disableHeadings={true}
+ />
+
+
{!isTemplate && (
-
+
)}
diff --git a/packages/api/src/routers/card.ts b/packages/api/src/routers/card.ts
index b39256c5..949804d1 100644
--- a/packages/api/src/routers/card.ts
+++ b/packages/api/src/routers/card.ts
@@ -10,6 +10,7 @@ import * as workspaceRepo from "@kan/db/repository/workspace.repo";
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
import { mergeActivities } from "../utils/activities";
+import { sendMentionEmails } from "../utils/notifications";
import { assertCanDelete, assertCanEdit, assertPermission } from "../utils/permissions";
import { generateAttachmentUrl, generateAvatarUrl } from "@kan/shared/utils";
@@ -153,6 +154,17 @@ export const cardRouter = createTRPCRouter({
await cardActivityRepo.bulkCreate(ctx.db, cardActivitesInsert);
}
+ if (input.description) {
+ sendMentionEmails({
+ db: ctx.db,
+ cardPublicId: newCard.publicId,
+ commentHtml: input.description,
+ commenterUserId: userId,
+ }).catch((error) => {
+ console.error("Failed to send mention emails:", error);
+ });
+ }
+
return newCard;
}),
addComment: protectedProcedure
@@ -215,6 +227,16 @@ export const cardRouter = createTRPCRouter({
createdBy: userId,
});
+ sendMentionEmails({
+ db: ctx.db,
+ cardPublicId: input.cardPublicId,
+ commentHtml: input.comment,
+ commenterUserId: userId,
+ commentId: newComment.id,
+ }).catch((error) => {
+ console.error("Failed to send mention emails:", error);
+ });
+
return newComment;
}),
updateComment: protectedProcedure
@@ -295,6 +317,16 @@ export const cardRouter = createTRPCRouter({
createdBy: userId,
});
+ sendMentionEmails({
+ db: ctx.db,
+ cardPublicId: input.cardPublicId,
+ commentHtml: input.comment,
+ commenterUserId: userId,
+ commentId: updatedComment.id,
+ }).catch((error) => {
+ console.error("Failed to send mention emails:", error);
+ });
+
return updatedComment;
}),
deleteComment: protectedProcedure
@@ -924,6 +956,15 @@ export const cardRouter = createTRPCRouter({
fromDescription: existingCard.description ?? undefined,
toDescription: input.description,
});
+
+ sendMentionEmails({
+ db: ctx.db,
+ cardPublicId: input.cardPublicId,
+ commentHtml: input.description,
+ commenterUserId: userId,
+ }).catch((error) => {
+ console.error("Failed to send mention emails:", error);
+ });
}
if (
diff --git a/packages/api/src/utils/notifications.ts b/packages/api/src/utils/notifications.ts
new file mode 100644
index 00000000..32b32cc9
--- /dev/null
+++ b/packages/api/src/utils/notifications.ts
@@ -0,0 +1,133 @@
+import { env } from "next-runtime-env";
+
+import type { dbClient } from "@kan/db/client";
+import * as cardRepo from "@kan/db/repository/card.repo";
+import * as memberRepo from "@kan/db/repository/member.repo";
+import * as notificationRepo from "@kan/db/repository/notification.repo";
+import * as userRepo from "@kan/db/repository/user.repo";
+import * as workspaceRepo from "@kan/db/repository/workspace.repo";
+import { sendEmail } from "@kan/email";
+import { parseMentionsFromHTML } from "@kan/shared/utils";
+
+/**
+ * Sends mention notification emails to mentioned members
+ * Only sends emails for new mentions (checks notification table to avoid duplicates)
+ */
+export async function sendMentionEmails({
+ db,
+ cardPublicId,
+ commentHtml,
+ commenterUserId,
+ commentId,
+}: {
+ db: dbClient;
+ cardPublicId: string;
+ commentHtml: string;
+ commenterUserId: string;
+ commentId?: number;
+}) {
+ try {
+ // Parse mentions from HTML
+ const mentionPublicIds = parseMentionsFromHTML(commentHtml);
+ if (mentionPublicIds.length === 0) return;
+
+ // Get card with board information
+ const card = await cardRepo.getWithListAndMembersByPublicId(db, cardPublicId);
+ if (!card?.list.board) return;
+
+ const board = card.list.board;
+ const boardName = board.name;
+ const cardTitle = card.title;
+ const cardId = card.id;
+
+ // Get workspace ID from workspace publicId
+ const workspace = await workspaceRepo.getByPublicId(
+ db,
+ board.workspace.publicId,
+ );
+ if (!workspace?.id) return;
+
+ const workspaceId = workspace.id;
+
+ // Get commenter information
+ const commenter = await userRepo.getById(db, commenterUserId);
+ if (!commenter) return;
+
+ const commenterName = commenter.name ?? commenter.email;
+
+ // Get mentioned members with full details (filtered by workspace)
+ const membersWithDetails = await memberRepo.getByPublicIdsWithUsers(
+ db,
+ mentionPublicIds,
+ workspaceId,
+ );
+
+ // Filter out the commenter
+ const membersToNotify = membersWithDetails.filter(
+ (member) => member.user?.id !== commenterUserId,
+ );
+
+ if (membersToNotify.length === 0) return;
+
+ const baseUrl = env("NEXT_PUBLIC_BASE_URL");
+ const cardUrl = `${baseUrl}/cards/${cardPublicId}`;
+
+ // Send emails to all mentioned members (only if notification doesn't exist)
+ await Promise.all(
+ membersToNotify.map(async (member) => {
+ const userId = member.user?.id;
+ const email = member.user?.email ?? member.email;
+
+ // Skip pending members (no userId) - they can be mentioned but won't receive emails
+ if (!userId || !email) return;
+
+ try {
+ // Check if notification already exists for this mention
+ const notificationExists = await notificationRepo.exists(db, {
+ userId,
+ cardId,
+ type: "mention",
+ });
+
+ // If notification already exists, skip sending email
+ if (notificationExists) {
+ return;
+ }
+
+ // Create notification record
+ await notificationRepo.create(db, {
+ type: "mention",
+ userId,
+ cardId,
+ commentId,
+ });
+
+ // Send email
+ await sendEmail(
+ email,
+ `${commenterName} mentioned you in a comment on ${cardTitle}`,
+ "MENTION",
+ {
+ commenterName,
+ boardName,
+ cardTitle,
+ cardUrl,
+ },
+ );
+ } catch (error) {
+ console.error("Failed to send mention email:", {
+ email,
+ cardPublicId,
+ error: error instanceof Error ? error.message : String(error),
+ });
+ }
+ }),
+ );
+ } catch (error) {
+ console.error("Error sending mention emails:", {
+ cardPublicId,
+ error: error instanceof Error ? error.message : String(error),
+ });
+ }
+}
+
diff --git a/packages/db/migrations/20260207214056_AddNotificationsTable.sql b/packages/db/migrations/20260207214056_AddNotificationsTable.sql
new file mode 100644
index 00000000..f5c792cd
--- /dev/null
+++ b/packages/db/migrations/20260207214056_AddNotificationsTable.sql
@@ -0,0 +1,46 @@
+CREATE TYPE "public"."notification_type" AS ENUM('mention', 'workspace.member.added', 'workspace.member.removed', 'workspace.role.changed');--> statement-breakpoint
+CREATE TABLE IF NOT EXISTS "notification" (
+ "id" bigserial PRIMARY KEY NOT NULL,
+ "publicId" varchar(12) NOT NULL,
+ "type" "notification_type" NOT NULL,
+ "userId" uuid NOT NULL,
+ "cardId" bigint,
+ "commentId" bigint,
+ "workspaceId" bigint,
+ "metadata" text,
+ "readAt" timestamp,
+ "createdAt" timestamp DEFAULT now() NOT NULL,
+ "deletedAt" timestamp,
+ CONSTRAINT "notification_publicId_unique" UNIQUE("publicId")
+);
+--> statement-breakpoint
+ALTER TABLE "notification" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
+DO $$ BEGIN
+ ALTER TABLE "notification" ADD CONSTRAINT "notification_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
+EXCEPTION
+ WHEN duplicate_object THEN null;
+END $$;
+--> statement-breakpoint
+DO $$ BEGIN
+ ALTER TABLE "notification" ADD CONSTRAINT "notification_cardId_card_id_fk" FOREIGN KEY ("cardId") REFERENCES "public"."card"("id") ON DELETE cascade ON UPDATE no action;
+EXCEPTION
+ WHEN duplicate_object THEN null;
+END $$;
+--> statement-breakpoint
+DO $$ BEGIN
+ ALTER TABLE "notification" ADD CONSTRAINT "notification_commentId_card_comments_id_fk" FOREIGN KEY ("commentId") REFERENCES "public"."card_comments"("id") ON DELETE cascade ON UPDATE no action;
+EXCEPTION
+ WHEN duplicate_object THEN null;
+END $$;
+--> statement-breakpoint
+DO $$ BEGIN
+ ALTER TABLE "notification" ADD CONSTRAINT "notification_workspaceId_workspace_id_fk" FOREIGN KEY ("workspaceId") REFERENCES "public"."workspace"("id") ON DELETE cascade ON UPDATE no action;
+EXCEPTION
+ WHEN duplicate_object THEN null;
+END $$;
+--> statement-breakpoint
+CREATE INDEX IF NOT EXISTS "notification_user_deleted_idx" ON "notification" USING btree ("userId","deletedAt");--> statement-breakpoint
+CREATE INDEX IF NOT EXISTS "notification_user_read_deleted_idx" ON "notification" USING btree ("userId","readAt","deletedAt");--> statement-breakpoint
+CREATE INDEX IF NOT EXISTS "notification_user_type_card_idx" ON "notification" USING btree ("userId","type","cardId");--> statement-breakpoint
+CREATE INDEX IF NOT EXISTS "notification_user_type_workspace_idx" ON "notification" USING btree ("userId","type","workspaceId");--> statement-breakpoint
+CREATE INDEX IF NOT EXISTS "notification_user_created_idx" ON "notification" USING btree ("userId","createdAt");
\ No newline at end of file
diff --git a/packages/db/migrations/meta/20260207214056_snapshot.json b/packages/db/migrations/meta/20260207214056_snapshot.json
new file mode 100644
index 00000000..efc779da
--- /dev/null
+++ b/packages/db/migrations/meta/20260207214056_snapshot.json
@@ -0,0 +1,3687 @@
+{
+ "id": "99b49af4-cc01-43ef-9d34-de8a49a61573",
+ "prevId": "3d6e154d-19ee-4e43-8b74-954a644f70d2",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.account": {
+ "name": "account",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "accountId": {
+ "name": "accountId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "providerId": {
+ "name": "providerId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "userId": {
+ "name": "userId",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "accessToken": {
+ "name": "accessToken",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refreshToken": {
+ "name": "refreshToken",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "idToken": {
+ "name": "idToken",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "accessTokenExpiresAt": {
+ "name": "accessTokenExpiresAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refreshTokenExpiresAt": {
+ "name": "refreshTokenExpiresAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "account_userId_user_id_fk": {
+ "name": "account_userId_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.apiKey": {
+ "name": "apiKey",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "start": {
+ "name": "start",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "prefix": {
+ "name": "prefix",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "userId": {
+ "name": "userId",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refillInterval": {
+ "name": "refillInterval",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refillAmount": {
+ "name": "refillAmount",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lastRefillAt": {
+ "name": "lastRefillAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "rateLimitEnabled": {
+ "name": "rateLimitEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "rateLimitTimeWindow": {
+ "name": "rateLimitTimeWindow",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "rateLimitMax": {
+ "name": "rateLimitMax",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "requestCount": {
+ "name": "requestCount",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "remaining": {
+ "name": "remaining",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lastRequest": {
+ "name": "lastRequest",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expiresAt": {
+ "name": "expiresAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "permissions": {
+ "name": "permissions",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "apiKey_userId_user_id_fk": {
+ "name": "apiKey_userId_user_id_fk",
+ "tableFrom": "apiKey",
+ "tableTo": "user",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.session": {
+ "name": "session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "expiresAt": {
+ "name": "expiresAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ipAddress": {
+ "name": "ipAddress",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "userAgent": {
+ "name": "userAgent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "session_userId_user_id_fk": {
+ "name": "session_userId_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "session_token_unique": {
+ "name": "session_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.verification": {
+ "name": "verification",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expiresAt": {
+ "name": "expiresAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.board": {
+ "name": "board",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "publicId": {
+ "name": "publicId",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletedAt": {
+ "name": "deletedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletedBy": {
+ "name": "deletedBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "importId": {
+ "name": "importId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "workspaceId": {
+ "name": "workspaceId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "visibility": {
+ "name": "visibility",
+ "type": "board_visibility",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'private'"
+ },
+ "type": {
+ "name": "type",
+ "type": "board_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'regular'"
+ },
+ "sourceBoardId": {
+ "name": "sourceBoardId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "board_visibility_idx": {
+ "name": "board_visibility_idx",
+ "columns": [
+ {
+ "expression": "visibility",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "board_type_idx": {
+ "name": "board_type_idx",
+ "columns": [
+ {
+ "expression": "type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "board_source_idx": {
+ "name": "board_source_idx",
+ "columns": [
+ {
+ "expression": "sourceBoardId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "unique_slug_per_workspace": {
+ "name": "unique_slug_per_workspace",
+ "columns": [
+ {
+ "expression": "workspaceId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "slug",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"board\".\"deletedAt\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "board_createdBy_user_id_fk": {
+ "name": "board_createdBy_user_id_fk",
+ "tableFrom": "board",
+ "tableTo": "user",
+ "columnsFrom": [
+ "createdBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "board_deletedBy_user_id_fk": {
+ "name": "board_deletedBy_user_id_fk",
+ "tableFrom": "board",
+ "tableTo": "user",
+ "columnsFrom": [
+ "deletedBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "board_importId_import_id_fk": {
+ "name": "board_importId_import_id_fk",
+ "tableFrom": "board",
+ "tableTo": "import",
+ "columnsFrom": [
+ "importId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "board_workspaceId_workspace_id_fk": {
+ "name": "board_workspaceId_workspace_id_fk",
+ "tableFrom": "board",
+ "tableTo": "workspace",
+ "columnsFrom": [
+ "workspaceId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "board_publicId_unique": {
+ "name": "board_publicId_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "publicId"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.user_board_favorites": {
+ "name": "user_board_favorites",
+ "schema": "",
+ "columns": {
+ "userId": {
+ "name": "userId",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "boardId": {
+ "name": "boardId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "user_board_favorite_user_idx": {
+ "name": "user_board_favorite_user_idx",
+ "columns": [
+ {
+ "expression": "userId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "user_board_favorite_board_idx": {
+ "name": "user_board_favorite_board_idx",
+ "columns": [
+ {
+ "expression": "boardId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "user_board_favorites_userId_user_id_fk": {
+ "name": "user_board_favorites_userId_user_id_fk",
+ "tableFrom": "user_board_favorites",
+ "tableTo": "user",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "user_board_favorites_boardId_board_id_fk": {
+ "name": "user_board_favorites_boardId_board_id_fk",
+ "tableFrom": "user_board_favorites",
+ "tableTo": "board",
+ "columnsFrom": [
+ "boardId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "user_board_favorites_userId_boardId_pk": {
+ "name": "user_board_favorites_userId_boardId_pk",
+ "columns": [
+ "userId",
+ "boardId"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.card_activity": {
+ "name": "card_activity",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "publicId": {
+ "name": "publicId",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "card_activity_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cardId": {
+ "name": "cardId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "fromIndex": {
+ "name": "fromIndex",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "toIndex": {
+ "name": "toIndex",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "fromListId": {
+ "name": "fromListId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "toListId": {
+ "name": "toListId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "labelId": {
+ "name": "labelId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "workspaceMemberId": {
+ "name": "workspaceMemberId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "fromTitle": {
+ "name": "fromTitle",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "toTitle": {
+ "name": "toTitle",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "fromDescription": {
+ "name": "fromDescription",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "toDescription": {
+ "name": "toDescription",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "commentId": {
+ "name": "commentId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "fromComment": {
+ "name": "fromComment",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "toComment": {
+ "name": "toComment",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "fromDueDate": {
+ "name": "fromDueDate",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "toDueDate": {
+ "name": "toDueDate",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sourceBoardId": {
+ "name": "sourceBoardId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "card_activity_cardId_card_id_fk": {
+ "name": "card_activity_cardId_card_id_fk",
+ "tableFrom": "card_activity",
+ "tableTo": "card",
+ "columnsFrom": [
+ "cardId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "card_activity_fromListId_list_id_fk": {
+ "name": "card_activity_fromListId_list_id_fk",
+ "tableFrom": "card_activity",
+ "tableTo": "list",
+ "columnsFrom": [
+ "fromListId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "card_activity_toListId_list_id_fk": {
+ "name": "card_activity_toListId_list_id_fk",
+ "tableFrom": "card_activity",
+ "tableTo": "list",
+ "columnsFrom": [
+ "toListId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "card_activity_labelId_label_id_fk": {
+ "name": "card_activity_labelId_label_id_fk",
+ "tableFrom": "card_activity",
+ "tableTo": "label",
+ "columnsFrom": [
+ "labelId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "card_activity_workspaceMemberId_workspace_members_id_fk": {
+ "name": "card_activity_workspaceMemberId_workspace_members_id_fk",
+ "tableFrom": "card_activity",
+ "tableTo": "workspace_members",
+ "columnsFrom": [
+ "workspaceMemberId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "card_activity_createdBy_user_id_fk": {
+ "name": "card_activity_createdBy_user_id_fk",
+ "tableFrom": "card_activity",
+ "tableTo": "user",
+ "columnsFrom": [
+ "createdBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "card_activity_commentId_card_comments_id_fk": {
+ "name": "card_activity_commentId_card_comments_id_fk",
+ "tableFrom": "card_activity",
+ "tableTo": "card_comments",
+ "columnsFrom": [
+ "commentId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "card_activity_sourceBoardId_board_id_fk": {
+ "name": "card_activity_sourceBoardId_board_id_fk",
+ "tableFrom": "card_activity",
+ "tableTo": "board",
+ "columnsFrom": [
+ "sourceBoardId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "card_activity_publicId_unique": {
+ "name": "card_activity_publicId_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "publicId"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.card_attachment": {
+ "name": "card_attachment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "publicId": {
+ "name": "publicId",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cardId": {
+ "name": "cardId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "filename": {
+ "name": "filename",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "originalFilename": {
+ "name": "originalFilename",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "contentType": {
+ "name": "contentType",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "size": {
+ "name": "size",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "s3Key": {
+ "name": "s3Key",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deletedAt": {
+ "name": "deletedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "card_attachment_cardId_card_id_fk": {
+ "name": "card_attachment_cardId_card_id_fk",
+ "tableFrom": "card_attachment",
+ "tableTo": "card",
+ "columnsFrom": [
+ "cardId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "card_attachment_createdBy_user_id_fk": {
+ "name": "card_attachment_createdBy_user_id_fk",
+ "tableFrom": "card_attachment",
+ "tableTo": "user",
+ "columnsFrom": [
+ "createdBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "card_attachment_publicId_unique": {
+ "name": "card_attachment_publicId_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "publicId"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public._card_workspace_members": {
+ "name": "_card_workspace_members",
+ "schema": "",
+ "columns": {
+ "cardId": {
+ "name": "cardId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "workspaceMemberId": {
+ "name": "workspaceMemberId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "_card_workspace_members_cardId_card_id_fk": {
+ "name": "_card_workspace_members_cardId_card_id_fk",
+ "tableFrom": "_card_workspace_members",
+ "tableTo": "card",
+ "columnsFrom": [
+ "cardId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "_card_workspace_members_workspaceMemberId_workspace_members_id_fk": {
+ "name": "_card_workspace_members_workspaceMemberId_workspace_members_id_fk",
+ "tableFrom": "_card_workspace_members",
+ "tableTo": "workspace_members",
+ "columnsFrom": [
+ "workspaceMemberId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "_card_workspace_members_cardId_workspaceMemberId_pk": {
+ "name": "_card_workspace_members_cardId_workspaceMemberId_pk",
+ "columns": [
+ "cardId",
+ "workspaceMemberId"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.card": {
+ "name": "card",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "publicId": {
+ "name": "publicId",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "index": {
+ "name": "index",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletedAt": {
+ "name": "deletedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletedBy": {
+ "name": "deletedBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "listId": {
+ "name": "listId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "importId": {
+ "name": "importId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "dueDate": {
+ "name": "dueDate",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "card_createdBy_user_id_fk": {
+ "name": "card_createdBy_user_id_fk",
+ "tableFrom": "card",
+ "tableTo": "user",
+ "columnsFrom": [
+ "createdBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "card_deletedBy_user_id_fk": {
+ "name": "card_deletedBy_user_id_fk",
+ "tableFrom": "card",
+ "tableTo": "user",
+ "columnsFrom": [
+ "deletedBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "card_listId_list_id_fk": {
+ "name": "card_listId_list_id_fk",
+ "tableFrom": "card",
+ "tableTo": "list",
+ "columnsFrom": [
+ "listId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "card_importId_import_id_fk": {
+ "name": "card_importId_import_id_fk",
+ "tableFrom": "card",
+ "tableTo": "import",
+ "columnsFrom": [
+ "importId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "card_publicId_unique": {
+ "name": "card_publicId_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "publicId"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public._card_labels": {
+ "name": "_card_labels",
+ "schema": "",
+ "columns": {
+ "cardId": {
+ "name": "cardId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "labelId": {
+ "name": "labelId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "_card_labels_cardId_card_id_fk": {
+ "name": "_card_labels_cardId_card_id_fk",
+ "tableFrom": "_card_labels",
+ "tableTo": "card",
+ "columnsFrom": [
+ "cardId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "_card_labels_labelId_label_id_fk": {
+ "name": "_card_labels_labelId_label_id_fk",
+ "tableFrom": "_card_labels",
+ "tableTo": "label",
+ "columnsFrom": [
+ "labelId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "_card_labels_cardId_labelId_pk": {
+ "name": "_card_labels_cardId_labelId_pk",
+ "columns": [
+ "cardId",
+ "labelId"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.card_comments": {
+ "name": "card_comments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "publicId": {
+ "name": "publicId",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "comment": {
+ "name": "comment",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cardId": {
+ "name": "cardId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletedAt": {
+ "name": "deletedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletedBy": {
+ "name": "deletedBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "card_comments_cardId_card_id_fk": {
+ "name": "card_comments_cardId_card_id_fk",
+ "tableFrom": "card_comments",
+ "tableTo": "card",
+ "columnsFrom": [
+ "cardId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "card_comments_createdBy_user_id_fk": {
+ "name": "card_comments_createdBy_user_id_fk",
+ "tableFrom": "card_comments",
+ "tableTo": "user",
+ "columnsFrom": [
+ "createdBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "card_comments_deletedBy_user_id_fk": {
+ "name": "card_comments_deletedBy_user_id_fk",
+ "tableFrom": "card_comments",
+ "tableTo": "user",
+ "columnsFrom": [
+ "deletedBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "card_comments_publicId_unique": {
+ "name": "card_comments_publicId_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "publicId"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.card_checklist_item": {
+ "name": "card_checklist_item",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "publicId": {
+ "name": "publicId",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "completed": {
+ "name": "completed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "index": {
+ "name": "index",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "checklistId": {
+ "name": "checklistId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletedAt": {
+ "name": "deletedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletedBy": {
+ "name": "deletedBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "card_checklist_item_checklistId_card_checklist_id_fk": {
+ "name": "card_checklist_item_checklistId_card_checklist_id_fk",
+ "tableFrom": "card_checklist_item",
+ "tableTo": "card_checklist",
+ "columnsFrom": [
+ "checklistId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "card_checklist_item_createdBy_user_id_fk": {
+ "name": "card_checklist_item_createdBy_user_id_fk",
+ "tableFrom": "card_checklist_item",
+ "tableTo": "user",
+ "columnsFrom": [
+ "createdBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "card_checklist_item_deletedBy_user_id_fk": {
+ "name": "card_checklist_item_deletedBy_user_id_fk",
+ "tableFrom": "card_checklist_item",
+ "tableTo": "user",
+ "columnsFrom": [
+ "deletedBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "card_checklist_item_publicId_unique": {
+ "name": "card_checklist_item_publicId_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "publicId"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.card_checklist": {
+ "name": "card_checklist",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "publicId": {
+ "name": "publicId",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "index": {
+ "name": "index",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cardId": {
+ "name": "cardId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletedAt": {
+ "name": "deletedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletedBy": {
+ "name": "deletedBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "card_checklist_cardId_card_id_fk": {
+ "name": "card_checklist_cardId_card_id_fk",
+ "tableFrom": "card_checklist",
+ "tableTo": "card",
+ "columnsFrom": [
+ "cardId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "card_checklist_createdBy_user_id_fk": {
+ "name": "card_checklist_createdBy_user_id_fk",
+ "tableFrom": "card_checklist",
+ "tableTo": "user",
+ "columnsFrom": [
+ "createdBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "card_checklist_deletedBy_user_id_fk": {
+ "name": "card_checklist_deletedBy_user_id_fk",
+ "tableFrom": "card_checklist",
+ "tableTo": "user",
+ "columnsFrom": [
+ "deletedBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "card_checklist_publicId_unique": {
+ "name": "card_checklist_publicId_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "publicId"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.feedback": {
+ "name": "feedback",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "feedback": {
+ "name": "feedback",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "url": {
+ "name": "url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reviewed": {
+ "name": "reviewed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "feedback_createdBy_user_id_fk": {
+ "name": "feedback_createdBy_user_id_fk",
+ "tableFrom": "feedback",
+ "tableTo": "user",
+ "columnsFrom": [
+ "createdBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.import": {
+ "name": "import",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "publicId": {
+ "name": "publicId",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source": {
+ "name": "source",
+ "type": "source",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "import_createdBy_user_id_fk": {
+ "name": "import_createdBy_user_id_fk",
+ "tableFrom": "import",
+ "tableTo": "user",
+ "columnsFrom": [
+ "createdBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "import_publicId_unique": {
+ "name": "import_publicId_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "publicId"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.label": {
+ "name": "label",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "publicId": {
+ "name": "publicId",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "colourCode": {
+ "name": "colourCode",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "boardId": {
+ "name": "boardId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "importId": {
+ "name": "importId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletedAt": {
+ "name": "deletedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletedBy": {
+ "name": "deletedBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "label_createdBy_user_id_fk": {
+ "name": "label_createdBy_user_id_fk",
+ "tableFrom": "label",
+ "tableTo": "user",
+ "columnsFrom": [
+ "createdBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "label_boardId_board_id_fk": {
+ "name": "label_boardId_board_id_fk",
+ "tableFrom": "label",
+ "tableTo": "board",
+ "columnsFrom": [
+ "boardId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "label_importId_import_id_fk": {
+ "name": "label_importId_import_id_fk",
+ "tableFrom": "label",
+ "tableTo": "import",
+ "columnsFrom": [
+ "importId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "label_deletedBy_user_id_fk": {
+ "name": "label_deletedBy_user_id_fk",
+ "tableFrom": "label",
+ "tableTo": "user",
+ "columnsFrom": [
+ "deletedBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "label_publicId_unique": {
+ "name": "label_publicId_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "publicId"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.list": {
+ "name": "list",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "publicId": {
+ "name": "publicId",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "index": {
+ "name": "index",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletedAt": {
+ "name": "deletedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletedBy": {
+ "name": "deletedBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "boardId": {
+ "name": "boardId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "importId": {
+ "name": "importId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "list_createdBy_user_id_fk": {
+ "name": "list_createdBy_user_id_fk",
+ "tableFrom": "list",
+ "tableTo": "user",
+ "columnsFrom": [
+ "createdBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "list_deletedBy_user_id_fk": {
+ "name": "list_deletedBy_user_id_fk",
+ "tableFrom": "list",
+ "tableTo": "user",
+ "columnsFrom": [
+ "deletedBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "list_boardId_board_id_fk": {
+ "name": "list_boardId_board_id_fk",
+ "tableFrom": "list",
+ "tableTo": "board",
+ "columnsFrom": [
+ "boardId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "list_importId_import_id_fk": {
+ "name": "list_importId_import_id_fk",
+ "tableFrom": "list",
+ "tableTo": "import",
+ "columnsFrom": [
+ "importId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "list_publicId_unique": {
+ "name": "list_publicId_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "publicId"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.user": {
+ "name": "user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "uuid_generate_v4()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "emailVerified": {
+ "name": "emailVerified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "image": {
+ "name": "image",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "stripeCustomerId": {
+ "name": "stripeCustomerId",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_email_unique": {
+ "name": "user_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.integration": {
+ "name": "integration",
+ "schema": "",
+ "columns": {
+ "provider": {
+ "name": "provider",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "userId": {
+ "name": "userId",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "accessToken": {
+ "name": "accessToken",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refreshToken": {
+ "name": "refreshToken",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expiresAt": {
+ "name": "expiresAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "integration_userId_user_id_fk": {
+ "name": "integration_userId_user_id_fk",
+ "tableFrom": "integration",
+ "tableTo": "user",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "integration_pkey": {
+ "name": "integration_pkey",
+ "columns": [
+ "userId",
+ "provider"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.workspace_slug_checks": {
+ "name": "workspace_slug_checks",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "available": {
+ "name": "available",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reserved": {
+ "name": "reserved",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "workspaceId": {
+ "name": "workspaceId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "workspace_slug_checks_workspaceId_workspace_id_fk": {
+ "name": "workspace_slug_checks_workspaceId_workspace_id_fk",
+ "tableFrom": "workspace_slug_checks",
+ "tableTo": "workspace",
+ "columnsFrom": [
+ "workspaceId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "workspace_slug_checks_createdBy_user_id_fk": {
+ "name": "workspace_slug_checks_createdBy_user_id_fk",
+ "tableFrom": "workspace_slug_checks",
+ "tableTo": "user",
+ "columnsFrom": [
+ "createdBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.workspace_slugs": {
+ "name": "workspace_slugs",
+ "schema": "",
+ "columns": {
+ "slug": {
+ "name": "slug",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "slug_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "workspace_slugs_slug_unique": {
+ "name": "workspace_slugs_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.workspace_members": {
+ "name": "workspace_members",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "publicId": {
+ "name": "publicId",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "userId": {
+ "name": "userId",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "workspaceId": {
+ "name": "workspaceId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletedAt": {
+ "name": "deletedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletedBy": {
+ "name": "deletedBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "role": {
+ "name": "role",
+ "type": "role",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "roleId": {
+ "name": "roleId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "member_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'invited'"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "workspace_members_userId_user_id_fk": {
+ "name": "workspace_members_userId_user_id_fk",
+ "tableFrom": "workspace_members",
+ "tableTo": "user",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "workspace_members_workspaceId_workspace_id_fk": {
+ "name": "workspace_members_workspaceId_workspace_id_fk",
+ "tableFrom": "workspace_members",
+ "tableTo": "workspace",
+ "columnsFrom": [
+ "workspaceId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "workspace_members_deletedBy_user_id_fk": {
+ "name": "workspace_members_deletedBy_user_id_fk",
+ "tableFrom": "workspace_members",
+ "tableTo": "user",
+ "columnsFrom": [
+ "deletedBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "workspace_members_roleId_workspace_roles_id_fk": {
+ "name": "workspace_members_roleId_workspace_roles_id_fk",
+ "tableFrom": "workspace_members",
+ "tableTo": "workspace_roles",
+ "columnsFrom": [
+ "roleId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "workspace_members_publicId_unique": {
+ "name": "workspace_members_publicId_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "publicId"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.workspace": {
+ "name": "workspace",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "publicId": {
+ "name": "publicId",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "plan": {
+ "name": "plan",
+ "type": "workspace_plan",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'free'"
+ },
+ "showEmailsToMembers": {
+ "name": "showEmailsToMembers",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletedAt": {
+ "name": "deletedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletedBy": {
+ "name": "deletedBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "workspace_createdBy_user_id_fk": {
+ "name": "workspace_createdBy_user_id_fk",
+ "tableFrom": "workspace",
+ "tableTo": "user",
+ "columnsFrom": [
+ "createdBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "workspace_deletedBy_user_id_fk": {
+ "name": "workspace_deletedBy_user_id_fk",
+ "tableFrom": "workspace",
+ "tableTo": "user",
+ "columnsFrom": [
+ "deletedBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "workspace_publicId_unique": {
+ "name": "workspace_publicId_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "publicId"
+ ]
+ },
+ "workspace_slug_unique": {
+ "name": "workspace_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.subscription": {
+ "name": "subscription",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "plan": {
+ "name": "plan",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "referenceId": {
+ "name": "referenceId",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripeCustomerId": {
+ "name": "stripeCustomerId",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripeSubscriptionId": {
+ "name": "stripeSubscriptionId",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "periodStart": {
+ "name": "periodStart",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "periodEnd": {
+ "name": "periodEnd",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cancelAtPeriodEnd": {
+ "name": "cancelAtPeriodEnd",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "seats": {
+ "name": "seats",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "unlimitedSeats": {
+ "name": "unlimitedSeats",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "trialStart": {
+ "name": "trialStart",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "trialEnd": {
+ "name": "trialEnd",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "subscription_referenceId_workspace_publicId_fk": {
+ "name": "subscription_referenceId_workspace_publicId_fk",
+ "tableFrom": "subscription",
+ "tableTo": "workspace",
+ "columnsFrom": [
+ "referenceId"
+ ],
+ "columnsTo": [
+ "publicId"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.workspace_invite_links": {
+ "name": "workspace_invite_links",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "publicId": {
+ "name": "publicId",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "workspaceId": {
+ "name": "workspaceId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "code": {
+ "name": "code",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "invite_link_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "expiresAt": {
+ "name": "expiresAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updatedBy": {
+ "name": "updatedBy",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "workspace_invite_links_workspaceId_workspace_id_fk": {
+ "name": "workspace_invite_links_workspaceId_workspace_id_fk",
+ "tableFrom": "workspace_invite_links",
+ "tableTo": "workspace",
+ "columnsFrom": [
+ "workspaceId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "workspace_invite_links_createdBy_user_id_fk": {
+ "name": "workspace_invite_links_createdBy_user_id_fk",
+ "tableFrom": "workspace_invite_links",
+ "tableTo": "user",
+ "columnsFrom": [
+ "createdBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "workspace_invite_links_updatedBy_user_id_fk": {
+ "name": "workspace_invite_links_updatedBy_user_id_fk",
+ "tableFrom": "workspace_invite_links",
+ "tableTo": "user",
+ "columnsFrom": [
+ "updatedBy"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "workspace_invite_links_publicId_unique": {
+ "name": "workspace_invite_links_publicId_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "publicId"
+ ]
+ },
+ "workspace_invite_links_code_unique": {
+ "name": "workspace_invite_links_code_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "code"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.workspace_member_permissions": {
+ "name": "workspace_member_permissions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "workspaceMemberId": {
+ "name": "workspaceMemberId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "permission": {
+ "name": "permission",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "granted": {
+ "name": "granted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "unique_member_permission": {
+ "name": "unique_member_permission",
+ "columns": [
+ {
+ "expression": "workspaceMemberId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "permission",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "permission_member_idx": {
+ "name": "permission_member_idx",
+ "columns": [
+ {
+ "expression": "workspaceMemberId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.workspace_role_permissions": {
+ "name": "workspace_role_permissions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "workspaceRoleId": {
+ "name": "workspaceRoleId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "permission": {
+ "name": "permission",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "granted": {
+ "name": "granted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "unique_role_permission": {
+ "name": "unique_role_permission",
+ "columns": [
+ {
+ "expression": "workspaceRoleId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "permission",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "role_permissions_role_idx": {
+ "name": "role_permissions_role_idx",
+ "columns": [
+ {
+ "expression": "workspaceRoleId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "workspace_role_permissions_workspaceRoleId_workspace_roles_id_fk": {
+ "name": "workspace_role_permissions_workspaceRoleId_workspace_roles_id_fk",
+ "tableFrom": "workspace_role_permissions",
+ "tableTo": "workspace_roles",
+ "columnsFrom": [
+ "workspaceRoleId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.workspace_roles": {
+ "name": "workspace_roles",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "publicId": {
+ "name": "publicId",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "workspaceId": {
+ "name": "workspaceId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "hierarchyLevel": {
+ "name": "hierarchyLevel",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "isSystem": {
+ "name": "isSystem",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "unique_role_per_workspace": {
+ "name": "unique_role_per_workspace",
+ "columns": [
+ {
+ "expression": "workspaceId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "workspace_roles_workspace_idx": {
+ "name": "workspace_roles_workspace_idx",
+ "columns": [
+ {
+ "expression": "workspaceId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "workspace_roles_workspaceId_workspace_id_fk": {
+ "name": "workspace_roles_workspaceId_workspace_id_fk",
+ "tableFrom": "workspace_roles",
+ "tableTo": "workspace",
+ "columnsFrom": [
+ "workspaceId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "workspace_roles_publicId_unique": {
+ "name": "workspace_roles_publicId_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "publicId"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.notification": {
+ "name": "notification",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "publicId": {
+ "name": "publicId",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "notification_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "userId": {
+ "name": "userId",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cardId": {
+ "name": "cardId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "commentId": {
+ "name": "commentId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "workspaceId": {
+ "name": "workspaceId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "readAt": {
+ "name": "readAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deletedAt": {
+ "name": "deletedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "notification_user_deleted_idx": {
+ "name": "notification_user_deleted_idx",
+ "columns": [
+ {
+ "expression": "userId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "deletedAt",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_user_read_deleted_idx": {
+ "name": "notification_user_read_deleted_idx",
+ "columns": [
+ {
+ "expression": "userId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "readAt",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "deletedAt",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_user_type_card_idx": {
+ "name": "notification_user_type_card_idx",
+ "columns": [
+ {
+ "expression": "userId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "cardId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_user_type_workspace_idx": {
+ "name": "notification_user_type_workspace_idx",
+ "columns": [
+ {
+ "expression": "userId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "workspaceId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_user_created_idx": {
+ "name": "notification_user_created_idx",
+ "columns": [
+ {
+ "expression": "userId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "createdAt",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "notification_userId_user_id_fk": {
+ "name": "notification_userId_user_id_fk",
+ "tableFrom": "notification",
+ "tableTo": "user",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "notification_cardId_card_id_fk": {
+ "name": "notification_cardId_card_id_fk",
+ "tableFrom": "notification",
+ "tableTo": "card",
+ "columnsFrom": [
+ "cardId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "notification_commentId_card_comments_id_fk": {
+ "name": "notification_commentId_card_comments_id_fk",
+ "tableFrom": "notification",
+ "tableTo": "card_comments",
+ "columnsFrom": [
+ "commentId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "notification_workspaceId_workspace_id_fk": {
+ "name": "notification_workspaceId_workspace_id_fk",
+ "tableFrom": "notification",
+ "tableTo": "workspace",
+ "columnsFrom": [
+ "workspaceId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "notification_publicId_unique": {
+ "name": "notification_publicId_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "publicId"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ }
+ },
+ "enums": {
+ "public.board_type": {
+ "name": "board_type",
+ "schema": "public",
+ "values": [
+ "regular",
+ "template"
+ ]
+ },
+ "public.board_visibility": {
+ "name": "board_visibility",
+ "schema": "public",
+ "values": [
+ "private",
+ "public"
+ ]
+ },
+ "public.card_activity_type": {
+ "name": "card_activity_type",
+ "schema": "public",
+ "values": [
+ "card.created",
+ "card.updated.title",
+ "card.updated.description",
+ "card.updated.index",
+ "card.updated.list",
+ "card.updated.label.added",
+ "card.updated.label.removed",
+ "card.updated.member.added",
+ "card.updated.member.removed",
+ "card.updated.comment.added",
+ "card.updated.comment.updated",
+ "card.updated.comment.deleted",
+ "card.updated.checklist.added",
+ "card.updated.checklist.renamed",
+ "card.updated.checklist.deleted",
+ "card.updated.checklist.item.added",
+ "card.updated.checklist.item.updated",
+ "card.updated.checklist.item.completed",
+ "card.updated.checklist.item.uncompleted",
+ "card.updated.checklist.item.deleted",
+ "card.updated.attachment.added",
+ "card.updated.attachment.removed",
+ "card.updated.dueDate.added",
+ "card.updated.dueDate.updated",
+ "card.updated.dueDate.removed",
+ "card.archived"
+ ]
+ },
+ "public.source": {
+ "name": "source",
+ "schema": "public",
+ "values": [
+ "trello"
+ ]
+ },
+ "public.status": {
+ "name": "status",
+ "schema": "public",
+ "values": [
+ "started",
+ "success",
+ "failed"
+ ]
+ },
+ "public.role": {
+ "name": "role",
+ "schema": "public",
+ "values": [
+ "admin",
+ "member",
+ "guest"
+ ]
+ },
+ "public.member_status": {
+ "name": "member_status",
+ "schema": "public",
+ "values": [
+ "invited",
+ "active",
+ "removed",
+ "paused"
+ ]
+ },
+ "public.slug_type": {
+ "name": "slug_type",
+ "schema": "public",
+ "values": [
+ "reserved",
+ "premium"
+ ]
+ },
+ "public.workspace_plan": {
+ "name": "workspace_plan",
+ "schema": "public",
+ "values": [
+ "free",
+ "pro",
+ "enterprise"
+ ]
+ },
+ "public.invite_link_status": {
+ "name": "invite_link_status",
+ "schema": "public",
+ "values": [
+ "active",
+ "inactive"
+ ]
+ },
+ "public.notification_type": {
+ "name": "notification_type",
+ "schema": "public",
+ "values": [
+ "mention",
+ "workspace.member.added",
+ "workspace.member.removed",
+ "workspace.role.changed"
+ ]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/packages/db/migrations/meta/_journal.json b/packages/db/migrations/meta/_journal.json
index b514daac..85dfcf6c 100644
--- a/packages/db/migrations/meta/_journal.json
+++ b/packages/db/migrations/meta/_journal.json
@@ -176,6 +176,13 @@
"when": 1769983198190,
"tag": "20260201215958_AddUserBoardFavouritesTable",
"breakpoints": true
+ },
+ {
+ "idx": 25,
+ "version": "7",
+ "when": 1770500457005,
+ "tag": "20260207214056_AddNotificationsTable",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/packages/db/src/repository/board.repo.ts b/packages/db/src/repository/board.repo.ts
index 7878b20a..3d709f41 100644
--- a/packages/db/src/repository/board.repo.ts
+++ b/packages/db/src/repository/board.repo.ts
@@ -212,6 +212,7 @@ export const getByPublicId = async (
columns: {
publicId: true,
email: true,
+ status: true,
},
with: {
user: {
diff --git a/packages/db/src/repository/card.repo.ts b/packages/db/src/repository/card.repo.ts
index 805ddce0..5dd08580 100644
--- a/packages/db/src/repository/card.repo.ts
+++ b/packages/db/src/repository/card.repo.ts
@@ -420,6 +420,7 @@ export const getWithListAndMembersByPublicId = async (
) => {
const card = await db.query.cards.findFirst({
columns: {
+ id: true,
publicId: true,
title: true,
description: true,
@@ -507,6 +508,7 @@ export const getWithListAndMembersByPublicId = async (
columns: {
publicId: true,
email: true,
+ status: true,
},
with: {
user: {
diff --git a/packages/db/src/repository/member.repo.ts b/packages/db/src/repository/member.repo.ts
index 5aba6423..4035784e 100644
--- a/packages/db/src/repository/member.repo.ts
+++ b/packages/db/src/repository/member.repo.ts
@@ -63,6 +63,36 @@ export const getById = async (db: dbClient, memberId: number) => {
});
};
+export const getByPublicIdsWithUsers = async (
+ db: dbClient,
+ memberPublicIds: string[],
+ workspaceId?: number,
+) => {
+ return db.query.workspaceMembers.findMany({
+ where: (members, { inArray: inArrayFn, eq, and, isNull: isNullFn }) => {
+ const conditions = [inArrayFn(members.publicId, memberPublicIds)];
+
+ if (workspaceId) {
+ conditions.push(eq(members.workspaceId, workspaceId));
+ }
+
+ conditions.push(eq(members.status, "active"));
+ conditions.push(isNullFn(members.deletedAt));
+
+ return and(...conditions);
+ },
+ with: {
+ user: {
+ columns: {
+ id: true,
+ name: true,
+ email: true,
+ },
+ },
+ },
+ });
+};
+
export const getByEmailAndStatus = async (
db: dbClient,
email: string,
diff --git a/packages/db/src/repository/notification.repo.ts b/packages/db/src/repository/notification.repo.ts
new file mode 100644
index 00000000..67bd2b4c
--- /dev/null
+++ b/packages/db/src/repository/notification.repo.ts
@@ -0,0 +1,98 @@
+import { and, count, eq, isNull } from "drizzle-orm";
+
+import type { dbClient } from "@kan/db/client";
+import type { NotificationType } from "@kan/db/schema";
+import { notifications } from "@kan/db/schema";
+import { generateUID } from "@kan/shared/utils";
+
+export const create = async (
+ db: dbClient,
+ notificationInput: {
+ type: NotificationType;
+ userId: string;
+ cardId?: number;
+ commentId?: number;
+ workspaceId?: number;
+ metadata?: string;
+ },
+) => {
+ const [result] = await db
+ .insert(notifications)
+ .values({
+ publicId: generateUID(),
+ type: notificationInput.type,
+ userId: notificationInput.userId,
+ cardId: notificationInput.cardId,
+ commentId: notificationInput.commentId,
+ workspaceId: notificationInput.workspaceId,
+ metadata: notificationInput.metadata,
+ })
+ .returning();
+
+ return result;
+};
+
+export const exists = async (
+ db: dbClient,
+ args: {
+ userId: string;
+ type: NotificationType;
+ cardId?: number;
+ workspaceId?: number;
+ commentId?: number;
+ },
+) => {
+ const result = await db.query.notifications.findFirst({
+ where: (notifications, { eq, and, isNull: isNullFn }) => {
+ const conditions = [
+ eq(notifications.userId, args.userId),
+ eq(notifications.type, args.type),
+ isNullFn(notifications.deletedAt),
+ ];
+
+ if (args.cardId) {
+ conditions.push(eq(notifications.cardId, args.cardId));
+ }
+
+ if (args.workspaceId) {
+ conditions.push(eq(notifications.workspaceId, args.workspaceId));
+ }
+
+ return and(...conditions);
+ },
+ });
+
+ return !!result;
+};
+
+export const markAsRead = async (
+ db: dbClient,
+ notificationId: number,
+) => {
+ const [result] = await db
+ .update(notifications)
+ .set({ readAt: new Date() })
+ .where(eq(notifications.id, notificationId))
+ .returning();
+
+ return result;
+};
+
+export const getUnreadCount = async (
+ db: dbClient,
+ userId: string,
+) => {
+ const result = await db
+ .select({ count: count() })
+ .from(notifications)
+ .where(
+ and(
+ eq(notifications.userId, userId),
+ isNull(notifications.readAt),
+ isNull(notifications.deletedAt),
+ ),
+ );
+
+ return result[0]?.count ?? 0;
+};
+
diff --git a/packages/db/src/schema/index.ts b/packages/db/src/schema/index.ts
index 0162cf55..b72f722a 100644
--- a/packages/db/src/schema/index.ts
+++ b/packages/db/src/schema/index.ts
@@ -13,3 +13,4 @@ export * from "./workspaces";
export * from "./subscriptions";
export * from "./workspaceInviteLinks";
export * from "./permissions";
+export * from "./notifications";
diff --git a/packages/db/src/schema/notifications.ts b/packages/db/src/schema/notifications.ts
new file mode 100644
index 00000000..cc3437f9
--- /dev/null
+++ b/packages/db/src/schema/notifications.ts
@@ -0,0 +1,98 @@
+import { relations } from "drizzle-orm";
+import {
+ bigint,
+ bigserial,
+ index,
+ pgEnum,
+ pgTable,
+ text,
+ timestamp,
+ uuid,
+ varchar,
+} from "drizzle-orm/pg-core";
+
+import { cards } from "./cards";
+import { comments } from "./cards";
+import { users } from "./users";
+import { workspaces } from "./workspaces";
+
+export const notificationTypes = [
+ "mention",
+ "workspace.member.added",
+ "workspace.member.removed",
+ "workspace.role.changed",
+] as const;
+
+export type NotificationType = (typeof notificationTypes)[number];
+
+export const notificationTypeEnum = pgEnum("notification_type", notificationTypes);
+
+export const notifications = pgTable(
+ "notification",
+ {
+ id: bigserial("id", { mode: "number" }).primaryKey(),
+ publicId: varchar("publicId", { length: 12 }).notNull().unique(),
+ type: notificationTypeEnum("type").notNull(),
+ userId: uuid("userId")
+ .notNull()
+ .references(() => users.id, { onDelete: "cascade" }),
+ cardId: bigint("cardId", { mode: "number" }).references(() => cards.id, {
+ onDelete: "cascade",
+ }),
+ commentId: bigint("commentId", { mode: "number" }).references(
+ () => comments.id,
+ { onDelete: "cascade" },
+ ),
+ workspaceId: bigint("workspaceId", { mode: "number" }).references(
+ () => workspaces.id,
+ { onDelete: "cascade" },
+ ),
+ metadata: text("metadata"),
+ readAt: timestamp("readAt"),
+ createdAt: timestamp("createdAt").defaultNow().notNull(),
+ deletedAt: timestamp("deletedAt"),
+ },
+ (table) => [
+ index("notification_user_deleted_idx").on(table.userId, table.deletedAt),
+ index("notification_user_read_deleted_idx").on(
+ table.userId,
+ table.readAt,
+ table.deletedAt,
+ ),
+ index("notification_user_type_card_idx").on(
+ table.userId,
+ table.type,
+ table.cardId,
+ ),
+ index("notification_user_type_workspace_idx").on(
+ table.userId,
+ table.type,
+ table.workspaceId,
+ ),
+ index("notification_user_created_idx").on(table.userId, table.createdAt),
+ ],
+).enableRLS();
+
+export const notificationsRelations = relations(notifications, ({ one }) => ({
+ user: one(users, {
+ fields: [notifications.userId],
+ references: [users.id],
+ relationName: "notificationsUser",
+ }),
+ card: one(cards, {
+ fields: [notifications.cardId],
+ references: [cards.id],
+ relationName: "notificationsCard",
+ }),
+ comment: one(comments, {
+ fields: [notifications.commentId],
+ references: [comments.id],
+ relationName: "notificationsComment",
+ }),
+ workspace: one(workspaces, {
+ fields: [notifications.workspaceId],
+ references: [workspaces.id],
+ relationName: "notificationsWorkspace",
+ }),
+}));
+
diff --git a/packages/email/src/sendEmail.tsx b/packages/email/src/sendEmail.tsx
index a087606c..1a696c62 100644
--- a/packages/email/src/sendEmail.tsx
+++ b/packages/email/src/sendEmail.tsx
@@ -3,14 +3,16 @@ import nodemailer from "nodemailer";
import JoinWorkspaceTemplate from "./templates/join-workspace";
import MagicLinkTemplate from "./templates/magic-link";
+import MentionTemplate from "./templates/mention";
import ResetPasswordTemplate from "./templates/reset-password";
-type Templates = "MAGIC_LINK" | "JOIN_WORKSPACE" | "RESET_PASSWORD";
+type Templates = "MAGIC_LINK" | "JOIN_WORKSPACE" | "RESET_PASSWORD" | "MENTION";
-const emailTemplates: Record