From 56aff5db0a27d2d66d3d4de23a87602dbb117a9c Mon Sep 17 00:00:00 2001 From: Ben Allfree Date: Wed, 14 Jun 2023 11:52:07 -0700 Subject: [PATCH] chore: log context update --- .../pocketbase-client-helpers/WatchHelper.ts | 2 + packages/common/src/safeCatch.ts | 5 +- packages/daemon/src/server.ts | 26 +-- .../daemon/src/services/CentralDbService.ts | 46 ++--- .../src/services/FtpService/FtpService.ts | 15 +- .../daemon/src/services/FtpService/PhFs.ts | 10 +- .../InstanceLoggerService/DaemonContext.ts | 5 + .../InstanceLoggerService/SqliteLogger.ts | 74 ++++++++ .../services/InstanceLoggerService/index.ts | 110 ++++-------- .../InstanceService/Deno/DenoProcess.ts | 11 +- .../InstanceService/InstanceService.ts | 57 ++++--- .../daemon/src/services/PocketBaseService.ts | 160 +++++++++--------- packages/daemon/src/services/ProxyService.ts | 27 ++- packages/daemon/src/services/RealtimeLog.ts | 21 +-- .../src/services/RpcService/RpcService.ts | 7 +- .../services/SqliteService/SqliteService.ts | 10 +- .../services/clientService/InstanceMIxin.ts | 14 +- .../services/clientService/InvocationMixin.ts | 7 +- .../src/services/clientService/PbClient.ts | 19 ++- .../src/services/clientService/RawPbClient.ts | 6 +- .../src/services/clientService/RpcHelper.ts | 9 +- .../services/clientService/clientService.ts | 15 +- packages/daemon/src/services/index.ts | 1 - .../daemon/src/util/downloadAndExtract.ts | 10 +- packages/daemon/src/util/index.ts | 1 - packages/daemon/src/util/pexec.ts | 31 ++-- packages/daemon/src/util/tryFetch.ts | 55 +++--- .../src/pocketbase/PocketbaseClient.ts | 19 ++- 28 files changed, 449 insertions(+), 324 deletions(-) create mode 100644 packages/daemon/src/services/InstanceLoggerService/DaemonContext.ts create mode 100644 packages/daemon/src/services/InstanceLoggerService/SqliteLogger.ts diff --git a/packages/common/src/pocketbase-client-helpers/WatchHelper.ts b/packages/common/src/pocketbase-client-helpers/WatchHelper.ts index df5d66e6..b81e9e45 100644 --- a/packages/common/src/pocketbase-client-helpers/WatchHelper.ts +++ b/packages/common/src/pocketbase-client-helpers/WatchHelper.ts @@ -21,6 +21,7 @@ export const createWatchHelper = (config: WatchHelperConfig) => { const watchById = safeCatch( `watchById`, + logger(), async ( collectionName: string, id: RecordId, @@ -93,6 +94,7 @@ export const createWatchHelper = (config: WatchHelperConfig) => { const watchAllById = safeCatch( `watchAllById`, + logger(), async ( collectionName: string, idName: keyof TRec, diff --git a/packages/common/src/safeCatch.ts b/packages/common/src/safeCatch.ts index 42e823b8..ac122b5b 100644 --- a/packages/common/src/safeCatch.ts +++ b/packages/common/src/safeCatch.ts @@ -1,9 +1,10 @@ import { ClientResponseError } from 'pocketbase' -import { logger } from './Logger' +import { Logger } from './Logger' let c = 0 export const safeCatch = ( name: string, + logger: Logger, cb: (...args: TIn) => Promise, timeoutMs = 5000 ) => { @@ -11,7 +12,7 @@ export const safeCatch = ( const _c = c++ const uuid = `${name}:${_c}` const pfx = `safeCatch:${uuid}` - const { raw, error, warn, dbg } = logger().create(pfx) + const { raw, error, warn, dbg } = logger.create(pfx) raw(`args`, args) const tid = setTimeout(() => { error(`timeout ${timeoutMs}ms waiting for ${pfx}`) diff --git a/packages/daemon/src/server.ts b/packages/daemon/src/server.ts index 184ecb76..42f18827 100644 --- a/packages/daemon/src/server.ts +++ b/packages/daemon/src/server.ts @@ -16,17 +16,18 @@ import { rpcService, sqliteService, } from '$services' -import { logger } from '@pockethost/common' +import { logger as loggerService } from '@pockethost/common' import { exec } from 'child_process' import { centralDbService } from './services/CentralDbService' import { instanceLoggerService } from './services/InstanceLoggerService' -logger({ debug: DEBUG, trace: TRACE, errorTrace: !DEBUG }) +loggerService({ debug: DEBUG, trace: TRACE, errorTrace: !DEBUG }) // npm install eventsource --save global.EventSource = require('eventsource') ;(async () => { - const { dbg, error, info, warn } = logger().create(`server.ts`) + const logger = loggerService().create(`server.ts`) + const { dbg, error, info, warn } = logger info(`Starting`) /** @@ -45,6 +46,7 @@ global.EventSource = require('eventsource') const pbService = await pocketbase({ cachePath: PH_BIN_CACHE, checkIntervalMs: 1000 * 5 * 60, + logger, }) /** @@ -73,18 +75,18 @@ global.EventSource = require('eventsource') /** * Launch services */ - await clientService(url) - ftpService({}) - await rpcService({}) + await clientService({ url, logger }) + ftpService({ logger }) + await rpcService({ logger }) await proxyService({ + logger, coreInternalUrl: url, }) - await instanceLoggerService({}) - await sqliteService({}) - await realtimeLog({}) - await instanceService({}) - await centralDbService({}) - await backupService({}) + await instanceLoggerService({ logger }) + await sqliteService({ logger }) + await realtimeLog({ logger }) + await instanceService({ logger }) + await centralDbService({ logger }) info(`Hooking into process exit event`) diff --git a/packages/daemon/src/services/CentralDbService.ts b/packages/daemon/src/services/CentralDbService.ts index 6b9f6489..05013cc2 100644 --- a/packages/daemon/src/services/CentralDbService.ts +++ b/packages/daemon/src/services/CentralDbService.ts @@ -1,28 +1,34 @@ import { PUBLIC_APP_DB } from '$constants' -import { logger, mkSingleton } from '@pockethost/common' +import { mkSingleton, SingletonBaseConfig } from '@pockethost/common' import { proxyService } from './ProxyService' -export const centralDbService = mkSingleton(async () => { - const { dbg } = logger().create(`centralDbService`) +export type CentralDbServiceConfig = SingletonBaseConfig - ;(await proxyService()).use( - PUBLIC_APP_DB, - ['/api(/*)', '/_(/*)', '/'], - (req, res, meta) => { - const { subdomain, coreInternalUrl, proxy } = meta +export const centralDbService = mkSingleton( + async (config: CentralDbServiceConfig) => { + const { logger } = config + const { dbg } = logger.create(`centralDbService`) - if (subdomain !== PUBLIC_APP_DB) return + ;(await proxyService()).use( + PUBLIC_APP_DB, + ['/api(/*)', '/_(/*)', '/'], + (req, res, meta, logger) => { + const { dbg } = logger + const { subdomain, coreInternalUrl, proxy } = meta - const target = coreInternalUrl - dbg( - `Forwarding proxy request for ${req.url} to central instance ${target}` - ) - proxy.web(req, res, { target }) - }, - `CentralDbService` - ) + if (subdomain !== PUBLIC_APP_DB) return - return { - shutdown() {}, + const target = coreInternalUrl + dbg( + `Forwarding proxy request for ${req.url} to central instance ${target}` + ) + proxy.web(req, res, { target }) + }, + `CentralDbService` + ) + + return { + shutdown() {}, + } } -}) +) diff --git a/packages/daemon/src/services/FtpService/FtpService.ts b/packages/daemon/src/services/FtpService/FtpService.ts index 808f6f7a..d2d803e3 100644 --- a/packages/daemon/src/services/FtpService/FtpService.ts +++ b/packages/daemon/src/services/FtpService/FtpService.ts @@ -7,12 +7,12 @@ import { SSL_KEY, } from '$constants' import { clientService, createPbClient } from '$services' -import { logger, mkSingleton } from '@pockethost/common' +import { mkSingleton, SingletonBaseConfig } from '@pockethost/common' import { readFileSync } from 'fs' import { FtpSrv } from 'ftp-srv' import { PhFs } from './PhFs' -export type FtpConfig = {} +export type FtpConfig = SingletonBaseConfig & {} export enum FolderNames { PbData = 'pb_data', @@ -49,13 +49,14 @@ const tls = { } export const ftpService = mkSingleton((config: FtpConfig) => { - const log = logger().create('FtpService') - const { dbg, info } = log + const { logger } = config + const _ftpServiceLogger = logger.create('FtpService') + const { dbg, info } = _ftpServiceLogger const ftpServer = new FtpSrv({ url: 'ftp://0.0.0.0:' + PH_FTP_PORT, anonymous: false, - log: log.create(`ftpServer`, { errorTrace: false }), + log: _ftpServiceLogger.create(`ftpServer`, { errorTrace: false }), tls, pasv_url: PH_FTP_PASV_IP, pasv_max: PH_FTP_PASV_PORT_MAX, @@ -66,13 +67,13 @@ export const ftpService = mkSingleton((config: FtpConfig) => { 'login', async ({ connection, username, password }, resolve, reject) => { const url = (await clientService()).client.url - const client = createPbClient(url) + const client = createPbClient(url, _ftpServiceLogger) try { await client.client .collection('users') .authWithPassword(username, password) dbg(`Logged in`) - const fs = new PhFs(connection, client) + const fs = new PhFs(connection, client, _ftpServiceLogger) resolve({ fs }) } catch (e) { reject(new Error(`Invalid username or password`)) diff --git a/packages/daemon/src/services/FtpService/PhFs.ts b/packages/daemon/src/services/FtpService/PhFs.ts index 4d2d3381..a7b9a865 100644 --- a/packages/daemon/src/services/FtpService/PhFs.ts +++ b/packages/daemon/src/services/FtpService/PhFs.ts @@ -1,5 +1,5 @@ import { DAEMON_PB_DATA_DIR } from '$constants' -import { Logger, logger } from '@pockethost/common' +import { Logger } from '@pockethost/common' import { existsSync, mkdirSync } from 'fs' import { FileStat, FileSystem, FtpConnection } from 'ftp-srv' import { join } from 'path' @@ -16,10 +16,14 @@ export class PhFs extends FileSystem { private log: Logger private client: PocketbaseClientApi - constructor(connection: FtpConnection, client: PocketbaseClientApi) { + constructor( + connection: FtpConnection, + client: PocketbaseClientApi, + logger: Logger + ) { super(connection, { root: '/', cwd: '/' }) this.client = client - this.log = logger().create(`PhFs`) + this.log = logger.create(`PhFs`) } async chdir(path?: string | undefined): Promise { diff --git a/packages/daemon/src/services/InstanceLoggerService/DaemonContext.ts b/packages/daemon/src/services/InstanceLoggerService/DaemonContext.ts new file mode 100644 index 00000000..b16884e5 --- /dev/null +++ b/packages/daemon/src/services/InstanceLoggerService/DaemonContext.ts @@ -0,0 +1,5 @@ +import { Logger } from '@pockethost/common' + +export type DaemonContext = { + parentLogger: Logger +} diff --git a/packages/daemon/src/services/InstanceLoggerService/SqliteLogger.ts b/packages/daemon/src/services/InstanceLoggerService/SqliteLogger.ts new file mode 100644 index 00000000..fa1d04f2 --- /dev/null +++ b/packages/daemon/src/services/InstanceLoggerService/SqliteLogger.ts @@ -0,0 +1,74 @@ +import { SqliteChangeEvent, sqliteService } from '$services' +import { + InstanceLogFields, + InstanceLogFields_Create, + newId, + pocketNow, + RecordId, + safeCatch, + StreamNames, +} from '@pockethost/common' +import knex from 'knex' +import { AsyncReturnType } from 'type-fest' +import { DaemonContext } from './DaemonContext' + +export type SqliteLogger = AsyncReturnType +export const createSqliteLogger = async ( + logDbPath: string, + context: DaemonContext +) => { + const { parentLogger } = context + const _dbLogger = parentLogger.create(`${logDbPath}`) + const { dbg } = _dbLogger + + const { getDatabase } = sqliteService() + const db = await getDatabase(logDbPath) + + const conn = knex({ + client: 'sqlite3', + connection: { + filename: logDbPath, + }, + useNullAsDefault: true, + }) + + const write = safeCatch( + `write`, + _dbLogger, + async (message: string, stream: StreamNames = StreamNames.Info) => { + const _in: InstanceLogFields_Create = { + id: newId(), + message, + stream, + created: pocketNow(), + updated: pocketNow(), + } + const sql = conn('logs').insert(_in).toString() + dbg(`Writing log ${JSON.stringify(_in)} ${sql}`) + await db.exec(sql) + } + ) + + const subscribe = (cb: (e: SqliteChangeEvent) => void) => { + let _seenIds: { [_: RecordId]: boolean } | undefined = {} + + const unsub = db.subscribe((e) => { + // dbg(`Caught db modification ${logDbPath}`, e) + const { table, record } = e + if (table !== 'logs') return + if (_seenIds) { + _seenIds[record.id] = true + } + cb(e) + }) + return unsub + } + + const fetch = async (limit: number = 100) => { + return db.all( + `select * from logs order by created desc limit ${limit}` + ) + } + + return { write, subscribe, fetch } +} diff --git a/packages/daemon/src/services/InstanceLoggerService/index.ts b/packages/daemon/src/services/InstanceLoggerService/index.ts index a2430c6f..bbba3e99 100644 --- a/packages/daemon/src/services/InstanceLoggerService/index.ts +++ b/packages/daemon/src/services/InstanceLoggerService/index.ts @@ -1,85 +1,30 @@ import { DAEMON_PB_DATA_DIR } from '$constants' -import { SqliteChangeEvent, sqliteService } from '$services' +import { sqliteService } from '$services' import { InstanceId, - InstanceLogFields, - InstanceLogFields_Create, - logger, mkSingleton, - newId, - pocketNow, - RecordId, - safeCatch, + SingletonBaseConfig, StreamNames, } from '@pockethost/common' import { mkdirSync } from 'fs' -import knex from 'knex' import { dirname, join } from 'path' -import { AsyncReturnType } from 'type-fest' - -export type InstanceLogger = AsyncReturnType -const mkApi = async (logDbPath: string) => { - const { dbg } = logger().create(`InstanceLogger ${logDbPath}`) - - const { getDatabase } = sqliteService() - const db = await getDatabase(logDbPath) - - const conn = knex({ - client: 'sqlite3', - connection: { - filename: logDbPath, - }, - useNullAsDefault: true, - }) - - const write = safeCatch( - `workerLogger:write`, - async (message: string, stream: StreamNames = StreamNames.Info) => { - const _in: InstanceLogFields_Create = { - id: newId(), - message, - stream, - created: pocketNow(), - updated: pocketNow(), - } - const sql = conn('logs').insert(_in).toString() - dbg(`Writing log ${JSON.stringify(_in)} ${sql}`) - await db.exec(sql) - } - ) - - const subscribe = (cb: (e: SqliteChangeEvent) => void) => { - let _seenIds: { [_: RecordId]: boolean } | undefined = {} - - const unsub = db.subscribe((e) => { - // dbg(`Caught db modification ${logDbPath}`, e) - const { table, record } = e - if (table !== 'logs') return - if (_seenIds) { - _seenIds[record.id] = true - } - cb(e) - }) - return unsub - } - - const fetch = async (limit: number = 100) => { - return db.all( - `select * from logs order by created desc limit ${limit}` - ) - } - - return { write, subscribe, fetch } -} +import { DaemonContext } from './DaemonContext' +import { createSqliteLogger, SqliteLogger } from './SqliteLogger' const instances: { - [instanceId: InstanceId]: Promise + [instanceId: InstanceId]: Promise } = {} -export const createInstanceLogger = (instanceId: InstanceId) => { +export const createInstanceLogger = ( + instanceId: InstanceId, + context: DaemonContext +) => { + const { parentLogger } = context + if (!instances[instanceId]) { - instances[instanceId] = new Promise(async (resolve) => { - const { dbg } = logger().create(`WorkerLogger:${instanceId}`) + instances[instanceId] = new Promise(async (resolve) => { + const _instanceLogger = parentLogger.create(`InstanceLogger`) + const { dbg } = _instanceLogger const logDbPath = join( DAEMON_PB_DATA_DIR, @@ -98,7 +43,9 @@ export const createInstanceLogger = (instanceId: InstanceId) => { migrationsPath: join(__dirname, 'migrations'), }) - const api = await mkApi(logDbPath) + const api = await createSqliteLogger(logDbPath, { + parentLogger: _instanceLogger, + }) await api.write(`Ran migrations`, StreamNames.System) resolve(api) }) @@ -107,13 +54,18 @@ export const createInstanceLogger = (instanceId: InstanceId) => { return instances[instanceId]! } -export const instanceLoggerService = mkSingleton(() => { - const { dbg } = logger().create(`InstanceLoggerService`) - dbg(`Starting up`) - return { - get: createInstanceLogger, - shutdown() { - dbg(`Shutting down`) - }, +export type InstanceLoggerServiceConfig = SingletonBaseConfig + +export const instanceLoggerService = mkSingleton( + (config: InstanceLoggerServiceConfig) => { + const { logger } = config + const { dbg } = logger.create(`InstanceLoggerService`) + dbg(`Starting up`) + return { + get: createInstanceLogger, + shutdown() { + dbg(`Shutting down`) + }, + } } -}) +) diff --git a/packages/daemon/src/services/InstanceService/Deno/DenoProcess.ts b/packages/daemon/src/services/InstanceService/Deno/DenoProcess.ts index c1195a81..2c3a5c19 100644 --- a/packages/daemon/src/services/InstanceService/Deno/DenoProcess.ts +++ b/packages/daemon/src/services/InstanceService/Deno/DenoProcess.ts @@ -1,5 +1,5 @@ import { DAEMON_PB_DATA_DIR, DENO_PATH } from '$constants' -import { InstanceFields, logger, StreamNames } from '@pockethost/common' +import { InstanceFields, Logger, StreamNames } from '@pockethost/common' import { keys } from '@s-libs/micro-dash' import { spawn } from 'child_process' import { join } from 'path' @@ -11,12 +11,15 @@ export type DenoProcessConfig = { path: string port: number instance: InstanceFields + logger: Logger } export type DenoApi = AsyncReturnType export const createDenoProcess = async (config: DenoProcessConfig) => { - const { dbg, error } = logger().create(`DenoProcess.ts`) + const { logger } = config + const _denoLogger = logger.create(`DenoProcess.ts`) + const { dbg, error } = _denoLogger const { instance, port, path } = config const internalUrl = mkInternalUrl(port) @@ -34,7 +37,9 @@ export const createDenoProcess = async (config: DenoProcessConfig) => { path, ] - const denoLogger = await instanceLoggerService().get(instance.id) + const denoLogger = await instanceLoggerService().get(instance.id, { + parentLogger: _denoLogger, + }) const denoWrite = ( message: string, diff --git a/packages/daemon/src/services/InstanceService/InstanceService.ts b/packages/daemon/src/services/InstanceService/InstanceService.ts index 17c57745..eefd1db5 100644 --- a/packages/daemon/src/services/InstanceService/InstanceService.ts +++ b/packages/daemon/src/services/InstanceService/InstanceService.ts @@ -16,13 +16,13 @@ import { createTimerManager, InstanceId, InstanceStatus, - logger, mkSingleton, RpcCommands, safeCatch, SaveSecretsPayload, SaveSecretsPayloadSchema, SaveSecretsResult, + SingletonBaseConfig, } from '@pockethost/common' import { forEachRight, map } from '@s-libs/micro-dash' import Bottleneck from 'bottleneck' @@ -42,12 +42,14 @@ type InstanceApi = { startRequest: () => () => void } -export type InstanceServiceConfig = {} +export type InstanceServiceConfig = SingletonBaseConfig & {} export type InstanceServiceApi = AsyncReturnType export const instanceService = mkSingleton( async (config: InstanceServiceConfig) => { - const { dbg, raw, error, warn } = logger().create('InstanceService') + const { logger } = config + const _instanceLogger = logger.create('InstanceService') + const { dbg, raw, error, warn } = _instanceLogger const { client } = await clientService() const { registerCommand } = await rpcService() @@ -91,9 +93,8 @@ export const instanceService = mkSingleton( const getInstance = (subdomain: string) => instanceLimiter .schedule(async () => { - const { dbg, warn, error } = logger().create( - `InstanceService ${subdomain}` - ) + const _subdomainLogger = _instanceLogger.create(subdomain) + const { dbg, warn, error } = _subdomainLogger dbg(`Getting instance`) { const instance = instances[subdomain] @@ -112,6 +113,7 @@ export const instanceService = mkSingleton( ) } dbg(`Instance found`) + _subdomainLogger.breadcrumb(instance.id) dbg(`Checking for verified account`) if (!owner?.verified) { @@ -132,9 +134,13 @@ export const instanceService = mkSingleton( error(`Failed to get port for ${subdomain}`) throw e }) + _subdomainLogger.breadcrumb(newPort.toString()) dbg(`Found port: ${newPort}`) - const instanceLogger = await instanceLoggerService().get(instance.id) + const instanceLogger = await instanceLoggerService().get( + instance.id, + { parentLogger: _subdomainLogger } + ) await clientLimiter.schedule(() => { dbg(`Instance status: starting`) @@ -146,16 +152,25 @@ export const instanceService = mkSingleton( dbg(`Starting instance`) await instanceLogger.write(`Starting instance`) - const childProcess = await pbService.spawn({ - command: 'serve', - slug: instance.id, - port: newPort, - version: instance.version, - onUnexpectedStop: (code) => { - warn(`${subdomain} exited unexpectedly with ${code}`) - api.shutdown() - }, - }) + const childProcess = await (async () => { + try { + const cp = await pbService.spawn({ + command: 'serve', + slug: instance.id, + port: newPort, + version: instance.version, + onUnexpectedStop: (code) => { + warn(`${subdomain} exited unexpectedly with ${code}`) + api.shutdown() + }, + }) + return cp + } catch (e) { + throw new Error( + `Could not launch PocketBase ${instance.version}. It may be time to upgrade.` + ) + } + })() const { pid } = childProcess assertTruthy(pid, `Expected PID here but got ${pid}`) @@ -190,6 +205,7 @@ export const instanceService = mkSingleton( path: workerPath, port: newPort, instance, + logger: _instanceLogger, }) return api } else { @@ -210,6 +226,7 @@ export const instanceService = mkSingleton( port: newPort, shutdown: safeCatch( `Instance ${subdomain} invocation ${invocation.id} pid ${pid} shutdown`, + _subdomainLogger, async () => { dbg(`Shutting down`) await instanceLogger.write(`Shutting down instance`) @@ -247,7 +264,7 @@ export const instanceService = mkSingleton( { tm.repeat( - safeCatch(`idleCheck`, async () => { + safeCatch(`idleCheck`, _subdomainLogger, async () => { raw( `${subdomain} idle check: ${openRequestCount} open requests` ) @@ -272,7 +289,7 @@ export const instanceService = mkSingleton( } { - const uptime = safeCatch(`uptime`, async () => { + const uptime = safeCatch(`uptime`, _subdomainLogger, async () => { raw(`${subdomain} uptime`) await clientLimiter.schedule(() => client.pingInvocation(invocation) @@ -314,7 +331,7 @@ export const instanceService = mkSingleton( ;(await proxyService()).use( (subdomain) => subdomain !== PUBLIC_APP_DB, ['/api(/*)', '/_(/*)', '(/*)'], - async (req, res, meta) => { + async (req, res, meta, logger) => { const { subdomain, host, proxy } = meta // Do not handle central db requests, that is handled separately diff --git a/packages/daemon/src/services/PocketBaseService.ts b/packages/daemon/src/services/PocketBaseService.ts index 515e6048..54ad6ae9 100644 --- a/packages/daemon/src/services/PocketBaseService.ts +++ b/packages/daemon/src/services/PocketBaseService.ts @@ -9,10 +9,12 @@ import { import { createCleanupManager, createTimerManager, - logger, safeCatch, } from '@pockethost/common' -import { mkSingleton } from '@pockethost/common/src/mkSingleton' +import { + mkSingleton, + SingletonBaseConfig, +} from '@pockethost/common/src/mkSingleton' import { keys } from '@s-libs/micro-dash' import { spawn } from 'child_process' import { chmodSync, existsSync } from 'fs' @@ -35,7 +37,7 @@ export type PocketbaseServiceApi = AsyncReturnType< typeof createPocketbaseService > -export type PocketbaseServiceConfig = { +export type PocketbaseServiceConfig = SingletonBaseConfig & { cachePath: string checkIntervalMs: number } @@ -60,7 +62,9 @@ export type Releases = Release[] export const createPocketbaseService = async ( config: PocketbaseServiceConfig ) => { - const { dbg, error } = logger().create('PocketbaseService') + const { logger } = config + const _serviceLogger = logger.create('PocketbaseService') + const { dbg, error } = _serviceLogger const { cachePath, checkIntervalMs } = config @@ -100,7 +104,7 @@ export const createPocketbaseService = async ( versions[sanitizedTagName] = Promise.resolve('') return } - await downloadAndExtract(url, binPath) + await downloadAndExtract(url, binPath, _serviceLogger) resolve(binPath) }) @@ -138,85 +142,89 @@ export const createPocketbaseService = async ( } } - const _spawn = safeCatch(`spawnInstance`, async (cfg: SpawnConfig) => { - 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)) { - throw new Error( - `PocketBase binary (${bin}) not found. Contact pockethost.io.` - ) - } + const _spawn = safeCatch( + `spawnInstance`, + _serviceLogger, + async (cfg: SpawnConfig) => { + 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)) { + throw new Error( + `PocketBase binary (${bin}) not found. Contact pockethost.io.` + ) + } - const args = [ - command, - `--dir`, - `${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`) - args.push(mkInternalAddress(port)) - } + const args = [ + command, + `--dir`, + `${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`) + args.push(mkInternalAddress(port)) + } - let isRunning = true + let isRunning = true - dbg(`Spawning ${slug}`, { bin, args, cli: [bin, ...args].join(' ') }) - const ls = spawn(bin, args) - cm.add(() => ls.kill()) + dbg(`Spawning ${slug}`, { bin, args, cli: [bin, ...args].join(' ') }) + const ls = spawn(bin, args) + cm.add(() => ls.kill()) - ls.stdout.on('data', (data) => { - dbg(`${slug} stdout: ${data}`) - }) - - ls.stderr.on('data', (data) => { - error(`${slug} stderr: ${data}`) - }) - - ls.on('close', (code) => { - dbg(`${slug} closed with code ${code}`) - }) - - const exited = new Promise((resolve) => { - ls.on('exit', (code) => { - dbg(`${slug} exited with code ${code}`) - isRunning = false - if (code) onUnexpectedStop?.(code) - resolve(code) + ls.stdout.on('data', (data) => { + dbg(`${slug} stdout: ${data}`) }) - }) - ls.on('error', (err) => { - dbg(`${slug} had error ${err}`) - }) + ls.stderr.on('data', (data) => { + error(`${slug} stderr: ${data}`) + }) - const url = mkInternalUrl(port) - if (command === 'serve') { - await tryFetch(url, async () => isRunning) + ls.on('close', (code) => { + dbg(`${slug} closed with code ${code}`) + }) + + const exited = new Promise((resolve) => { + ls.on('exit', (code) => { + dbg(`${slug} exited with code ${code}`) + isRunning = false + if (code) onUnexpectedStop?.(code) + resolve(code) + }) + }) + + ls.on('error', (err) => { + dbg(`${slug} had error ${err}`) + }) + + const url = mkInternalUrl(port) + if (command === 'serve') { + await tryFetch(_serviceLogger)(url, async () => isRunning) + } + const api: PocketbaseProcess = { + url, + exited, + pid: ls.pid, + kill: () => ls.kill(), + } + return api } - const api: PocketbaseProcess = { - url, - exited, - pid: ls.pid, - kill: () => ls.kill(), - } - return api - }) + ) const shutdown = () => { dbg(`Shutting down pocketbaseService`) diff --git a/packages/daemon/src/services/ProxyService.ts b/packages/daemon/src/services/ProxyService.ts index 9a3c6e59..3c53ed8f 100644 --- a/packages/daemon/src/services/ProxyService.ts +++ b/packages/daemon/src/services/ProxyService.ts @@ -1,5 +1,5 @@ import { PUBLIC_APP_DOMAIN } from '$constants' -import { logger, mkSingleton } from '@pockethost/common' +import { Logger, mkSingleton, SingletonBaseConfig } from '@pockethost/common' import { isFunction } from '@s-libs/micro-dash' import { createServer, @@ -21,14 +21,17 @@ export type ProxyMiddleware = ( coreInternalUrl: string proxy: Server host: string - } + }, + logger: Logger ) => void | Promise -export type ProxyServiceConfig = { +export type ProxyServiceConfig = SingletonBaseConfig & { coreInternalUrl: string } export const proxyService = mkSingleton(async (config: ProxyServiceConfig) => { - const { dbg, error, info, trace, warn } = logger().create('ProxyService') + const { logger } = config + const _proxyLogger = logger.create('ProxyService') + const { dbg, error, info, trace, warn } = _proxyLogger const { coreInternalUrl } = config @@ -38,7 +41,7 @@ export const proxyService = mkSingleton(async (config: ProxyServiceConfig) => { }) const server = createServer(async (req, res) => { - dbg(`Incoming request ${req.headers.host}/${req.url}`) + dbg(`Incoming request ${req.method} ${req.headers.host}/${req.url}`) if (!req.headers.host?.endsWith(PUBLIC_APP_DOMAIN)) { warn( `Request for ${req.headers.host} rejected because host does not end in ${PUBLIC_APP_DOMAIN}` @@ -89,7 +92,8 @@ export const proxyService = mkSingleton(async (config: ProxyServiceConfig) => { handler: ProxyMiddleware, handlerName: string ) => { - const { dbg, trace } = logger().create(`ProxyService:${handlerName}`) + const _handlerLogger = _proxyLogger.create(`${handlerName}`) + const { dbg, trace } = _handlerLogger dbg({ subdomainFilter, urlFilters }) const _urlFilters = Array.isArray(urlFilters) @@ -105,6 +109,10 @@ export const proxyService = mkSingleton(async (config: ProxyServiceConfig) => { if (!host) { throw new Error(`Host not found`) } + const _requestLogger = _handlerLogger.create(host) + const { dbg, trace } = _requestLogger + _requestLogger.breadcrumb(req.method) + _requestLogger.breadcrumb(req.url) const [subdomain, ...junk] = host.split('.') if (!subdomain) { throw new Error(`${host} has no subdomain.`) @@ -133,7 +141,12 @@ export const proxyService = mkSingleton(async (config: ProxyServiceConfig) => { return } dbg(`${url} matches ${urlFilters}, sending to handler`) - return handler(req, res, { host, subdomain, coreInternalUrl, proxy }) + return handler( + req, + res, + { host, subdomain, coreInternalUrl, proxy }, + _requestLogger + ) }) } diff --git a/packages/daemon/src/services/RealtimeLog.ts b/packages/daemon/src/services/RealtimeLog.ts index 937ce047..29970245 100644 --- a/packages/daemon/src/services/RealtimeLog.ts +++ b/packages/daemon/src/services/RealtimeLog.ts @@ -1,9 +1,9 @@ import { PUBLIC_APP_DB } from '$src/constants' import { InstanceFields, - logger, mkSingleton, RecordId, + SingletonBaseConfig, } from '@pockethost/common' import Bottleneck from 'bottleneck' import { text } from 'node:stream/consumers' @@ -12,7 +12,7 @@ import { JsonifiableObject } from 'type-fest/source/jsonifiable' import { instanceLoggerService } from './InstanceLoggerService' import { proxyService } from './ProxyService' -export type RealtimeLogConfig = {} +export type RealtimeLogConfig = SingletonBaseConfig & {} const mkEvent = (name: string, data: JsonifiableObject) => { return `event: ${name}\ndata: ${JSON.stringify(data)}\n\n` @@ -20,20 +20,21 @@ const mkEvent = (name: string, data: JsonifiableObject) => { export type RealtimeLog = ReturnType export const realtimeLog = mkSingleton(async (config: RealtimeLogConfig) => { - const { dbg, error } = logger().create(`RealtimeLog.ts`) + const { logger } = config + const _realtimeLogger = logger.create(`RealtimeLog`) + const { dbg, error } = _realtimeLogger ;(await proxyService()).use( PUBLIC_APP_DB, '/logs', - async (req, res, meta) => { + async (req, res, meta, logger) => { const { subdomain, host, coreInternalUrl } = meta if (!req.url?.startsWith('/logs')) { return } - const { dbg, error, trace } = logger().create( - `RealtimeLog:${subdomain}:${host}` - ) + const _requestLogger = logger.create(`${subdomain}`) + const { dbg, error, trace } = _requestLogger const write = async (data: any) => { return new Promise((resolve) => { @@ -115,7 +116,9 @@ export const realtimeLog = mkSingleton(async (config: RealtimeLogConfig) => { /** * Get a database connection */ - const instanceLogger = await instanceLoggerService().get(instanceId) + const instanceLogger = await instanceLoggerService().get(instanceId, { + parentLogger: _requestLogger, + }) const { subscribe } = instanceLogger /** @@ -182,8 +185,6 @@ export const realtimeLog = mkSingleton(async (config: RealtimeLogConfig) => { }) .catch(error) } - - return true }, `RealtimeLogService` ) diff --git a/packages/daemon/src/services/RpcService/RpcService.ts b/packages/daemon/src/services/RpcService/RpcService.ts index 85425b85..61b3d722 100644 --- a/packages/daemon/src/services/RpcService/RpcService.ts +++ b/packages/daemon/src/services/RpcService/RpcService.ts @@ -1,12 +1,12 @@ import { clientService } from '$services' import { assertTruthy, - logger, mkSingleton, RpcCommands, RpcFields, RpcStatus, RPC_COMMANDS, + SingletonBaseConfig, } from '@pockethost/common' import { isObject } from '@s-libs/micro-dash' import Ajv, { JSONSchemaType, ValidateFunction } from 'ajv' @@ -29,10 +29,11 @@ export type RpcRunner< TResult extends JsonObject > = (job: RpcFields) => Promise -export type RpcServiceConfig = {} +export type RpcServiceConfig = SingletonBaseConfig & {} export const rpcService = mkSingleton(async (config: RpcServiceConfig) => { - const { dbg, error } = logger().create('RpcService') + const { logger } = config + const { dbg, error } = logger.create('RpcService') const { client } = await clientService() const limiter = new Bottleneck({ maxConcurrent: 1 }) diff --git a/packages/daemon/src/services/SqliteService/SqliteService.ts b/packages/daemon/src/services/SqliteService/SqliteService.ts index ebfc2fbf..18489fa9 100644 --- a/packages/daemon/src/services/SqliteService/SqliteService.ts +++ b/packages/daemon/src/services/SqliteService/SqliteService.ts @@ -1,8 +1,8 @@ import { createCleanupManager, createEvent, - logger, mkSingleton, + SingletonBaseConfig, } from '@pockethost/common' import Bottleneck from 'bottleneck' import { Database as SqliteDatabase, open } from 'sqlite' @@ -28,12 +28,13 @@ export type SqliteServiceApi = { cb: SqliteChangeHandler ) => SqliteUnsubscribe } -export type SqliteServiceConfig = {} +export type SqliteServiceConfig = SingletonBaseConfig & {} export type SqliteService = ReturnType export const sqliteService = mkSingleton((config: SqliteServiceConfig) => { - const { dbg, trace } = logger().create(`sqliteService`) + const { logger } = config + const { dbg, trace } = logger.create(`sqliteService`) const connections: { [_: string]: Promise } = {} const cm = createCleanupManager() @@ -41,7 +42,8 @@ export const sqliteService = mkSingleton((config: SqliteServiceConfig) => { const limiter = new Bottleneck({ maxConcurrent: 1 }) const getDatabase = async (filename: string): Promise => { - const { dbg } = logger().create(`sqliteService:${filename}`) + const _dbLogger = logger.create(`${filename}`) + const { dbg } = _dbLogger trace(`Fetching database for ${filename}`, connections) if (!connections[filename]) { diff --git a/packages/daemon/src/services/clientService/InstanceMIxin.ts b/packages/daemon/src/services/clientService/InstanceMIxin.ts index b8bd29b0..2c59a7b3 100644 --- a/packages/daemon/src/services/clientService/InstanceMIxin.ts +++ b/packages/daemon/src/services/clientService/InstanceMIxin.ts @@ -4,7 +4,6 @@ import { InstanceFields_Create, InstanceId, InstanceStatus, - logger, safeCatch, UserFields, } from '@pockethost/common' @@ -16,12 +15,14 @@ import { MixinContext } from './PbClient' export type InstanceApi = ReturnType export const createInstanceMixin = (context: MixinContext) => { - const { dbg, raw } = logger().create('InstanceMixin') + const { logger } = context + const { dbg, raw } = logger.create('InstanceMixin') const { client, rawDb } = context const createInstance = safeCatch( `createInstance`, + logger, (payload: InstanceFields_Create): Promise => { return client.collection('instances').create(payload) } @@ -29,6 +30,7 @@ export const createInstanceMixin = (context: MixinContext) => { const getInstanceBySubdomain = safeCatch( `getInstanceBySubdomain`, + logger, (subdomain: string): Promise<[InstanceFields, UserFields] | []> => client .collection('instances') @@ -46,6 +48,7 @@ export const createInstanceMixin = (context: MixinContext) => { const getInstanceById = safeCatch( `getInstanceById`, + logger, async ( instanceId: InstanceId ): Promise<[InstanceFields, UserFields] | []> => { @@ -66,6 +69,7 @@ export const createInstanceMixin = (context: MixinContext) => { const updateInstance = safeCatch( `updateInstance`, + logger, async (instanceId: InstanceId, fields: Partial) => { await client.collection('instances').update(instanceId, fields) } @@ -73,6 +77,7 @@ export const createInstanceMixin = (context: MixinContext) => { const updateInstanceStatus = safeCatch( `updateInstanceStatus`, + logger, async (instanceId: InstanceId, status: InstanceStatus) => { await updateInstance(instanceId, { status }) } @@ -80,17 +85,19 @@ export const createInstanceMixin = (context: MixinContext) => { const getInstance = safeCatch( `getInstance`, + logger, async (instanceId: InstanceId) => { return client.collection('instances').getOne(instanceId) } ) - const getInstances = safeCatch(`getInstances`, async () => { + const getInstances = safeCatch(`getInstances`, logger, async () => { return client.collection('instances').getFullList() }) const updateInstances = safeCatch( 'updateInstances', + logger, async (cb: (rec: InstanceFields) => Partial) => { const res = await client .collection('instances') @@ -116,6 +123,7 @@ export const createInstanceMixin = (context: MixinContext) => { const updateInstanceSeconds = safeCatch( `updateInstanceSeconds`, + logger, async (instanceId: InstanceId, forPeriod = new Date()) => { const startIso = startOfMonth(forPeriod).toISOString() const endIso = endOfMonth(forPeriod).toISOString() diff --git a/packages/daemon/src/services/clientService/InvocationMixin.ts b/packages/daemon/src/services/clientService/InvocationMixin.ts index 21d3ea11..f87e070a 100644 --- a/packages/daemon/src/services/clientService/InvocationMixin.ts +++ b/packages/daemon/src/services/clientService/InvocationMixin.ts @@ -1,7 +1,6 @@ import { InstanceFields, InvocationFields, - logger, pocketNow, safeCatch, } from '@pockethost/common' @@ -12,12 +11,14 @@ export const createInvocationMixin = ( context: MixinContext, instanceApi: InstanceApi ) => { - const { dbg } = logger().create('InvocationMixin') + const { logger } = context + const { dbg } = logger.create('InvocationMixin') const { client } = context const createInvocation = safeCatch( `createInvocation`, + logger, async (instance: InstanceFields, pid: number) => { const init: Partial = { startedAt: pocketNow(), @@ -34,6 +35,7 @@ export const createInvocationMixin = ( const pingInvocation = safeCatch( `pingInvocation`, + logger, async (invocation: InvocationFields) => { const totalSeconds = (+new Date() - Date.parse(invocation.startedAt)) / 1000 @@ -50,6 +52,7 @@ export const createInvocationMixin = ( const finalizeInvocation = safeCatch( `finalizeInvocation`, + logger, async (invocation: InvocationFields) => { dbg('finalizing') const totalSeconds = diff --git a/packages/daemon/src/services/clientService/PbClient.ts b/packages/daemon/src/services/clientService/PbClient.ts index 75ad6848..0d917fe8 100644 --- a/packages/daemon/src/services/clientService/PbClient.ts +++ b/packages/daemon/src/services/clientService/PbClient.ts @@ -1,8 +1,7 @@ import { DAEMON_PB_DATA_DIR, PUBLIC_APP_DB } from '$constants' -import { logger, safeCatch } from '@pockethost/common' +import { Logger, safeCatch } from '@pockethost/common' import { Knex } from 'knex' import { default as PocketBase, default as pocketbaseEs } from 'pocketbase' -import { createBackupMixin } from './BackupMixin' import { createInstanceMixin } from './InstanceMIxin' import { createInvocationMixin } from './InvocationMixin' import { createRawPbClient } from './RawPbClient' @@ -10,26 +9,30 @@ import { createRpcHelper } from './RpcHelper' export type PocketbaseClientApi = ReturnType -export type MixinContext = { client: pocketbaseEs; rawDb: Knex } +export type MixinContext = { client: pocketbaseEs; rawDb: Knex; logger: Logger } -export const createPbClient = (url: string) => { - const { info } = logger().create('PbClient') +export const createPbClient = (url: string, logger: Logger) => { + const _clientLogger = logger.create('PbClient') + const { info } = _clientLogger info(`Initializing client: ${url}`) const rawDb = createRawPbClient( - `${DAEMON_PB_DATA_DIR}/${PUBLIC_APP_DB}/pb_data/data.db` + `${DAEMON_PB_DATA_DIR}/${PUBLIC_APP_DB}/pb_data/data.db`, + _clientLogger ) const client = new PocketBase(url) const adminAuthViaEmail = safeCatch( `adminAuthViaEmail`, + _clientLogger, (email: string, password: string) => client.admins.authWithPassword(email, password) ) const createFirstAdmin = safeCatch( `createFirstAdmin`, + _clientLogger, (email: string, password: string) => client.admins .create({ email, password, passwordConfirm: password }) @@ -40,10 +43,9 @@ export const createPbClient = (url: string) => { }) ) - const context: MixinContext = { client, rawDb } + const context: MixinContext = { client, rawDb, logger: _clientLogger } const rpcApi = createRpcHelper(context) const instanceApi = createInstanceMixin(context) - const backupApi = createBackupMixin(context) const invocationApi = createInvocationMixin(context, instanceApi) const api = { @@ -55,7 +57,6 @@ export const createPbClient = (url: string) => { ...rpcApi, ...instanceApi, ...invocationApi, - ...backupApi, } return api diff --git a/packages/daemon/src/services/clientService/RawPbClient.ts b/packages/daemon/src/services/clientService/RawPbClient.ts index 4ebf336a..caaa1f4c 100644 --- a/packages/daemon/src/services/clientService/RawPbClient.ts +++ b/packages/daemon/src/services/clientService/RawPbClient.ts @@ -1,9 +1,9 @@ -import { logger } from '@pockethost/common' +import { Logger } from '@pockethost/common' import { existsSync } from 'fs' import knex from 'knex' -export const createRawPbClient = (filename: string) => { - const { dbg } = logger().create(`rawPbClient`) +export const createRawPbClient = (filename: string, logger: Logger) => { + const { dbg } = logger.create(`rawPbClient`) dbg(filename) if (!existsSync(filename)) { diff --git a/packages/daemon/src/services/clientService/RpcHelper.ts b/packages/daemon/src/services/clientService/RpcHelper.ts index ec0b748d..2212e6f9 100644 --- a/packages/daemon/src/services/clientService/RpcHelper.ts +++ b/packages/daemon/src/services/clientService/RpcHelper.ts @@ -18,9 +18,10 @@ export type RpcHelperConfig = MixinContext export type RpcHelper = ReturnType export const createRpcHelper = (config: RpcHelperConfig) => { - const { client, rawDb } = config + const { client, rawDb, logger } = config const onNewRpc = safeCatch( `onNewRpc`, + logger, async (cb: (e: RpcFields) => void) => { const unsub = await client .collection(RPC_COLLECTION) @@ -32,7 +33,7 @@ export const createRpcHelper = (config: RpcHelperConfig) => { } ) - const resetRpcs = safeCatch(`resetRpcs`, async () => + const resetRpcs = safeCatch(`resetRpcs`, logger, async () => rawDb(RPC_COLLECTION) .whereNotIn('status', [ RpcStatus.FinishedError, @@ -44,7 +45,7 @@ export const createRpcHelper = (config: RpcHelperConfig) => { }) ) - const incompleteRpcs = safeCatch(`incompleteRpcs`, async () => { + const incompleteRpcs = safeCatch(`incompleteRpcs`, logger, async () => { return client .collection(RPC_COLLECTION) .getFullList>(100, { @@ -54,6 +55,7 @@ export const createRpcHelper = (config: RpcHelperConfig) => { const rejectRpc = safeCatch( `rejectRpc`, + logger, async (rpc: RpcFields, err: Error) => { const fields: Partial> = { status: RpcStatus.FinishedError, @@ -67,6 +69,7 @@ export const createRpcHelper = (config: RpcHelperConfig) => { const setRpcStatus = safeCatch( `setRpcStatus`, + logger, async ( rpc: RpcFields, status: RpcStatus, diff --git a/packages/daemon/src/services/clientService/clientService.ts b/packages/daemon/src/services/clientService/clientService.ts index 3ae5fd79..25ff4c7e 100644 --- a/packages/daemon/src/services/clientService/clientService.ts +++ b/packages/daemon/src/services/clientService/clientService.ts @@ -5,12 +5,19 @@ import { PUBLIC_APP_DOMAIN, PUBLIC_APP_PROTOCOL, } from '$constants' -import { logger, mkSingleton } from '@pockethost/common' +import { Logger, mkSingleton } from '@pockethost/common' import { createPbClient } from './PbClient' -export const clientService = mkSingleton(async (url: string) => { - const { dbg, error } = logger().create(`client singleton`) - const client = createPbClient(url) +export type ClientServiceConfig = { + logger: Logger + url: string +} + +export const clientService = mkSingleton(async (cfg: ClientServiceConfig) => { + const { logger, url } = cfg + const _clientLogger = logger.create(`client singleton`) + const { dbg, error } = _clientLogger + const client = createPbClient(url, _clientLogger) try { await client.adminAuthViaEmail(DAEMON_PB_USERNAME, DAEMON_PB_PASSWORD) diff --git a/packages/daemon/src/services/index.ts b/packages/daemon/src/services/index.ts index 1c15a574..d769b958 100644 --- a/packages/daemon/src/services/index.ts +++ b/packages/daemon/src/services/index.ts @@ -1,4 +1,3 @@ -export * from './BackupService' export * from './clientService/clientService' export * from './clientService/PbClient' export * from './FtpService/FtpService' diff --git a/packages/daemon/src/util/downloadAndExtract.ts b/packages/daemon/src/util/downloadAndExtract.ts index 2bfec009..a2ad8db8 100644 --- a/packages/daemon/src/util/downloadAndExtract.ts +++ b/packages/daemon/src/util/downloadAndExtract.ts @@ -1,11 +1,15 @@ -import { logger } from '@pockethost/common' +import { Logger } from '@pockethost/common' import { chmodSync } from 'fs' import fetch from 'node-fetch' import { dirname } from 'path' import { Extract } from 'unzipper' -export const downloadAndExtract = async (url: string, binPath: string) => { - const { dbg, error } = logger().create('downloadAndExtract') +export const downloadAndExtract = async ( + url: string, + binPath: string, + logger: Logger +) => { + const { dbg, error } = logger.create('downloadAndExtract') await new Promise(async (resolve, reject) => { dbg(`Fetching ${url}`) diff --git a/packages/daemon/src/util/index.ts b/packages/daemon/src/util/index.ts index 5dfa10a3..94bcca56 100644 --- a/packages/daemon/src/util/index.ts +++ b/packages/daemon/src/util/index.ts @@ -1,4 +1,3 @@ -export * from './backupInstance' export * from './downloadAndExtract' export * from './ensureDirExists' export * from './env' diff --git a/packages/daemon/src/util/pexec.ts b/packages/daemon/src/util/pexec.ts index adcfed74..5c507532 100644 --- a/packages/daemon/src/util/pexec.ts +++ b/packages/daemon/src/util/pexec.ts @@ -1,19 +1,20 @@ -import { logger, safeCatch } from '@pockethost/common' +import { Logger, safeCatch } from '@pockethost/common' import { exec } from 'child_process' -export const pexec = safeCatch(`pexec`, (cmd: string) => { - const { dbg, error } = logger().create('pexec') - return new Promise((resolve, reject) => { - dbg(cmd) - exec(cmd, (err, stdout, stderr) => { - dbg(stdout) - if (err) { - error(`${err}`) - error(stderr) - reject(err) - return - } - resolve() +export const pexec = (logger: Logger) => + safeCatch(`pexec`, logger, (cmd: string) => { + const { dbg, error } = logger.create('pexec') + return new Promise((resolve, reject) => { + dbg(cmd) + exec(cmd, (err, stdout, stderr) => { + dbg(stdout) + if (err) { + error(`${err}`) + error(stderr) + reject(err) + return + } + resolve() + }) }) }) -}) diff --git a/packages/daemon/src/util/tryFetch.ts b/packages/daemon/src/util/tryFetch.ts index 4ebc0ded..ce2479bd 100644 --- a/packages/daemon/src/util/tryFetch.ts +++ b/packages/daemon/src/util/tryFetch.ts @@ -1,29 +1,32 @@ -import { logger, safeCatch } from '@pockethost/common' +import { Logger, safeCatch } from '@pockethost/common' -export const tryFetch = safeCatch( - `tryFetch`, - (url: string, preflight?: () => Promise) => { - const { dbg } = logger().create('tryFetch') - return new Promise((resolve, reject) => { - const tryFetch = async () => { - if (preflight) { - dbg(`Checking preflight`) - const shouldFetch = await preflight() - if (!shouldFetch) { - throw new Error(`tryFetch failed preflight, aborting`) +export const tryFetch = (logger: Logger) => + safeCatch( + `tryFetch`, + logger, + (url: string, preflight?: () => Promise) => { + const { dbg } = logger.create('tryFetch') + return new Promise((resolve, reject) => { + const tryFetch = async () => { + if (preflight) { + dbg(`Checking preflight`) + const shouldFetch = await preflight() + if (!shouldFetch) { + reject(new Error(`tryFetch failed preflight, aborting`)) + return + } + } + try { + dbg(`Trying to fetch ${url} `) + const res = await fetch(url) + dbg(`Fetch ${url} successful`) + resolve() + } catch (e) { + dbg(`Could not fetch ${url}, trying again in 1s`) + setTimeout(tryFetch, 1000) } } - try { - dbg(`Trying to fetch ${url} `) - const res = await fetch(url) - dbg(`Fetch ${url} successful`) - resolve() - } catch (e) { - dbg(`Could not fetch ${url}, trying again in 1s`) - setTimeout(tryFetch, 1000) - } - } - tryFetch() - }) - } -) + tryFetch() + }) + } + ) diff --git a/packages/pockethost.io/src/pocketbase/PocketbaseClient.ts b/packages/pockethost.io/src/pocketbase/PocketbaseClient.ts index c8bef993..812f2f34 100644 --- a/packages/pockethost.io/src/pocketbase/PocketbaseClient.ts +++ b/packages/pockethost.io/src/pocketbase/PocketbaseClient.ts @@ -44,7 +44,8 @@ export type PocketbaseClient = ReturnType export const createPocketbaseClient = (config: PocketbaseClientConfig) => { const { url } = config - const { dbg, error } = logger() + const _logger = logger() + const { dbg, error } = _logger const client = new PocketBase(url) @@ -56,7 +57,7 @@ export const createPocketbaseClient = (config: PocketbaseClientConfig) => { const logOut = () => authStore.clear() - const createUser = safeCatch(`createUser`, (email: string, password: string) => + const createUser = safeCatch(`createUser`, _logger, (email: string, password: string) => client .collection('users') .create({ @@ -70,7 +71,7 @@ export const createPocketbaseClient = (config: PocketbaseClientConfig) => { }) ) - const confirmVerification = safeCatch(`confirmVerification`, (token: string) => + const confirmVerification = safeCatch(`confirmVerification`, _logger, (token: string) => client .collection('users') .confirmVerification(token) @@ -79,7 +80,7 @@ export const createPocketbaseClient = (config: PocketbaseClientConfig) => { }) ) - const requestPasswordReset = safeCatch(`requestPasswordReset`, (email: string) => + const requestPasswordReset = safeCatch(`requestPasswordReset`, _logger, (email: string) => client .collection('users') .requestPasswordReset(email) @@ -90,6 +91,7 @@ export const createPocketbaseClient = (config: PocketbaseClientConfig) => { const requestPasswordResetConfirm = safeCatch( `requestPasswordResetConfirm`, + _logger, (token: string, password: string) => client .collection('users') @@ -99,11 +101,11 @@ export const createPocketbaseClient = (config: PocketbaseClientConfig) => { }) ) - const authViaEmail = safeCatch(`authViaEmail`, (email: string, password: string) => + const authViaEmail = safeCatch(`authViaEmail`, _logger, (email: string, password: string) => client.collection('users').authWithPassword(email, password) ) - const refreshAuthToken = safeCatch(`refreshAuthToken`, () => + const refreshAuthToken = safeCatch(`refreshAuthToken`, _logger, () => client.collection('users').authRefresh() ) @@ -123,6 +125,7 @@ export const createPocketbaseClient = (config: PocketbaseClientConfig) => { const getInstanceById = safeCatch( `getInstanceById`, + _logger, (id: InstanceId): Promise => client.collection('instances').getOne(id) ) @@ -145,7 +148,7 @@ export const createPocketbaseClient = (config: PocketbaseClientConfig) => { return map(e.data.data, (v, k) => (v ? v.message : undefined)).filter((v) => !!v) } - const resendVerificationEmail = safeCatch(`resendVerificationEmail`, async () => { + const resendVerificationEmail = safeCatch(`resendVerificationEmail`, _logger, async () => { const user = client.authStore.model assertExists(user, `Login required`) await client.collection('users').requestVerification(user.email) @@ -210,7 +213,7 @@ export const createPocketbaseClient = (config: PocketbaseClientConfig) => { unsub() return } - const _check = safeCatch(`_checkVerified`, refreshAuthToken) + const _check = safeCatch(`_checkVerified`, _logger, refreshAuthToken) setTimeout(_check, 1000) // FIXME - THIS DOES NOT WORK, WE HAVE TO POLL INSTEAD. FIX IN V0.8