From 8bbe3071916f775c08b150cf57e65c8c306e4698 Mon Sep 17 00:00:00 2001 From: Ben Allfree Date: Fri, 18 Jul 2025 18:06:08 -0700 Subject: [PATCH] fix(mothership): improve cname/domains migration mangement --- .../src/mothership-app/pb_hooks/mothership.js | 93 ++++++++++------ .../1752885537_updated_domains.js | 18 +++ .../1752885583_updated_instances.js | 33 ++++++ .../bootstrap/HandleMigrateCnamesToDomains.ts | 103 +++++++++++++----- 4 files changed, 186 insertions(+), 61 deletions(-) create mode 100644 packages/pockethost/src/mothership-app/pb_migrations/1752885537_updated_domains.js create mode 100644 packages/pockethost/src/mothership-app/pb_migrations/1752885583_updated_instances.js diff --git a/packages/pockethost/src/mothership-app/pb_hooks/mothership.js b/packages/pockethost/src/mothership-app/pb_hooks/mothership.js index 645c70ae..4c024b34 100644 --- a/packages/pockethost/src/mothership-app/pb_hooks/mothership.js +++ b/packages/pockethost/src/mothership-app/pb_hooks/mothership.js @@ -26,20 +26,20 @@ const HandleInstanceCreate = (c) => { const dao = $app.dao(); const log = mkLog(`POST:instance`); const authRecord = c.get("authRecord"); - log(`***authRecord`, JSON.stringify(authRecord)); + log(`authRecord`, JSON.stringify(authRecord)); if (!authRecord) throw new Error(`Expected authRecord here`); - log(`***TOP OF POST`); + log(`TOP OF POST`); let data = new DynamicModel({ subdomain: "", version: versions[0], region: "sfo-2" }); - log(`***before bind`); + log(`before bind`); c.bind(data); - log(`***after bind`); + log(`after bind`); data = JSON.parse(JSON.stringify(data)); const { subdomain, version, region } = data; - log(`***vars`, JSON.stringify({ + log(`vars`, JSON.stringify({ subdomain, region })); @@ -280,36 +280,65 @@ const HandleMigrateCnamesToDomains = (e) => { return; } log(`Found ${instancesWithCnames.length} instances with cnames`); - const unmigrated = instancesWithCnames.filter((instance) => { - if (!instance) return false; - try { - const existingDomain = dao.findFirstRecordByFilter("domains", `instance = "${instance.getId()}"`); - return !existingDomain; - } catch (e$1) { - return true; - } - }); - if (unmigrated.length === 0) { - log(`All cnames already migrated`); - return; - } - log(`Found ${unmigrated.length} cnames to migrate`); - let migrated = 0; - unmigrated.forEach((instance) => { + log(`Phase 1: Migrating cnames to domains collection`); + let cnameMigrated = 0; + instancesWithCnames.forEach((instance) => { if (!instance) return; try { - const domainsCollection$1 = dao.findCollectionByNameOrId("domains"); - const domainRecord = new Record(domainsCollection$1); - domainRecord.set("instance", instance.getId()); - domainRecord.set("domain", instance.getString("cname")); - domainRecord.set("active", instance.getBool("cname_active")); - dao.saveRecord(domainRecord); - migrated++; + const cname = instance.getString("cname"); + if (!cname) return; + const instanceId = instance.getId(); + let domainExists = false; + try { + dao.findFirstRecordByFilter("domains", `instance = "${instanceId}" && domain = "${cname}"`); + domainExists = true; + } catch (e$1) {} + if (!domainExists) { + const domainsCollection$1 = dao.findCollectionByNameOrId("domains"); + const domainRecord = new Record(domainsCollection$1); + domainRecord.set("instance", instanceId); + domainRecord.set("domain", cname); + domainRecord.set("active", instance.getBool("cname_active")); + dao.saveRecord(domainRecord); + log(`Created domain record for ${cname}`); + cnameMigrated++; + } } catch (error$1) { log(`Failed to migrate cname for instance ${instance.getId()}:`, error$1); } }); - log(`Successfully migrated ${migrated} cnames to domains table`); + log(`Phase 1 complete: migrated ${cnameMigrated} cnames to domains collection`); + log(`Phase 2: Syncing domains collection with instances.domains arrays`); + const allDomainRecords = dao.findRecordsByFilter("domains", "1=1"); + log(`Found ${allDomainRecords.length} domain records`); + let instancesUpdated = 0; + const domainsByInstance = /* @__PURE__ */ new Map(); + allDomainRecords.forEach((domainRecord) => { + if (!domainRecord) return; + const instanceId = domainRecord.getString("instance"); + if (!domainsByInstance.has(instanceId)) domainsByInstance.set(instanceId, []); + domainsByInstance.get(instanceId).push(domainRecord.getId()); + }); + log(`Updating instances.domains arrays`); + domainsByInstance.forEach((domainIds, instanceId) => { + try { + const instance = dao.findRecordById("instances", instanceId); + if (!instance) return; + const currentDomains = instance.get("domains") || []; + log(`Current domains:`, currentDomains); + const missingIds = domainIds.filter((id) => !currentDomains.includes(id)); + if (missingIds.length > 0) { + const updatedDomains = [...currentDomains, ...missingIds]; + instance.set("domains", updatedDomains); + dao.saveRecord(instance); + log(`Updated instance ${instanceId}: added ${missingIds.length} domain IDs to domains array`); + instancesUpdated++; + } + } catch (error$1) { + log(`Failed to update domains array for instance ${instanceId}:`, error$1); + } + }); + log(`Phase 2 complete: updated domains arrays for ${instancesUpdated} instances`); } catch (error$1) { log(`Error migrating cnames: ${error$1}`); } @@ -350,8 +379,10 @@ const HandleMigrateInstanceVersions = (e) => { /** Migrate version numbers */ const HandleMigrateRegions = (e) => { const dao = $app.dao(); - console.log(`***Migrating regions`); + const log = mkLog(`HandleMigrateRegions`); + log(`Migrating regions`); dao.db().newQuery(`update instances set region='sfo-1' where region=''`).execute(); + log(`Migrated regions`); }; //#endregion @@ -654,7 +685,7 @@ const HandleMailSend = (c) => { const HandleMetaUpdateAtBoot = (c) => { const log = mkLog("HandleMetaUpdateAtBoot"); log(`At top of HandleMetaUpdateAtBoot`); - log(`***app URL`, process.env.APP_URL); + log(`app URL`, process.env.APP_URL); const form = new SettingsUpsertForm($app); form.meta = { ...$app.settings().meta, diff --git a/packages/pockethost/src/mothership-app/pb_migrations/1752885537_updated_domains.js b/packages/pockethost/src/mothership-app/pb_migrations/1752885537_updated_domains.js new file mode 100644 index 00000000..5f95869b --- /dev/null +++ b/packages/pockethost/src/mothership-app/pb_migrations/1752885537_updated_domains.js @@ -0,0 +1,18 @@ +/// +migrate((db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("jw1wz9nmkvnhcm6") + + collection.listRule = "@request.auth.id=instance.uid" + collection.viewRule = "@request.auth.id=instance.uid" + + return dao.saveCollection(collection) +}, (db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("jw1wz9nmkvnhcm6") + + collection.listRule = null + collection.viewRule = null + + return dao.saveCollection(collection) +}) diff --git a/packages/pockethost/src/mothership-app/pb_migrations/1752885583_updated_instances.js b/packages/pockethost/src/mothership-app/pb_migrations/1752885583_updated_instances.js new file mode 100644 index 00000000..05cdc089 --- /dev/null +++ b/packages/pockethost/src/mothership-app/pb_migrations/1752885583_updated_instances.js @@ -0,0 +1,33 @@ +/// +migrate((db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("etae8tuiaxl6xfv") + + // add + collection.schema.addField(new SchemaField({ + "system": false, + "id": "smblegnt", + "name": "domains", + "type": "relation", + "required": false, + "presentable": false, + "unique": false, + "options": { + "collectionId": "jw1wz9nmkvnhcm6", + "cascadeDelete": false, + "minSelect": null, + "maxSelect": null, + "displayFields": null + } + })) + + return dao.saveCollection(collection) +}, (db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("etae8tuiaxl6xfv") + + // remove + collection.schema.removeField("smblegnt") + + return dao.saveCollection(collection) +}) diff --git a/packages/pockethost/src/mothership-app/src/lib/handlers/instance/bootstrap/HandleMigrateCnamesToDomains.ts b/packages/pockethost/src/mothership-app/src/lib/handlers/instance/bootstrap/HandleMigrateCnamesToDomains.ts index bc789d9a..fb134694 100644 --- a/packages/pockethost/src/mothership-app/src/lib/handlers/instance/bootstrap/HandleMigrateCnamesToDomains.ts +++ b/packages/pockethost/src/mothership-app/src/lib/handlers/instance/bootstrap/HandleMigrateCnamesToDomains.ts @@ -25,45 +25,88 @@ export const HandleMigrateCnamesToDomains = (e: core.BootstrapEvent) => { log(`Found ${instancesWithCnames.length} instances with cnames`) - // Check which ones are already migrated - const unmigrated = instancesWithCnames.filter((instance) => { - if (!instance) return false - try { - const existingDomain = dao.findFirstRecordByFilter('domains', `instance = "${instance.getId()}"`) - return !existingDomain - } catch (e) { - // Not found means not migrated - return true - } - }) - - if (unmigrated.length === 0) { - log(`All cnames already migrated`) - return - } - - log(`Found ${unmigrated.length} cnames to migrate`) - - // Migrate cnames to domains table - let migrated = 0 - unmigrated.forEach((instance) => { + // Phase 1: Migrate cnames to domains collection + log(`Phase 1: Migrating cnames to domains collection`) + let cnameMigrated = 0 + instancesWithCnames.forEach((instance) => { if (!instance) return + try { - const domainsCollection = dao.findCollectionByNameOrId('domains') - const domainRecord = new Record(domainsCollection) + const cname = instance.getString('cname') + if (!cname) return - domainRecord.set('instance', instance.getId()) - domainRecord.set('domain', instance.getString('cname')) - domainRecord.set('active', instance.getBool('cname_active')) + const instanceId = instance.getId() - dao.saveRecord(domainRecord) - migrated++ + // Check if domain record already exists + let domainExists = false + try { + dao.findFirstRecordByFilter('domains', `instance = "${instanceId}" && domain = "${cname}"`) + domainExists = true + } catch (e) { + // Domain record doesn't exist + } + + if (!domainExists) { + const domainsCollection = dao.findCollectionByNameOrId('domains') + const domainRecord = new Record(domainsCollection) + + domainRecord.set('instance', instanceId) + domainRecord.set('domain', cname) + domainRecord.set('active', instance.getBool('cname_active')) + + dao.saveRecord(domainRecord) + log(`Created domain record for ${cname}`) + cnameMigrated++ + } } catch (error) { log(`Failed to migrate cname for instance ${instance.getId()}:`, error) } }) - log(`Successfully migrated ${migrated} cnames to domains table`) + log(`Phase 1 complete: migrated ${cnameMigrated} cnames to domains collection`) + + // Phase 2: Sync all domain records with instances.domains arrays + log(`Phase 2: Syncing domains collection with instances.domains arrays`) + const allDomainRecords = dao.findRecordsByFilter('domains', '1=1') + log(`Found ${allDomainRecords.length} domain records`) + let instancesUpdated = 0 + + // Group domains by instance for efficiency + const domainsByInstance = new Map() + allDomainRecords.forEach((domainRecord) => { + if (!domainRecord) return + + const instanceId = domainRecord.getString('instance') + if (!domainsByInstance.has(instanceId)) { + domainsByInstance.set(instanceId, []) + } + domainsByInstance.get(instanceId).push(domainRecord.getId()) + }) + + // Update each instance's domains array + log(`Updating instances.domains arrays`) + domainsByInstance.forEach((domainIds, instanceId) => { + try { + const instance = dao.findRecordById('instances', instanceId) + if (!instance) return + + const currentDomains = instance.get('domains') || [] + log(`Current domains:`, currentDomains) + const missingIds = domainIds.filter((id: string) => !currentDomains.includes(id)) + + if (missingIds.length > 0) { + const updatedDomains = [...currentDomains, ...missingIds] + instance.set('domains', updatedDomains) + dao.saveRecord(instance) + log(`Updated instance ${instanceId}: added ${missingIds.length} domain IDs to domains array`) + instancesUpdated++ + } + } catch (error) { + log(`Failed to update domains array for instance ${instanceId}:`, error) + } + }) + + log(`Phase 2 complete: updated domains arrays for ${instancesUpdated} instances`) } catch (error) { log(`Error migrating cnames: ${error}`) }