pockethost 1.4.0 → 1.6.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 +55 -0
- package/changesetx.ts +110 -0
- package/package.json +11 -5
- package/readme.md +73 -15
- package/src/cli/commands/ConfigCommand/index.ts +4 -75
- package/src/cli/commands/PluginCommand/index.ts +5 -46
- package/src/cli/commands/ServeCommand/index.ts +4 -3
- package/src/cli/index.ts +21 -3
- package/src/common/Logger.ts +8 -6
- package/src/common/plugin/action.ts +44 -12
- package/src/common/plugin/filter.ts +89 -12
- package/src/common/schema/BaseFields.ts +10 -0
- package/src/common/schema/Instance.ts +12 -2
- package/src/common/schema/util.ts +7 -1
- package/src/constants.ts +16 -15
- package/src/core/config.ts +108 -0
- package/src/core/index.ts +1 -0
- package/src/core/tryFetch.ts +6 -3
- package/src/instance-app/types/all.d.ts +3 -0
- package/src/instance-app/types/goja.d.ts +58 -0
- package/src/instance-app/types/lib.d.ts +5 -0
- package/src/instance-app/types/types.d.ts +19573 -0
- package/src/plugin.ts +54 -0
- package/src/server.ts +29 -28
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { uniqueId } from '@s-libs/micro-dash'
|
|
1
2
|
import { Express, Request, Response } from 'express'
|
|
3
|
+
import { dbg } from '../../cli'
|
|
2
4
|
import { LogLevelName } from '../Logger'
|
|
3
5
|
import { DEBUG } from '../debug'
|
|
4
6
|
import { InstanceFields } from '../schema'
|
|
5
7
|
|
|
6
8
|
enum CoreActions {
|
|
9
|
+
AfterPluginsLoaded = 'core_after_plugins_loaded',
|
|
7
10
|
Init = 'core_init',
|
|
8
11
|
Serve = 'core_serve',
|
|
9
12
|
AppMounted = 'core_on_app_mount',
|
|
@@ -18,6 +21,8 @@ enum CoreActions {
|
|
|
18
21
|
BeforeInstanceStarted = 'core_before_instance_started',
|
|
19
22
|
AfterInstanceStarted = 'core_after_instance_started',
|
|
20
23
|
|
|
24
|
+
KillInstance = 'core_kill_instance',
|
|
25
|
+
|
|
21
26
|
BeforeInstanceStopped = 'core_before_instance_stopped',
|
|
22
27
|
AfterInstanceStopped = 'core_after_instance_stopped',
|
|
23
28
|
|
|
@@ -27,20 +32,34 @@ enum CoreActions {
|
|
|
27
32
|
Request = 'core_edge_request',
|
|
28
33
|
}
|
|
29
34
|
|
|
30
|
-
const actions: {
|
|
31
|
-
[key: string]: ActionHandler<any>[]
|
|
32
|
-
} = {}
|
|
33
|
-
|
|
34
35
|
export type ActionHandler<TContext = {}> = (
|
|
35
36
|
context: TContext extends never ? [] : TContext,
|
|
36
37
|
) => Promise<void>
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
interface Action {
|
|
40
|
+
priority: number
|
|
41
|
+
handler: ActionHandler<any>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const actions: {
|
|
45
|
+
[key: string]: Action[]
|
|
46
|
+
} = {}
|
|
47
|
+
|
|
48
|
+
function registerAction(
|
|
39
49
|
action: string,
|
|
40
50
|
handler: (...args: any[]) => Promise<void>,
|
|
41
|
-
|
|
51
|
+
priority: number = 10,
|
|
52
|
+
): () => void {
|
|
42
53
|
if (!(action in actions)) actions[action] = []
|
|
43
|
-
|
|
54
|
+
const uid = uniqueId(`a:${action}:`)
|
|
55
|
+
dbg(`${uid}:subscribe`)
|
|
56
|
+
actions[action]!.push({ priority, handler })
|
|
57
|
+
actions[action]!.sort((a, b) => a.priority - b.priority)
|
|
58
|
+
|
|
59
|
+
return () => {
|
|
60
|
+
dbg(`${uid}:unsubscribe`)
|
|
61
|
+
actions[action] = actions[action]!.filter((a) => a.handler !== handler)
|
|
62
|
+
}
|
|
44
63
|
}
|
|
45
64
|
|
|
46
65
|
async function action(actionName: string, context: any) {
|
|
@@ -49,7 +68,9 @@ async function action(actionName: string, context: any) {
|
|
|
49
68
|
}
|
|
50
69
|
const action = actions[actionName]
|
|
51
70
|
if (!action) return false
|
|
52
|
-
|
|
71
|
+
for (const { handler } of action) {
|
|
72
|
+
await handler(context)
|
|
73
|
+
}
|
|
53
74
|
return true
|
|
54
75
|
}
|
|
55
76
|
|
|
@@ -58,18 +79,22 @@ export function createCustomActionWithContext<TContext extends {}>(
|
|
|
58
79
|
) {
|
|
59
80
|
return [
|
|
60
81
|
async (context: TContext) => action(actionName, context),
|
|
61
|
-
|
|
62
|
-
registerAction(actionName, handler),
|
|
82
|
+
(handler: ActionHandler<TContext>, priority = 10) =>
|
|
83
|
+
registerAction(actionName, handler, priority),
|
|
63
84
|
] as const
|
|
64
85
|
}
|
|
65
86
|
|
|
66
87
|
export function createCustomAction(actionName: CoreActions) {
|
|
67
88
|
return [
|
|
68
89
|
async () => action(actionName, {}),
|
|
69
|
-
|
|
90
|
+
(handler: ActionHandler, priority = 10) =>
|
|
91
|
+
registerAction(actionName, handler, priority),
|
|
70
92
|
] as const
|
|
71
93
|
}
|
|
72
94
|
|
|
95
|
+
export const [doAfterPluginsLoadedAction, onAfterPluginsLoadedAction] =
|
|
96
|
+
createCustomAction(CoreActions.AfterPluginsLoaded)
|
|
97
|
+
|
|
73
98
|
export const [doRequestErrorAction, onRequestErrorAction] =
|
|
74
99
|
createCustomActionWithContext<{
|
|
75
100
|
err: Error
|
|
@@ -92,7 +117,9 @@ export const [doInstanceLogAction, onInstanceLogAction] =
|
|
|
92
117
|
}>(CoreActions.InstanceLog)
|
|
93
118
|
|
|
94
119
|
export const [doAppMountedAction, onAppMountedAction] =
|
|
95
|
-
createCustomActionWithContext<{ app: Express }>(
|
|
120
|
+
createCustomActionWithContext<{ app: Express; internalApp: Express }>(
|
|
121
|
+
CoreActions.AppMounted,
|
|
122
|
+
)
|
|
96
123
|
|
|
97
124
|
export const [doIncomingRequestAction, onIncomingRequestAction] =
|
|
98
125
|
createCustomActionWithContext<{ req: Request; res: Response; host: string }>(
|
|
@@ -117,6 +144,11 @@ export const [doAfterInstanceStartedAction, onAfterInstanceStartedAction] =
|
|
|
117
144
|
CoreActions.AfterInstanceStarted,
|
|
118
145
|
)
|
|
119
146
|
|
|
147
|
+
export const [doKillInstanceAction, onKillInstanceAction] =
|
|
148
|
+
createCustomActionWithContext<{ instance: InstanceFields }>(
|
|
149
|
+
CoreActions.KillInstance,
|
|
150
|
+
)
|
|
151
|
+
|
|
120
152
|
export const [doBeforeInstanceStoppedAction, onBeforeInstanceStoppedAction] =
|
|
121
153
|
createCustomActionWithContext<{ instance: InstanceFields; url: string }>(
|
|
122
154
|
CoreActions.BeforeInstanceStopped,
|
|
@@ -2,13 +2,19 @@ import { Command } from 'commander'
|
|
|
2
2
|
import { Request, Response } from 'express'
|
|
3
3
|
import { DEBUG } from '../debug'
|
|
4
4
|
import { InstanceFields, InstanceId } from '../schema'
|
|
5
|
+
import { UserId } from '../schema/BaseFields'
|
|
5
6
|
|
|
6
7
|
export enum CoreFilters {
|
|
8
|
+
Settings = 'core_settings',
|
|
7
9
|
CliCommands = 'core_cli_commands',
|
|
8
10
|
ServeSlugs = 'core_serve_slugs',
|
|
9
11
|
SpawnConfig = 'core_spawn_config',
|
|
12
|
+
AuthenticateUser = 'core_authenticate_user',
|
|
10
13
|
GetInstanceByRequestInfo = 'core_get_instance_by_request_info',
|
|
11
14
|
GetInstanceById = 'core_get_instance_by_id',
|
|
15
|
+
GetOneInstanceByExactCriteria = 'core_get_one_instance_by_exact_criteria',
|
|
16
|
+
GetAllInstancesByExactCriteria = 'core_get_all_instances_by_exact_criteria',
|
|
17
|
+
InstanceConfig = 'core_instance_config',
|
|
12
18
|
ErrorSpawningInstanceMessage = 'core_error_spawning_instance_message',
|
|
13
19
|
FailedToLaunchInstanceMessage = 'core_failed_to_launch_instance_message',
|
|
14
20
|
AuthenticateRequest = 'core_authenticate_request',
|
|
@@ -16,6 +22,7 @@ export enum CoreFilters {
|
|
|
16
22
|
GetOrProvisionInstanceUrl = 'core_get_or_provision_instance_url',
|
|
17
23
|
PocketBaseLaunchHandler = 'core_pocket_base_launch_handler',
|
|
18
24
|
NewInstanceRecord = 'core_new_instance_record',
|
|
25
|
+
IsInstanceRunning = 'core_is_instance_running',
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
export type FilterHandler<TCarry, TContext> = (
|
|
@@ -30,14 +37,23 @@ const filters: {
|
|
|
30
37
|
[key: string]: FilterEntry<any>[]
|
|
31
38
|
} = {}
|
|
32
39
|
|
|
33
|
-
|
|
40
|
+
function registerFilter<TCarry, TContext extends {} = {}>(
|
|
34
41
|
filter: string,
|
|
35
42
|
handler: FilterHandler<TCarry, TContext>,
|
|
36
43
|
priority = 10,
|
|
37
|
-
) {
|
|
44
|
+
): () => void {
|
|
38
45
|
if (!(filter in filters)) filters[filter] = []
|
|
39
46
|
filters[filter]!.push({ priority, handler })
|
|
40
47
|
filters[filter]!.sort((a, b) => a.priority - b.priority)
|
|
48
|
+
|
|
49
|
+
return () => {
|
|
50
|
+
const index = filters[filter]!.findIndex(
|
|
51
|
+
(entry) => entry.handler === handler,
|
|
52
|
+
)
|
|
53
|
+
if (index !== -1) {
|
|
54
|
+
filters[filter]!.splice(index, 1)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
41
57
|
}
|
|
42
58
|
|
|
43
59
|
async function filter<TCarry, TContext extends {} = {}>(
|
|
@@ -52,7 +68,14 @@ async function filter<TCarry, TContext extends {} = {}>(
|
|
|
52
68
|
if (!filter) return initialValue
|
|
53
69
|
return filter.reduce(
|
|
54
70
|
(carry, entry) =>
|
|
55
|
-
carry
|
|
71
|
+
carry
|
|
72
|
+
.then((carryRes) => entry.handler(carryRes, context))
|
|
73
|
+
.then((carryRes) => {
|
|
74
|
+
if (DEBUG()) {
|
|
75
|
+
console.log(`f:${filterName}`, carryRes, context)
|
|
76
|
+
}
|
|
77
|
+
return carryRes
|
|
78
|
+
}),
|
|
56
79
|
Promise.resolve(initialValue),
|
|
57
80
|
) as TCarry
|
|
58
81
|
}
|
|
@@ -63,8 +86,15 @@ function createCustomFilterWithContext<TCarry, TContext extends {} = {}>(
|
|
|
63
86
|
return [
|
|
64
87
|
async (initialValue: TCarry, context: TContext) =>
|
|
65
88
|
filter<TCarry, TContext>(filterName, initialValue, context),
|
|
66
|
-
async
|
|
67
|
-
|
|
89
|
+
async <TExtraCarry = unknown>(
|
|
90
|
+
handler: FilterHandler<TCarry & TExtraCarry, TContext>,
|
|
91
|
+
priority = 10,
|
|
92
|
+
) =>
|
|
93
|
+
registerFilter<TCarry & TExtraCarry, TContext>(
|
|
94
|
+
filterName,
|
|
95
|
+
handler,
|
|
96
|
+
priority,
|
|
97
|
+
),
|
|
68
98
|
] as const
|
|
69
99
|
}
|
|
70
100
|
|
|
@@ -72,11 +102,24 @@ function createCustomFilter<TCarry>(filterName: string) {
|
|
|
72
102
|
return [
|
|
73
103
|
async (initialValue: TCarry) =>
|
|
74
104
|
filter<TCarry>(filterName, initialValue, {}),
|
|
75
|
-
async
|
|
76
|
-
|
|
105
|
+
async <TExtraCarry = unknown>(
|
|
106
|
+
handler: FilterHandler<TCarry & TExtraCarry, {}>,
|
|
107
|
+
priority = 10,
|
|
108
|
+
) =>
|
|
109
|
+
registerFilter<TCarry & TExtraCarry, {}>(filterName, handler, priority),
|
|
77
110
|
] as const
|
|
78
111
|
}
|
|
79
112
|
|
|
113
|
+
export const [doSettingsFilter, onSettingsFilter] = createCustomFilter<{
|
|
114
|
+
[_: string]: any
|
|
115
|
+
}>(CoreFilters.Settings)
|
|
116
|
+
|
|
117
|
+
export const [doAuthenticateUserFilter, onAuthenticateUserFilter] =
|
|
118
|
+
createCustomFilterWithContext<
|
|
119
|
+
UserId | undefined,
|
|
120
|
+
{ username: string; password: string }
|
|
121
|
+
>(CoreFilters.AuthenticateUser)
|
|
122
|
+
|
|
80
123
|
export const [
|
|
81
124
|
doGetInstanceByRequestInfoFilter,
|
|
82
125
|
onGetInstanceByRequestInfoFilter,
|
|
@@ -91,6 +134,22 @@ export const [doGetInstanceByIdFilter, onGetInstanceByIdFilter] =
|
|
|
91
134
|
{ instanceId: InstanceId }
|
|
92
135
|
>(CoreFilters.GetInstanceById)
|
|
93
136
|
|
|
137
|
+
export const [
|
|
138
|
+
doGetOneInstanceByExactCriteriaFilter,
|
|
139
|
+
onGetOneInstanceByExactCriteriaFilter,
|
|
140
|
+
] = createCustomFilterWithContext<
|
|
141
|
+
InstanceFields | undefined,
|
|
142
|
+
Partial<InstanceFields & { [_: string]: string | number }>
|
|
143
|
+
>(CoreFilters.GetOneInstanceByExactCriteria)
|
|
144
|
+
|
|
145
|
+
export const [
|
|
146
|
+
doGetAllInstancesByExactCriteriaFilter,
|
|
147
|
+
onGetAllInstancesByExactCriteriaFilter,
|
|
148
|
+
] = createCustomFilterWithContext<
|
|
149
|
+
InstanceFields[],
|
|
150
|
+
Partial<InstanceFields & { [_: string]: string | number }>
|
|
151
|
+
>(CoreFilters.GetAllInstancesByExactCriteria)
|
|
152
|
+
|
|
94
153
|
export const [doCliCommandsFilter, onCliCommandsFilter] = createCustomFilter<
|
|
95
154
|
Command[]
|
|
96
155
|
>(CoreFilters.CliCommands)
|
|
@@ -105,12 +164,8 @@ export const [
|
|
|
105
164
|
doGetOrProvisionInstanceUrlFilter,
|
|
106
165
|
onGetOrProvisionInstanceUrlFilter,
|
|
107
166
|
] = createCustomFilterWithContext<
|
|
108
|
-
string,
|
|
167
|
+
string | undefined,
|
|
109
168
|
{
|
|
110
|
-
req: Request
|
|
111
|
-
res: Response
|
|
112
|
-
host: string
|
|
113
|
-
subdomain: string
|
|
114
169
|
instance: InstanceFields
|
|
115
170
|
}
|
|
116
171
|
>(CoreFilters.GetOrProvisionInstanceUrl)
|
|
@@ -118,3 +173,25 @@ export const [
|
|
|
118
173
|
export const [doServeSlugsFilter, onServeSlugsFilter] = createCustomFilter<
|
|
119
174
|
string[]
|
|
120
175
|
>(CoreFilters.ServeSlugs)
|
|
176
|
+
|
|
177
|
+
export type Bind = {
|
|
178
|
+
base: string
|
|
179
|
+
src: string
|
|
180
|
+
}
|
|
181
|
+
export type Binds = {
|
|
182
|
+
data: Bind[]
|
|
183
|
+
hooks: Bind[]
|
|
184
|
+
migrations: Bind[]
|
|
185
|
+
public: Bind[]
|
|
186
|
+
}
|
|
187
|
+
export type InstanceConfig = {
|
|
188
|
+
binds: Binds
|
|
189
|
+
env: { [_: string]: string }
|
|
190
|
+
}
|
|
191
|
+
export const [doInstanceConfigFilter, onInstanceConfigFilter] =
|
|
192
|
+
createCustomFilter<InstanceConfig>(CoreFilters.InstanceConfig)
|
|
193
|
+
|
|
194
|
+
export const [doIsInstanceRunningFilter, onIsInstanceRunningFilter] =
|
|
195
|
+
createCustomFilterWithContext<boolean, { instance: InstanceFields }>(
|
|
196
|
+
CoreFilters.IsInstanceRunning,
|
|
197
|
+
)
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { doNewInstanceRecordFilter } from '../plugin'
|
|
2
|
+
import { IsoDate } from './BaseFields'
|
|
3
|
+
import { newId, pocketNow } from './util'
|
|
2
4
|
|
|
3
5
|
export type VersionId = string
|
|
4
6
|
|
|
@@ -21,16 +23,24 @@ export type InstanceSecretCollection = {
|
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export type InstanceFields = {
|
|
26
|
+
id: string
|
|
24
27
|
subdomain: string
|
|
25
28
|
version: VersionId
|
|
26
29
|
secrets: InstanceSecretCollection
|
|
27
30
|
dev: boolean // Should instance run in --dev mode
|
|
31
|
+
created: IsoDate
|
|
32
|
+
updated: IsoDate
|
|
28
33
|
}
|
|
29
34
|
|
|
30
|
-
export const mkInstance = (subdomain: string) =>
|
|
31
|
-
|
|
35
|
+
export const mkInstance = (subdomain: string, id = newId()) => {
|
|
36
|
+
const now = pocketNow()
|
|
37
|
+
return doNewInstanceRecordFilter({
|
|
38
|
+
id,
|
|
32
39
|
subdomain,
|
|
33
40
|
version: '*',
|
|
34
41
|
secrets: {},
|
|
35
42
|
dev: false,
|
|
43
|
+
created: now,
|
|
44
|
+
updated: now,
|
|
36
45
|
})
|
|
46
|
+
}
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { customAlphabet } from 'nanoid'
|
|
2
|
+
import { IsoDate } from './BaseFields'
|
|
3
|
+
|
|
4
|
+
export const newId = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10)
|
|
5
|
+
export const pocketNow = () => toPocketDate(new Date())
|
|
6
|
+
export const toPocketDate = (date: Date) => date.toISOString()
|
|
7
|
+
export const fromPocketDate = (iso: IsoDate) => new Date(iso)
|
package/src/constants.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { forEach } from '@s-libs/micro-dash'
|
|
2
1
|
import { config } from 'dotenv'
|
|
3
2
|
import envPaths from 'env-paths'
|
|
4
3
|
import { dirname, join } from 'path'
|
|
5
4
|
import { cwd } from 'process'
|
|
6
5
|
import { fileURLToPath } from 'url'
|
|
7
6
|
import { mkBoolean, mkCsvString, mkNumber, mkPath, mkString } from '../core'
|
|
8
|
-
import { InstanceId } from './common'
|
|
7
|
+
import { InstanceFields, InstanceId } from './common'
|
|
9
8
|
import { DEBUG, IS_DEV } from './common/debug'
|
|
10
9
|
import { Settings } from './core/Settings'
|
|
11
10
|
|
|
@@ -17,11 +16,7 @@ config({ path: [join(_PH_HOME, '.env'), join(cwd(), `.env`)] })
|
|
|
17
16
|
|
|
18
17
|
export const _PH_PROJECT_DIR = join(__dirname, '..')
|
|
19
18
|
export const _APEX_DOMAIN = process.env.APEX_DOMAIN || 'pockethost.lvh.me'
|
|
20
|
-
export const _HTTP_PROTOCOL = process.env.HTTP_PROTOCOL || `https:`
|
|
21
19
|
|
|
22
|
-
export function isSettingKey(key: string): key is keyof typeof settings {
|
|
23
|
-
return key in settings
|
|
24
|
-
}
|
|
25
20
|
export const settings = Settings({
|
|
26
21
|
PH_PLUGINS: mkCsvString([
|
|
27
22
|
`@pockethost/plugin-console-logger`,
|
|
@@ -32,6 +27,7 @@ export const settings = Settings({
|
|
|
32
27
|
PH_HOME: mkPath(_PH_HOME),
|
|
33
28
|
PH_PROJECT_DIR: mkPath(_PH_PROJECT_DIR),
|
|
34
29
|
|
|
30
|
+
PH_INTERNAL_HOST: mkString('localhost'),
|
|
35
31
|
PH_APEX_DOMAIN: mkString(_APEX_DOMAIN),
|
|
36
32
|
|
|
37
33
|
PH_PORT: mkNumber(3000),
|
|
@@ -39,6 +35,7 @@ export const settings = Settings({
|
|
|
39
35
|
PH_DATA_DIR: mkPath(join(_PH_HOME, 'data'), { create: true }),
|
|
40
36
|
PH_DEV: mkBoolean(IS_DEV()),
|
|
41
37
|
PH_DEBUG: mkBoolean(DEBUG()),
|
|
38
|
+
PH_INTERNAL_APP_SECRET: mkString(''),
|
|
42
39
|
})
|
|
43
40
|
|
|
44
41
|
/** Accessors */
|
|
@@ -57,13 +54,17 @@ export const DATA_DIR = (...paths: string[]) =>
|
|
|
57
54
|
export const NODE_ENV = () => process.env.NODE_ENV
|
|
58
55
|
export const INSTANCE_DATA_DIR = (id: InstanceId, ...paths: string[]) =>
|
|
59
56
|
join(DATA_DIR(), id, ...paths)
|
|
60
|
-
|
|
61
|
-
export const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
57
|
+
export const INTERNAL_HOST = () => settings.PH_INTERNAL_HOST
|
|
58
|
+
export const INTERNAL_APP_SECRET = () => settings.PH_INTERNAL_APP_SECRET
|
|
59
|
+
export const INTERNAL_APP_URL = (...paths: string[]) =>
|
|
60
|
+
`${HTTP_PROTOCOL()}//${INTERNAL_HOST()}:${PORT()}/${INTERNAL_APP_PREFIX}/${join(
|
|
61
|
+
...paths,
|
|
62
|
+
)}`
|
|
63
|
+
export const INTERNAL_APP_PREFIX = `_internal`
|
|
64
|
+
export const INTERNAL_APP_AUTH_HEADER = `x-pockethost-authorization`
|
|
65
|
+
export const HTTP_PROTOCOL = () => (PORT() === 443 ? 'https:' : 'http:')
|
|
66
|
+
export const PUBLIC_INSTANCE_URL = ({ subdomain }: Partial<InstanceFields>) => {
|
|
67
|
+
const url = new URL(`${HTTP_PROTOCOL()}//${subdomain}.${APEX_DOMAIN()}`)
|
|
68
|
+
url.port = `${PORT() === 80 || PORT() == 443 ? '' : PORT()}`
|
|
69
|
+
return url
|
|
69
70
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
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/index.ts
CHANGED
package/src/core/tryFetch.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import fetch, { Response } from 'node-fetch'
|
|
1
|
+
import fetch, { RequestInit, Response } from 'node-fetch'
|
|
2
2
|
import { LoggerService } from '../common'
|
|
3
3
|
|
|
4
4
|
export const TRYFETCH_RETRY_MS = 50
|
|
@@ -6,7 +6,7 @@ export const TRYFETCH_RETRY_MS = 50
|
|
|
6
6
|
export type TryFetchConfig = {
|
|
7
7
|
preflight: () => Promise<boolean>
|
|
8
8
|
retryMs: number
|
|
9
|
-
}
|
|
9
|
+
} & RequestInit
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* @param url The URL to fetch
|
|
@@ -53,7 +53,10 @@ export const tryFetch = async (
|
|
|
53
53
|
}
|
|
54
54
|
try {
|
|
55
55
|
dbg(`Fetch: START`)
|
|
56
|
-
const res = await fetch(url, {
|
|
56
|
+
const res = await fetch(url, {
|
|
57
|
+
...config,
|
|
58
|
+
signal: AbortSignal.timeout(500),
|
|
59
|
+
})
|
|
57
60
|
dbg(`Fetch: SUCCESS`)
|
|
58
61
|
resolve(res)
|
|
59
62
|
} catch (e) {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
interface Require {
|
|
2
|
+
(id: string): any
|
|
3
|
+
}
|
|
4
|
+
declare var require: Require
|
|
5
|
+
|
|
6
|
+
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/console) */
|
|
7
|
+
interface Console {
|
|
8
|
+
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/assert) */
|
|
9
|
+
assert(condition?: boolean, ...data: any[]): void
|
|
10
|
+
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/clear) */
|
|
11
|
+
clear(): void
|
|
12
|
+
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/count) */
|
|
13
|
+
count(label?: string): void
|
|
14
|
+
/**
|
|
15
|
+
* [MDN
|
|
16
|
+
* Reference](https://developer.mozilla.org/docs/Web/API/console/countReset)
|
|
17
|
+
*/
|
|
18
|
+
countReset(label?: string): void
|
|
19
|
+
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/debug) */
|
|
20
|
+
debug(...data: any[]): void
|
|
21
|
+
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/dir) */
|
|
22
|
+
dir(item?: any, options?: any): void
|
|
23
|
+
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/dirxml) */
|
|
24
|
+
dirxml(...data: any[]): void
|
|
25
|
+
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/error) */
|
|
26
|
+
error(...data: any[]): void
|
|
27
|
+
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/group) */
|
|
28
|
+
group(...data: any[]): void
|
|
29
|
+
/**
|
|
30
|
+
* [MDN
|
|
31
|
+
* Reference](https://developer.mozilla.org/docs/Web/API/console/groupCollapsed)
|
|
32
|
+
*/
|
|
33
|
+
groupCollapsed(...data: any[]): void
|
|
34
|
+
/**
|
|
35
|
+
* [MDN
|
|
36
|
+
* Reference](https://developer.mozilla.org/docs/Web/API/console/groupEnd)
|
|
37
|
+
*/
|
|
38
|
+
groupEnd(): void
|
|
39
|
+
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/info) */
|
|
40
|
+
info(...data: any[]): void
|
|
41
|
+
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log) */
|
|
42
|
+
log(...data: any[]): void
|
|
43
|
+
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/table) */
|
|
44
|
+
table(tabularData?: any, properties?: string[]): void
|
|
45
|
+
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/time) */
|
|
46
|
+
time(label?: string): void
|
|
47
|
+
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/timeEnd) */
|
|
48
|
+
timeEnd(label?: string): void
|
|
49
|
+
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/timeLog) */
|
|
50
|
+
timeLog(label?: string, ...data: any[]): void
|
|
51
|
+
timeStamp(label?: string): void
|
|
52
|
+
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/trace) */
|
|
53
|
+
trace(...data: any[]): void
|
|
54
|
+
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/warn) */
|
|
55
|
+
warn(...data: any[]): void
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
declare var console: Console
|