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); try { const resources = db .prepare( "SELECT resourceId FROM resources" ) .all() as Array<{ resourceId: number }>; const siteResources = db .prepare( "SELECT siteResourceId FROM siteResources" ) .all() as Array<{ siteResourceId: 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 'siteResources' ADD 'niceId' text DEFAULT '' NOT NULL; ALTER TABLE 'userOrgs' ADD 'autoProvisioned' integer DEFAULT false; ALTER TABLE 'targets' ADD 'pathMatchType' text; ALTER TABLE 'targets' ADD 'path' text; ALTER TABLE 'resources' ADD 'headers' text; `); // 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... const 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); } for (const resourceId of siteResources) { // 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 siteResources SET niceId = ? WHERE siteResourceId = ?` ).run(niceId, resourceId.siteResourceId); } // Handle auto-provisioned users for identity providers const autoProvisionIdps = db .prepare( "SELECT idpId FROM idp WHERE autoProvision = 1" ) .all() as Array<{ idpId: number }>; for (const idp of autoProvisionIdps) { // Get all users with this identity provider const usersWithIdp = db .prepare( "SELECT id FROM user WHERE idpId = ?" ) .all(idp.idpId) as Array<{ id: string }>; // Update userOrgs to set autoProvisioned to true for these users for (const user of usersWithIdp) { db.prepare( "UPDATE userOrgs SET autoProvisioned = 1 WHERE userId = ?" ).run(user.id); } } })(); 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, ""); }