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..f631d59b6 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( ` @@ -236,7 +255,7 @@ export default async function migration() { ).run(); db.prepare( ` - INSERT INTO '__new_siteResources'("siteResourceId", "orgId", "networkId", "defaultNetworkId", "niceId", "name", "ssl", "mode", "scheme", "proxyPort", "destinationPort", "destination", "enabled", "alias", "aliasAddress", "tcpPortRangeString", "udpPortRangeString", "disableIcmp", "authDaemonPort", "authDaemonMode", "domainId", "subdomain", "fullDomain") SELECT "siteResourceId", "orgId", "networkId", "defaultNetworkId", "niceId", "name", "ssl", "mode", "scheme", "proxyPort", "destinationPort", "destination", "enabled", "alias", "aliasAddress", "tcpPortRangeString", "udpPortRangeString", "disableIcmp", "authDaemonPort", "authDaemonMode", "domainId", "subdomain", "fullDomain" FROM 'siteResources'; + INSERT INTO '__new_siteResources'("siteResourceId", "orgId", "networkId", "defaultNetworkId", "niceId", "name", "ssl", "mode", "scheme", "proxyPort", "destinationPort", "destination", "enabled", "alias", "aliasAddress", "tcpPortRangeString", "udpPortRangeString", "disableIcmp", "authDaemonPort", "authDaemonMode", "domainId", "subdomain", "fullDomain") SELECT "siteResourceId", "orgId", NULL, NULL, "niceId", "name", 0, "mode", NULL, "proxyPort", "destinationPort", "destination", "enabled", "alias", "aliasAddress", "tcpPortRangeString", "udpPortRangeString", "disableIcmp", "authDaemonPort", "authDaemonMode", NULL, NULL, NULL FROM 'siteResources'; ` ).run(); 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. @@ -401,3 +450,5 @@ export default async function migration() { console.log(`${version} migration complete`); } + +await migration(); diff --git a/src/hooks/useSubscriptionStatusContext.ts b/src/hooks/useSubscriptionStatusContext.ts index 240168b10..59d6a6b9a 100644 --- a/src/hooks/useSubscriptionStatusContext.ts +++ b/src/hooks/useSubscriptionStatusContext.ts @@ -3,7 +3,7 @@ import { build } from "@server/build"; import { useContext } from "react"; export function useSubscriptionStatusContext() { - if (build == "oss") { + if (build != "saas") { return null; } const context = useContext(SubscriptionStatusContext);