From 17ab6c5fa7db139ef3a7719c7d2b81c6c2935fc1 Mon Sep 17 00:00:00 2001 From: Ben Allfree Date: Wed, 7 Jun 2023 17:06:34 -0700 Subject: [PATCH] feat: native PB migrations --- .env-template | 1 + package.json | 4 +- .../1686178611_collections_snapshot.js | 492 ++++++++++++++++++ packages/daemon/src/constants.ts | 14 + packages/daemon/src/migrate/migrate.ts | 49 -- packages/daemon/src/migrate/schema.ts | 357 ------------- packages/daemon/src/migrate/withInstance.ts | 45 -- packages/daemon/src/server.ts | 14 + .../daemon/src/services/PocketBaseService.ts | 12 +- .../src/services/clientService/PbClient.ts | 15 +- .../services/clientService/clientService.ts | 10 - packages/daemon/src/util/backupInstance.ts | 2 +- .../daemon/src/{migrate => util}/pexec.ts | 0 readme.md | 5 + roadmap.md | 3 + 15 files changed, 542 insertions(+), 481 deletions(-) create mode 100644 packages/daemon/migrations/1686178611_collections_snapshot.js delete mode 100644 packages/daemon/src/migrate/migrate.ts delete mode 100644 packages/daemon/src/migrate/schema.ts delete mode 100644 packages/daemon/src/migrate/withInstance.ts rename packages/daemon/src/{migrate => util}/pexec.ts (100%) diff --git a/.env-template b/.env-template index 666eb0c3..2b6f7044 100644 --- a/.env-template +++ b/.env-template @@ -3,6 +3,7 @@ PUBLIC_APP_DOMAIN=pockethost.test PUBLIC_APP_DB=pockethost-central DAEMON_PB_BIN_DIR=`pwd`/packages/pocketbase/dist DAEMON_PB_DATA_DIR=`pwd`/.data +DAEMON_PB_MIGRATIONS_DIR=`pwd`/packages/daemon/migrations DAEMON_PB_USERNAME=admin@pockethost.test DAEMON_PB_PASSWORD=admin@pockethost.test DAEMON_PB_PORT=8090 diff --git a/package.json b/package.json index 5d24bea8..0453e5eb 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,6 @@ "pm2:proxy": "cd packages/proxy && yarn pm2", "pm2:www": "cd packages/pockethost.io && yarn pm2", "pm2:daemon": "cd packages/daemon && yarn pm2", - "migrate": "yarn migrate:daemon", - "migrate:daemon": "cd packages/daemon && yarn migrate", "postinstall": "patch-package" }, "workspaces": { @@ -50,4 +48,4 @@ "dependencies": { "postinstall-postinstall": "^2.1.0" } -} +} \ No newline at end of file diff --git a/packages/daemon/migrations/1686178611_collections_snapshot.js b/packages/daemon/migrations/1686178611_collections_snapshot.js new file mode 100644 index 00000000..468773b3 --- /dev/null +++ b/packages/daemon/migrations/1686178611_collections_snapshot.js @@ -0,0 +1,492 @@ +migrate((db) => { + const snapshot = [ + { + "id": "etae8tuiaxl6xfv", + "created": "2022-10-20 08:51:44.195Z", + "updated": "2023-06-07 22:41:11.725Z", + "name": "instances", + "type": "base", + "system": false, + "schema": [ + { + "system": false, + "id": "qdtuuld1", + "name": "subdomain", + "type": "text", + "required": true, + "unique": true, + "options": { + "min": null, + "max": 50, + "pattern": "^[a-z][\\-a-z]+$" + } + }, + { + "system": false, + "id": "rbj14krn", + "name": "uid", + "type": "relation", + "required": true, + "unique": false, + "options": { + "collectionId": "systemprofiles0", + "cascadeDelete": false, + "minSelect": null, + "maxSelect": 1, + "displayFields": null + } + }, + { + "system": false, + "id": "c2y74d7h", + "name": "status", + "type": "text", + "required": true, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "yxby5r6b", + "name": "platform", + "type": "text", + "required": true, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "4ydffkv3", + "name": "version", + "type": "text", + "required": true, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "1arlklqq", + "name": "secondsThisMonth", + "type": "number", + "required": false, + "unique": false, + "options": { + "min": null, + "max": null + } + }, + { + "system": false, + "id": "66vjgzcg", + "name": "isBackupAllowed", + "type": "bool", + "required": false, + "unique": false, + "options": {} + }, + { + "system": false, + "id": "qew2o2d6", + "name": "currentWorkerBundleId", + "type": "text", + "required": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "3yu1db4p", + "name": "secrets", + "type": "json", + "required": false, + "unique": false, + "options": {} + } + ], + "indexes": [ + "CREATE UNIQUE INDEX \"idx_unique_qdtuuld1\" on \"instances\" (\"subdomain\")" + ], + "listRule": "uid=@request.auth.id", + "viewRule": "uid = @request.auth.id", + "createRule": null, + "updateRule": null, + "deleteRule": null, + "options": {} + }, + { + "id": "systemprofiles0", + "created": "2022-10-31 21:31:52.175Z", + "updated": "2023-06-07 22:41:11.723Z", + "name": "users", + "type": "auth", + "system": false, + "schema": [ + { + "system": false, + "id": "pbfieldname", + "name": "name", + "type": "text", + "required": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "pbfieldavatar", + "name": "avatar", + "type": "file", + "required": false, + "unique": false, + "options": { + "maxSelect": 1, + "maxSize": 5242880, + "mimeTypes": [ + "image/jpg", + "image/jpeg", + "image/png", + "image/svg+xml", + "image/gif" + ], + "thumbs": null, + "protected": false + } + } + ], + "indexes": [], + "listRule": "id = @request.auth.id", + "viewRule": "id = @request.auth.id", + "createRule": "", + "updateRule": "id = @request.auth.id", + "deleteRule": null, + "options": { + "allowEmailAuth": true, + "allowOAuth2Auth": true, + "allowUsernameAuth": false, + "exceptEmailDomains": null, + "manageRule": null, + "minPasswordLength": 8, + "onlyEmailDomains": null, + "requireEmail": true + } + }, + { + "id": "aiw8te7y7atklwn", + "created": "2022-11-04 13:54:23.745Z", + "updated": "2023-06-07 22:41:11.723Z", + "name": "invocations", + "type": "base", + "system": false, + "schema": [ + { + "system": false, + "id": "st9ydrbo", + "name": "instanceId", + "type": "relation", + "required": true, + "unique": false, + "options": { + "collectionId": "etae8tuiaxl6xfv", + "cascadeDelete": false, + "minSelect": null, + "maxSelect": 1, + "displayFields": null + } + }, + { + "system": false, + "id": "av4mpuyh", + "name": "startedAt", + "type": "date", + "required": true, + "unique": false, + "options": { + "min": "", + "max": "" + } + }, + { + "system": false, + "id": "fnwatixg", + "name": "endedAt", + "type": "date", + "required": false, + "unique": false, + "options": { + "min": "", + "max": "" + } + }, + { + "system": false, + "id": "awjozhbn", + "name": "pid", + "type": "number", + "required": false, + "unique": false, + "options": { + "min": null, + "max": null + } + }, + { + "system": false, + "id": "vdkfqege", + "name": "totalSeconds", + "type": "number", + "required": false, + "unique": false, + "options": { + "min": null, + "max": null + } + } + ], + "indexes": [], + "listRule": null, + "viewRule": null, + "createRule": null, + "updateRule": null, + "deleteRule": null, + "options": {} + }, + { + "id": "v7s41iokt1vizxd", + "created": "2022-11-06 17:23:25.947Z", + "updated": "2023-06-07 22:41:11.723Z", + "name": "rpc", + "type": "base", + "system": false, + "schema": [ + { + "system": false, + "id": "yv38czcf", + "name": "userId", + "type": "relation", + "required": true, + "unique": false, + "options": { + "collectionId": "systemprofiles0", + "cascadeDelete": false, + "minSelect": null, + "maxSelect": 1, + "displayFields": null + } + }, + { + "system": false, + "id": "tgvaxwfv", + "name": "payload", + "type": "json", + "required": true, + "unique": false, + "options": {} + }, + { + "system": false, + "id": "zede8pci", + "name": "status", + "type": "text", + "required": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "nd7cwqmn", + "name": "result", + "type": "json", + "required": false, + "unique": false, + "options": {} + }, + { + "system": false, + "id": "2hlrcx5j", + "name": "cmd", + "type": "text", + "required": true, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + } + ], + "indexes": [], + "listRule": "userId = @request.auth.id", + "viewRule": "userId = @request.auth.id", + "createRule": "userId = @request.auth.id && status='' && result='' && cmd ?= @collection.rpc_cmds.name", + "updateRule": null, + "deleteRule": null, + "options": {} + }, + { + "id": "72clb6v41bzsay9", + "created": "2022-11-09 15:23:20.313Z", + "updated": "2023-06-07 22:41:11.723Z", + "name": "backups", + "type": "base", + "system": false, + "schema": [ + { + "system": false, + "id": "someqtjw", + "name": "message", + "type": "text", + "required": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "jk4zwiaj", + "name": "instanceId", + "type": "relation", + "required": true, + "unique": false, + "options": { + "collectionId": "etae8tuiaxl6xfv", + "cascadeDelete": false, + "minSelect": null, + "maxSelect": 1, + "displayFields": null + } + }, + { + "system": false, + "id": "wsy3l5gm", + "name": "status", + "type": "text", + "required": true, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "gmkrc5d9", + "name": "bytes", + "type": "number", + "required": false, + "unique": false, + "options": { + "min": null, + "max": null + } + }, + { + "system": false, + "id": "4lmammjz", + "name": "platform", + "type": "text", + "required": true, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "fheqxmbj", + "name": "version", + "type": "text", + "required": true, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "cinbmdwe", + "name": "progress", + "type": "json", + "required": false, + "unique": false, + "options": {} + } + ], + "indexes": [], + "listRule": "@request.auth.id = instanceId.uid", + "viewRule": null, + "createRule": null, + "updateRule": null, + "deleteRule": null, + "options": {} + }, + { + "id": "enp8mrv5ewtrltj", + "created": "2023-01-06 10:21:51.659Z", + "updated": "2023-06-07 22:41:11.725Z", + "name": "rpc_cmds", + "type": "base", + "system": false, + "schema": [ + { + "system": false, + "id": "jbostfhp", + "name": "name", + "type": "text", + "required": true, + "unique": true, + "options": { + "min": null, + "max": null, + "pattern": "" + } + } + ], + "indexes": [ + "CREATE UNIQUE INDEX \"idx_unique_jbostfhp\" on \"rpc_cmds\" (\"name\")" + ], + "listRule": null, + "viewRule": null, + "createRule": null, + "updateRule": null, + "deleteRule": null, + "options": {} + } + ]; + + const collections = snapshot.map((item) => new Collection(item)); + + return Dao(db).importCollections(collections, true, null); +}, (db) => { + return null; +}) diff --git a/packages/daemon/src/constants.ts b/packages/daemon/src/constants.ts index 74380f0e..38efaedb 100644 --- a/packages/daemon/src/constants.ts +++ b/packages/daemon/src/constants.ts @@ -21,6 +21,19 @@ export const DAEMON_PB_PASSWORD = (() => { })() export const DAEMON_PB_PORT_BASE = envi('DAEMON_PB_PORT_BASE', 8090) export const DAEMON_PB_IDLE_TTL = envi('DAEMON_PB_IDLE_TTL', 5000) +export const DAEMON_PB_MIGRATIONS_DIR = (() => { + const v = env('DAEMON_PB_MIGRATIONS_DIR') + if (!v) { + throw new Error( + `DAEMON_PB_MIGRATIONS_DIR (${v}) environment variable must be specified` + ) + } + if (!existsSync(v)) { + throw new Error(`DAEMON_PB_MIGRATIONS_DIR (${v}) path must exist`) + } + return v +})() + export const DAEMON_PB_DATA_DIR = (() => { const v = env('DAEMON_PB_DATA_DIR') if (!v) { @@ -68,6 +81,7 @@ console.log({ PUBLIC_APP_DB, DAEMON_PB_USERNAME, DAEMON_PB_PASSWORD, + DAEMON_PB_MIGRATIONS_DIR, DAEMON_PB_SEMVER, DENO_PATH, PH_FTP_PASV_IP, diff --git a/packages/daemon/src/migrate/migrate.ts b/packages/daemon/src/migrate/migrate.ts deleted file mode 100644 index d5d9af9b..00000000 --- a/packages/daemon/src/migrate/migrate.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { DEBUG, PH_BIN_CACHE, PUBLIC_APP_DB } from '$constants' -import { pocketbase } from '$services' -import { InstanceStatus, logger, safeCatch } from '@pockethost/common' -import { schema } from './schema' -import { withInstance } from './withInstance' -;(async () => { - const { info } = logger({ debug: DEBUG }).create('migrate') - - const pbService = await pocketbase({ - cachePath: PH_BIN_CACHE, - checkIntervalMs: 5 * 60 * 1000, - }) - - safeCatch(`root`, async () => { - // await backupInstance( - // PUBLIC_PB_SUBDOMAIN, - // `${+new Date()}`, - // async (progress) => { - // dbg(progress) - // } - // ) - - info(`Upgrading`) - const upgradeProcess = await pbService.spawn({ - command: 'upgrade', - slug: PUBLIC_APP_DB, - }) - await upgradeProcess.exited - - await withInstance(async (client) => { - await client.applySchema(schema) - - await client.updateInstances((instance) => { - const version = (() => { - if (instance.platform === 'ermine') return '~0.7.0' - if (instance.platform === 'lollipop') return '~0.10.0' - return pbService.getLatestVersion() - })() - console.log(`Updating instance ${instance.id} to ${version}`) - return { - status: InstanceStatus.Idle, - version, - } - }) - }) - console.log(`All instances updated`) - })() - pbService.shutdown() -})() diff --git a/packages/daemon/src/migrate/schema.ts b/packages/daemon/src/migrate/schema.ts deleted file mode 100644 index 84f86404..00000000 --- a/packages/daemon/src/migrate/schema.ts +++ /dev/null @@ -1,357 +0,0 @@ -import { Collection, SchemaField } from 'pocketbase' - -export type Collection_Serialized = Omit, 'schema'> & { - schema: Array> -} - -export const schema: Collection_Serialized[] = [ - { - id: 'etae8tuiaxl6xfv', - name: 'instances', - type: 'base', - system: false, - schema: [ - { - id: 'qdtuuld1', - name: 'subdomain', - type: 'text', - system: false, - required: true, - unique: true, - options: { - min: null, - max: 50, - pattern: '^[a-z][\\-a-z]+$', - }, - }, - { - id: 'rbj14krn', - name: 'uid', - type: 'relation', - system: false, - required: true, - unique: false, - options: { - maxSelect: 1, - collectionId: '_pb_users_auth_', - cascadeDelete: false, - }, - }, - { - id: 'c2y74d7h', - name: 'status', - type: 'text', - system: false, - required: true, - unique: false, - options: { - min: null, - max: null, - pattern: '', - }, - }, - { - id: 'yxby5r6b', - name: 'platform', - type: 'text', - system: false, - required: true, - unique: false, - options: { - min: null, - max: null, - pattern: '', - }, - }, - { - id: '4ydffkv3', - name: 'version', - type: 'text', - system: false, - required: true, - unique: false, - options: { - min: null, - max: null, - pattern: '', - }, - }, - { - id: '1arlklqq', - name: 'secondsThisMonth', - type: 'number', - system: false, - required: false, - unique: false, - options: { - min: null, - max: null, - }, - }, - { - id: '66vjgzcg', - name: 'isBackupAllowed', - type: 'bool', - system: false, - required: false, - unique: false, - options: {}, - }, - ], - listRule: 'uid=@request.auth.id', - viewRule: 'uid = @request.auth.id', - createRule: null, - updateRule: null, - deleteRule: null, - options: {}, - }, - { - id: 'aiw8te7y7atklwn', - name: 'invocations', - type: 'base', - system: false, - schema: [ - { - id: 'st9ydrbo', - name: 'instanceId', - type: 'relation', - system: false, - required: true, - unique: false, - options: { - maxSelect: 1, - collectionId: 'etae8tuiaxl6xfv', - cascadeDelete: false, - }, - }, - { - id: 'av4mpuyh', - name: 'startedAt', - type: 'date', - system: false, - required: true, - unique: false, - options: { - min: '', - max: '', - }, - }, - { - id: 'fnwatixg', - name: 'endedAt', - type: 'date', - system: false, - required: false, - unique: false, - options: { - min: '', - max: '', - }, - }, - { - id: 'awjozhbn', - name: 'pid', - type: 'number', - system: false, - required: false, - unique: false, - options: { - min: null, - max: null, - }, - }, - { - id: 'vdkfqege', - name: 'totalSeconds', - type: 'number', - system: false, - required: false, - unique: false, - options: { - min: null, - max: null, - }, - }, - ], - listRule: null, - viewRule: null, - createRule: null, - updateRule: null, - deleteRule: null, - options: {}, - }, - { - id: 'v7s41iokt1vizxd', - name: 'rpc', - type: 'base', - system: false, - schema: [ - { - id: 'yv38czcf', - name: 'userId', - type: 'relation', - system: false, - required: true, - unique: false, - options: { - maxSelect: 1, - collectionId: '_pb_users_auth_', - cascadeDelete: false, - }, - }, - { - id: 'tgvaxwfv', - name: 'payload', - type: 'json', - system: false, - required: true, - unique: false, - options: {}, - }, - { - id: 'zede8pci', - name: 'status', - type: 'text', - system: false, - required: false, - unique: false, - options: { - min: null, - max: null, - pattern: '', - }, - }, - { - id: 'nd7cwqmn', - name: 'result', - type: 'json', - system: false, - required: false, - unique: false, - options: {}, - }, - { - id: '2hlrcx5j', - name: 'cmd', - type: 'text', - system: false, - required: true, - unique: false, - options: { - min: null, - max: null, - pattern: '', - }, - }, - ], - listRule: 'userId = @request.auth.id', - viewRule: 'userId = @request.auth.id', - createRule: - "userId = @request.auth.id && status='' && (cmd='backup-instance' || cmd='restore-instance' || cmd='create-instance') && payload!='' && result=''", - updateRule: null, - deleteRule: null, - options: {}, - }, - { - id: '72clb6v41bzsay9', - name: 'backups', - type: 'base', - system: false, - schema: [ - { - id: 'someqtjw', - name: 'message', - type: 'text', - system: false, - required: false, - unique: false, - options: { - min: null, - max: null, - pattern: '', - }, - }, - { - id: 'jk4zwiaj', - name: 'instanceId', - type: 'relation', - system: false, - required: true, - unique: false, - options: { - maxSelect: 1, - collectionId: 'etae8tuiaxl6xfv', - cascadeDelete: false, - }, - }, - { - id: 'wsy3l5gm', - name: 'status', - type: 'text', - system: false, - required: true, - unique: false, - options: { - min: null, - max: null, - pattern: '', - }, - }, - { - id: 'gmkrc5d9', - name: 'bytes', - type: 'number', - system: false, - required: false, - unique: false, - options: { - min: null, - max: null, - }, - }, - { - id: '4lmammjz', - name: 'platform', - type: 'text', - system: false, - required: true, - unique: false, - options: { - min: null, - max: null, - pattern: '', - }, - }, - { - id: 'fheqxmbj', - name: 'version', - type: 'text', - system: false, - required: true, - unique: false, - options: { - min: null, - max: null, - pattern: '', - }, - }, - { - id: 'cinbmdwe', - name: 'progress', - type: 'json', - system: false, - required: false, - unique: false, - options: {}, - }, - ], - listRule: '@request.auth.id = instanceId.uid', - viewRule: null, - createRule: null, - updateRule: null, - deleteRule: null, - options: {}, - }, -] - -console.log(JSON.stringify(schema)) diff --git a/packages/daemon/src/migrate/withInstance.ts b/packages/daemon/src/migrate/withInstance.ts deleted file mode 100644 index 2a2914fc..00000000 --- a/packages/daemon/src/migrate/withInstance.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { - DAEMON_PB_PASSWORD, - DAEMON_PB_USERNAME, - PUBLIC_APP_DB, - PUBLIC_APP_DOMAIN, - PUBLIC_APP_PROTOCOL, -} from '$constants' -import { createPbClient, pocketbase, PocketbaseClientApi } from '$services' -import { logger, safeCatch } from '@pockethost/common' - -export const withInstance = safeCatch( - `withInstance`, - async (cb: (client: PocketbaseClientApi) => Promise) => { - const { info, error } = logger().create('withInstance') - - // Add `platform` and `bin` required columns (migrate db json) - try { - const mainProcess = await ( - await pocketbase() - ).spawn({ - command: 'serve', - slug: PUBLIC_APP_DB, - }) - - try { - const { url } = mainProcess - const client = createPbClient(url) - await client.adminAuthViaEmail(DAEMON_PB_USERNAME, DAEMON_PB_PASSWORD) - await cb(client) - } catch (e) { - error( - `***WARNING*** CANNOT AUTHENTICATE TO ${PUBLIC_APP_PROTOCOL}://${PUBLIC_APP_DB}.${PUBLIC_APP_DOMAIN}/_/` - ) - error(`***WARNING*** LOG IN MANUALLY, ADJUST .env, AND RESTART DOCKER`) - process.exit(-1) - } finally { - info(`Exiting process`) - mainProcess.kill() - await mainProcess.exited - } - } catch (e) { - error(`${e}`) - } - } -) diff --git a/packages/daemon/src/server.ts b/packages/daemon/src/server.ts index 02f63ddc..184ecb76 100644 --- a/packages/daemon/src/server.ts +++ b/packages/daemon/src/server.ts @@ -50,8 +50,22 @@ global.EventSource = require('eventsource') /** * Launch central database */ + { + info(`Migrating mothership`) + await ( + await pbService.spawn({ + command: 'migrate', + isMothership: true, + version: DAEMON_PB_SEMVER, + slug: PUBLIC_APP_DB, + }) + ).exited + info(`Migrating done`) + } + info(`Serving`) const { url } = await pbService.spawn({ command: 'serve', + isMothership: true, version: DAEMON_PB_SEMVER, slug: PUBLIC_APP_DB, }) diff --git a/packages/daemon/src/services/PocketBaseService.ts b/packages/daemon/src/services/PocketBaseService.ts index 1714b10d..23c00efe 100644 --- a/packages/daemon/src/services/PocketBaseService.ts +++ b/packages/daemon/src/services/PocketBaseService.ts @@ -1,4 +1,4 @@ -import { DAEMON_PB_DATA_DIR } from '$constants' +import { DAEMON_PB_DATA_DIR, DAEMON_PB_MIGRATIONS_DIR } from '$constants' import { downloadAndExtract, mkInternalAddress, @@ -22,12 +22,13 @@ import { join } from 'path' import { maxSatisfying, rsort } from 'semver' import { AsyncReturnType } from 'type-fest' -export type PocketbaseCommand = 'serve' | 'upgrade' +export type PocketbaseCommand = 'serve' | 'migrate' export type SpawnConfig = { command: PocketbaseCommand slug: string version?: string port?: number + isMothership?: boolean onUnexpectedStop?: (code: number | null) => void } export type PocketbaseServiceApi = AsyncReturnType< @@ -138,11 +139,14 @@ export const createPocketbaseService = async ( const _cfg: Required = { version: maxVersion, port: await getPort(), + isMothership: false, onUnexpectedStop: (code) => { dbg(`Unexpected stop default handler. Exit code: ${code}`) }, ...cfg, } + const { version, command, slug, port, onUnexpectedStop, isMothership } = + _cfg const _version = version || maxVersion // If _version is blank, we use the max version available const bin = (await getVersion(_version)).binPath if (!existsSync(bin)) { @@ -157,6 +161,10 @@ export const createPocketbaseService = async ( `${DAEMON_PB_DATA_DIR}/${slug}/pb_data`, `--publicDir`, `${DAEMON_PB_DATA_DIR}/${slug}/pb_static`, + `--migrationsDir`, + isMothership + ? DAEMON_PB_MIGRATIONS_DIR + : `${DAEMON_PB_DATA_DIR}/${slug}/pb_migrations`, ] if (command === 'serve') { args.push(`--http`) diff --git a/packages/daemon/src/services/clientService/PbClient.ts b/packages/daemon/src/services/clientService/PbClient.ts index 35225fa9..75ad6848 100644 --- a/packages/daemon/src/services/clientService/PbClient.ts +++ b/packages/daemon/src/services/clientService/PbClient.ts @@ -1,12 +1,7 @@ import { DAEMON_PB_DATA_DIR, PUBLIC_APP_DB } from '$constants' import { logger, safeCatch } from '@pockethost/common' import { Knex } from 'knex' -import { - Collection, - default as PocketBase, - default as pocketbaseEs, -} from 'pocketbase' -import { Collection_Serialized } from '../../migrate/schema' +import { default as PocketBase, default as pocketbaseEs } from 'pocketbase' import { createBackupMixin } from './BackupMixin' import { createInstanceMixin } from './InstanceMIxin' import { createInvocationMixin } from './InvocationMixin' @@ -45,13 +40,6 @@ export const createPbClient = (url: string) => { }) ) - const applySchema = safeCatch( - `applySchema`, - async (collections: Collection_Serialized[]) => { - await client.collections.import(collections as Collection[], false) - } - ) - const context: MixinContext = { client, rawDb } const rpcApi = createRpcHelper(context) const instanceApi = createInstanceMixin(context) @@ -64,7 +52,6 @@ export const createPbClient = (url: string) => { knex: rawDb, createFirstAdmin, adminAuthViaEmail, - applySchema, ...rpcApi, ...instanceApi, ...invocationApi, diff --git a/packages/daemon/src/services/clientService/clientService.ts b/packages/daemon/src/services/clientService/clientService.ts index 732a0b6f..3ae5fd79 100644 --- a/packages/daemon/src/services/clientService/clientService.ts +++ b/packages/daemon/src/services/clientService/clientService.ts @@ -5,7 +5,6 @@ import { PUBLIC_APP_DOMAIN, PUBLIC_APP_PROTOCOL, } from '$constants' -import { schema } from '$src/migrate/schema' import { logger, mkSingleton } from '@pockethost/common' import { createPbClient } from './PbClient' @@ -31,15 +30,6 @@ export const clientService = mkSingleton(async (url: string) => { } } - try { - dbg(`Applying schema`) - await client.applySchema(schema) - dbg(`Schema applied`) - } catch (e) { - error(`Failed to apply base migration schema`) - process.exit(-1) - } - return { client, shutdown() { diff --git a/packages/daemon/src/util/backupInstance.ts b/packages/daemon/src/util/backupInstance.ts index 72212167..78cf2e67 100644 --- a/packages/daemon/src/util/backupInstance.ts +++ b/packages/daemon/src/util/backupInstance.ts @@ -10,8 +10,8 @@ import { basename, resolve } from 'path' import { chdir, cwd } from 'process' import { Database } from 'sqlite3' import tmp from 'tmp' -import { pexec } from '../migrate/pexec' import { ensureDirExists } from './ensureDirExists' +import { pexec } from './pexec' export type BackupProgress = { current: number diff --git a/packages/daemon/src/migrate/pexec.ts b/packages/daemon/src/util/pexec.ts similarity index 100% rename from packages/daemon/src/migrate/pexec.ts rename to packages/daemon/src/util/pexec.ts diff --git a/readme.md b/readme.md index 55f5dadd..f8f19cca 100644 --- a/readme.md +++ b/readme.md @@ -128,6 +128,11 @@ By default, PocketHost will download and run the latest version of PocketBase. I # Release History +**0.7.0** + +- PocketHost will now always select and run the latest version of PocketBase for new instances and for the PocketHost central database. This was previously restricted until PocketBase matured more, but we think it is safe now. +- Now using native PocketBase migrations to manage PocketHost central database migrations. Roadmap has been updated with task to allow end users to put their PocketBase instance in maintenance mode and run migrations. + **0.6.1** - Fixed semver locking error diff --git a/roadmap.md b/roadmap.md index 03500395..6d6e7b4e 100644 --- a/roadmap.md +++ b/roadmap.md @@ -61,6 +61,9 @@ Ideas, in no particular order... - Version upgrades/downgrades + - Allow instance to be placed into maintenance mode + - Allow maintenance mode instance to run migrations (check automigrate first?) + - Allow instance to be shut down and restarted - [Allow user to move to platform that requires migrations](https://github.com/benallfree/pockethost/issues/72) - [Allow user to move between platforms that don't require migrations](https://github.com/benallfree/pockethost/issues/60) - [Allow user to move between versions on a given pocketbase platform](https://github.com/benallfree/pockethost/issues/59)