chore(pockethost): improve CLI launching and logging

This commit is contained in:
Ben Allfree 2025-07-22 08:13:27 -07:00
parent 630e4ec8f8
commit 7946c8344e
12 changed files with 70 additions and 72 deletions

View File

@ -1,6 +1,6 @@
import { import {
DOCKER_INSTANCE_IMAGE_NAME, DOCKER_INSTANCE_IMAGE_NAME,
LoggerService, Logger,
MOTHERSHIP_ADMIN_PASSWORD, MOTHERSHIP_ADMIN_PASSWORD,
MOTHERSHIP_ADMIN_USERNAME, MOTHERSHIP_ADMIN_USERNAME,
MOTHERSHIP_URL, MOTHERSHIP_URL,
@ -8,18 +8,21 @@ import {
PocketbaseService, PocketbaseService,
discordAlert, discordAlert,
instanceService, instanceService,
neverendingPromise,
proxyService, proxyService,
realtimeLog, realtimeLog,
tryFetch, tryFetch,
} from '@' } from '@'
import Dockerode from 'dockerode' import Dockerode from 'dockerode'
import { ErrorRequestHandler } from 'express' import { ErrorRequestHandler } from 'express'
import { CronService } from 'src/services/CronService'
import { MothershipMirrorService } from 'src/services/MothershipMirrorService' import { MothershipMirrorService } from 'src/services/MothershipMirrorService'
export async function daemon() { export type DaemonOptions = {
const logger = LoggerService().create(`cli:daemon`) logger: Logger
const { info, warn } = logger }
export async function daemon({ logger }: DaemonOptions) {
const { info, warn } = logger.create(`daemon`)
info(`Starting`) info(`Starting`)
const docker = new Dockerode() const docker = new Dockerode()
@ -58,6 +61,8 @@ export async function daemon() {
await MothershipMirrorService({ client: (await MothershipAdminClientService()).client.client, logger }) await MothershipMirrorService({ client: (await MothershipAdminClientService()).client.client, logger })
await CronService({ logger })
await proxyService({ await proxyService({
coreInternalUrl: MOTHERSHIP_URL(), coreInternalUrl: MOTHERSHIP_URL(),
logger, logger,
@ -74,6 +79,4 @@ export async function daemon() {
res.status(500).send(err.toString()) res.status(500).send(err.toString())
} }
;(await proxyService()).use(errorHandler) ;(await proxyService()).use(errorHandler)
await neverendingPromise(logger)
} }

View File

@ -1,4 +1,4 @@
import { logger } from '@' import { LoggerService, neverendingPromise } from '@'
import { Command } from 'commander' import { Command } from 'commander'
import { daemon } from './daemon' import { daemon } from './daemon'
@ -8,8 +8,12 @@ type Options = {
export const ServeCommand = () => { export const ServeCommand = () => {
const cmd = new Command(`serve`).description(`Run an edge daemon server`).action(async (options: Options) => { const cmd = new Command(`serve`).description(`Run an edge daemon server`).action(async (options: Options) => {
logger().context({ cli: 'edge:daemon:serve' }) const logger = LoggerService().create(`cli:edge:daemon:serve`)
await daemon() const { info, warn } = logger
info(`Starting`)
await daemon({ logger })
await neverendingPromise(logger)
}) })
return cmd return cmd
} }

View File

@ -1,5 +1,5 @@
import { LoggerService } from '@' import { LoggerService } from '@'
import { MOTHERSHIP_URL, neverendingPromise, tryFetch } from '../../../../..' import { MOTHERSHIP_URL, tryFetch } from '../../../../..'
import { ftpService } from '../FtpService' import { ftpService } from '../FtpService'
export async function ftp() { export async function ftp() {
@ -13,6 +13,4 @@ export async function ftp() {
mothershipUrl: MOTHERSHIP_URL(), mothershipUrl: MOTHERSHIP_URL(),
logger, logger,
}) })
await neverendingPromise(logger)
} }

View File

@ -1,4 +1,4 @@
import { logger } from '@' import { LoggerService, neverendingPromise } from '@'
import { Command } from 'commander' import { Command } from 'commander'
import { ftp } from './ftp' import { ftp } from './ftp'
@ -8,8 +8,11 @@ type Options = {
export const ServeCommand = () => { export const ServeCommand = () => {
const cmd = new Command(`serve`).description(`Run an edge FTP server`).action(async (options: Options) => { const cmd = new Command(`serve`).description(`Run an edge FTP server`).action(async (options: Options) => {
logger().context({ cli: 'edge:ftp:serve' }) const logger = LoggerService().create(`cli:edge:ftp:serve`)
const { info } = logger
info(`Starting`)
await ftp() await ftp()
await neverendingPromise(logger)
}) })
return cmd return cmd
} }

View File

@ -1,10 +1,10 @@
import { logger } from '@' import { Logger } from '@'
import { Handler, Request } from 'express' import { Handler, Request } from 'express'
import { createProxyMiddleware } from 'http-proxy-middleware' import { createProxyMiddleware } from 'http-proxy-middleware'
import vhost from 'vhost' import vhost from 'vhost'
export function createVhostProxyMiddleware(host: string, target: string, ws = false): Handler { export function createVhostProxyMiddleware(host: string, target: string, ws = false, logger: Logger): Handler {
const { dbg } = logger() const { dbg } = logger
dbg(`Creating ${host}->${target}`) dbg(`Creating ${host}->${target}`)
const handler = createProxyMiddleware({ target, ws, changeOrigin: ws }) const handler = createProxyMiddleware({ target, ws, changeOrigin: ws })
return vhost(host, (_req, res, next) => { return vhost(host, (_req, res, next) => {

View File

@ -3,10 +3,9 @@ import {
DAEMON_PORT, DAEMON_PORT,
IPCIDR_LIST, IPCIDR_LIST,
IS_DEV, IS_DEV,
LoggerService, Logger,
MOTHERSHIP_NAME, MOTHERSHIP_NAME,
MOTHERSHIP_PORT, MOTHERSHIP_PORT,
neverendingPromise,
SSL_CERT, SSL_CERT,
SSL_KEY, SSL_KEY,
} from '@' } from '@'
@ -22,9 +21,12 @@ import https from 'https'
import { createIpWhitelistMiddleware } from './cidr' import { createIpWhitelistMiddleware } from './cidr'
import { createVhostProxyMiddleware } from './createVhostProxyMiddleware' import { createVhostProxyMiddleware } from './createVhostProxyMiddleware'
export const firewall = async () => { export type FirewallOptions = {
const logger = LoggerService().create(`cli:firewall:serve`) logger: Logger
const { dbg, error } = logger }
export const firewall = async ({ logger }: FirewallOptions) => {
const { dbg, error } = logger.create(`firewall`)
const PROD_ROUTES = { const PROD_ROUTES = {
[`${MOTHERSHIP_NAME()}.${APEX_DOMAIN()}`]: `http://localhost:${MOTHERSHIP_PORT()}`, [`${MOTHERSHIP_NAME()}.${APEX_DOMAIN()}`]: `http://localhost:${MOTHERSHIP_PORT()}`,
@ -53,7 +55,7 @@ export const firewall = async () => {
app.use(createIpWhitelistMiddleware(IPCIDR_LIST())) app.use(createIpWhitelistMiddleware(IPCIDR_LIST()))
forEach(hostnameRoutes, (target, host) => { forEach(hostnameRoutes, (target, host) => {
app.use(createVhostProxyMiddleware(host, target, IS_DEV())) app.use(createVhostProxyMiddleware(host, target, IS_DEV(), logger))
}) })
// Fall-through // Fall-through
@ -98,6 +100,4 @@ export const firewall = async () => {
httpsServer.listen(443, () => { httpsServer.listen(443, () => {
dbg('HTTPS server running on port 443') dbg('HTTPS server running on port 443')
}) })
await neverendingPromise(logger)
} }

View File

@ -1,4 +1,4 @@
import { logger } from '@' import { LoggerService, neverendingPromise } from '@'
import { Command } from 'commander' import { Command } from 'commander'
import { firewall } from './firewall/server' import { firewall } from './firewall/server'
@ -8,8 +8,11 @@ type Options = {
export const ServeCommand = () => { export const ServeCommand = () => {
const cmd = new Command(`serve`).description(`Serve the root firewall`).action(async (options: Options) => { const cmd = new Command(`serve`).description(`Serve the root firewall`).action(async (options: Options) => {
logger().context({ cli: 'firewall:serve' }) const logger = LoggerService().create(`cli:firewall:serve`)
await firewall() const { info, warn } = logger
info(`Starting`)
await firewall({ logger })
await neverendingPromise(logger)
}) })
return cmd return cmd
} }

View File

@ -1,4 +1,4 @@
import { logger } from '@' import { LoggerService } from '@'
import { Command } from 'commander' import { Command } from 'commander'
import { schema } from './schema' import { schema } from './schema'
@ -6,8 +6,8 @@ export const SchemaCommand = () => {
const cmd = new Command(`schema`) const cmd = new Command(`schema`)
.description(`Create snapshot of the current PocketHost mothership schema`) .description(`Create snapshot of the current PocketHost mothership schema`)
.action(async (options) => { .action(async (options) => {
logger().context({ cli: 'mothership:schema' }) const logger = LoggerService().create(`cli:mothership:schema`)
await schema() await schema({ logger })
}) })
return cmd return cmd
} }

View File

@ -1,16 +1,12 @@
import { import { GobotService, IS_DEV, Logger, MOTHERSHIP_DATA_ROOT, MOTHERSHIP_MIGRATIONS_DIR, MOTHERSHIP_SEMVER } from '@'
GobotService,
IS_DEV,
LoggerService,
MOTHERSHIP_DATA_ROOT,
MOTHERSHIP_MIGRATIONS_DIR,
MOTHERSHIP_SEMVER,
} from '@'
import { GobotOptions } from 'gobot' import { GobotOptions } from 'gobot'
export async function schema() { export type SchemaOptions = {
const logger = LoggerService().create(`MothershipSchema`) logger: Logger
const { dbg, error, info, warn } = logger }
export async function schema({ logger }: SchemaOptions) {
const { dbg, error, info, warn } = logger.create(`schema`)
info(`Starting`) info(`Starting`)
const options: Partial<GobotOptions> = { const options: Partial<GobotOptions> = {

View File

@ -2,19 +2,11 @@ import { LoggerService, neverendingPromise } from '@'
import { Command } from 'commander' import { Command } from 'commander'
import { mothership } from './mothership' import { mothership } from './mothership'
type Options = {
isolate: boolean
}
export const ServeCommand = () => { export const ServeCommand = () => {
const cmd = new Command(`serve`) const cmd = new Command(`serve`).description(`Run the PocketHost mothership`).action(async () => {
.description(`Run the PocketHost mothership`)
.option(`--isolate`, `Use Docker for process isolation.`, false)
.action(async (options: Options) => {
const logger = LoggerService().create(`cli:mothership:serve`) const logger = LoggerService().create(`cli:mothership:serve`)
const { dbg } = logger const { dbg } = logger
dbg({ options }) await mothership({ logger })
await mothership(options)
await neverendingPromise(logger) await neverendingPromise(logger)
}) })
return cmd return cmd

View File

@ -8,7 +8,7 @@ import {
exitHook, exitHook,
GobotService, GobotService,
IS_DEV, IS_DEV,
LoggerService, Logger,
LS_WEBHOOK_SECRET, LS_WEBHOOK_SECRET,
mkContainerHomePath, mkContainerHomePath,
MOTHERSHIP_CLOUDFLARE_ACCOUNT_ID, MOTHERSHIP_CLOUDFLARE_ACCOUNT_ID,
@ -25,11 +25,12 @@ import {
} from '@' } from '@'
import { GobotOptions } from 'gobot' import { GobotOptions } from 'gobot'
export type MothershipConfig = {} export type MothershipConfig = {
logger: Logger
}
export async function mothership(cfg: MothershipConfig) { export async function mothership({ logger }: MothershipConfig) {
const logger = LoggerService().create(`cli:mothership`) const { dbg, error, info, warn } = logger.create(`mothership`)
const { dbg, error, info, warn } = logger
info(`Starting`) info(`Starting`)
/** Launch central database */ /** Launch central database */
@ -77,10 +78,12 @@ export async function mothership(cfg: MothershipConfig) {
bot.run(args, { env, cwd: _MOTHERSHIP_APP_ROOT() }, (proc) => { bot.run(args, { env, cwd: _MOTHERSHIP_APP_ROOT() }, (proc) => {
proc.stdout.on('data', (data) => { proc.stdout.on('data', (data) => {
info(data.toString()) const lines = data.toString().split(`\n`) as string[]
lines.forEach((line) => info(line))
}) })
proc.stderr.on('data', (data) => { proc.stderr.on('data', (data) => {
error(data.toString()) const lines = data.toString().split(`\n`) as string[]
lines.forEach((line) => error(line))
}) })
proc.on('close', (code, signal) => { proc.on('close', (code, signal) => {
error(`Pocketbase exited with code ${code} and signal ${signal}`) error(`Pocketbase exited with code ${code} and signal ${signal}`)

View File

@ -4,21 +4,17 @@ import { daemon } from '../EdgeCommand/DaemonCommand/ServeCommand/daemon'
import { firewall } from '../FirewallCommand/ServeCommand/firewall/server' import { firewall } from '../FirewallCommand/ServeCommand/firewall/server'
import { mothership } from '../MothershipCommand/ServeCommand/mothership' import { mothership } from '../MothershipCommand/ServeCommand/mothership'
type Options = {
isolate: boolean
}
export const ServeCommand = () => { export const ServeCommand = () => {
const cmd = new Command(`serve`).description(`Run the entire PocketHost stack`).action(async (options: Options) => { const cmd = new Command(`serve`).description(`Run the entire PocketHost stack`).action(async () => {
const logger = LoggerService().create(`cli:serve`) const logger = LoggerService().create(`cli:serve`)
const { dbg, error, info, warn } = logger const { dbg, error, info, warn } = logger
info(`Starting`) info(`Starting`)
await mothership(options) await mothership({ logger })
dbg(`Mothership ready`) dbg(`Mothership ready`)
await daemon() await daemon({ logger })
dbg(`Daemon ready`) dbg(`Daemon ready`)
await firewall() await firewall({ logger })
dbg(`Firewall ready`) dbg(`Firewall ready`)
await neverendingPromise(logger) await neverendingPromise(logger)
}) })