mirror of
https://github.com/pockethost/pockethost.git
synced 2025-03-30 15:08:30 +00:00
enh: add exit-hook support
This commit is contained in:
parent
89e8d96c16
commit
4085a73c25
@ -31,6 +31,7 @@
|
||||
"dotenv": "^16.3.1",
|
||||
"event-source-polyfill": "^1.0.31",
|
||||
"eventsource": "^2.0.2",
|
||||
"exit-hook": "^4.0.0",
|
||||
"ftp-srv": "^4.6.2",
|
||||
"get-port": "^6.1.2",
|
||||
"http-proxy": "^1.18.1",
|
||||
|
@ -110,19 +110,5 @@ global.EventSource = require('eventsource')
|
||||
|
||||
info(`Hooking into process exit event`)
|
||||
|
||||
const shutdown = async (signal: NodeJS.Signals) => {
|
||||
info(`Got signal ${signal}`)
|
||||
info(`Shutting down`)
|
||||
ftpService().shutdown()
|
||||
;(await realtimeLog()).shutdown()
|
||||
;(await proxyService()).shutdown()
|
||||
;(await instanceService()).shutdown()
|
||||
;(await rpcService()).shutdown()
|
||||
pbService.shutdown()
|
||||
}
|
||||
|
||||
await (await rpcService()).initRpcs()
|
||||
process.on('SIGTERM', shutdown)
|
||||
process.on('SIGINT', shutdown)
|
||||
process.on('SIGHUP', shutdown)
|
||||
})()
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
SSL_KEY,
|
||||
} from '$constants'
|
||||
import { clientService, createPbClient } from '$services'
|
||||
import { exitHook } from '$util'
|
||||
import { SingletonBaseConfig, mkSingleton } from '@pockethost/common'
|
||||
import { readFileSync } from 'fs'
|
||||
import { FtpSrv } from 'ftp-srv'
|
||||
@ -81,10 +82,10 @@ export const ftpService = mkSingleton((config: FtpConfig) => {
|
||||
info('Ftp server started...')
|
||||
})
|
||||
|
||||
const shutdown = () => {
|
||||
exitHook(() => {
|
||||
info(`Closing FTP server`)
|
||||
ftpServer.close()
|
||||
}
|
||||
})
|
||||
|
||||
return { shutdown }
|
||||
return {}
|
||||
})
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
port,
|
||||
proxyService,
|
||||
} from '$services'
|
||||
import { mkInternalUrl, now } from '$util'
|
||||
import { asyncExitHook, mkInternalUrl, now } from '$util'
|
||||
import {
|
||||
assertTruthy,
|
||||
CLEANUP_PRIORITY_LAST,
|
||||
@ -165,6 +165,7 @@ export const instanceService = mkSingleton(
|
||||
return startRequest()
|
||||
},
|
||||
shutdown: async (reason) => {
|
||||
dbg(`Shutting down`)
|
||||
if (reason) {
|
||||
_shutdownReason = reason
|
||||
error(`Panic shutdown for ${reason}`)
|
||||
@ -462,14 +463,14 @@ export const instanceService = mkSingleton(
|
||||
`InstanceService`,
|
||||
)
|
||||
|
||||
const shutdown = async () => {
|
||||
asyncExitHook(async () => {
|
||||
dbg(`Shutting down instance manager`)
|
||||
const p = Promise.all(map(instanceApis, (api) => api.shutdown()))
|
||||
await p
|
||||
}
|
||||
})
|
||||
|
||||
const getInstanceApiIfExistsById = (id: InstanceId) => instanceApis[id]
|
||||
|
||||
return { shutdown, getInstanceApiIfExistsById }
|
||||
return { getInstanceApiIfExistsById }
|
||||
},
|
||||
)
|
||||
|
@ -5,7 +5,13 @@ import {
|
||||
PUBLIC_DEBUG,
|
||||
} from '$constants'
|
||||
import { port as getPort, InstanceLogger, updaterService } from '$services'
|
||||
import { assert, AsyncContext, mkInternalUrl, tryFetch } from '$util'
|
||||
import {
|
||||
assert,
|
||||
AsyncContext,
|
||||
asyncExitHook,
|
||||
mkInternalUrl,
|
||||
tryFetch,
|
||||
} from '$util'
|
||||
import {
|
||||
createCleanupManager,
|
||||
createTimerManager,
|
||||
@ -58,10 +64,10 @@ export const createPocketbaseService = async (
|
||||
const { getLatestVersion, getVersion } = await updaterService()
|
||||
const maxVersion = getLatestVersion()
|
||||
|
||||
const cm = createCleanupManager()
|
||||
const tm = createTimerManager({})
|
||||
|
||||
const _spawn = async (cfg: SpawnConfig, context?: AsyncContext) => {
|
||||
const cm = createCleanupManager()
|
||||
const logger = (context?.logger || _serviceLogger).create('spawn')
|
||||
const { dbg, warn, error } = logger
|
||||
const defaultPort = await (async () => {
|
||||
@ -195,6 +201,8 @@ export const createPocketbaseService = async (
|
||||
iLogger.info(`${slug} closed with code ${StatusCode}`)
|
||||
dbg({ err, data })
|
||||
isRunning = false
|
||||
container = undefined
|
||||
unsub()
|
||||
if (StatusCode > 0 || err) {
|
||||
iLogger.error(
|
||||
`Unexpected stop with code ${StatusCode} and error ${err}`,
|
||||
@ -212,24 +220,7 @@ export const createPocketbaseService = async (
|
||||
resolve(container)
|
||||
})
|
||||
})
|
||||
if (container) {
|
||||
cm.add(async () => {
|
||||
dbg(`Stopping ${slug} for cleanup`)
|
||||
iLogger.info(`Stopping instance`)
|
||||
await container
|
||||
?.stop()
|
||||
.then(() => {
|
||||
iLogger.info(`Instance stopped`)
|
||||
})
|
||||
.catch((err) => {
|
||||
iLogger.error(`Error stopping instance`)
|
||||
warn(`Possible error stopping container: ${err}`)
|
||||
})
|
||||
|
||||
stderr.off('data', _stdErrData)
|
||||
stdout.off('data', _stdoutData)
|
||||
})
|
||||
} else {
|
||||
if (!container) {
|
||||
iLogger.error(`Could not start container`)
|
||||
error(`${slug} could not start container`)
|
||||
onUnexpectedStop?.(999)
|
||||
@ -244,6 +235,10 @@ export const createPocketbaseService = async (
|
||||
logger: _serviceLogger,
|
||||
})
|
||||
}
|
||||
const unsub = asyncExitHook(async () => {
|
||||
dbg(`Exiting process ${slug}`)
|
||||
await api.kill()
|
||||
})
|
||||
const api: PocketbaseProcess = {
|
||||
url,
|
||||
pid: () => {
|
||||
@ -252,26 +247,33 @@ export const createPocketbaseService = async (
|
||||
},
|
||||
exited,
|
||||
kill: async () => {
|
||||
unsub()
|
||||
if (!container) {
|
||||
throw new Error(
|
||||
`Attempt to kill a PocketBase process that was never running.`,
|
||||
)
|
||||
}
|
||||
iLogger.info(`Stopping instance`)
|
||||
await container.stop()
|
||||
iLogger.info(`Instance stopped`)
|
||||
stderr.off('data', _stdErrData)
|
||||
stdout.off('data', _stdoutData)
|
||||
|
||||
container = undefined
|
||||
await cm.shutdown()
|
||||
},
|
||||
}
|
||||
|
||||
return api
|
||||
}
|
||||
|
||||
const shutdown = () => {
|
||||
asyncExitHook(async () => {
|
||||
dbg(`Shutting down pocketbaseService`)
|
||||
tm.shutdown()
|
||||
cm.shutdown()
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
spawn: _spawn,
|
||||
shutdown,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { DAEMON_PORT, PUBLIC_EDGE_APEX_DOMAIN } from '$constants'
|
||||
import { asyncExitHook } from '$util'
|
||||
import { Logger, SingletonBaseConfig, mkSingleton } from '@pockethost/common'
|
||||
import { isFunction } from '@s-libs/micro-dash'
|
||||
import {
|
||||
@ -10,7 +11,6 @@ import {
|
||||
import { default as Server, default as httpProxy } from 'http-proxy'
|
||||
import { AsyncReturnType, SetReturnType } from 'type-fest'
|
||||
import UrlPattern from 'url-pattern'
|
||||
|
||||
export type ProxyServiceApi = AsyncReturnType<typeof proxyService>
|
||||
|
||||
export type ProxyMiddleware = (
|
||||
@ -88,7 +88,7 @@ export const proxyService = mkSingleton(async (config: ProxyServiceConfig) => {
|
||||
info(`daemon on port ${DAEMON_PORT}`)
|
||||
server.listen(DAEMON_PORT)
|
||||
|
||||
const shutdown = async () => {
|
||||
asyncExitHook(() => {
|
||||
info(`Shutting down proxy server`)
|
||||
return new Promise<void>((resolve) => {
|
||||
server.close((err) => {
|
||||
@ -97,7 +97,7 @@ export const proxyService = mkSingleton(async (config: ProxyServiceConfig) => {
|
||||
})
|
||||
server.closeAllConnections()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
type MiddlewareListener = SetReturnType<
|
||||
RequestListener,
|
||||
@ -169,5 +169,5 @@ export const proxyService = mkSingleton(async (config: ProxyServiceConfig) => {
|
||||
})
|
||||
}
|
||||
|
||||
return { shutdown, use }
|
||||
return { use }
|
||||
})
|
||||
|
@ -118,9 +118,5 @@ export const realtimeLog = mkSingleton(async (config: RealtimeLogConfig) => {
|
||||
`RealtimeLogService`,
|
||||
)
|
||||
|
||||
return {
|
||||
shutdown() {
|
||||
dbg(`shutdown`)
|
||||
},
|
||||
}
|
||||
return {}
|
||||
})
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
import { isObject } from '@s-libs/micro-dash'
|
||||
import Ajv, { JSONSchemaType, ValidateFunction } from 'ajv'
|
||||
import Bottleneck from 'bottleneck'
|
||||
import exitHook from 'exit-hook'
|
||||
import { default as knexFactory } from 'knex'
|
||||
import pocketbaseEs, { ClientResponseError } from 'pocketbase'
|
||||
import { AsyncReturnType, JsonObject } from 'type-fest'
|
||||
@ -107,9 +108,7 @@ export const rpcService = mkSingleton(async (config: RpcServiceConfig) => {
|
||||
|
||||
const unsub = await client.onNewRpc(run)
|
||||
|
||||
const shutdown = () => {
|
||||
unsub()
|
||||
}
|
||||
exitHook(unsub)
|
||||
|
||||
const ajv = new Ajv()
|
||||
|
||||
@ -133,6 +132,5 @@ export const rpcService = mkSingleton(async (config: RpcServiceConfig) => {
|
||||
return {
|
||||
registerCommand,
|
||||
initRpcs,
|
||||
shutdown,
|
||||
}
|
||||
})
|
||||
|
5
packages/daemon/src/util/exit.ts
Normal file
5
packages/daemon/src/util/exit.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import exitHook, { asyncExitHook as _ } from 'exit-hook'
|
||||
|
||||
const asyncExitHook = (cb: () => Promise<void>) => _(cb, { wait: 1000 })
|
||||
|
||||
export { asyncExitHook, exitHook }
|
@ -4,6 +4,7 @@ export * from './assert'
|
||||
export * from './downloadAndExtract'
|
||||
export * from './ensureDirExists'
|
||||
export * from './env'
|
||||
export * from './exit'
|
||||
export * from './internal'
|
||||
export * from './now'
|
||||
export * from './smartFetch'
|
||||
|
@ -2724,6 +2724,11 @@ executable@^4.1.0:
|
||||
dependencies:
|
||||
pify "^2.2.0"
|
||||
|
||||
exit-hook@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-4.0.0.tgz#c1e16ebd03d3166f837b1502dac755bb5c460d58"
|
||||
integrity sha512-Fqs7ChZm72y40wKjOFXBKg7nJZvQJmewP5/7LtePDdnah/+FH9Hp5sgMujSCMPXlxOAW2//1jrW9pnsY7o20vQ==
|
||||
|
||||
expand-template@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
|
||||
|
Loading…
x
Reference in New Issue
Block a user