pockethost 1.7.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +119 -86
- package/README.md +96 -0
- package/dist/cli.js +21542 -0
- package/package.json +55 -55
- package/changesetx.ts +0 -110
- package/common.ts +0 -1
- package/core.ts +0 -1
- package/readme.md +0 -129
- package/src/cli/commands/ConfigCommand/index.ts +0 -28
- package/src/cli/commands/PluginCommand/index.ts +0 -113
- package/src/cli/commands/PluginCommand/plugin-template/package.json +0 -38
- package/src/cli/commands/PluginCommand/plugin-template/readme.md +0 -33
- package/src/cli/commands/PluginCommand/plugin-template/src/constants.ts +0 -22
- package/src/cli/commands/PluginCommand/plugin-template/src/db.ts +0 -30
- package/src/cli/commands/PluginCommand/plugin-template/src/index.ts +0 -3
- package/src/cli/commands/PluginCommand/plugin-template/src/log.ts +0 -5
- package/src/cli/commands/PluginCommand/plugin-template/src/plugin.ts +0 -58
- package/src/cli/commands/PluginCommand/util.ts +0 -140
- package/src/cli/commands/ServeCommand/index.ts +0 -36
- package/src/cli/index.ts +0 -70
- package/src/common/CleanupManager.ts +0 -65
- package/src/common/Logger.ts +0 -174
- package/src/common/ResourceAllocator.ts +0 -49
- package/src/common/TimerManager.ts +0 -64
- package/src/common/assert.ts +0 -26
- package/src/common/debug.ts +0 -8
- package/src/common/error.ts +0 -16
- package/src/common/events.ts +0 -53
- package/src/common/index.ts +0 -12
- package/src/common/mergeConfig.ts +0 -27
- package/src/common/mkSingleton.ts +0 -28
- package/src/common/plugin/action.ts +0 -164
- package/src/common/plugin/filter.ts +0 -197
- package/src/common/plugin/index.ts +0 -21
- package/src/common/promiseSerializer.ts +0 -22
- package/src/common/schema/BaseFields.ts +0 -10
- package/src/common/schema/Instance.ts +0 -46
- package/src/common/schema/InstanceLog.ts +0 -12
- package/src/common/schema/index.ts +0 -3
- package/src/common/schema/util.ts +0 -7
- package/src/constants.ts +0 -70
- package/src/core/Settings.ts +0 -119
- package/src/core/asyncExecutionGuard.ts +0 -49
- package/src/core/config.ts +0 -108
- package/src/core/exit.ts +0 -11
- package/src/core/index.ts +0 -8
- package/src/core/internal.ts +0 -3
- package/src/core/smartFetch.ts +0 -32
- package/src/core/tryFetch.ts +0 -69
- package/src/plugin.ts +0 -54
- package/src/server.ts +0 -129
- /package/{src/cli/commands/PluginCommand/plugin-template/LICENSE.md → LICENSE.md} +0 -0
package/src/core/Settings.ts
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { boolean as castToBoolean } from 'boolean'
|
|
2
|
-
import { existsSync, mkdirSync } from 'fs'
|
|
3
|
-
|
|
4
|
-
export type SettingsCaster<TValue, TConfig = {}> = {
|
|
5
|
-
stringToType: (value: string, config?: Partial<TConfig>) => TValue
|
|
6
|
-
typeToString: (value: TValue, config?: Partial<TConfig>) => string
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export type SettingsHandler<TValue> = {
|
|
10
|
-
get: () => TValue
|
|
11
|
-
set: (value: TValue) => void
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export type SettingsHandlerFactory<TValue> = (
|
|
15
|
-
key: string,
|
|
16
|
-
) => SettingsHandler<TValue>
|
|
17
|
-
|
|
18
|
-
export type SettingsMaker<TValue, TConfig = {}> = (
|
|
19
|
-
_default?: TValue,
|
|
20
|
-
config?: Partial<TConfig>,
|
|
21
|
-
) => SettingsHandlerFactory<TValue>
|
|
22
|
-
|
|
23
|
-
const mkMaker =
|
|
24
|
-
<TValue, TConfig = {}>(
|
|
25
|
-
caster: SettingsCaster<TValue, TConfig>,
|
|
26
|
-
): SettingsMaker<TValue, TConfig> =>
|
|
27
|
-
(_default, config) =>
|
|
28
|
-
(name: string) => {
|
|
29
|
-
return {
|
|
30
|
-
get(): TValue {
|
|
31
|
-
const v = process.env[name]
|
|
32
|
-
if (typeof v === `undefined`) {
|
|
33
|
-
if (typeof _default === `undefined`)
|
|
34
|
-
throw new Error(`${name} must be defined`)
|
|
35
|
-
this.set(_default)
|
|
36
|
-
return this.get()
|
|
37
|
-
}
|
|
38
|
-
try {
|
|
39
|
-
return caster.stringToType(v, config)
|
|
40
|
-
} catch (e) {
|
|
41
|
-
throw new Error(`${name}: ${e}`)
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
set(v: TValue) {
|
|
45
|
-
try {
|
|
46
|
-
process.env[name] = caster.typeToString(v, config)
|
|
47
|
-
} catch (e) {
|
|
48
|
-
throw new Error(`${name}: ${e}`)
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export const mkBoolean = mkMaker<boolean>({
|
|
55
|
-
stringToType: (v) => castToBoolean(v),
|
|
56
|
-
typeToString: (v) => `${v}`,
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
export const mkNumber = mkMaker<number>({
|
|
60
|
-
stringToType: (s) => parseInt(s, 10),
|
|
61
|
-
typeToString: (v) => v.toString(),
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
export const mkPath = mkMaker<string, { create: boolean; required: boolean }>({
|
|
65
|
-
stringToType: (v, options) => {
|
|
66
|
-
const { create = false, required = true } = options || {}
|
|
67
|
-
if (create) {
|
|
68
|
-
mkdirSync(v, { recursive: true })
|
|
69
|
-
}
|
|
70
|
-
if (required && !existsSync(v)) {
|
|
71
|
-
throw new Error(`${v} must exist.`)
|
|
72
|
-
}
|
|
73
|
-
return v
|
|
74
|
-
},
|
|
75
|
-
typeToString: (v, options) => {
|
|
76
|
-
const { create = false, required = true } = options || {}
|
|
77
|
-
if (create) {
|
|
78
|
-
mkdirSync(v, { recursive: true })
|
|
79
|
-
}
|
|
80
|
-
if (required && !existsSync(v)) {
|
|
81
|
-
throw new Error(`${v} must exist.`)
|
|
82
|
-
}
|
|
83
|
-
return v
|
|
84
|
-
},
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
export const mkString = mkMaker<string>({
|
|
88
|
-
typeToString: (v) => v,
|
|
89
|
-
stringToType: (v) => v,
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
export const mkCsvString = mkMaker<string[]>({
|
|
93
|
-
typeToString: (v) => v.join(','),
|
|
94
|
-
stringToType: (s) =>
|
|
95
|
-
s
|
|
96
|
-
.split(/,/)
|
|
97
|
-
.map((s) => s.trim())
|
|
98
|
-
.filter((v) => !!v),
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
type Config<T> = {
|
|
102
|
-
[K in keyof T]: SettingsHandlerFactory<T[K]>
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export const Settings = <T extends Object>(config: Config<T>) => {
|
|
106
|
-
const lookup: Partial<T> = {}
|
|
107
|
-
|
|
108
|
-
for (const key in config) {
|
|
109
|
-
const handler = config[key as keyof T](key)
|
|
110
|
-
Object.defineProperty(lookup, key, {
|
|
111
|
-
get: () => handler.get(),
|
|
112
|
-
set: (value) => handler.set(value),
|
|
113
|
-
enumerable: true,
|
|
114
|
-
})
|
|
115
|
-
handler.get() // Initialize process.env
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return lookup as T
|
|
119
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { uniqueId } from '@s-libs/micro-dash'
|
|
2
|
-
import Bottleneck from 'bottleneck'
|
|
3
|
-
import { SetReturnType } from 'type-fest'
|
|
4
|
-
import { LoggerService } from '../common/Logger'
|
|
5
|
-
|
|
6
|
-
const limiters: { [lane: string]: Bottleneck } = {}
|
|
7
|
-
export const serialAsyncExecutionGuard = <
|
|
8
|
-
T extends (...args: any[]) => Promise<any>,
|
|
9
|
-
>(
|
|
10
|
-
cb: T,
|
|
11
|
-
lane?: SetReturnType<T, string>,
|
|
12
|
-
): T => {
|
|
13
|
-
const uuid = uniqueId()
|
|
14
|
-
const _lane = lane || (() => uuid)
|
|
15
|
-
const wrapper = (...args: Parameters<T>) => {
|
|
16
|
-
const { dbg } = LoggerService().create('serialAsyncExecutionGuard')
|
|
17
|
-
const key = _lane(...args)
|
|
18
|
-
if (!limiters[key]) {
|
|
19
|
-
dbg(`New serial limiter with key ${key}`)
|
|
20
|
-
limiters[key] = new Bottleneck({ maxConcurrent: 1 })
|
|
21
|
-
}
|
|
22
|
-
const limiter = limiters[key]!
|
|
23
|
-
return limiter.schedule(() => cb(...args)) as unknown as ReturnType<T>
|
|
24
|
-
}
|
|
25
|
-
return wrapper as unknown as T
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const singletons: { [_: string]: Promise<any> } = {}
|
|
29
|
-
export const singletonAsyncExecutionGuard = <
|
|
30
|
-
T extends (...args: any[]) => Promise<any>,
|
|
31
|
-
>(
|
|
32
|
-
cb: T,
|
|
33
|
-
key: SetReturnType<T, string>,
|
|
34
|
-
): T => {
|
|
35
|
-
const uuid = uniqueId()
|
|
36
|
-
const keyFactory = key || (() => uuid)
|
|
37
|
-
const wrapper = (...args: Parameters<T>) => {
|
|
38
|
-
const { dbg } = LoggerService().create(`singletonAsyncExecutionGuard`)
|
|
39
|
-
const key = keyFactory(...args)
|
|
40
|
-
//@ts-ignore
|
|
41
|
-
if (singletons[key]) {
|
|
42
|
-
return singletons[key] as unknown as ReturnType<T>
|
|
43
|
-
}
|
|
44
|
-
dbg(`New singleton guard with key ${key}`)
|
|
45
|
-
singletons[key] = cb(...args)
|
|
46
|
-
return singletons[key] as unknown as ReturnType<T>
|
|
47
|
-
}
|
|
48
|
-
return wrapper as unknown as T
|
|
49
|
-
}
|
package/src/core/config.ts
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { forEach, keys, omit, uniq } from '@s-libs/micro-dash'
|
|
2
|
-
import { parse } from 'dotenv'
|
|
3
|
-
import { existsSync, readFileSync, writeFileSync } from 'fs'
|
|
4
|
-
import { info } from '../cli'
|
|
5
|
-
import { PH_HOME, settings } from '../constants'
|
|
6
|
-
|
|
7
|
-
const envFile = () => {
|
|
8
|
-
const envFile = PH_HOME(`.env`)
|
|
9
|
-
if (!existsSync(envFile)) {
|
|
10
|
-
writeFileSync(envFile, '')
|
|
11
|
-
}
|
|
12
|
-
return envFile
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const setConfig = (name: string, value: string) => {
|
|
16
|
-
if (value === '=') throw new Error(`Invalid value ${value}`)
|
|
17
|
-
|
|
18
|
-
const values = _parse()
|
|
19
|
-
values[name] = value
|
|
20
|
-
writeFileSync(
|
|
21
|
-
envFile(),
|
|
22
|
-
Object.entries(values)
|
|
23
|
-
.map(([k, v]) => `${k}=${v}`)
|
|
24
|
-
.join('\n'),
|
|
25
|
-
)
|
|
26
|
-
process.env[name] = value
|
|
27
|
-
info(`Set ${name}=${value}`)
|
|
28
|
-
info(`Written to ${envFile()}`)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export const unsetConfig = (name: string) => {
|
|
32
|
-
const values = _parse()
|
|
33
|
-
delete values[name]
|
|
34
|
-
writeFileSync(
|
|
35
|
-
envFile(),
|
|
36
|
-
Object.entries(values)
|
|
37
|
-
.map(([k, v]) => `${k}=${v}`)
|
|
38
|
-
.join('\n'),
|
|
39
|
-
)
|
|
40
|
-
info(`Unset ${name}`)
|
|
41
|
-
info(`Written to ${envFile()}`)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export const listConfig = () => {
|
|
45
|
-
const values = _parse()
|
|
46
|
-
|
|
47
|
-
if (keys(values).length > 0) {
|
|
48
|
-
info()
|
|
49
|
-
info(`Config values from ${envFile()}:`)
|
|
50
|
-
forEach(values, (v, k) => {
|
|
51
|
-
info(`\t${k}=${v}`)
|
|
52
|
-
})
|
|
53
|
-
info()
|
|
54
|
-
} else {
|
|
55
|
-
info(`No config values found in ${envFile()}`)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const defaults = omit(settings, keys(values) as any)
|
|
59
|
-
if (keys(defaults).length > 0) {
|
|
60
|
-
info(`Default values:`)
|
|
61
|
-
forEach(settings, (v, k) => {
|
|
62
|
-
if (k in values) return
|
|
63
|
-
info(`\t${k}=${v}`)
|
|
64
|
-
})
|
|
65
|
-
} else {
|
|
66
|
-
info(`No default values because all values are defined in ${envFile()}`)
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export const appendConfig = (name: string, value: string) => {
|
|
71
|
-
const values = _parse()
|
|
72
|
-
values[name] = uniq([
|
|
73
|
-
...(values[name]
|
|
74
|
-
?.split(/,/)
|
|
75
|
-
.map((v) => v.trim())
|
|
76
|
-
.filter((v) => !!v) || []),
|
|
77
|
-
value,
|
|
78
|
-
]).join(`,`)
|
|
79
|
-
writeFileSync(
|
|
80
|
-
envFile(),
|
|
81
|
-
Object.entries(values)
|
|
82
|
-
.map(([k, v]) => `${k}=${v}`)
|
|
83
|
-
.join('\n'),
|
|
84
|
-
)
|
|
85
|
-
info(`Added ${value} to ${name}`)
|
|
86
|
-
info(`Written to ${envFile()}`)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export const filterConfig = (name: string, value: string) => {
|
|
90
|
-
const values = _parse()
|
|
91
|
-
values[name] = uniq([
|
|
92
|
-
...(values[name]
|
|
93
|
-
?.split(/,/)
|
|
94
|
-
.map((v) => v.trim())
|
|
95
|
-
.filter((v) => v !== value) || []),
|
|
96
|
-
]).join(`,`)
|
|
97
|
-
writeFileSync(
|
|
98
|
-
envFile(),
|
|
99
|
-
Object.entries(values)
|
|
100
|
-
.map(([k, v]) => `${k}=${v}`)
|
|
101
|
-
.join('\n'),
|
|
102
|
-
)
|
|
103
|
-
info(`Filtered ${value} from ${name}`)
|
|
104
|
-
info(`Written to ${envFile()}`)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export const _parse = () =>
|
|
108
|
-
parse(readFileSync(envFile(), { encoding: 'utf8' }).toString())
|
package/src/core/exit.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import exitHook, { asyncExitHook as _, gracefulExit as __ } from 'exit-hook'
|
|
2
|
-
|
|
3
|
-
export const asyncExitHook = (cb: () => Promise<void>) => _(cb, { wait: 5000 })
|
|
4
|
-
|
|
5
|
-
export const gracefulExit = async (signal?: number) => {
|
|
6
|
-
__(signal)
|
|
7
|
-
await new Promise((resolve) => {
|
|
8
|
-
process.on('exit', resolve)
|
|
9
|
-
})
|
|
10
|
-
}
|
|
11
|
-
export { exitHook }
|
package/src/core/index.ts
DELETED
package/src/core/internal.ts
DELETED
package/src/core/smartFetch.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'
|
|
2
|
-
import fetch from 'node-fetch'
|
|
3
|
-
import { dirname } from 'path'
|
|
4
|
-
import { LoggerService } from '../common'
|
|
5
|
-
|
|
6
|
-
export const smartFetch = async <TRet>(
|
|
7
|
-
url: string,
|
|
8
|
-
path: string,
|
|
9
|
-
): Promise<TRet> => {
|
|
10
|
-
const { dbg } = LoggerService().create(`smartFetch`)
|
|
11
|
-
|
|
12
|
-
const data = await (async () => {
|
|
13
|
-
try {
|
|
14
|
-
const res = await fetch(url)
|
|
15
|
-
if (res.status !== 200) {
|
|
16
|
-
throw new Error(`API appears to be down`)
|
|
17
|
-
}
|
|
18
|
-
const data = (await res.json()) as TRet
|
|
19
|
-
return data
|
|
20
|
-
} catch (e) {
|
|
21
|
-
dbg(`Caught error on fetch: ${e}`)
|
|
22
|
-
if (!existsSync(path)) {
|
|
23
|
-
throw new Error(`API down and no cache`)
|
|
24
|
-
}
|
|
25
|
-
dbg(`Using data from cache`)
|
|
26
|
-
return JSON.parse(readFileSync(path).toString()) as TRet
|
|
27
|
-
}
|
|
28
|
-
})()
|
|
29
|
-
mkdirSync(dirname(path), { recursive: true })
|
|
30
|
-
writeFileSync(path, JSON.stringify(data))
|
|
31
|
-
return data as TRet
|
|
32
|
-
}
|
package/src/core/tryFetch.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import fetch, { RequestInit, Response } from 'node-fetch'
|
|
2
|
-
import { LoggerService } from '../common'
|
|
3
|
-
|
|
4
|
-
export const TRYFETCH_RETRY_MS = 50
|
|
5
|
-
|
|
6
|
-
export type TryFetchConfig = {
|
|
7
|
-
preflight: () => Promise<boolean>
|
|
8
|
-
retryMs: number
|
|
9
|
-
} & RequestInit
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @param url The URL to fetch
|
|
13
|
-
* @param config { preflight: ()=>Promise<boolean>, retryMs: number}
|
|
14
|
-
* @returns Promise<Response>
|
|
15
|
-
*
|
|
16
|
-
* TryFetch will continuously try to fetch a URL every `retryMs`, with an
|
|
17
|
-
* optional `preflight`. If `preflight` is specified, it must resolve to
|
|
18
|
-
* `true` before tryFetch will attempt to fetch the URL. If `preflight`
|
|
19
|
-
* resolves to `false`, tryFetch will delay by `retryMs` and then check again.
|
|
20
|
-
* If `preflight` rejects, then tryFetch rejects and exits.
|
|
21
|
-
*
|
|
22
|
-
* Note: tryFetch exits ONLY on success or a rejected preflight.
|
|
23
|
-
*/
|
|
24
|
-
export const tryFetch = async (
|
|
25
|
-
url: string,
|
|
26
|
-
config?: Partial<TryFetchConfig>,
|
|
27
|
-
) => {
|
|
28
|
-
const { preflight, retryMs }: TryFetchConfig = {
|
|
29
|
-
preflight: async () => true,
|
|
30
|
-
retryMs: TRYFETCH_RETRY_MS,
|
|
31
|
-
...config,
|
|
32
|
-
}
|
|
33
|
-
const logger = LoggerService().create(`tryFetch`).breadcrumb(url)
|
|
34
|
-
const { dbg } = logger
|
|
35
|
-
return new Promise<Response>((resolve, reject) => {
|
|
36
|
-
const again = () => setTimeout(_real_tryFetch, retryMs)
|
|
37
|
-
const _real_tryFetch = async () => {
|
|
38
|
-
if (preflight) {
|
|
39
|
-
try {
|
|
40
|
-
dbg(`Preflight: CHECK`)
|
|
41
|
-
const shouldFetch = await preflight()
|
|
42
|
-
if (!shouldFetch) {
|
|
43
|
-
dbg(`Preflight: NOT READY`)
|
|
44
|
-
again()
|
|
45
|
-
return
|
|
46
|
-
}
|
|
47
|
-
dbg(`Preflight: READY`)
|
|
48
|
-
} catch (e) {
|
|
49
|
-
dbg(`Preflight: ABORT`)
|
|
50
|
-
reject(e)
|
|
51
|
-
return
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
try {
|
|
55
|
-
dbg(`Fetch: START`)
|
|
56
|
-
const res = await fetch(url, {
|
|
57
|
-
...config,
|
|
58
|
-
signal: AbortSignal.timeout(500),
|
|
59
|
-
})
|
|
60
|
-
dbg(`Fetch: SUCCESS`)
|
|
61
|
-
resolve(res)
|
|
62
|
-
} catch (e) {
|
|
63
|
-
dbg(`Fetch: ERROR: trying again in ${retryMs}ms | ${e}`)
|
|
64
|
-
again()
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
_real_tryFetch()
|
|
68
|
-
})
|
|
69
|
-
}
|
package/src/plugin.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { forEach } from '@s-libs/micro-dash'
|
|
2
|
-
import { produce } from 'immer'
|
|
3
|
-
import {
|
|
4
|
-
PocketHostPlugin,
|
|
5
|
-
doSettingsFilter,
|
|
6
|
-
namespace,
|
|
7
|
-
newId,
|
|
8
|
-
onAfterPluginsLoadedAction,
|
|
9
|
-
onInstanceConfigFilter,
|
|
10
|
-
onInstanceLogAction,
|
|
11
|
-
onServeAction,
|
|
12
|
-
onServeSlugsFilter,
|
|
13
|
-
} from '../common'
|
|
14
|
-
import { dbg, info } from './cli'
|
|
15
|
-
import { INTERNAL_APP_SECRET, PH_PROJECT_DIR, settings } from './constants'
|
|
16
|
-
import { setConfig } from './core/config'
|
|
17
|
-
import { serve } from './server'
|
|
18
|
-
|
|
19
|
-
export const pockethost: PocketHostPlugin = async ({}) => {
|
|
20
|
-
onInstanceConfigFilter(async (config) => {
|
|
21
|
-
return produce(config, (draft) => {
|
|
22
|
-
draft.binds.hooks.push({
|
|
23
|
-
src: PH_PROJECT_DIR(`src/instance-app/hooks/**/*`),
|
|
24
|
-
base: PH_PROJECT_DIR(`src/instance-app/hooks`),
|
|
25
|
-
})
|
|
26
|
-
})
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
onInstanceLogAction(async ({ instance, type, data }) => {
|
|
30
|
-
dbg(namespace(instance.subdomain, type), data.toString())
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
onServeAction(async ({ only }) => {
|
|
34
|
-
if (!only.includes(`pockethost`)) return
|
|
35
|
-
serve()
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
onServeSlugsFilter(async (slugs) => {
|
|
39
|
-
return [...slugs, `pockethost`]
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
onAfterPluginsLoadedAction(async () => {
|
|
43
|
-
const allSettings = await doSettingsFilter(settings)
|
|
44
|
-
forEach(allSettings, (v, k) => {
|
|
45
|
-
dbg(`${k}: ${v}`)
|
|
46
|
-
})
|
|
47
|
-
}, 99)
|
|
48
|
-
|
|
49
|
-
onAfterPluginsLoadedAction(async () => {
|
|
50
|
-
if (INTERNAL_APP_SECRET()) return
|
|
51
|
-
info(`Generating internal app secret...`)
|
|
52
|
-
setConfig(`PH_INTERNAL_APP_SECRET`, newId(30))
|
|
53
|
-
})
|
|
54
|
-
}
|
package/src/server.ts
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import express, { ErrorRequestHandler } from 'express'
|
|
2
|
-
import 'express-async-errors'
|
|
3
|
-
import { default as httpProxy } from 'http-proxy'
|
|
4
|
-
import {
|
|
5
|
-
LoggerService,
|
|
6
|
-
doAfterInstanceFoundAction,
|
|
7
|
-
doAfterServerStartAction,
|
|
8
|
-
doAppMountedAction,
|
|
9
|
-
doGetInstanceByRequestInfoFilter,
|
|
10
|
-
doGetOrProvisionInstanceUrlFilter,
|
|
11
|
-
doIncomingRequestAction,
|
|
12
|
-
doRequestErrorAction,
|
|
13
|
-
doRequestErrorMessageFilter,
|
|
14
|
-
} from '../common'
|
|
15
|
-
import {
|
|
16
|
-
APEX_DOMAIN,
|
|
17
|
-
INTERNAL_APP_AUTH_HEADER,
|
|
18
|
-
INTERNAL_APP_PREFIX,
|
|
19
|
-
INTERNAL_APP_SECRET,
|
|
20
|
-
PORT,
|
|
21
|
-
asyncExitHook,
|
|
22
|
-
} from '../core'
|
|
23
|
-
|
|
24
|
-
export const serve = async () => {
|
|
25
|
-
const _proxyLogger = LoggerService().create('ProxyService')
|
|
26
|
-
const { dbg, error, info, trace, warn } = _proxyLogger
|
|
27
|
-
|
|
28
|
-
// registerFilter(
|
|
29
|
-
// CoreFilters.Waf_VHosts,
|
|
30
|
-
// async (routes: { [_: string]: string }) => {
|
|
31
|
-
// return { ...routes, '*': `http://localhost:${PORT()}` }
|
|
32
|
-
// },
|
|
33
|
-
// 99,
|
|
34
|
-
// )
|
|
35
|
-
|
|
36
|
-
const proxy = httpProxy.createProxyServer({})
|
|
37
|
-
proxy.on('error', (err, req, res, target) => {
|
|
38
|
-
warn(`Proxy error ${err} on ${req.url} (${req.headers.host})`)
|
|
39
|
-
console.log(err)
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
const app = express()
|
|
43
|
-
|
|
44
|
-
app.get('/_api/health', (req, res, next) => {
|
|
45
|
-
res.json({ status: 'ok' })
|
|
46
|
-
res.end
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
const errorHandler: ErrorRequestHandler = async (err, req, res, next) => {
|
|
50
|
-
doRequestErrorAction({ err })
|
|
51
|
-
res.status(500).send(await doRequestErrorMessageFilter(err.toString()))
|
|
52
|
-
}
|
|
53
|
-
app.use(errorHandler)
|
|
54
|
-
|
|
55
|
-
const internalApp = express()
|
|
56
|
-
internalApp.use((req, res, next) => {
|
|
57
|
-
dbg(`Got a request to internal app ${req.url}`)
|
|
58
|
-
next()
|
|
59
|
-
})
|
|
60
|
-
internalApp.use((req, res, next) => {
|
|
61
|
-
const authHeader = req.headers[INTERNAL_APP_AUTH_HEADER]
|
|
62
|
-
const internalAppSecret = INTERNAL_APP_SECRET()
|
|
63
|
-
|
|
64
|
-
if (authHeader !== internalAppSecret) {
|
|
65
|
-
res.status(403).send('Forbidden')
|
|
66
|
-
return
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
next()
|
|
70
|
-
})
|
|
71
|
-
app.use(`/${INTERNAL_APP_PREFIX}`, internalApp)
|
|
72
|
-
|
|
73
|
-
doAppMountedAction({ app, internalApp })
|
|
74
|
-
|
|
75
|
-
app.all(`*`, async (req, res, next) => {
|
|
76
|
-
const method = req.method
|
|
77
|
-
const fullUrl = new URL(
|
|
78
|
-
req.protocol + '://' + req.get('host') + req.originalUrl,
|
|
79
|
-
)
|
|
80
|
-
const host = fullUrl.hostname
|
|
81
|
-
if (!host) {
|
|
82
|
-
throw new Error(`Host not found`)
|
|
83
|
-
}
|
|
84
|
-
const subdomain = host.slice(0, -`.${APEX_DOMAIN()}`.length)
|
|
85
|
-
|
|
86
|
-
dbg(
|
|
87
|
-
`${method} ${fullUrl} (${subdomain}) -> ${`http://localhost:${PORT()}`}`,
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
await doIncomingRequestAction({ req, res, host })
|
|
91
|
-
|
|
92
|
-
const instance = await doGetInstanceByRequestInfoFilter(undefined, {
|
|
93
|
-
req,
|
|
94
|
-
res,
|
|
95
|
-
host,
|
|
96
|
-
subdomain,
|
|
97
|
-
})
|
|
98
|
-
if (!instance) {
|
|
99
|
-
res.status(404).end(`${host} not found`)
|
|
100
|
-
return
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
await doAfterInstanceFoundAction({
|
|
104
|
-
instance,
|
|
105
|
-
req,
|
|
106
|
-
res,
|
|
107
|
-
host,
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
const url = await doGetOrProvisionInstanceUrlFilter('', {
|
|
111
|
-
instance,
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
dbg(`Forwarding request for ${req.url} to instance ${url}`)
|
|
115
|
-
|
|
116
|
-
proxy.web(req, res, { target: url })
|
|
117
|
-
|
|
118
|
-
// next()
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
app.listen(PORT(), () => {
|
|
122
|
-
info(`pockethost listening on port ${PORT()}`)
|
|
123
|
-
doAfterServerStartAction()
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
asyncExitHook(async () => {
|
|
127
|
-
info(`Shutting down pockethost`)
|
|
128
|
-
})
|
|
129
|
-
}
|
|
File without changes
|