/** * Copyright (c) 2023 MERCENARIES.AI PTE. LTD. * All rights reserved. */ // NO THIRD PARTY DEPS const { spawn, spawnSync, execSync } = require('child_process'); const fs = require('fs'); const path = require('path'); const os = require('os'); const { omniCwd } = require('./utils.js'); const packagejson = JSON.parse( fs.readFileSync(path.join(omniCwd(), 'package.json'), { encoding: 'utf-8' })); const { unzip, sleep, checkInternet } = require('./utils'); const depjson = packagejson.dependenciesBin; const VERSION_MARKER = '##version##'; const DB_ID = 'pocketbase'; const platform = os.platform(); const arch = os.arch(); const ownedProcesses = new Array(); function verifyInstall(path) { return fs.existsSync(path); } function downloadURL() { let file = depjson[DB_ID].zipfile[platform][arch]; let version = packagejson.engines[DB_ID]; return `${depjson[DB_ID].base_url}/${file}`.replaceAll(VERSION_MARKER, version); } async function fetchAndExtract(zipfile, destdir) { const curl = `curl -L -o ${zipfile} ${downloadURL()}`; execSync(curl, { stdio: 'inherit' }); console.log(`\nDownload completed, now extracting to ${path.dirname(destdir)}...`); unzip(zipfile, destdir); } async function createDefaultAdmin(installpath) { let timeout_guard = 10000; console.log('Pre-warm pocketbase...'); let dbprocess = spawn(path.join(installpath, getExecutable()), ['serve'], { stdio: 'inherit' }); ownedProcesses.push(dbprocess); // ensure initial boot of pocketbase to create base tables while (!fs.existsSync(path.join(installpath, 'pb_data'))) { await sleep(1000); timeout_guard -= 1000; if (timeout_guard <= 0) { console.error('Failed to initialize DB'); process.exit(1); } } // ready to administrate console.log('Creating default DB Admin ' + depjson[DB_ID].admin); spawnSync(path.join(installpath, getExecutable()), ['admin', 'create', depjson[DB_ID].admin, depjson[DB_ID].admin]); // create default tables for OMNI console.log(`Success!`); let pbSchemas = require('../packages/omni-server/src/services/DBService/pb_schema.json'); const PocketBase = (await import('pocketbase')).default; let pb = new PocketBase('http://127.0.0.1:8090'); await pb.admins.authWithPassword(depjson[DB_ID].admin, depjson[DB_ID].admin); for (const schema of pbSchemas) { console.log('Creating collection - ' + schema.name); await pb.collections.create(schema); const record = pb.collection(schema.name); // validate get, if not found, throw await record.getList(); } pb.autoCancellation(false); // shutdown pocketbase serve console.info('PocketBase initial configuration completed - READY FOR USE...'); dbprocess.kill(); ownedProcesses.pop(); } // move to config function getExecutable() { switch (`${platform}`) { case 'win32': return 'pocketbase.exe'; case 'darwin': return 'pocketbase'; case 'linux': return 'pocketbase'; } throw new Error('Unhandled executable type for ' + platform); } function successCleanup(installpath) { fs.rmSync(installpath + '/_download.zip'); } async function failCleanup(installpath) { console.error('Installation failed. Cleaning up.'); while (ownedProcesses.length > 0) { ownedProcesses.pop().kill(); } // there are cases when the shutdown isn't complete // and rm fails, we retry until it succeeds or throw let retries = 30; while (true) { try { fs.rmSync(installpath, { recursive: true, force: true }); break; } catch (e) { if (retries-- <= 0) { throw e; } await sleep(100); } } } async function installPocketBase(installpath) { // create directory console.log('Creating dir ' + installpath); fs.mkdirSync(installpath, { recursive: true }); const downloadfile = installpath + '/_download.zip'; await fetchAndExtract(downloadfile, installpath); console.log('Installation successful'); await createDefaultAdmin(installpath); } async function ensure_online() { if (!(await checkInternet())) { console.error('DNS resolution failed. Please check your internet connection.'); process.exit(1); } console.log('Connectivity OK!'); } async function ensure(installpath) { // check if installed if (verifyInstall(path.join(installpath, getExecutable()))) { console.info(`Found installation for PocketBase`); return true; } else { ensure_online(); console.info(`Missing PocketBase DB - Installing...`); try { await installPocketBase(installpath); successCleanup(installpath); return true; } catch (e) { console.error(e); await failCleanup(installpath); return false; } } } module.exports = { ensure };