diff --git a/apps/web/src/views/members/components/InviteMemberForm.tsx b/apps/web/src/views/members/components/InviteMemberForm.tsx
index f22049cf..3646ef62 100644
--- a/apps/web/src/views/members/components/InviteMemberForm.tsx
+++ b/apps/web/src/views/members/components/InviteMemberForm.tsx
@@ -7,7 +7,9 @@ import { HiXMark } from "react-icons/hi2";
import { z } from "zod";
import type { InviteMemberInput } from "@kan/api/types";
+import type { Subscription } from "@kan/shared/utils";
import { authClient } from "@kan/auth/client";
+import { getSubscriptionByPlan } from "@kan/shared/utils";
import Button from "~/components/Button";
import Input from "~/components/Input";
@@ -19,20 +21,13 @@ import { api } from "~/utils/api";
export function InviteMemberForm({
numberOfMembers,
- activeTeamSubscription,
+ subscriptions,
+ unlimitedSeats,
userId,
}: {
numberOfMembers: number;
- activeTeamSubscription:
- | {
- id: number | null;
- plan: string;
- status: string;
- seats: number | null;
- periodStart: Date | null;
- periodEnd: Date | null;
- }
- | undefined;
+ subscriptions: Subscription[] | undefined;
+ unlimitedSeats: boolean;
userId: string | undefined;
}) {
const utils = api.useUtils();
@@ -87,16 +82,19 @@ export function InviteMemberForm({
},
});
+ const teamSubscription = getSubscriptionByPlan(subscriptions, "team");
+ const proSubscription = getSubscriptionByPlan(subscriptions, "pro");
+
+ const hasTeamSubscription = !!teamSubscription;
+ const hasProSubscription = !!proSubscription;
+
let isYearly = false;
let price = t`$10/month`;
let billingType = t`monthly billing`;
- if (
- activeTeamSubscription?.periodStart &&
- activeTeamSubscription?.periodEnd
- ) {
- const periodStartDate = new Date(activeTeamSubscription.periodStart);
- const periodEndDate = new Date(activeTeamSubscription.periodEnd);
+ if (teamSubscription?.periodStart && teamSubscription?.periodEnd) {
+ const periodStartDate = new Date(teamSubscription.periodStart);
+ const periodEndDate = new Date(teamSubscription.periodEnd);
const diffInDays = Math.round(
(periodEndDate.getTime() - periodStartDate.getTime()) /
(1000 * 60 * 60 * 24),
@@ -163,7 +161,8 @@ export function InviteMemberForm({
placeholder={t`Email`}
disabled={
env("NEXT_PUBLIC_KAN_ENV") === "cloud" &&
- !activeTeamSubscription?.id
+ !hasTeamSubscription &&
+ !hasProSubscription
}
{...register("email", { required: true })}
onKeyDown={async (e) => {
@@ -177,13 +176,15 @@ export function InviteMemberForm({
{env("NEXT_PUBLIC_KAN_ENV") === "cloud" && (
- {activeTeamSubscription?.id ? (
+ {hasTeamSubscription || hasProSubscription ? (
- {t`Team Plan`}
+ {hasTeamSubscription ? t`Team Plan` : t`Pro Plan ∞`}
- {t`Adding a new member will cost an additional ${price} (${billingType}) per seat.`}
+ {unlimitedSeats
+ ? t`You have unlimited seats with your Pro Plan. There is no additional charge for new members!`
+ : t`Adding a new member will cost an additional ${price} (${billingType}) per seat.`}
) : (
@@ -201,7 +202,7 @@ export function InviteMemberForm({
- {activeTeamSubscription?.id &&
+ {(hasTeamSubscription || hasProSubscription) &&
env("NEXT_PUBLIC_KAN_ENV") === "cloud" && (
{env("NEXT_PUBLIC_KAN_ENV") === "cloud" &&
- !activeTeamSubscription?.id ? (
+ !hasTeamSubscription &&
+ !hasProSubscription ? (
- sub.status === "active" ||
- (sub.status === "trialing" && sub.plan === "team"),
- );
+ const teamSubscription = getSubscriptionByPlan(subscriptions, "team");
+ const proSubscription = getSubscriptionByPlan(subscriptions, "pro");
+
+ const unlimitedSeats = hasUnlimitedSeats(subscriptions);
const TableRow = ({
memberPublicId,
@@ -175,13 +176,20 @@ export default function MembersPage() {
- {activeTeamSubscription ? t`Team Plan` : t`Free Plan`}
+ {proSubscription
+ ? t`Pro Plan`
+ : teamSubscription
+ ? t`Team Plan`
+ : t`Free Plan`}
+ {proSubscription && unlimitedSeats && (
+ ∞
+ )}
)}
@@ -268,7 +276,8 @@ export default function MembersPage() {
diff --git a/apps/web/src/views/settings/components/CustomURLConfirmation.tsx b/apps/web/src/views/settings/components/CustomURLConfirmation.tsx
index ee88f8a5..45bec735 100644
--- a/apps/web/src/views/settings/components/CustomURLConfirmation.tsx
+++ b/apps/web/src/views/settings/components/CustomURLConfirmation.tsx
@@ -1,37 +1,42 @@
import { t } from "@lingui/core/macro";
+import { authClient } from "@kan/auth/client";
+
import Button from "~/components/Button";
import { useModal } from "~/providers/modal";
+import { usePopup } from "~/providers/popup";
export function CustomURLConfirmation({
+ userId,
workspacePublicId,
}: {
+ userId: string;
workspacePublicId: string;
}) {
const { closeModal, entityId } = useModal();
+ const { showPopup } = usePopup();
const handleUpgrade = async () => {
- try {
- const response = await fetch("/api/stripe/create_checkout_session", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- slug: entityId,
- workspacePublicId: workspacePublicId,
- cancelUrl: "/settings",
- successUrl: "/settings",
- }),
+ const { data, error } = await authClient.subscription.upgrade({
+ plan: "pro",
+ referenceId: workspacePublicId,
+ metadata: { userId, workspacePublicId, workspaceSlug: entityId },
+ successUrl: "/settings",
+ cancelUrl: "/settings",
+ returnUrl: "/settings",
+ disableRedirect: true,
+ });
+
+ if (data?.url) {
+ window.location.href = data.url;
+ }
+
+ if (error) {
+ showPopup({
+ header: t`Error upgrading subscription`,
+ message: t`Please try again later, or contact customer support.`,
+ icon: "error",
});
-
- const { url } = (await response.json()) as { url: string };
-
- if (url) {
- window.location.href = url;
- }
- } catch (error) {
- console.error("Error creating checkout session:", error);
}
};
diff --git a/packages/api/src/routers/member.ts b/packages/api/src/routers/member.ts
index 1a245a75..17134d0e 100644
--- a/packages/api/src/routers/member.ts
+++ b/packages/api/src/routers/member.ts
@@ -2,8 +2,10 @@ import { TRPCError } from "@trpc/server";
import { z } from "zod";
import * as memberRepo from "@kan/db/repository/member.repo";
+import * as subscriptionRepo from "@kan/db/repository/subscription.repo";
import * as userRepo from "@kan/db/repository/user.repo";
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
+import { getSubscriptionByPlan, hasUnlimitedSeats } from "@kan/shared/utils";
import { updateSubscriptionSeats } from "@kan/stripe";
import { createTRPCRouter, protectedProcedure } from "../trpc";
@@ -62,29 +64,34 @@ export const memberRouter = createTRPCRouter({
}
if (process.env.NEXT_PUBLIC_KAN_ENV === "cloud") {
- const subscriptions = await ctx.auth.api.listActiveSubscriptions({
- workspacePublicId: workspace.publicId,
- });
-
- // get the active subscription
- const activeSubscription = subscriptions.find(
- (sub) =>
- sub.status === "active" ||
- (sub.status === "trialing" && sub.plan === "team"),
+ const subscriptions = await subscriptionRepo.getByReferenceId(
+ ctx.db,
+ workspace.publicId,
);
- if (!activeSubscription) {
+ // get the active subscriptions
+ const activeTeamSubscription = getSubscriptionByPlan(
+ subscriptions,
+ "team",
+ );
+ const activeProSubscription = getSubscriptionByPlan(
+ subscriptions,
+ "pro",
+ );
+ const unlimitedSeats = hasUnlimitedSeats(subscriptions);
+
+ if (!activeTeamSubscription && !activeProSubscription) {
throw new TRPCError({
message: `Workspace with public ID ${workspace.publicId} does not have an active subscription`,
code: "NOT_FOUND",
});
}
- // Update the Stripe subscription to add a seat with immediate proration
- if (activeSubscription.stripeSubscriptionId) {
+ // Update the Stripe subscription
+ if (activeTeamSubscription?.stripeSubscriptionId && !unlimitedSeats) {
try {
await updateSubscriptionSeats(
- activeSubscription.stripeSubscriptionId,
+ activeTeamSubscription.stripeSubscriptionId,
1,
);
} catch (error) {
@@ -204,22 +211,23 @@ export const memberRouter = createTRPCRouter({
// Handle subscription seat decrement for cloud environment
if (process.env.NEXT_PUBLIC_KAN_ENV === "cloud") {
- const subscriptions = await ctx.auth.api.listActiveSubscriptions({
- workspacePublicId: workspace.publicId,
- });
-
- // get the active subscription
- const activeSubscription = subscriptions.find(
- (sub) =>
- sub.status === "active" ||
- (sub.status === "trialing" && sub.plan === "team"),
+ const subscriptions = await subscriptionRepo.getByReferenceId(
+ ctx.db,
+ workspace.publicId,
);
+ // get the active subscriptions
+ const activeTeamSubscription = getSubscriptionByPlan(
+ subscriptions,
+ "team",
+ );
+ const unlimitedSeats = hasUnlimitedSeats(subscriptions);
+
// Only decrease seats if there's an active subscription and stripeSubscriptionId
- if (activeSubscription?.stripeSubscriptionId) {
+ if (activeTeamSubscription?.stripeSubscriptionId && !unlimitedSeats) {
try {
await updateSubscriptionSeats(
- activeSubscription.stripeSubscriptionId,
+ activeTeamSubscription.stripeSubscriptionId,
-1,
);
} catch (error) {
diff --git a/packages/auth/src/auth.ts b/packages/auth/src/auth.ts
index 9822f271..a4bc0ea4 100644
--- a/packages/auth/src/auth.ts
+++ b/packages/auth/src/auth.ts
@@ -10,6 +10,7 @@ import { env } from "next-runtime-env";
import type { dbClient } from "@kan/db/client";
import * as memberRepo from "@kan/db/repository/member.repo";
+import * as subscriptionRepo from "@kan/db/repository/subscription.repo";
import * as userRepo from "@kan/db/repository/user.repo";
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
import * as schema from "@kan/db/schema";
@@ -203,6 +204,24 @@ export const initAuth = (db: dbClient) => {
},
};
},
+ onSubscriptionComplete: async ({
+ subscription,
+ stripeSubscription,
+ }) => {
+ // Set unlimited seats to true for pro plans
+ if (subscription.plan === "pro") {
+ await subscriptionRepo.updateByStripeSubscriptionId(
+ db,
+ stripeSubscription.id,
+ {
+ unlimitedSeats: true,
+ },
+ );
+ console.log(
+ `Pro subscription ${stripeSubscription.id} activated with unlimited seats`,
+ );
+ }
+ },
},
}),
]
diff --git a/packages/db/migrations/20250907185512_AddUnlimitedSeatsToSubscription.sql b/packages/db/migrations/20250907185512_AddUnlimitedSeatsToSubscription.sql
new file mode 100644
index 00000000..ae5c1999
--- /dev/null
+++ b/packages/db/migrations/20250907185512_AddUnlimitedSeatsToSubscription.sql
@@ -0,0 +1 @@
+ALTER TABLE "subscription" ADD COLUMN "unlimitedSeats" boolean DEFAULT false NOT NULL;
\ No newline at end of file
diff --git a/packages/db/migrations/meta/20250907185512_snapshot.json b/packages/db/migrations/meta/20250907185512_snapshot.json
new file mode 100644
index 00000000..098b3f96
--- /dev/null
+++ b/packages/db/migrations/meta/20250907185512_snapshot.json
@@ -0,0 +1,2543 @@
+{
+ "id": "4fb840a2-caea-41d4-baf3-8ba081c1ec0a",
+ "prevId": "d16ab16a-b4af-4d34-abb9-e663f4e63307",
+ "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'"
+ }
+ },
+ "indexes": {
+ "board_visibility_idx": {
+ "name": "board_visibility_idx",
+ "columns": [
+ {
+ "expression": "visibility",
+ "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.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": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "toTitle": {
+ "name": "toTitle",
+ "type": "varchar(255)",
+ "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
+ }
+ },
+ "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"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "card_activity_publicId_unique": {
+ "name": "card_activity_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": "varchar(255)",
+ "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
+ }
+ },
+ "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_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
+ },
+ "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"
+ }
+ },
+ "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'"
+ },
+ "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": true
+ },
+ "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": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ }
+ },
+ "enums": {
+ "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.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"
+ ]
+ },
+ "public.slug_type": {
+ "name": "slug_type",
+ "schema": "public",
+ "values": [
+ "reserved",
+ "premium"
+ ]
+ },
+ "public.workspace_plan": {
+ "name": "workspace_plan",
+ "schema": "public",
+ "values": [
+ "free",
+ "pro",
+ "enterprise"
+ ]
+ }
+ },
+ "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 53fe8ae3..42201fee 100644
--- a/packages/db/migrations/meta/_journal.json
+++ b/packages/db/migrations/meta/_journal.json
@@ -71,6 +71,13 @@
"when": 1756803246096,
"tag": "20250902085406_AddSubscriptions",
"breakpoints": true
+ },
+ {
+ "idx": 10,
+ "version": "7",
+ "when": 1757271312974,
+ "tag": "20250907185512_AddUnlimitedSeatsToSubscription",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/packages/db/src/repository/subscription.repo.ts b/packages/db/src/repository/subscription.repo.ts
new file mode 100644
index 00000000..a763f048
--- /dev/null
+++ b/packages/db/src/repository/subscription.repo.ts
@@ -0,0 +1,77 @@
+import { eq } from "drizzle-orm";
+
+import type { dbClient } from "@kan/db/client";
+import { subscription } from "@kan/db/schema";
+
+export const updateById = async (
+ db: dbClient,
+ subscriptionId: number,
+ updates: {
+ unlimitedSeats?: boolean;
+ status?: string;
+ seats?: number | null;
+ periodStart?: Date | null;
+ periodEnd?: Date | null;
+ cancelAtPeriodEnd?: boolean | null;
+ },
+) => {
+ const [result] = await db
+ .update(subscription)
+ .set({
+ ...updates,
+ updatedAt: new Date(),
+ })
+ .where(eq(subscription.id, subscriptionId))
+ .returning({
+ id: subscription.id,
+ plan: subscription.plan,
+ status: subscription.status,
+ unlimitedSeats: subscription.unlimitedSeats,
+ });
+
+ return result;
+};
+
+export const updateByStripeSubscriptionId = async (
+ db: dbClient,
+ stripeSubscriptionId: string,
+ updates: {
+ unlimitedSeats?: boolean;
+ status?: string;
+ seats?: number | null;
+ periodStart?: Date | null;
+ periodEnd?: Date | null;
+ cancelAtPeriodEnd?: boolean | null;
+ },
+) => {
+ const [result] = await db
+ .update(subscription)
+ .set({
+ ...updates,
+ updatedAt: new Date(),
+ })
+ .where(eq(subscription.stripeSubscriptionId, stripeSubscriptionId))
+ .returning({
+ id: subscription.id,
+ plan: subscription.plan,
+ status: subscription.status,
+ unlimitedSeats: subscription.unlimitedSeats,
+ });
+
+ return result;
+};
+
+export const getByStripeSubscriptionId = async (
+ db: dbClient,
+ stripeSubscriptionId: string,
+) => {
+ return await db.query.subscription.findFirst({
+ where: eq(subscription.stripeSubscriptionId, stripeSubscriptionId),
+ });
+};
+
+export const getByReferenceId = async (db: dbClient, referenceId: string) => {
+ return await db.query.subscription.findMany({
+ where: eq(subscription.referenceId, referenceId),
+ });
+};
diff --git a/packages/db/src/repository/workspace.repo.ts b/packages/db/src/repository/workspace.repo.ts
index 09dd19bb..3ef933d5 100644
--- a/packages/db/src/repository/workspace.repo.ts
+++ b/packages/db/src/repository/workspace.repo.ts
@@ -127,6 +127,7 @@ export const getByPublicIdWithMembers = (
plan: true,
status: true,
seats: true,
+ unlimitedSeats: true,
periodStart: true,
periodEnd: true,
},
diff --git a/packages/db/src/schema/subscriptions.ts b/packages/db/src/schema/subscriptions.ts
index 3acd9092..24e15688 100644
--- a/packages/db/src/schema/subscriptions.ts
+++ b/packages/db/src/schema/subscriptions.ts
@@ -23,6 +23,7 @@ export const subscription = pgTable("subscription", {
periodEnd: timestamp("periodEnd"),
cancelAtPeriodEnd: boolean("cancelAtPeriodEnd"),
seats: integer("seats"),
+ unlimitedSeats: boolean("unlimitedSeats").default(false).notNull(),
trialStart: timestamp("trialStart"),
trialEnd: timestamp("trialEnd"),
createdAt: timestamp("createdAt").notNull().defaultNow(),
diff --git a/packages/shared/src/utils/index.ts b/packages/shared/src/utils/index.ts
index 64c9bd95..87404978 100644
--- a/packages/shared/src/utils/index.ts
+++ b/packages/shared/src/utils/index.ts
@@ -1,2 +1,3 @@
export * from "./generateUID";
export * from "./generateSlug";
+export * from "./subscriptions";
diff --git a/packages/shared/src/utils/subscriptions.ts b/packages/shared/src/utils/subscriptions.ts
new file mode 100644
index 00000000..0779306e
--- /dev/null
+++ b/packages/shared/src/utils/subscriptions.ts
@@ -0,0 +1,57 @@
+export type SubscriptionStatus =
+ | "active"
+ | "trialing"
+ | "past_due"
+ | "canceled"
+ | "unpaid";
+export type SubscriptionPlan = "team" | "pro";
+
+export interface Subscription {
+ id: number | null;
+ plan: string;
+ status: string;
+ seats: number | null;
+ unlimitedSeats: boolean;
+ periodStart: Date | null;
+ periodEnd: Date | null;
+ referenceId: string;
+ stripeSubscriptionId: string | null;
+ stripeCustomerId: string | null;
+ createdAt: Date;
+ updatedAt: Date;
+}
+
+export const getActiveSubscriptions = (
+ subscriptions: Subscription[] | undefined,
+) => {
+ if (!subscriptions) return [];
+ return subscriptions.filter(
+ (sub) => sub.status === "active" || sub.status === "trialing",
+ );
+};
+
+export const getSubscriptionByPlan = (
+ subscriptions: Subscription[] | undefined,
+ plan: SubscriptionPlan,
+) => {
+ if (!subscriptions) return undefined;
+ return subscriptions.find(
+ (sub) =>
+ sub.plan === plan &&
+ (sub.status === "active" || sub.status === "trialing"),
+ );
+};
+
+export const hasActiveSubscription = (
+ subscriptions: Subscription[] | undefined,
+ plan: SubscriptionPlan,
+) => {
+ return getSubscriptionByPlan(subscriptions, plan) !== undefined;
+};
+
+export const hasUnlimitedSeats = (
+ subscriptions: Subscription[] | undefined,
+) => {
+ const activeSubscriptions = getActiveSubscriptions(subscriptions);
+ return activeSubscriptions.some((sub) => sub.unlimitedSeats);
+};