From fb26dfad65a9775bfa29a79a68b03324a546684a Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 10 Sep 2025 17:15:54 -0700 Subject: [PATCH] Add migrations for 1.10.0 --- server/lib/consts.ts | 2 +- server/setup/migrationsPg.ts | 4 +- server/setup/migrationsSqlite.ts | 2 + server/setup/scriptsPg/1.10.0.ts | 81 +++++++++++++++++++++++++++ server/setup/scriptsSqlite/1.10.0.ts | 83 ++++++++++++++++++++++++++++ 5 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 server/setup/scriptsPg/1.10.0.ts create mode 100644 server/setup/scriptsSqlite/1.10.0.ts diff --git a/server/lib/consts.ts b/server/lib/consts.ts index b9afa792..30efc07e 100644 --- a/server/lib/consts.ts +++ b/server/lib/consts.ts @@ -2,7 +2,7 @@ import path from "path"; import { fileURLToPath } from "url"; // This is a placeholder value replaced by the build process -export const APP_VERSION = "1.9.0"; +export const APP_VERSION = "1.10.0"; export const __FILENAME = fileURLToPath(import.meta.url); export const __DIRNAME = path.dirname(__FILENAME); diff --git a/server/setup/migrationsPg.ts b/server/setup/migrationsPg.ts index 6b3f20b9..c5950e1d 100644 --- a/server/setup/migrationsPg.ts +++ b/server/setup/migrationsPg.ts @@ -9,6 +9,7 @@ import m1 from "./scriptsPg/1.6.0"; import m2 from "./scriptsPg/1.7.0"; import m3 from "./scriptsPg/1.8.0"; import m4 from "./scriptsPg/1.9.0"; +import m5 from "./scriptsPg/1.10.0"; // THIS CANNOT IMPORT ANYTHING FROM THE SERVER // EXCEPT FOR THE DATABASE AND THE SCHEMA @@ -18,7 +19,8 @@ const migrations = [ { version: "1.6.0", run: m1 }, { version: "1.7.0", run: m2 }, { version: "1.8.0", run: m3 }, - { version: "1.9.0", run: m4 } + { version: "1.9.0", run: m4 }, + { version: "1.10.0", run: m5 }, // Add new migrations here as they are created ] as { version: string; diff --git a/server/setup/migrationsSqlite.ts b/server/setup/migrationsSqlite.ts index 5b0850c8..79a7d0ab 100644 --- a/server/setup/migrationsSqlite.ts +++ b/server/setup/migrationsSqlite.ts @@ -26,6 +26,7 @@ import m21 from "./scriptsSqlite/1.6.0"; import m22 from "./scriptsSqlite/1.7.0"; import m23 from "./scriptsSqlite/1.8.0"; import m24 from "./scriptsSqlite/1.9.0"; +import m25 from "./scriptsSqlite/1.10.0"; // THIS CANNOT IMPORT ANYTHING FROM THE SERVER // EXCEPT FOR THE DATABASE AND THE SCHEMA @@ -51,6 +52,7 @@ const migrations = [ { version: "1.7.0", run: m22 }, { version: "1.8.0", run: m23 }, { version: "1.9.0", run: m24 }, + { version: "1.10.0", run: m25 }, // Add new migrations here as they are created ] as const; diff --git a/server/setup/scriptsPg/1.10.0.ts b/server/setup/scriptsPg/1.10.0.ts new file mode 100644 index 00000000..7eb101d5 --- /dev/null +++ b/server/setup/scriptsPg/1.10.0.ts @@ -0,0 +1,81 @@ +import { db } from "@server/db/pg/driver"; +import { sql } from "drizzle-orm"; +import { __DIRNAME, APP_PATH } from "@server/lib/consts"; +import { readFileSync } from "fs"; +import path, { join } from "path"; + +const version = "1.10.0"; + +export default async function migration() { + console.log(`Running setup script ${version}...`); + + try { + const resources = await db.execute(sql` + SELECT "resourceId" FROM "resources" WHERE "siteId" IS NOT NULL + `); + + await db.execute(sql`BEGIN`); + + await db.execute(sql`ALTER TABLE "exitNodes" ADD COLUMN "region" text;`); + + await db.execute(sql`ALTER TABLE "idpOidcConfig" ADD COLUMN "variant" text DEFAULT 'oidc' NOT NULL;`); + + await db.execute(sql`ALTER TABLE "resources" ADD COLUMN "niceId" text DEFAULT '' NOT NULL;`); + + await db.execute(sql`ALTER TABLE "userOrgs" ADD COLUMN "autoProvisioned" boolean DEFAULT false;`); + + let usedNiceIds: string[] = []; + + for (const resource of resources.rows) { + // Generate a unique name and ensure it's unique + let niceId = ""; + let loops = 0; + while (true) { + if (loops > 100) { + throw new Error("Could not generate a unique name"); + } + + niceId = generateName(); + if (!usedNiceIds.includes(niceId)) { + usedNiceIds.push(niceId); + break; + } + loops++; + } + await db.execute(sql` + UPDATE "resources" SET "niceId" = ${niceId} WHERE "resourceId" = ${resource.resourceId} + `); + } + + await db.execute(sql`COMMIT`); + console.log(`Migrated database`); + } catch (e) { + await db.execute(sql`ROLLBACK`); + console.log("Failed to migrate db:", e); + throw e; + } +} + +const dev = process.env.ENVIRONMENT !== "prod"; +let file; +if (!dev) { + file = join(__DIRNAME, "names.json"); +} else { + file = join("server/db/names.json"); +} +export const names = JSON.parse(readFileSync(file, "utf-8")); + +export function generateName(): string { + const name = ( + names.descriptors[ + Math.floor(Math.random() * names.descriptors.length) + ] + + "-" + + names.animals[Math.floor(Math.random() * names.animals.length)] + ) + .toLowerCase() + .replace(/\s/g, "-"); + + // clean out any non-alphanumeric characters except for dashes + return name.replace(/[^a-z0-9-]/g, ""); +} diff --git a/server/setup/scriptsSqlite/1.10.0.ts b/server/setup/scriptsSqlite/1.10.0.ts new file mode 100644 index 00000000..a4c27862 --- /dev/null +++ b/server/setup/scriptsSqlite/1.10.0.ts @@ -0,0 +1,83 @@ +import { __DIRNAME, APP_PATH } from "@server/lib/consts"; +import Database from "better-sqlite3"; +import { readFileSync } from "fs"; +import path, { join } from "path"; + +const version = "1.10.0"; + +export default async function migration() { + console.log(`Running setup script ${version}...`); + + const location = path.join(APP_PATH, "db", "db.sqlite"); + const db = new Database(location); + + const resourceSiteMap = new Map(); + let firstSiteId: number = 1; + + try { + const resources = db + .prepare( + "SELECT resourceId FROM resources WHERE siteId IS NOT NULL" + ) + .all() as Array<{ resourceId: number; }>; + + db.transaction(() => { + db.exec(` + ALTER TABLE 'exitNodes' ADD 'region' text; + ALTER TABLE 'idpOidcConfig' ADD 'variant' text DEFAULT 'oidc' NOT NULL; + ALTER TABLE 'resources' ADD 'niceId' text DEFAULT '' NOT NULL; + ALTER TABLE 'userOrgs' ADD 'autoProvisioned' integer DEFAULT false; + `); // this diverges from the schema a bit because the schema does not have a default on niceId but was required for the migration and I dont think it will effect much down the line... + + let usedNiceIds: string[] = []; + + for (const resourceId of resources) { + // Generate a unique name and ensure it's unique + let niceId = ""; + let loops = 0; + while (true) { + if (loops > 100) { + throw new Error("Could not generate a unique name"); + } + + niceId = generateName(); + if (!usedNiceIds.includes(niceId)) { + usedNiceIds.push(niceId); + break; + } + loops++; + } + db.prepare(`UPDATE resources SET niceId = ? WHERE resourceId = ?`).run(niceId, resourceId.resourceId); + } + })(); + + console.log(`Migrated database`); + } catch (e) { + console.log("Failed to migrate db:", e); + throw e; + } +} + +const dev = process.env.ENVIRONMENT !== "prod"; +let file; +if (!dev) { + file = join(__DIRNAME, "names.json"); +} else { + file = join("server/db/names.json"); +} +export const names = JSON.parse(readFileSync(file, "utf-8")); + +export function generateName(): string { + const name = ( + names.descriptors[ + Math.floor(Math.random() * names.descriptors.length) + ] + + "-" + + names.animals[Math.floor(Math.random() * names.animals.length)] + ) + .toLowerCase() + .replace(/\s/g, "-"); + + // clean out any non-alphanumeric characters except for dashes + return name.replace(/[^a-z0-9-]/g, ""); +}