···11+import { sql } from "drizzle-orm";
22+import db from "../database";
33+44+/**
55+ * Repairs notification preference tables for instances where migration state
66+ * drift left the schema partially applied.
77+ */
88+export async function migrateNotificationPreferencesSchema() {
99+ console.log("🔄 Checking notification preference schema...");
1010+1111+ try {
1212+ await db.execute(sql`
1313+ CREATE TABLE IF NOT EXISTS "user_notification_preference" (
1414+ "id" text PRIMARY KEY NOT NULL,
1515+ "user_id" text NOT NULL,
1616+ "email_enabled" boolean DEFAULT false NOT NULL,
1717+ "ntfy_enabled" boolean DEFAULT false NOT NULL,
1818+ "ntfy_server_url" text,
1919+ "ntfy_topic" text,
2020+ "ntfy_token" text,
2121+ "gotify_enabled" boolean DEFAULT false NOT NULL,
2222+ "gotify_server_url" text,
2323+ "gotify_token" text,
2424+ "webhook_enabled" boolean DEFAULT false NOT NULL,
2525+ "webhook_url" text,
2626+ "webhook_secret" text,
2727+ "created_at" timestamp DEFAULT now() NOT NULL,
2828+ "updated_at" timestamp DEFAULT now() NOT NULL
2929+ );
3030+ `);
3131+3232+ await db.execute(sql`
3333+ ALTER TABLE "user_notification_preference"
3434+ ADD COLUMN IF NOT EXISTS "email_enabled" boolean DEFAULT false NOT NULL,
3535+ ADD COLUMN IF NOT EXISTS "ntfy_enabled" boolean DEFAULT false NOT NULL,
3636+ ADD COLUMN IF NOT EXISTS "ntfy_server_url" text,
3737+ ADD COLUMN IF NOT EXISTS "ntfy_topic" text,
3838+ ADD COLUMN IF NOT EXISTS "ntfy_token" text,
3939+ ADD COLUMN IF NOT EXISTS "gotify_enabled" boolean DEFAULT false NOT NULL,
4040+ ADD COLUMN IF NOT EXISTS "gotify_server_url" text,
4141+ ADD COLUMN IF NOT EXISTS "gotify_token" text,
4242+ ADD COLUMN IF NOT EXISTS "webhook_enabled" boolean DEFAULT false NOT NULL,
4343+ ADD COLUMN IF NOT EXISTS "webhook_url" text,
4444+ ADD COLUMN IF NOT EXISTS "webhook_secret" text,
4545+ ADD COLUMN IF NOT EXISTS "created_at" timestamp DEFAULT now() NOT NULL,
4646+ ADD COLUMN IF NOT EXISTS "updated_at" timestamp DEFAULT now() NOT NULL;
4747+ `);
4848+4949+ await db.execute(sql`
5050+ CREATE TABLE IF NOT EXISTS "user_notification_workspace_rule" (
5151+ "id" text PRIMARY KEY NOT NULL,
5252+ "user_id" text NOT NULL,
5353+ "workspace_id" text NOT NULL,
5454+ "is_active" boolean DEFAULT true NOT NULL,
5555+ "email_enabled" boolean DEFAULT false NOT NULL,
5656+ "ntfy_enabled" boolean DEFAULT false NOT NULL,
5757+ "gotify_enabled" boolean DEFAULT false NOT NULL,
5858+ "webhook_enabled" boolean DEFAULT false NOT NULL,
5959+ "project_mode" text DEFAULT 'all' NOT NULL,
6060+ "created_at" timestamp DEFAULT now() NOT NULL,
6161+ "updated_at" timestamp DEFAULT now() NOT NULL
6262+ );
6363+ `);
6464+6565+ await db.execute(sql`
6666+ ALTER TABLE "user_notification_workspace_rule"
6767+ ADD COLUMN IF NOT EXISTS "is_active" boolean DEFAULT true NOT NULL,
6868+ ADD COLUMN IF NOT EXISTS "email_enabled" boolean DEFAULT false NOT NULL,
6969+ ADD COLUMN IF NOT EXISTS "ntfy_enabled" boolean DEFAULT false NOT NULL,
7070+ ADD COLUMN IF NOT EXISTS "gotify_enabled" boolean DEFAULT false NOT NULL,
7171+ ADD COLUMN IF NOT EXISTS "webhook_enabled" boolean DEFAULT false NOT NULL,
7272+ ADD COLUMN IF NOT EXISTS "project_mode" text DEFAULT 'all' NOT NULL,
7373+ ADD COLUMN IF NOT EXISTS "created_at" timestamp DEFAULT now() NOT NULL,
7474+ ADD COLUMN IF NOT EXISTS "updated_at" timestamp DEFAULT now() NOT NULL;
7575+ `);
7676+7777+ await db.execute(sql`
7878+ CREATE TABLE IF NOT EXISTS "user_notification_workspace_project" (
7979+ "id" text PRIMARY KEY NOT NULL,
8080+ "workspace_id" text NOT NULL,
8181+ "workspace_rule_id" text NOT NULL,
8282+ "project_id" text NOT NULL,
8383+ "created_at" timestamp DEFAULT now() NOT NULL,
8484+ "updated_at" timestamp DEFAULT now() NOT NULL
8585+ );
8686+ `);
8787+8888+ await db.execute(sql`
8989+ ALTER TABLE "user_notification_workspace_project"
9090+ ADD COLUMN IF NOT EXISTS "created_at" timestamp DEFAULT now() NOT NULL,
9191+ ADD COLUMN IF NOT EXISTS "updated_at" timestamp DEFAULT now() NOT NULL;
9292+ `);
9393+9494+ await db.execute(sql`
9595+ CREATE UNIQUE INDEX IF NOT EXISTS "user_notification_preference_user_id_unique"
9696+ ON "user_notification_preference" ("user_id");
9797+ `);
9898+ await db.execute(sql`
9999+ CREATE UNIQUE INDEX IF NOT EXISTS "user_notification_workspace_rule_user_workspace_unique"
100100+ ON "user_notification_workspace_rule" ("user_id", "workspace_id");
101101+ `);
102102+ await db.execute(sql`
103103+ CREATE UNIQUE INDEX IF NOT EXISTS "user_notification_workspace_rule_workspace_id_id_unique"
104104+ ON "user_notification_workspace_rule" ("workspace_id", "id");
105105+ `);
106106+ await db.execute(sql`
107107+ CREATE UNIQUE INDEX IF NOT EXISTS "user_notification_workspace_project_rule_project_unique"
108108+ ON "user_notification_workspace_project" ("workspace_rule_id", "project_id");
109109+ `);
110110+ await db.execute(sql`
111111+ CREATE INDEX IF NOT EXISTS "user_notification_workspace_rule_userId_idx"
112112+ ON "user_notification_workspace_rule" ("user_id");
113113+ `);
114114+ await db.execute(sql`
115115+ CREATE INDEX IF NOT EXISTS "user_notification_workspace_rule_workspaceId_idx"
116116+ ON "user_notification_workspace_rule" ("workspace_id");
117117+ `);
118118+ await db.execute(sql`
119119+ CREATE INDEX IF NOT EXISTS "user_notification_workspace_project_ruleId_idx"
120120+ ON "user_notification_workspace_project" ("workspace_rule_id");
121121+ `);
122122+ await db.execute(sql`
123123+ CREATE INDEX IF NOT EXISTS "user_notification_workspace_project_projectId_idx"
124124+ ON "user_notification_workspace_project" ("project_id");
125125+ `);
126126+127127+ await db.execute(sql`
128128+ DO $$
129129+ BEGIN
130130+ IF NOT EXISTS (
131131+ SELECT 1 FROM pg_constraint
132132+ WHERE conname = 'user_notification_preference_user_id_user_id_fk'
133133+ ) THEN
134134+ ALTER TABLE "user_notification_preference"
135135+ ADD CONSTRAINT "user_notification_preference_user_id_user_id_fk"
136136+ FOREIGN KEY ("user_id") REFERENCES "public"."user"("id")
137137+ ON DELETE cascade ON UPDATE cascade;
138138+ END IF;
139139+ END $$;
140140+ `);
141141+142142+ await db.execute(sql`
143143+ DO $$
144144+ BEGIN
145145+ IF NOT EXISTS (
146146+ SELECT 1 FROM pg_constraint
147147+ WHERE conname = 'user_notification_workspace_rule_user_id_user_id_fk'
148148+ ) THEN
149149+ ALTER TABLE "user_notification_workspace_rule"
150150+ ADD CONSTRAINT "user_notification_workspace_rule_user_id_user_id_fk"
151151+ FOREIGN KEY ("user_id") REFERENCES "public"."user"("id")
152152+ ON DELETE cascade ON UPDATE cascade;
153153+ END IF;
154154+ END $$;
155155+ `);
156156+157157+ await db.execute(sql`
158158+ DO $$
159159+ BEGIN
160160+ IF NOT EXISTS (
161161+ SELECT 1 FROM pg_constraint
162162+ WHERE conname = 'user_notification_workspace_rule_workspace_id_workspace_id_fk'
163163+ ) THEN
164164+ ALTER TABLE "user_notification_workspace_rule"
165165+ ADD CONSTRAINT "user_notification_workspace_rule_workspace_id_workspace_id_fk"
166166+ FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id")
167167+ ON DELETE cascade ON UPDATE cascade;
168168+ END IF;
169169+ END $$;
170170+ `);
171171+172172+ await db.execute(sql`
173173+ DO $$
174174+ BEGIN
175175+ IF NOT EXISTS (
176176+ SELECT 1 FROM pg_constraint
177177+ WHERE conname = 'user_notification_workspace_project_workspace_id_workspace_id_fk'
178178+ ) THEN
179179+ ALTER TABLE "user_notification_workspace_project"
180180+ ADD CONSTRAINT "user_notification_workspace_project_workspace_id_workspace_id_fk"
181181+ FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id")
182182+ ON DELETE cascade ON UPDATE cascade;
183183+ END IF;
184184+ END $$;
185185+ `);
186186+187187+ console.log("✅ Notification preference schema check complete!");
188188+ } catch (error) {
189189+ console.error("❌ Error during notification preference migration:", error);
190190+ throw error;
191191+ }
192192+}