mirror of
https://github.com/pockethost/pockethost.git
synced 2025-11-24 06:25:48 +00:00
chore(pockethost): remove winston
This commit is contained in:
parent
e1f90aa22b
commit
8ec5d00df4
@ -20,7 +20,7 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"check:types": "tsc --noEmit ",
|
"check:types": "tsc --noEmit ",
|
||||||
"dev": "NODE_ENV=development tsx --watch ./src/cli/index.ts",
|
"dev": "NODE_ENV=development tsx ./src/cli/index.ts",
|
||||||
"start": "tsx ./src/cli/index.ts"
|
"start": "tsx ./src/cli/index.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -30,6 +30,7 @@
|
|||||||
"bottleneck": "^2.19.5",
|
"bottleneck": "^2.19.5",
|
||||||
"commander": "^13.0.0",
|
"commander": "^13.0.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
"cron": "^4.3.2",
|
||||||
"devcert": "^1.2.2",
|
"devcert": "^1.2.2",
|
||||||
"dockerode": "^4.0.3",
|
"dockerode": "^4.0.3",
|
||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
@ -56,9 +57,7 @@
|
|||||||
"tail": "^2.2.6",
|
"tail": "^2.2.6",
|
||||||
"tsx": "^4.20.3",
|
"tsx": "^4.20.3",
|
||||||
"type-fest": "^4.32.0",
|
"type-fest": "^4.32.0",
|
||||||
"vhost": "^3.0.2",
|
"vhost": "^3.0.2"
|
||||||
"winston": "^3.17.0",
|
|
||||||
"winston-transport": "^4.9.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/better-sqlite3": "^7.6.12",
|
"@types/better-sqlite3": "^7.6.12",
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
DOCKER_INSTANCE_IMAGE_NAME,
|
DOCKER_INSTANCE_IMAGE_NAME,
|
||||||
|
LoggerService,
|
||||||
MOTHERSHIP_ADMIN_PASSWORD,
|
MOTHERSHIP_ADMIN_PASSWORD,
|
||||||
MOTHERSHIP_ADMIN_USERNAME,
|
MOTHERSHIP_ADMIN_USERNAME,
|
||||||
MOTHERSHIP_URL,
|
MOTHERSHIP_URL,
|
||||||
@ -7,7 +8,6 @@ import {
|
|||||||
PocketbaseService,
|
PocketbaseService,
|
||||||
discordAlert,
|
discordAlert,
|
||||||
instanceService,
|
instanceService,
|
||||||
logger,
|
|
||||||
neverendingPromise,
|
neverendingPromise,
|
||||||
proxyService,
|
proxyService,
|
||||||
realtimeLog,
|
realtimeLog,
|
||||||
@ -18,7 +18,8 @@ import { ErrorRequestHandler } from 'express'
|
|||||||
import { MothershipMirrorService } from 'src/services/MothershipMirrorService'
|
import { MothershipMirrorService } from 'src/services/MothershipMirrorService'
|
||||||
|
|
||||||
export async function daemon() {
|
export async function daemon() {
|
||||||
const { info, warn } = logger()
|
const logger = LoggerService().create(`cli:daemon`)
|
||||||
|
const { info, warn } = logger
|
||||||
info(`Starting`)
|
info(`Starting`)
|
||||||
|
|
||||||
const docker = new Dockerode()
|
const docker = new Dockerode()
|
||||||
@ -41,9 +42,9 @@ export async function daemon() {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
await PocketbaseService({})
|
await PocketbaseService({ logger })
|
||||||
|
|
||||||
await tryFetch(MOTHERSHIP_URL(`/api/health`), {})
|
await tryFetch(MOTHERSHIP_URL(`/api/health`), { logger })
|
||||||
|
|
||||||
info(`Serving`)
|
info(`Serving`)
|
||||||
|
|
||||||
@ -52,17 +53,20 @@ export async function daemon() {
|
|||||||
url: MOTHERSHIP_URL(),
|
url: MOTHERSHIP_URL(),
|
||||||
username: MOTHERSHIP_ADMIN_USERNAME(),
|
username: MOTHERSHIP_ADMIN_USERNAME(),
|
||||||
password: MOTHERSHIP_ADMIN_PASSWORD(),
|
password: MOTHERSHIP_ADMIN_PASSWORD(),
|
||||||
|
logger,
|
||||||
})
|
})
|
||||||
|
|
||||||
await MothershipMirrorService({ client: (await MothershipAdminClientService()).client.client })
|
await MothershipMirrorService({ client: (await MothershipAdminClientService()).client.client, logger })
|
||||||
|
|
||||||
await proxyService({
|
await proxyService({
|
||||||
coreInternalUrl: MOTHERSHIP_URL(),
|
coreInternalUrl: MOTHERSHIP_URL(),
|
||||||
|
logger,
|
||||||
})
|
})
|
||||||
await realtimeLog({})
|
await realtimeLog({ logger })
|
||||||
await instanceService({
|
await instanceService({
|
||||||
instanceApiCheckIntervalMs: 50,
|
instanceApiCheckIntervalMs: 50,
|
||||||
instanceApiTimeoutMs: 5000,
|
instanceApiTimeoutMs: 5000,
|
||||||
|
logger,
|
||||||
})
|
})
|
||||||
|
|
||||||
const errorHandler: ErrorRequestHandler = (err: Error, req, res, next) => {
|
const errorHandler: ErrorRequestHandler = (err: Error, req, res, next) => {
|
||||||
@ -71,5 +75,5 @@ export async function daemon() {
|
|||||||
}
|
}
|
||||||
;(await proxyService()).use(errorHandler)
|
;(await proxyService()).use(errorHandler)
|
||||||
|
|
||||||
await neverendingPromise()
|
await neverendingPromise(logger)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { InstanceFields, InstanceLogWriter, InstanceLogWriterApi, Logger, PocketBase, assert, seqid } from '@'
|
import { InstanceFields, InstanceLogWriterApi, Logger, PocketBase, assert, seqid } from '@'
|
||||||
import { compact, forEach, map } from '@s-libs/micro-dash'
|
import { compact, forEach, map } from '@s-libs/micro-dash'
|
||||||
import Bottleneck from 'bottleneck'
|
import Bottleneck from 'bottleneck'
|
||||||
import { spawn } from 'child_process'
|
import { spawn } from 'child_process'
|
||||||
@ -26,7 +26,7 @@ export type PathError = {
|
|||||||
const UNIX_SEP_REGEX = /\//g
|
const UNIX_SEP_REGEX = /\//g
|
||||||
const WIN_SEP_REGEX = /\\/g
|
const WIN_SEP_REGEX = /\\/g
|
||||||
|
|
||||||
const checkBun = (instance: InstanceFields, virtualPath: string, cwd: string) => {
|
const checkBun = (instance: InstanceFields, virtualPath: string, cwd: string, logger: Logger) => {
|
||||||
const [subdomain, maybeImportant, ...rest] = virtualPath.split('/').filter((p) => !!p)
|
const [subdomain, maybeImportant, ...rest] = virtualPath.split('/').filter((p) => !!p)
|
||||||
|
|
||||||
const isImportant =
|
const isImportant =
|
||||||
@ -34,9 +34,8 @@ const checkBun = (instance: InstanceFields, virtualPath: string, cwd: string) =>
|
|||||||
(rest.length === 0 && [`bun.lock`, `bun.lockb`, `package.json`].includes(maybeImportant || ''))
|
(rest.length === 0 && [`bun.lock`, `bun.lockb`, `package.json`].includes(maybeImportant || ''))
|
||||||
|
|
||||||
if (isImportant) {
|
if (isImportant) {
|
||||||
const logger = InstanceLogWriter(instance.id, instance.volume, `exec`)
|
|
||||||
logger.info(`${maybeImportant} changed, running bun install`)
|
logger.info(`${maybeImportant} changed, running bun install`)
|
||||||
launchBunInstall(instance, virtualPath, cwd).catch(logger.error)
|
launchBunInstall(instance, virtualPath, cwd, logger).catch(logger.error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +97,7 @@ const runBun = (() => {
|
|||||||
|
|
||||||
const launchBunInstall = (() => {
|
const launchBunInstall = (() => {
|
||||||
const runCache: { [key: string]: { runAgain: boolean } } = {}
|
const runCache: { [key: string]: { runAgain: boolean } } = {}
|
||||||
return async (instance: InstanceFields, virtualPath: string, cwd: string) => {
|
return async (instance: InstanceFields, virtualPath: string, cwd: string, logger: Logger) => {
|
||||||
if (cwd in runCache) {
|
if (cwd in runCache) {
|
||||||
runCache[cwd]!.runAgain = true
|
runCache[cwd]!.runAgain = true
|
||||||
return
|
return
|
||||||
@ -106,7 +105,6 @@ const launchBunInstall = (() => {
|
|||||||
runCache[cwd] = { runAgain: true }
|
runCache[cwd] = { runAgain: true }
|
||||||
while (runCache[cwd]!.runAgain) {
|
while (runCache[cwd]!.runAgain) {
|
||||||
runCache[cwd]!.runAgain = false
|
runCache[cwd]!.runAgain = false
|
||||||
const logger = InstanceLogWriter(instance.id, instance.volume, `exec`)
|
|
||||||
logger.info(`Launching 'bun install' in ${virtualPath}`)
|
logger.info(`Launching 'bun install' in ${virtualPath}`)
|
||||||
await prepPackageJson(cwd, logger)
|
await prepPackageJson(cwd, logger)
|
||||||
await runBun(cwd, logger)
|
await runBun(cwd, logger)
|
||||||
@ -229,7 +227,7 @@ export class PhFs implements FileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async list(path = '.') {
|
async list(path = '.') {
|
||||||
const { dbg, error } = this.log.create(`list`).breadcrumb({ cwd: this.cwd, path })
|
const { dbg, error } = this.log.create(`list`).breadcrumb(this.cwd).breadcrumb(path)
|
||||||
|
|
||||||
const { fsPath, instance } = await this._resolvePath(path)
|
const { fsPath, instance } = await this._resolvePath(path)
|
||||||
|
|
||||||
@ -278,7 +276,7 @@ export class PhFs implements FileSystem {
|
|||||||
async get(fileName: string): Promise<FileStat> {
|
async get(fileName: string): Promise<FileStat> {
|
||||||
const { fsPath, instance, clientPath } = await this._resolvePath(fileName)
|
const { fsPath, instance, clientPath } = await this._resolvePath(fileName)
|
||||||
|
|
||||||
const { dbg, error } = this.log.create(`get`).breadcrumb({ cwd: this.cwd, fileName, fsPath })
|
const { dbg, error } = this.log.create(`get`).breadcrumb(this.cwd).breadcrumb(fileName).breadcrumb(fsPath)
|
||||||
dbg(`get`)
|
dbg(`get`)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -319,7 +317,8 @@ export class PhFs implements FileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async write(fileName: string, options?: { append?: boolean | undefined; start?: any } | undefined) {
|
async write(fileName: string, options?: { append?: boolean | undefined; start?: any } | undefined) {
|
||||||
const { dbg, error } = this.log.create(`write`).breadcrumb({ cwd: this.cwd, fileName })
|
const logger = this.log.create(`write`).breadcrumb(this.cwd).breadcrumb(fileName)
|
||||||
|
const { dbg, error } = this.log.create(`write`).breadcrumb(this.cwd).breadcrumb(fileName)
|
||||||
dbg(`write`)
|
dbg(`write`)
|
||||||
|
|
||||||
const { fsPath, clientPath, instance } = await this._resolvePath(fileName)
|
const { fsPath, clientPath, instance } = await this._resolvePath(fileName)
|
||||||
@ -341,7 +340,7 @@ export class PhFs implements FileSystem {
|
|||||||
const virtualPath = join(this.cwd, fileName)
|
const virtualPath = join(this.cwd, fileName)
|
||||||
dbg(`write(${virtualPath}) closing`)
|
dbg(`write(${virtualPath}) closing`)
|
||||||
stream.end(() => {
|
stream.end(() => {
|
||||||
checkBun(instance, virtualPath, dirname(fsPath))
|
checkBun(instance, virtualPath, dirname(fsPath), logger)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
@ -351,7 +350,7 @@ export class PhFs implements FileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async read(fileName: string, options: { start?: any } | undefined): Promise<any> {
|
async read(fileName: string, options: { start?: any } | undefined): Promise<any> {
|
||||||
const { dbg, error } = this.log.create(`read`).breadcrumb({ cwd: this.cwd, fileName })
|
const { dbg, error } = this.log.create(`read`).breadcrumb(this.cwd).breadcrumb(fileName)
|
||||||
dbg(`read`)
|
dbg(`read`)
|
||||||
|
|
||||||
const { fsPath, clientPath } = await this._resolvePath(fileName)
|
const { fsPath, clientPath } = await this._resolvePath(fileName)
|
||||||
@ -373,7 +372,7 @@ export class PhFs implements FileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async delete(path: string) {
|
async delete(path: string) {
|
||||||
const { dbg, error } = this.log.create(`delete`).breadcrumb({ cwd: this.cwd, path })
|
const { dbg, error } = this.log.create(`delete`).breadcrumb(this.cwd).breadcrumb(path)
|
||||||
dbg(`delete`)
|
dbg(`delete`)
|
||||||
|
|
||||||
const { fsPath, instance } = await this._resolvePath(path)
|
const { fsPath, instance } = await this._resolvePath(path)
|
||||||
@ -388,7 +387,7 @@ export class PhFs implements FileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async mkdir(path: string) {
|
async mkdir(path: string) {
|
||||||
const { dbg, error } = this.log.create(`mkdir`).breadcrumb({ cwd: this.cwd, path })
|
const { dbg, error } = this.log.create(`mkdir`).breadcrumb(this.cwd).breadcrumb(path)
|
||||||
dbg(`mkdir`)
|
dbg(`mkdir`)
|
||||||
|
|
||||||
const { fsPath } = await this._resolvePath(path)
|
const { fsPath } = await this._resolvePath(path)
|
||||||
@ -397,7 +396,7 @@ export class PhFs implements FileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async rename(from: string, to: string) {
|
async rename(from: string, to: string) {
|
||||||
const { dbg, error } = this.log.create(`rename`).breadcrumb({ cwd: this.cwd, from, to })
|
const { dbg, error } = this.log.create(`rename`).breadcrumb(this.cwd).breadcrumb(from).breadcrumb(to)
|
||||||
dbg(`rename`)
|
dbg(`rename`)
|
||||||
|
|
||||||
const { fsPath: fromPath, instance } = await this._resolvePath(from)
|
const { fsPath: fromPath, instance } = await this._resolvePath(from)
|
||||||
@ -410,7 +409,7 @@ export class PhFs implements FileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async chmod(path: string, mode: Mode) {
|
async chmod(path: string, mode: Mode) {
|
||||||
const { dbg, error } = this.log.create(`chmod`).breadcrumb({ cwd: this.cwd, path, mode })
|
const { dbg, error } = this.log.create(`chmod`).breadcrumb(this.cwd).breadcrumb(path).breadcrumb(`${mode}`)
|
||||||
dbg(`chmod`)
|
dbg(`chmod`)
|
||||||
|
|
||||||
const { fsPath } = await this._resolvePath(path)
|
const { fsPath } = await this._resolvePath(path)
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import {
|
|||||||
PocketBase,
|
PocketBase,
|
||||||
SSL_CERT,
|
SSL_CERT,
|
||||||
SSL_KEY,
|
SSL_KEY,
|
||||||
|
SingletonBaseConfig,
|
||||||
asyncExitHook,
|
asyncExitHook,
|
||||||
logger,
|
logger,
|
||||||
mergeConfig,
|
mergeConfig,
|
||||||
@ -16,9 +17,9 @@ import { readFileSync } from 'fs'
|
|||||||
import { FtpSrv } from 'ftp-srv'
|
import { FtpSrv } from 'ftp-srv'
|
||||||
import { PhFs } from './PhFs'
|
import { PhFs } from './PhFs'
|
||||||
|
|
||||||
export type FtpConfig = { mothershipUrl: string }
|
export type FtpConfig = SingletonBaseConfig & { mothershipUrl: string }
|
||||||
|
|
||||||
export const ftpService = mkSingleton((config: Partial<FtpConfig> = {}) => {
|
export const ftpService = mkSingleton((config: FtpConfig) => {
|
||||||
const { mothershipUrl } = mergeConfig(
|
const { mothershipUrl } = mergeConfig(
|
||||||
{
|
{
|
||||||
mothershipUrl: MOTHERSHIP_URL(),
|
mothershipUrl: MOTHERSHIP_URL(),
|
||||||
|
|||||||
@ -1,16 +1,18 @@
|
|||||||
import { logger } from '@'
|
import { LoggerService } from '@'
|
||||||
import { MOTHERSHIP_URL, neverendingPromise, tryFetch } from '../../../../..'
|
import { MOTHERSHIP_URL, neverendingPromise, tryFetch } from '../../../../..'
|
||||||
import { ftpService } from '../FtpService'
|
import { ftpService } from '../FtpService'
|
||||||
|
|
||||||
export async function ftp() {
|
export async function ftp() {
|
||||||
const { info } = logger()
|
const logger = LoggerService().create(`cli:edge:ftp:serve`)
|
||||||
|
const { info } = logger
|
||||||
info(`Starting`)
|
info(`Starting`)
|
||||||
|
|
||||||
await tryFetch(MOTHERSHIP_URL(`/api/health`), {})
|
await tryFetch(MOTHERSHIP_URL(`/api/health`), {})
|
||||||
|
|
||||||
await ftpService({
|
await ftpService({
|
||||||
mothershipUrl: MOTHERSHIP_URL(),
|
mothershipUrl: MOTHERSHIP_URL(),
|
||||||
|
logger,
|
||||||
})
|
})
|
||||||
|
|
||||||
await neverendingPromise()
|
await neverendingPromise(logger)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
InstanceFields,
|
InstanceFields,
|
||||||
|
LoggerService,
|
||||||
mkInstanceDataPath,
|
mkInstanceDataPath,
|
||||||
MOTHERSHIP_ADMIN_PASSWORD,
|
MOTHERSHIP_ADMIN_PASSWORD,
|
||||||
MOTHERSHIP_ADMIN_USERNAME,
|
MOTHERSHIP_ADMIN_USERNAME,
|
||||||
@ -18,7 +19,10 @@ export const MigrateCommand = () => {
|
|||||||
.option(`-i, --instance <instanceId>`, `The instance to migrate`)
|
.option(`-i, --instance <instanceId>`, `The instance to migrate`)
|
||||||
.option(`-m, --mount-point <mountPoint>`, `The mount point`, `cloud-storage`)
|
.option(`-m, --mount-point <mountPoint>`, `The mount point`, `cloud-storage`)
|
||||||
.action(async (options) => {
|
.action(async (options) => {
|
||||||
console.log({ options })
|
const logger = LoggerService().create(`cli:edge:volume:migrate`)
|
||||||
|
const { dbg } = logger
|
||||||
|
dbg({ options })
|
||||||
|
|
||||||
const { instance: instanceId, mountPoint } = options
|
const { instance: instanceId, mountPoint } = options
|
||||||
|
|
||||||
const pb = new PocketBase(MOTHERSHIP_URL())
|
const pb = new PocketBase(MOTHERSHIP_URL())
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import {
|
|||||||
DAEMON_PORT,
|
DAEMON_PORT,
|
||||||
IPCIDR_LIST,
|
IPCIDR_LIST,
|
||||||
IS_DEV,
|
IS_DEV,
|
||||||
logger,
|
LoggerService,
|
||||||
MOTHERSHIP_NAME,
|
MOTHERSHIP_NAME,
|
||||||
MOTHERSHIP_PORT,
|
MOTHERSHIP_PORT,
|
||||||
neverendingPromise,
|
neverendingPromise,
|
||||||
@ -23,7 +23,8 @@ import { createIpWhitelistMiddleware } from './cidr'
|
|||||||
import { createVhostProxyMiddleware } from './createVhostProxyMiddleware'
|
import { createVhostProxyMiddleware } from './createVhostProxyMiddleware'
|
||||||
|
|
||||||
export const firewall = async () => {
|
export const firewall = async () => {
|
||||||
const { dbg, error } = logger()
|
const logger = LoggerService().create(`cli:firewall:serve`)
|
||||||
|
const { dbg, error } = logger
|
||||||
|
|
||||||
const PROD_ROUTES = {
|
const PROD_ROUTES = {
|
||||||
[`${MOTHERSHIP_NAME()}.${APEX_DOMAIN()}`]: `http://localhost:${MOTHERSHIP_PORT()}`,
|
[`${MOTHERSHIP_NAME()}.${APEX_DOMAIN()}`]: `http://localhost:${MOTHERSHIP_PORT()}`,
|
||||||
@ -98,5 +99,5 @@ export const firewall = async () => {
|
|||||||
dbg('HTTPS server running on port 443')
|
dbg('HTTPS server running on port 443')
|
||||||
})
|
})
|
||||||
|
|
||||||
await neverendingPromise()
|
await neverendingPromise(logger)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { logger } from '@'
|
import { LoggerService, neverendingPromise } from '@'
|
||||||
import { Command } from 'commander'
|
import { Command } from 'commander'
|
||||||
import { mothership } from './mothership'
|
import { mothership } from './mothership'
|
||||||
|
|
||||||
@ -11,9 +11,11 @@ export const ServeCommand = () => {
|
|||||||
.description(`Run the PocketHost mothership`)
|
.description(`Run the PocketHost mothership`)
|
||||||
.option(`--isolate`, `Use Docker for process isolation.`, false)
|
.option(`--isolate`, `Use Docker for process isolation.`, false)
|
||||||
.action(async (options: Options) => {
|
.action(async (options: Options) => {
|
||||||
logger().context({ cli: 'mothership:serve' })
|
const logger = LoggerService().create(`cli:mothership:serve`)
|
||||||
console.log({ options })
|
const { dbg } = logger
|
||||||
|
dbg({ options })
|
||||||
await mothership(options)
|
await mothership(options)
|
||||||
|
await neverendingPromise(logger)
|
||||||
})
|
})
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,16 @@
|
|||||||
import {
|
import {
|
||||||
|
_MOTHERSHIP_APP_ROOT,
|
||||||
APP_URL,
|
APP_URL,
|
||||||
DISCORD_ALERT_CHANNEL_URL,
|
DISCORD_ALERT_CHANNEL_URL,
|
||||||
DISCORD_HEALTH_CHANNEL_URL,
|
DISCORD_HEALTH_CHANNEL_URL,
|
||||||
DISCORD_STREAM_CHANNEL_URL,
|
DISCORD_STREAM_CHANNEL_URL,
|
||||||
DISCORD_TEST_CHANNEL_URL,
|
DISCORD_TEST_CHANNEL_URL,
|
||||||
|
exitHook,
|
||||||
GobotService,
|
GobotService,
|
||||||
IS_DEV,
|
IS_DEV,
|
||||||
LS_WEBHOOK_SECRET,
|
|
||||||
LoggerService,
|
LoggerService,
|
||||||
|
LS_WEBHOOK_SECRET,
|
||||||
|
mkContainerHomePath,
|
||||||
MOTHERSHIP_CLOUDFLARE_ACCOUNT_ID,
|
MOTHERSHIP_CLOUDFLARE_ACCOUNT_ID,
|
||||||
MOTHERSHIP_CLOUDFLARE_API_TOKEN,
|
MOTHERSHIP_CLOUDFLARE_API_TOKEN,
|
||||||
MOTHERSHIP_CLOUDFLARE_ZONE_ID,
|
MOTHERSHIP_CLOUDFLARE_ZONE_ID,
|
||||||
@ -16,16 +19,16 @@ import {
|
|||||||
MOTHERSHIP_MIGRATIONS_DIR,
|
MOTHERSHIP_MIGRATIONS_DIR,
|
||||||
MOTHERSHIP_PORT,
|
MOTHERSHIP_PORT,
|
||||||
MOTHERSHIP_SEMVER,
|
MOTHERSHIP_SEMVER,
|
||||||
|
MOTHERSHIP_URL,
|
||||||
TEST_EMAIL,
|
TEST_EMAIL,
|
||||||
_MOTHERSHIP_APP_ROOT,
|
tryFetch,
|
||||||
mkContainerHomePath,
|
|
||||||
} from '@'
|
} from '@'
|
||||||
import { GobotOptions } from 'gobot'
|
import { GobotOptions } from 'gobot'
|
||||||
|
|
||||||
export type MothershipConfig = {}
|
export type MothershipConfig = {}
|
||||||
|
|
||||||
export async function mothership(cfg: MothershipConfig) {
|
export async function mothership(cfg: MothershipConfig) {
|
||||||
const logger = LoggerService().create(`Mothership`)
|
const logger = LoggerService().create(`cli:mothership`)
|
||||||
const { dbg, error, info, warn } = logger
|
const { dbg, error, info, warn } = logger
|
||||||
info(`Starting`)
|
info(`Starting`)
|
||||||
|
|
||||||
@ -44,13 +47,13 @@ export async function mothership(cfg: MothershipConfig) {
|
|||||||
MOTHERSHIP_CLOUDFLARE_ZONE_ID: MOTHERSHIP_CLOUDFLARE_ZONE_ID(),
|
MOTHERSHIP_CLOUDFLARE_ZONE_ID: MOTHERSHIP_CLOUDFLARE_ZONE_ID(),
|
||||||
MOTHERSHIP_CLOUDFLARE_ACCOUNT_ID: MOTHERSHIP_CLOUDFLARE_ACCOUNT_ID(),
|
MOTHERSHIP_CLOUDFLARE_ACCOUNT_ID: MOTHERSHIP_CLOUDFLARE_ACCOUNT_ID(),
|
||||||
}
|
}
|
||||||
dbg(env)
|
dbg({ env })
|
||||||
|
|
||||||
const options: Partial<GobotOptions> = {
|
const options: Partial<GobotOptions> = {
|
||||||
version: MOTHERSHIP_SEMVER(),
|
version: MOTHERSHIP_SEMVER(),
|
||||||
env,
|
env,
|
||||||
}
|
}
|
||||||
dbg(`options`, options)
|
dbg({ options })
|
||||||
const { gobot } = GobotService()
|
const { gobot } = GobotService()
|
||||||
const bot = await gobot(`pocketbase`, options)
|
const bot = await gobot(`pocketbase`, options)
|
||||||
|
|
||||||
@ -71,6 +74,30 @@ export async function mothership(cfg: MothershipConfig) {
|
|||||||
args.push(`--dev`)
|
args.push(`--dev`)
|
||||||
}
|
}
|
||||||
dbg({ args })
|
dbg({ args })
|
||||||
const code = await bot.run(args, { env, cwd: _MOTHERSHIP_APP_ROOT() })
|
|
||||||
dbg({ code })
|
bot.run(args, { env, cwd: _MOTHERSHIP_APP_ROOT() }, (proc) => {
|
||||||
|
proc.stdout.on('data', (data) => {
|
||||||
|
info(data.toString())
|
||||||
|
})
|
||||||
|
proc.stderr.on('data', (data) => {
|
||||||
|
error(data.toString())
|
||||||
|
})
|
||||||
|
proc.on('close', (code, signal) => {
|
||||||
|
error(`Pocketbase exited with code ${code} and signal ${signal}`)
|
||||||
|
})
|
||||||
|
proc.on('error', (err) => {
|
||||||
|
error(`Pocketbase error: ${err}`)
|
||||||
|
})
|
||||||
|
proc.on('exit', (code, signal) => {
|
||||||
|
error(`Pocketbase exited with code ${code} and signal ${signal}`)
|
||||||
|
})
|
||||||
|
proc.on('message', (msg) => {
|
||||||
|
console.log(`***message`, msg)
|
||||||
|
})
|
||||||
|
exitHook(() => {
|
||||||
|
proc.kill()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const ready = tryFetch(MOTHERSHIP_URL(`/api/health`), { logger })
|
||||||
|
return ready
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { logger, neverendingPromise } from '@'
|
import { LoggerService, neverendingPromise } from '@'
|
||||||
import { Command } from 'commander'
|
import { Command } from 'commander'
|
||||||
import { daemon } from '../EdgeCommand/DaemonCommand/ServeCommand/daemon'
|
import { daemon } from '../EdgeCommand/DaemonCommand/ServeCommand/daemon'
|
||||||
import { firewall } from '../FirewallCommand/ServeCommand/firewall/server'
|
import { firewall } from '../FirewallCommand/ServeCommand/firewall/server'
|
||||||
@ -10,13 +10,17 @@ type Options = {
|
|||||||
|
|
||||||
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 (options: Options) => {
|
||||||
logger().context({ 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 Promise.all([mothership(options), daemon(), firewall()])
|
await mothership(options)
|
||||||
|
dbg(`Mothership ready`)
|
||||||
await neverendingPromise()
|
await daemon()
|
||||||
|
dbg(`Daemon ready`)
|
||||||
|
await firewall()
|
||||||
|
dbg(`Firewall ready`)
|
||||||
|
await neverendingPromise(logger)
|
||||||
})
|
})
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { GobotService, ioc, RegisterEnvSettingsService, WinstonLoggerService } from '@'
|
import { ConsoleLogger, DEBUG, GobotService, ioc, LogLevelName, RegisterEnvSettingsService } from '@'
|
||||||
import { version } from '../../package.json'
|
import { version } from '../../package.json'
|
||||||
|
|
||||||
export const initIoc = async () => {
|
export const initIoc = async () => {
|
||||||
const logger = await WinstonLoggerService({})
|
const logger = ConsoleLogger({ level: DEBUG() ? LogLevelName.Debug : LogLevelName.Info })
|
||||||
ioc('logger', logger.context('ph_version', version))
|
ioc('logger', logger.context('ph_version', version))
|
||||||
RegisterEnvSettingsService()
|
RegisterEnvSettingsService()
|
||||||
GobotService({})
|
GobotService({ logger })
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,45 +14,56 @@ export function ConsoleLogger(initialConfig: Partial<LoggerConfig> = {}): Logger
|
|||||||
let config: LoggerConfig = {
|
let config: LoggerConfig = {
|
||||||
level: LogLevelName.Info,
|
level: LogLevelName.Info,
|
||||||
pfx: [],
|
pfx: [],
|
||||||
|
breadcrumbs: [],
|
||||||
|
context: {},
|
||||||
...initialConfig,
|
...initialConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
function log(level: LogLevelName, ...args: any[]) {
|
function log(level: LogLevelName, args: any[]) {
|
||||||
if (isLevelGte(level, config.level)) {
|
if (isLevelGte(level, config.level)) {
|
||||||
const prefix = config.pfx.length > 0 ? `[${config.pfx.join(':')}] ` : ''
|
const prefix = config.pfx.length > 0 ? `[${config.pfx.join(':')}] ` : ''
|
||||||
CONSOLE_METHODS[level](`${prefix}${level.toUpperCase()}:`, ...args)
|
CONSOLE_METHODS[level](`${prefix}${level.toUpperCase()}:`, ...args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const breadcrumbs: string[] = []
|
||||||
|
const context: Record<string, string | number | undefined> = {}
|
||||||
|
const withBreadcrumbs = (args: any[]) => {
|
||||||
|
return [breadcrumbs.map((b) => `[${b}]`).join(' '), ...args]
|
||||||
|
}
|
||||||
|
|
||||||
const logger: Logger = {
|
const logger: Logger = {
|
||||||
raw(...args: any[]) {
|
raw(...args: any[]) {
|
||||||
log(LogLevelName.Raw, ...args)
|
log(LogLevelName.Raw, withBreadcrumbs(args))
|
||||||
},
|
},
|
||||||
trace(...args: any[]) {
|
trace(...args: any[]) {
|
||||||
log(LogLevelName.Trace, ...args)
|
log(LogLevelName.Trace, withBreadcrumbs(args))
|
||||||
},
|
},
|
||||||
debug(...args: any[]) {
|
debug(...args: any[]) {
|
||||||
log(LogLevelName.Debug, ...args)
|
log(LogLevelName.Debug, withBreadcrumbs(args))
|
||||||
},
|
},
|
||||||
dbg(...args: any[]) {
|
dbg(...args: any[]) {
|
||||||
logger.debug(...args)
|
logger.debug(...args)
|
||||||
},
|
},
|
||||||
info(...args: any[]) {
|
info(...args: any[]) {
|
||||||
log(LogLevelName.Info, ...args)
|
log(LogLevelName.Info, withBreadcrumbs(args))
|
||||||
},
|
},
|
||||||
warn(...args: any[]) {
|
warn(...args: any[]) {
|
||||||
log(LogLevelName.Warn, ...args)
|
log(LogLevelName.Warn, withBreadcrumbs(args))
|
||||||
},
|
},
|
||||||
error(...args: any[]) {
|
error(...args: any[]) {
|
||||||
log(LogLevelName.Error, ...args)
|
log(LogLevelName.Error, withBreadcrumbs(args))
|
||||||
},
|
},
|
||||||
criticalError(...args: any[]) {
|
criticalError(...args: any[]) {
|
||||||
logger.error('CRITICAL:', ...args)
|
logger.error('CRITICAL:', withBreadcrumbs(args))
|
||||||
},
|
},
|
||||||
create(name: string, configOverride?: Partial<LoggerConfig>): Logger {
|
create(name: string, configOverride?: Partial<LoggerConfig>): Logger {
|
||||||
const newConfig = {
|
const newConfig = {
|
||||||
...config,
|
...config,
|
||||||
|
breadcrumbs: [...breadcrumbs, name],
|
||||||
|
context: { ...context },
|
||||||
pfx: [...config.pfx, name],
|
pfx: [...config.pfx, name],
|
||||||
|
logger: logger,
|
||||||
...configOverride,
|
...configOverride,
|
||||||
}
|
}
|
||||||
return ConsoleLogger(newConfig)
|
return ConsoleLogger(newConfig)
|
||||||
@ -60,16 +71,22 @@ export function ConsoleLogger(initialConfig: Partial<LoggerConfig> = {}): Logger
|
|||||||
child(name: string): Logger {
|
child(name: string): Logger {
|
||||||
return logger.create(name)
|
return logger.create(name)
|
||||||
},
|
},
|
||||||
breadcrumb(s: object): Logger {
|
breadcrumb(s: string): Logger {
|
||||||
console.log('Breadcrumb:', s)
|
breadcrumbs.push(s)
|
||||||
return logger
|
return logger
|
||||||
},
|
},
|
||||||
context(name: string | object, value?: string | number): Logger {
|
context(name: string | object, value?: string | number): Logger {
|
||||||
console.log('Context:', name, value)
|
if (typeof name === `object`) {
|
||||||
|
Object.entries(name).forEach(([k, v]) => {
|
||||||
|
context[k] = v
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
context[name] = value
|
||||||
|
}
|
||||||
return logger
|
return logger
|
||||||
},
|
},
|
||||||
abort(...args: any[]): never {
|
abort(...args: any[]): never {
|
||||||
log(LogLevelName.Abort, ...args)
|
log(LogLevelName.Abort, withBreadcrumbs(args))
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
},
|
},
|
||||||
shutdown() {
|
shutdown() {
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import { ConsoleLogger } from './ConsoleLogger'
|
|||||||
export type LoggerConfig = {
|
export type LoggerConfig = {
|
||||||
level: LogLevelName
|
level: LogLevelName
|
||||||
pfx: string[]
|
pfx: string[]
|
||||||
|
breadcrumbs: string[]
|
||||||
|
context: Record<string, LoggerContextValue>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isLevelLte = (a: LogLevelName, b: LogLevelName) => {
|
export const isLevelLte = (a: LogLevelName, b: LogLevelName) => {
|
||||||
@ -48,6 +50,8 @@ export const LogLevels = {
|
|||||||
[LogLevelName.Abort]: 6,
|
[LogLevelName.Abort]: 6,
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
|
export type LoggerContextValue = string | number | undefined
|
||||||
|
|
||||||
export type Logger = {
|
export type Logger = {
|
||||||
raw: (...args: any[]) => void
|
raw: (...args: any[]) => void
|
||||||
dbg: (...args: any[]) => void
|
dbg: (...args: any[]) => void
|
||||||
@ -59,8 +63,8 @@ export type Logger = {
|
|||||||
child: (name: string) => Logger
|
child: (name: string) => Logger
|
||||||
trace: (...args: any[]) => void
|
trace: (...args: any[]) => void
|
||||||
debug: (...args: any[]) => void
|
debug: (...args: any[]) => void
|
||||||
breadcrumb: (s: object) => Logger
|
breadcrumb: (s: string) => Logger
|
||||||
context: (name: string | object, value?: string | number) => Logger
|
context: (name: string | object, value?: LoggerContextValue) => Logger
|
||||||
abort: (...args: any[]) => never
|
abort: (...args: any[]) => never
|
||||||
shutdown: () => void
|
shutdown: () => void
|
||||||
setLevel: (level: LogLevelName) => void
|
setLevel: (level: LogLevelName) => void
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
|
import { Logger } from './Logger'
|
||||||
|
|
||||||
export type SingletonApi = Object
|
export type SingletonApi = Object
|
||||||
|
|
||||||
export type SingletonBaseConfig = {}
|
export type SingletonBaseConfig = {
|
||||||
|
logger: Logger
|
||||||
|
}
|
||||||
|
|
||||||
export function mkSingleton<TConfig extends SingletonBaseConfig, TInstance extends SingletonApi>(
|
export function mkSingleton<TConfig extends SingletonBaseConfig, TInstance extends SingletonApi>(
|
||||||
factory: (config: TConfig) => TInstance
|
factory: (config: TConfig) => TInstance
|
||||||
|
|||||||
@ -19,6 +19,14 @@ export type InstanceSecretCollection = {
|
|||||||
[name: InstanceSecretKey]: InstanceSecretValue
|
[name: InstanceSecretKey]: InstanceSecretValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type InstanceWebhookEndpoint = string
|
||||||
|
export type InstanceWebhookValue = string
|
||||||
|
export type InstanceWebhookCollection = InstanceWebhookItem[]
|
||||||
|
export type InstanceWebhookItem = {
|
||||||
|
endpoint: InstanceWebhookEndpoint
|
||||||
|
value: InstanceWebhookValue
|
||||||
|
}
|
||||||
|
|
||||||
export type InstanceFields<TExtra = {}> = BaseFields & {
|
export type InstanceFields<TExtra = {}> = BaseFields & {
|
||||||
region: string
|
region: string
|
||||||
subdomain: Subdomain
|
subdomain: Subdomain
|
||||||
@ -26,6 +34,7 @@ export type InstanceFields<TExtra = {}> = BaseFields & {
|
|||||||
status: InstanceStatus
|
status: InstanceStatus
|
||||||
version: VersionId
|
version: VersionId
|
||||||
secrets: InstanceSecretCollection | null
|
secrets: InstanceSecretCollection | null
|
||||||
|
webhooks: InstanceWebhookCollection | null
|
||||||
power: boolean
|
power: boolean
|
||||||
suspension: string
|
suspension: string
|
||||||
syncAdmin: boolean
|
syncAdmin: boolean
|
||||||
|
|||||||
@ -3,7 +3,9 @@ import { InstanceFields, InstanceId } from '..'
|
|||||||
|
|
||||||
export type UpdateInstancePayload = {
|
export type UpdateInstancePayload = {
|
||||||
id: InstanceId
|
id: InstanceId
|
||||||
fields: Partial<Pick<InstanceFields, 'power' | 'secrets' | 'subdomain' | 'syncAdmin' | 'version' | 'dev' | 'cname'>>
|
fields: Partial<
|
||||||
|
Pick<InstanceFields, 'power' | 'secrets' | 'webhooks' | 'subdomain' | 'syncAdmin' | 'version' | 'dev' | 'cname'>
|
||||||
|
>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SECRET_KEY_REGEX = /^[A-Z][A-Z0-9_]*$/
|
export const SECRET_KEY_REGEX = /^[A-Z][A-Z0-9_]*$/
|
||||||
@ -37,6 +39,18 @@ export const UpdateInstancePayloadSchema: JSONSchemaType<UpdateInstancePayload>
|
|||||||
},
|
},
|
||||||
required: [],
|
required: [],
|
||||||
},
|
},
|
||||||
|
webhooks: {
|
||||||
|
type: 'array',
|
||||||
|
nullable: true,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
endpoint: { type: 'string' },
|
||||||
|
value: { type: 'string' },
|
||||||
|
},
|
||||||
|
required: ['endpoint', 'value'],
|
||||||
|
},
|
||||||
|
},
|
||||||
dev: { type: 'boolean', nullable: true },
|
dev: { type: 'boolean', nullable: true },
|
||||||
cname: { type: 'string', nullable: true },
|
cname: { type: 'string', nullable: true },
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,19 +0,0 @@
|
|||||||
import TransportStream from 'winston-transport'
|
|
||||||
import { discordAlert } from '..'
|
|
||||||
|
|
||||||
export type DiscordTransportType = {
|
|
||||||
webhookUrl: string
|
|
||||||
} & TransportStream.TransportStreamOptions
|
|
||||||
|
|
||||||
export class DiscordTransport extends TransportStream {
|
|
||||||
private url: string
|
|
||||||
constructor(opts: DiscordTransportType) {
|
|
||||||
super(opts)
|
|
||||||
this.url = opts.webhookUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
log(info: any, callback: any) {
|
|
||||||
discordAlert(info)
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { logger } from '@'
|
import { Logger } from '@'
|
||||||
import exitHook, { asyncExitHook as _, gracefulExit as __ } from 'exit-hook'
|
import exitHook, { asyncExitHook as _, gracefulExit as __ } from 'exit-hook'
|
||||||
|
|
||||||
export const asyncExitHook = (cb: () => Promise<any>) => _(cb, { wait: 5000 })
|
export const asyncExitHook = (cb: () => Promise<any>) => _(cb, { wait: 5000 })
|
||||||
@ -11,7 +11,7 @@ export const gracefulExit = async (signal?: number) => {
|
|||||||
}
|
}
|
||||||
export { exitHook }
|
export { exitHook }
|
||||||
|
|
||||||
export const neverendingPromise = () =>
|
export const neverendingPromise = (logger: Logger) =>
|
||||||
new Promise((resolve) => {
|
new Promise((resolve) => {
|
||||||
logger().dbg('Neverending promise')
|
logger.dbg('Neverending promise')
|
||||||
})
|
})
|
||||||
|
|||||||
@ -10,4 +10,3 @@ export * from './process'
|
|||||||
export * from './Settings'
|
export * from './Settings'
|
||||||
export * from './smartFetch'
|
export * from './smartFetch'
|
||||||
export * from './tryFetch'
|
export * from './tryFetch'
|
||||||
export * from './winston'
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { LoggerService } from '@'
|
import { Logger, LoggerService } from '@'
|
||||||
import fetch, { Response } from 'node-fetch'
|
import fetch, { Response } from 'node-fetch'
|
||||||
|
|
||||||
export const TRYFETCH_RETRY_MS = 50
|
export const TRYFETCH_RETRY_MS = 50
|
||||||
@ -8,6 +8,7 @@ export type TryFetchConfig = {
|
|||||||
preflight: () => Promise<boolean>
|
preflight: () => Promise<boolean>
|
||||||
retryMs: number
|
retryMs: number
|
||||||
timeoutMs: number
|
timeoutMs: number
|
||||||
|
logger: Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,14 +24,14 @@ export type TryFetchConfig = {
|
|||||||
* Note: tryFetch exits ONLY on success or a rejected preflight.
|
* Note: tryFetch exits ONLY on success or a rejected preflight.
|
||||||
*/
|
*/
|
||||||
export const tryFetch = async (url: string, config?: Partial<TryFetchConfig>) => {
|
export const tryFetch = async (url: string, config?: Partial<TryFetchConfig>) => {
|
||||||
const { preflight, retryMs, timeoutMs }: TryFetchConfig = {
|
const { preflight, retryMs, timeoutMs, logger }: TryFetchConfig = {
|
||||||
preflight: async () => true,
|
preflight: async () => true,
|
||||||
retryMs: TRYFETCH_RETRY_MS,
|
retryMs: TRYFETCH_RETRY_MS,
|
||||||
timeoutMs: TRYFETCH_TIMEOUT_MS,
|
timeoutMs: TRYFETCH_TIMEOUT_MS,
|
||||||
|
logger: config?.logger ?? LoggerService(),
|
||||||
...config,
|
...config,
|
||||||
}
|
}
|
||||||
const logger = LoggerService().create(`tryFetch`).breadcrumb({ url })
|
const { dbg } = logger.create(`tryFetch`).breadcrumb(url)
|
||||||
const { dbg } = logger
|
|
||||||
return new Promise<Response>((resolve, reject) => {
|
return new Promise<Response>((resolve, reject) => {
|
||||||
const again = () => setTimeout(_real_tryFetch, retryMs)
|
const again = () => setTimeout(_real_tryFetch, retryMs)
|
||||||
const _real_tryFetch = async () => {
|
const _real_tryFetch = async () => {
|
||||||
|
|||||||
@ -1,143 +0,0 @@
|
|||||||
import { asyncExitHook, DEBUG, DISCORD_ALERT_CHANNEL_URL, Logger, mkSingleton } from '@'
|
|
||||||
import { inspect } from 'node:util'
|
|
||||||
import winston from 'winston'
|
|
||||||
import { DiscordTransport } from './DiscordTransport'
|
|
||||||
|
|
||||||
const format = winston.format.combine(
|
|
||||||
winston.format.colorize(),
|
|
||||||
winston.format.printf(({ level, message, timestamp, ...meta }) => {
|
|
||||||
const final: string[] = []
|
|
||||||
// @ts-expect-error
|
|
||||||
;[...message, meta].forEach((m: string) => {
|
|
||||||
if (typeof m === 'string' && !!m.match(/\n/)) {
|
|
||||||
final.push(...m.split(/\n/))
|
|
||||||
} else if (typeof m === 'object') {
|
|
||||||
// Filter out Symbol properties and inspect the object
|
|
||||||
const filtered = Object.fromEntries(Object.entries(m).filter(([key]) => typeof key === 'string'))
|
|
||||||
final.push(
|
|
||||||
inspect(filtered, {
|
|
||||||
depth: null,
|
|
||||||
compact: true,
|
|
||||||
breakLength: Infinity,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
final.push(m)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return `${level}: ${final.join(' ')}`
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
export const WinstonLoggerService = mkSingleton<{}, Logger>(() => {
|
|
||||||
const logger = winston.createLogger({
|
|
||||||
format: winston.format.json(),
|
|
||||||
transports: [
|
|
||||||
new winston.transports.Console({
|
|
||||||
level: DEBUG() ? 'debug' : 'info',
|
|
||||||
format,
|
|
||||||
}),
|
|
||||||
new winston.transports.File({
|
|
||||||
filename: 'error.log',
|
|
||||||
level: 'error',
|
|
||||||
maxsize: 100 * 1024 * 1024,
|
|
||||||
maxFiles: 10,
|
|
||||||
tailable: true,
|
|
||||||
}),
|
|
||||||
new winston.transports.File({
|
|
||||||
filename: 'debug.log',
|
|
||||||
level: 'debug',
|
|
||||||
maxsize: 100 * 1024 * 1024,
|
|
||||||
maxFiles: 10,
|
|
||||||
tailable: true,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
rejectionHandlers: [
|
|
||||||
new winston.transports.Console({
|
|
||||||
level: 'error',
|
|
||||||
format,
|
|
||||||
}),
|
|
||||||
new winston.transports.File({
|
|
||||||
filename: 'rejections.log',
|
|
||||||
maxsize: 100 * 1024 * 1024,
|
|
||||||
maxFiles: 10,
|
|
||||||
tailable: true,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
exceptionHandlers: [
|
|
||||||
new winston.transports.Console({
|
|
||||||
level: 'error',
|
|
||||||
format,
|
|
||||||
}),
|
|
||||||
new winston.transports.File({
|
|
||||||
filename: 'exceptions.log',
|
|
||||||
maxsize: 100 * 1024 * 1024,
|
|
||||||
maxFiles: 10,
|
|
||||||
tailable: true,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
defaultMeta: {},
|
|
||||||
})
|
|
||||||
logger.exitOnError = true
|
|
||||||
|
|
||||||
asyncExitHook(async () => {
|
|
||||||
console.log('Closing Winston logger outside')
|
|
||||||
return new Promise<void>((resolve) => {
|
|
||||||
console.log('Closing Winston logger inside promise')
|
|
||||||
setTimeout(() => {
|
|
||||||
console.log('Closing Winston logger inside timeout')
|
|
||||||
logger.close()
|
|
||||||
resolve()
|
|
||||||
}, 2000)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
{
|
|
||||||
const url = DISCORD_ALERT_CHANNEL_URL()
|
|
||||||
if (url) {
|
|
||||||
logger.add(new DiscordTransport({ level: 'error', webhookUrl: url }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const createApi = (logger: winston.Logger): Logger => {
|
|
||||||
const api: Logger = {
|
|
||||||
create: (name: string) => {
|
|
||||||
return createApi(logger.child({ ...logger.defaultMeta, name }))
|
|
||||||
},
|
|
||||||
raw: (...args: any[]) => logger.silly(args),
|
|
||||||
dbg: (...args: any[]) => logger.debug(args),
|
|
||||||
warn: (...args: any[]) => logger.warn(args),
|
|
||||||
info: (...args: any[]) => logger.info(args),
|
|
||||||
error: (...args: any[]) => logger.error(args),
|
|
||||||
criticalError: (...args: any[]) => logger.error(args),
|
|
||||||
setLevel: (level) => {},
|
|
||||||
trace: (...args: any[]) => logger.silly(args),
|
|
||||||
debug: (...args: any[]) => logger.debug(args),
|
|
||||||
breadcrumb: (s) => {
|
|
||||||
Object.assign(logger.defaultMeta, s)
|
|
||||||
return api
|
|
||||||
},
|
|
||||||
context: (name: string | object, value?: string | number) => {
|
|
||||||
if (typeof name === 'string') {
|
|
||||||
if (value !== undefined) {
|
|
||||||
logger.defaultMeta[name] = value
|
|
||||||
} else {
|
|
||||||
delete logger.defaultMeta[name]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Object.assign(logger.defaultMeta, name)
|
|
||||||
}
|
|
||||||
return api
|
|
||||||
},
|
|
||||||
shutdown: () => {},
|
|
||||||
child: (name) => api.create(name),
|
|
||||||
abort: (...args) => {
|
|
||||||
logger.error(args)
|
|
||||||
process.exit(1)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return api
|
|
||||||
}
|
|
||||||
|
|
||||||
return createApi(logger)
|
|
||||||
})
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { ensureInstanceDirectoryStructure, logger, LoggerService, mkInstanceDataPath, stringify } from '@'
|
import { ensureInstanceDirectoryStructure, Logger, mkInstanceDataPath, stringify } from '@'
|
||||||
import Bottleneck from 'bottleneck'
|
import Bottleneck from 'bottleneck'
|
||||||
import { existsSync } from 'fs'
|
import { existsSync } from 'fs'
|
||||||
import { appendFile, cp, stat, truncate } from 'fs/promises'
|
import { appendFile, cp, stat, truncate } from 'fs/promises'
|
||||||
@ -74,8 +74,8 @@ const MultiChannelLimiter = () => {
|
|||||||
|
|
||||||
const limiter = MultiChannelLimiter()
|
const limiter = MultiChannelLimiter()
|
||||||
|
|
||||||
export function InstanceLogWriter(instanceId: string, volume: string, target: string) {
|
export function InstanceLogWriter(instanceId: string, volume: string, target: string, logger: Logger) {
|
||||||
const lgr = logger().create(`InstanceLogWriter`).breadcrumb({ instanceId, target })
|
const lgr = logger.create(`InstanceLogWriter`).breadcrumb(`${instanceId}-${target}`)
|
||||||
const { dbg, info, error, warn } = lgr
|
const { dbg, info, error, warn } = lgr
|
||||||
|
|
||||||
ensureInstanceDirectoryStructure(instanceId, volume, lgr)
|
ensureInstanceDirectoryStructure(instanceId, volume, lgr)
|
||||||
@ -124,9 +124,8 @@ export function InstanceLogWriter(instanceId: string, volume: string, target: st
|
|||||||
return api
|
return api
|
||||||
}
|
}
|
||||||
|
|
||||||
export function InstanceLogReader(instanceId: string, volume: string, target: string) {
|
export function InstanceLogReader(instanceId: string, volume: string, target: string, logger: Logger) {
|
||||||
const logger = LoggerService().create(instanceId).breadcrumb({ target })
|
const { dbg, info, error, warn } = logger.create(`InstanceLogReader`).breadcrumb(`${instanceId}-${target}`)
|
||||||
const { dbg, info, error, warn } = logger
|
|
||||||
|
|
||||||
ensureInstanceDirectoryStructure(instanceId, volume, logger)
|
ensureInstanceDirectoryStructure(instanceId, volume, logger)
|
||||||
|
|
||||||
|
|||||||
@ -48,7 +48,7 @@ export type InstanceServiceConfig = SingletonBaseConfig & {
|
|||||||
|
|
||||||
export type InstanceServiceApi = AsyncReturnType<typeof instanceService>
|
export type InstanceServiceApi = AsyncReturnType<typeof instanceService>
|
||||||
export const instanceService = mkSingleton(async (config: InstanceServiceConfig) => {
|
export const instanceService = mkSingleton(async (config: InstanceServiceConfig) => {
|
||||||
const instanceServiceLogger = LoggerService().create('InstanceService')
|
const instanceServiceLogger = (config.logger ?? LoggerService()).create('InstanceService')
|
||||||
const { dbg, raw, error, warn } = instanceServiceLogger
|
const { dbg, raw, error, warn } = instanceServiceLogger
|
||||||
const { client } = await MothershipAdminClientService()
|
const { client } = await MothershipAdminClientService()
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ export const instanceService = mkSingleton(async (config: InstanceServiceConfig)
|
|||||||
const { id, subdomain, version } = instance
|
const { id, subdomain, version } = instance
|
||||||
const systemInstanceLogger = instanceServiceLogger.create(`${subdomain}:${id}:${version}`)
|
const systemInstanceLogger = instanceServiceLogger.create(`${subdomain}:${id}:${version}`)
|
||||||
const { dbg, warn, error, info, trace } = systemInstanceLogger
|
const { dbg, warn, error, info, trace } = systemInstanceLogger
|
||||||
const userInstanceLogger = InstanceLogWriter(instance.id, instance.volume, `exec`)
|
const userInstanceLogger = InstanceLogWriter(instance.id, instance.volume, `exec`, systemInstanceLogger)
|
||||||
|
|
||||||
shutdownManager.push(() => {
|
shutdownManager.push(() => {
|
||||||
dbg(`Shutting down`)
|
dbg(`Shutting down`)
|
||||||
@ -140,6 +140,7 @@ export const instanceService = mkSingleton(async (config: InstanceServiceConfig)
|
|||||||
PH_INSTANCE_URL: mkInstanceUrl(instance),
|
PH_INSTANCE_URL: mkInstanceUrl(instance),
|
||||||
},
|
},
|
||||||
version,
|
version,
|
||||||
|
logger: systemInstanceLogger,
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Add admin sync info if enabled */
|
/** Add admin sync info if enabled */
|
||||||
@ -183,6 +184,7 @@ export const instanceService = mkSingleton(async (config: InstanceServiceConfig)
|
|||||||
if (stopped()) throw new Error(`Container stopped ${id}`)
|
if (stopped()) throw new Error(`Container stopped ${id}`)
|
||||||
return started()
|
return started()
|
||||||
},
|
},
|
||||||
|
logger: systemInstanceLogger,
|
||||||
})
|
})
|
||||||
|
|
||||||
/** Idle check */
|
/** Idle check */
|
||||||
@ -234,7 +236,7 @@ export const instanceService = mkSingleton(async (config: InstanceServiceConfig)
|
|||||||
const mirror = await MothershipMirrorService()
|
const mirror = await MothershipMirrorService()
|
||||||
|
|
||||||
;(await proxyService()).use(async (req, res, next) => {
|
;(await proxyService()).use(async (req, res, next) => {
|
||||||
const logger = LoggerService().create(`InstanceRequest`)
|
const logger = (config.logger ?? LoggerService()).create(`InstanceRequest`)
|
||||||
|
|
||||||
const { dbg } = logger
|
const { dbg } = logger
|
||||||
|
|
||||||
|
|||||||
@ -11,32 +11,42 @@ import {
|
|||||||
import { forEach } from '@s-libs/micro-dash'
|
import { forEach } from '@s-libs/micro-dash'
|
||||||
|
|
||||||
export const mkInstanceCache = (client: PocketBase) => {
|
export const mkInstanceCache = (client: PocketBase) => {
|
||||||
const { dbg } = LoggerService().create(`InstanceCache`)
|
const { dbg, error } = LoggerService().create(`InstanceCache`)
|
||||||
|
|
||||||
const cache: { [_: InstanceId]: InstanceFields_WithUser | undefined } = {}
|
const cache: { [_: InstanceId]: InstanceFields_WithUser | undefined } = {}
|
||||||
const byUid: {
|
const byUid: {
|
||||||
[_: UserId]: { [_: InstanceId]: InstanceFields_WithUser }
|
[_: UserId]: { [_: InstanceId]: InstanceFields_WithUser }
|
||||||
} = {}
|
} = {}
|
||||||
|
|
||||||
client.collection(`users`).subscribe<UserFields>(`*`, (e) => {
|
client
|
||||||
const { action, record } = e
|
.collection(`users`)
|
||||||
if ([`create`, `update`].includes(action)) {
|
.subscribe<UserFields>(`*`, (e) => {
|
||||||
dbg({ action, record })
|
|
||||||
updateUser(record)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
client.collection(INSTANCE_COLLECTION).subscribe<InstanceFields_WithUser>(
|
|
||||||
`*`,
|
|
||||||
(e) => {
|
|
||||||
const { action, record } = e
|
const { action, record } = e
|
||||||
if ([`create`, `update`].includes(action)) {
|
if ([`create`, `update`].includes(action)) {
|
||||||
setItem(record)
|
|
||||||
dbg({ action, record })
|
dbg({ action, record })
|
||||||
|
updateUser(record)
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
{ expand: 'uid' }
|
.catch((e) => {
|
||||||
)
|
error(e)
|
||||||
|
})
|
||||||
|
|
||||||
|
client
|
||||||
|
.collection(INSTANCE_COLLECTION)
|
||||||
|
.subscribe<InstanceFields_WithUser>(
|
||||||
|
`*`,
|
||||||
|
(e) => {
|
||||||
|
const { action, record } = e
|
||||||
|
if ([`create`, `update`].includes(action)) {
|
||||||
|
setItem(record)
|
||||||
|
dbg({ action, record })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ expand: 'uid' }
|
||||||
|
)
|
||||||
|
.catch((e) => {
|
||||||
|
error(e)
|
||||||
|
})
|
||||||
|
|
||||||
function blankItem(host: string) {
|
function blankItem(host: string) {
|
||||||
cache[host] = undefined
|
cache[host] = undefined
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import {
|
|||||||
GetUserTokenPayload,
|
GetUserTokenPayload,
|
||||||
GetUserTokenPayloadSchema,
|
GetUserTokenPayloadSchema,
|
||||||
GetUserTokenResult,
|
GetUserTokenResult,
|
||||||
LoggerService,
|
Logger,
|
||||||
PocketBase,
|
PocketBase,
|
||||||
RestCommands,
|
RestCommands,
|
||||||
RestMethods,
|
RestMethods,
|
||||||
@ -14,8 +14,8 @@ import { createInstanceMixin } from './InstanceMIxin'
|
|||||||
|
|
||||||
export type PocketbaseClientApi = ReturnType<typeof createAdminPbClient>
|
export type PocketbaseClientApi = ReturnType<typeof createAdminPbClient>
|
||||||
|
|
||||||
export const createAdminPbClient = (url: string) => {
|
export const createAdminPbClient = (url: string, logger: Logger) => {
|
||||||
const _clientLogger = LoggerService().create('PbClient')
|
const _clientLogger = logger.create('PbClient')
|
||||||
const { info } = _clientLogger
|
const { info } = _clientLogger
|
||||||
|
|
||||||
info(`Initializing client: ${url}`)
|
info(`Initializing client: ${url}`)
|
||||||
|
|||||||
@ -5,15 +5,17 @@ import {
|
|||||||
MOTHERSHIP_ADMIN_USERNAME,
|
MOTHERSHIP_ADMIN_USERNAME,
|
||||||
MOTHERSHIP_URL,
|
MOTHERSHIP_URL,
|
||||||
PocketBase,
|
PocketBase,
|
||||||
|
SingletonBaseConfig,
|
||||||
mergeConfig,
|
mergeConfig,
|
||||||
mkSingleton,
|
mkSingleton,
|
||||||
} from '@'
|
} from '@'
|
||||||
import { createAdminPbClient } from './createAdminPbClient'
|
import { createAdminPbClient } from './createAdminPbClient'
|
||||||
|
|
||||||
export type ClientServiceConfig = {
|
export type ClientServiceConfig = SingletonBaseConfig & {
|
||||||
url: string
|
url: string
|
||||||
username: string
|
username: string
|
||||||
password: string
|
password: string
|
||||||
|
logger: Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MixinContext = {
|
export type MixinContext = {
|
||||||
@ -21,18 +23,18 @@ export type MixinContext = {
|
|||||||
logger: Logger
|
logger: Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MothershipAdminClientService = mkSingleton(async (cfg: Partial<ClientServiceConfig> = {}) => {
|
export const MothershipAdminClientService = mkSingleton(async (cfg: ClientServiceConfig) => {
|
||||||
const { url, username, password } = mergeConfig<ClientServiceConfig>(
|
const { url, username, password, logger } = mergeConfig<ClientServiceConfig>(
|
||||||
{
|
{
|
||||||
url: MOTHERSHIP_URL(),
|
url: MOTHERSHIP_URL(),
|
||||||
username: MOTHERSHIP_ADMIN_USERNAME(),
|
username: MOTHERSHIP_ADMIN_USERNAME(),
|
||||||
password: MOTHERSHIP_ADMIN_PASSWORD(),
|
password: MOTHERSHIP_ADMIN_PASSWORD(),
|
||||||
|
logger: LoggerService(),
|
||||||
},
|
},
|
||||||
cfg
|
cfg
|
||||||
)
|
)
|
||||||
const _clientLogger = LoggerService().create(`client singleton`)
|
const { dbg, error } = logger.create(`MothershipAdminClientService`)
|
||||||
const { dbg, error } = _clientLogger
|
const client = createAdminPbClient(url, logger)
|
||||||
const client = createAdminPbClient(url)
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -16,7 +16,7 @@ export type MothershipMirrorServiceConfig = SingletonBaseConfig & {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const MothershipMirrorService = mkSingleton(async (config: MothershipMirrorServiceConfig) => {
|
export const MothershipMirrorService = mkSingleton(async (config: MothershipMirrorServiceConfig) => {
|
||||||
const { dbg, error } = LoggerService().create(`MothershipMirrorService`)
|
const { dbg, error } = (config.logger ?? LoggerService()).create(`MothershipMirrorService`)
|
||||||
|
|
||||||
const client = config.client
|
const client = config.client
|
||||||
|
|
||||||
@ -117,6 +117,7 @@ export const MothershipMirrorService = mkSingleton(async (config: MothershipMirr
|
|||||||
upsertInstance(instance)
|
upsertInstance(instance)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
.catch(error)
|
||||||
const usersPromise = client
|
const usersPromise = client
|
||||||
.collection(`users`)
|
.collection(`users`)
|
||||||
.getFullList<UserFields>()
|
.getFullList<UserFields>()
|
||||||
@ -126,6 +127,7 @@ export const MothershipMirrorService = mkSingleton(async (config: MothershipMirr
|
|||||||
upsertUser(user)
|
upsertUser(user)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
.catch(error)
|
||||||
await Promise.all([instancesPromise, usersPromise])
|
await Promise.all([instancesPromise, usersPromise])
|
||||||
}
|
}
|
||||||
await init().catch(error)
|
await init().catch(error)
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import {
|
|||||||
APEX_DOMAIN,
|
APEX_DOMAIN,
|
||||||
GobotService,
|
GobotService,
|
||||||
InstanceLogWriter,
|
InstanceLogWriter,
|
||||||
|
Logger,
|
||||||
LoggerService,
|
LoggerService,
|
||||||
SingletonBaseConfig,
|
SingletonBaseConfig,
|
||||||
asyncExitHook,
|
asyncExitHook,
|
||||||
@ -30,6 +31,7 @@ export type SpawnConfig = {
|
|||||||
stdout?: MemoryStream
|
stdout?: MemoryStream
|
||||||
stderr?: MemoryStream
|
stderr?: MemoryStream
|
||||||
dev?: boolean
|
dev?: boolean
|
||||||
|
logger: Logger
|
||||||
}
|
}
|
||||||
export type PocketbaseServiceApi = AsyncReturnType<typeof createPocketbaseService>
|
export type PocketbaseServiceApi = AsyncReturnType<typeof createPocketbaseService>
|
||||||
|
|
||||||
@ -46,7 +48,7 @@ export type PocketbaseProcess = {
|
|||||||
export const DOCKER_INSTANCE_IMAGE_NAME = `benallfree/pockethost-instance`
|
export const DOCKER_INSTANCE_IMAGE_NAME = `benallfree/pockethost-instance`
|
||||||
|
|
||||||
export const createPocketbaseService = async (config: PocketbaseServiceConfig) => {
|
export const createPocketbaseService = async (config: PocketbaseServiceConfig) => {
|
||||||
const _serviceLogger = LoggerService().create('PocketbaseService')
|
const _serviceLogger = (config.logger ?? LoggerService()).create('PocketbaseService')
|
||||||
const { dbg, error, warn, abort } = _serviceLogger
|
const { dbg, error, warn, abort } = _serviceLogger
|
||||||
|
|
||||||
const { gobot } = GobotService()
|
const { gobot } = GobotService()
|
||||||
@ -58,7 +60,7 @@ export const createPocketbaseService = async (config: PocketbaseServiceConfig) =
|
|||||||
|
|
||||||
const _spawn = async (cfg: SpawnConfig) => {
|
const _spawn = async (cfg: SpawnConfig) => {
|
||||||
const cm = createCleanupManager()
|
const cm = createCleanupManager()
|
||||||
const logger = LoggerService().create('spawn')
|
const logger = (cfg.logger ?? config.logger ?? LoggerService()).create('spawn')
|
||||||
const { dbg, info, warn, error } = logger
|
const { dbg, info, warn, error } = logger
|
||||||
|
|
||||||
const _cfg: Required<SpawnConfig> = {
|
const _cfg: Required<SpawnConfig> = {
|
||||||
@ -72,8 +74,8 @@ export const createPocketbaseService = async (config: PocketbaseServiceConfig) =
|
|||||||
}
|
}
|
||||||
const { version, subdomain, instanceId, volume, extraBinds, env, stderr, stdout, dev } = _cfg
|
const { version, subdomain, instanceId, volume, extraBinds, env, stderr, stdout, dev } = _cfg
|
||||||
|
|
||||||
logger.breadcrumb({ subdomain, instanceId })
|
logger.breadcrumb(`${subdomain}-${instanceId}`)
|
||||||
const iLogger = InstanceLogWriter(instanceId, volume, 'exec')
|
const iLogger = InstanceLogWriter(instanceId, volume, 'exec', logger)
|
||||||
|
|
||||||
const _version = version || maxVersion // If _version is blank, we use the max version available
|
const _version = version || maxVersion // If _version is blank, we use the max version available
|
||||||
const realVersion = await bot.maxSatisfyingVersion(_version)
|
const realVersion = await bot.maxSatisfyingVersion(_version)
|
||||||
@ -260,7 +262,7 @@ export const createPocketbaseService = async (config: PocketbaseServiceConfig) =
|
|||||||
})
|
})
|
||||||
|
|
||||||
const url = mkInternalUrl(container.portBinding)
|
const url = mkInternalUrl(container.portBinding)
|
||||||
logger.breadcrumb({ url })
|
logger.breadcrumb(url)
|
||||||
dbg(`Making exit hook for ${url}`)
|
dbg(`Making exit hook for ${url}`)
|
||||||
const unsub = asyncExitHook(async () => {
|
const unsub = asyncExitHook(async () => {
|
||||||
await api.kill()
|
await api.kill()
|
||||||
|
|||||||
@ -60,7 +60,7 @@ export const realtimeLog = mkSingleton(async (config: RealtimeLogConfig) => {
|
|||||||
dbg(`Instance is `, instance)
|
dbg(`Instance is `, instance)
|
||||||
|
|
||||||
/** Get a database connection */
|
/** Get a database connection */
|
||||||
const instanceLogger = InstanceLogReader(instance.id, instance.volume, `exec`)
|
const instanceLogger = InstanceLogReader(instance.id, instance.volume, `exec`, logger)
|
||||||
|
|
||||||
/** Start the stream */
|
/** Start the stream */
|
||||||
res.writeHead(200, {
|
res.writeHead(200, {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user