diff --git a/server/setup/scriptsPg/1.18.0.ts b/server/setup/scriptsPg/1.18.0.ts index 9c0b7568b..2f2b3067c 100644 --- a/server/setup/scriptsPg/1.18.0.ts +++ b/server/setup/scriptsPg/1.18.0.ts @@ -61,6 +61,22 @@ export default async function migration() { `Found ${existingHealthChecks.length} existing targetHealthCheck row(s) to migrate` ); + // Query existing siteResources with siteId before it is dropped by the DDL below. + const siteResourcesForNetworkQuery = await db.execute( + sql`SELECT sr."siteResourceId", sr."orgId", sr."siteId" + FROM "siteResources" sr + WHERE sr."siteId" IS NOT NULL` + ); + const existingSiteResourcesForNetwork = siteResourcesForNetworkQuery.rows as { + siteResourceId: number; + orgId: string; + siteId: number; + }[]; + + console.log( + `Found ${existingSiteResourcesForNetwork.length} existing siteResource(s) to migrate to networks` + ); + try { await db.execute(sql`BEGIN`); @@ -430,5 +446,44 @@ export default async function migration() { } } + // Create a dedicated "resource"-scoped network for each existing siteResource, + // populate siteNetworks with the old siteId, and set networkId / defaultNetworkId + // on the siteResource row. + if (existingSiteResourcesForNetwork.length > 0) { + try { + for (const sr of existingSiteResourcesForNetwork) { + const networkResult = await db.execute(sql` + INSERT INTO "networks" ("scope", "orgId") + VALUES ('resource', ${sr.orgId}) + RETURNING "networkId" + `); + const networkId = ( + networkResult.rows[0] as { networkId: number } + ).networkId; + + await db.execute(sql` + INSERT INTO "siteNetworks" ("siteId", "networkId") + VALUES (${sr.siteId}, ${networkId}) + `); + + await db.execute(sql` + UPDATE "siteResources" + SET "networkId" = ${networkId}, "defaultNetworkId" = ${networkId} + WHERE "siteResourceId" = ${sr.siteResourceId} + `); + } + + console.log( + `Migrated ${existingSiteResourcesForNetwork.length} siteResource(s) to networks` + ); + } catch (e) { + console.error( + "Error while migrating siteResources to networks:", + e + ); + throw e; + } + } + console.log(`${version} migration complete`); } diff --git a/server/setup/scriptsSqlite/1.18.0.ts b/server/setup/scriptsSqlite/1.18.0.ts index edb7f9c23..91249166e 100644 --- a/server/setup/scriptsSqlite/1.18.0.ts +++ b/server/setup/scriptsSqlite/1.18.0.ts @@ -67,6 +67,25 @@ export default async function migration() { `Found ${existingHealthChecks.length} existing targetHealthCheck row(s) to migrate` ); + // Query existing siteResources with siteId before the transaction recreates + // the table without that column. We use this data below to create a dedicated + // network for each resource. + const existingSiteResourcesForNetwork = db + .prepare( + `SELECT sr."siteResourceId", sr."orgId", sr."siteId" + FROM 'siteResources' sr + WHERE sr."siteId" IS NOT NULL` + ) + .all() as { + siteResourceId: number; + orgId: string; + siteId: number; + }[]; + + console.log( + `Found ${existingSiteResourcesForNetwork.length} existing siteResource(s) to migrate to networks` + ); + db.transaction(() => { db.prepare( ` @@ -315,6 +334,36 @@ export default async function migration() { db.pragma("foreign_keys = ON"); + // Create a dedicated network for each existing siteResource and link the + // old siteId via siteNetworks. Then set networkId and defaultNetworkId on + // the siteResource row so the app can use the new network model. + if (existingSiteResourcesForNetwork.length > 0) { + const insertNetwork = db.prepare( + `INSERT INTO 'networks' ("scope", "orgId") VALUES (?, ?)` + ); + const insertSiteNetwork = db.prepare( + `INSERT INTO 'siteNetworks' ("siteId", "networkId") VALUES (?, ?)` + ); + const updateSiteResource = db.prepare( + `UPDATE 'siteResources' SET "networkId" = ?, "defaultNetworkId" = ? WHERE "siteResourceId" = ?` + ); + + const migrateNetworks = db.transaction(() => { + for (const sr of existingSiteResourcesForNetwork) { + const result = insertNetwork.run("resource", sr.orgId); + const networkId = result.lastInsertRowid as number; + insertSiteNetwork.run(sr.siteId, networkId); + updateSiteResource.run(networkId, networkId, sr.siteResourceId); + } + }); + + migrateNetworks(); + + console.log( + `Migrated ${existingSiteResourcesForNetwork.length} siteResource(s) to networks` + ); + } + // Re-insert targetHealthCheck rows with corrected IDs: // targetHealthCheckId is set to the same integer as targetId (1:1 mapping), // siteId and orgId are populated from the associated target and site.