pockethost 1.1.0 → 1.3.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 +43 -0
- package/package.json +8 -3
- package/src/cli/commands/PluginCommand/index.ts +0 -1
- package/src/cli/index.ts +17 -5
- package/src/common/Logger.ts +6 -2
- package/src/common/error.ts +16 -0
- package/src/common/index.ts +1 -1
- package/src/common/plugin/action.ts +101 -34
- package/src/common/plugin/filter.ts +89 -39
- package/src/common/plugin/index.ts +2 -8
- package/src/common/schema/Instance.ts +13 -22
- package/src/common/schema/index.ts +0 -3
- package/src/constants.ts +1 -0
- package/src/server.ts +48 -52
- package/src/common/plugin/helpers.ts +0 -17
- package/src/common/pocketbase-client-helpers/RestHelper.ts +0 -69
- package/src/common/pocketbase-client-helpers/index.ts +0 -1
- package/src/common/schema/Rest/CreateInstance.ts +0 -20
- package/src/common/schema/Rest/DeleteInstance.ts +0 -21
- package/src/common/schema/Rest/GetUserTokenInfo.ts +0 -22
- package/src/common/schema/Rest/UpdateInstance.ts +0 -61
- package/src/common/schema/Rest/index.ts +0 -23
- package/src/common/schema/User.ts +0 -24
- package/src/common/schema/types.ts +0 -18
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# pockethost
|
|
2
|
+
|
|
3
|
+
## 1.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 96f355a: Now auto-creates first admin account by default
|
|
8
|
+
- 2fc2eeb: Added before/after instance start/stop events
|
|
9
|
+
- 3f99ea0: Added subdomain to request action and filter context
|
|
10
|
+
- 1384a86: BREAKING: refactored actions/filters
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 6812784: Added improved error parsing support
|
|
15
|
+
- da82667: Fix: properly determine hostname when running on nonstandard port
|
|
16
|
+
- cc4aa02: Moved mothership-related code into mothership plugin
|
|
17
|
+
- Updated dependencies [9d626ec]
|
|
18
|
+
- @pockethost/plugin-launcher-spawn@0.1.0
|
|
19
|
+
- @pockethost/plugin-console-logger@0.1.0
|
|
20
|
+
|
|
21
|
+
## 1.2.0
|
|
22
|
+
|
|
23
|
+
### Minor Changes
|
|
24
|
+
|
|
25
|
+
- 72a6c6d: --version now displays version number
|
|
26
|
+
- ac10d1f: Display notification when new pockethost update is available.
|
|
27
|
+
- f9aea10: Debugging info removed from `plugin ls`
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- 76d57f6: CHANGELOG.md is now published with package.
|
|
32
|
+
- 3783447: plugin-console-logger and plugin-launcher-spawn are now automatically installed.
|
|
33
|
+
|
|
34
|
+
## 1.1.0
|
|
35
|
+
|
|
36
|
+
### Minor Changes
|
|
37
|
+
|
|
38
|
+
- 1695b59: New feature: list plugins (pockethost plugin ls)
|
|
39
|
+
|
|
40
|
+
### Patch Changes
|
|
41
|
+
|
|
42
|
+
- 2514c09: Fix: make sure PH_PLUGINS contains only unique values.
|
|
43
|
+
- 1228840: Fixed extra newline when listing configs
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pockethost",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "Ben Allfree",
|
|
6
6
|
"url": "https://github.com/benallfree"
|
|
@@ -37,7 +37,11 @@
|
|
|
37
37
|
"tsx": "^4.15.7",
|
|
38
38
|
"type-fest": "^4.6.0",
|
|
39
39
|
"typescript": "^5.4.5",
|
|
40
|
-
"
|
|
40
|
+
"update-notifier": "^7.0.0",
|
|
41
|
+
"url-pattern": "^1.0.3",
|
|
42
|
+
"@pockethost/plugin-console-logger": "^0.1.0",
|
|
43
|
+
"@pockethost/plugin-launcher-spawn": "^0.1.0",
|
|
44
|
+
"@pockethost/plugin-auto-admin": "^0.0.1"
|
|
41
45
|
},
|
|
42
46
|
"devDependencies": {
|
|
43
47
|
"@types/express": "^4.17.21",
|
|
@@ -45,7 +49,8 @@
|
|
|
45
49
|
"@types/json-stringify-safe": "^5.0.2",
|
|
46
50
|
"@types/node": "^20.8.10",
|
|
47
51
|
"@types/semver": "^7.5.4",
|
|
48
|
-
"@types/tmp": "^0.2.5"
|
|
52
|
+
"@types/tmp": "^0.2.5",
|
|
53
|
+
"@types/update-notifier": "^6.0.8"
|
|
49
54
|
},
|
|
50
55
|
"scripts": {
|
|
51
56
|
"check-types": "tsc --noEmit ",
|
package/src/cli/index.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
|
|
3
3
|
import { uniq } from '@s-libs/micro-dash'
|
|
4
|
-
import {
|
|
4
|
+
import { program } from 'commander'
|
|
5
|
+
import { readFileSync } from 'fs'
|
|
6
|
+
import updateNotifier from 'update-notifier'
|
|
5
7
|
import {
|
|
6
8
|
DEBUG,
|
|
7
9
|
LogLevelName,
|
|
8
10
|
LoggerService,
|
|
9
11
|
PH_PLUGINS,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
PH_PROJECT_DIR,
|
|
13
|
+
doCliCommandsFilter,
|
|
12
14
|
loadPlugins,
|
|
13
15
|
} from '../../core'
|
|
14
16
|
import { ConfigCommand } from './commands/ConfigCommand'
|
|
@@ -21,14 +23,24 @@ export const { dbg, info, error } = LoggerService({
|
|
|
21
23
|
|
|
22
24
|
export const main = async () => {
|
|
23
25
|
await loadPlugins(uniq(PH_PLUGINS()))
|
|
26
|
+
const pkg = await JSON.parse(
|
|
27
|
+
readFileSync(PH_PROJECT_DIR(`package.json`), 'utf8').toString(),
|
|
28
|
+
)
|
|
29
|
+
updateNotifier({ pkg }).notify()
|
|
24
30
|
|
|
25
|
-
|
|
31
|
+
const { version } = pkg
|
|
26
32
|
|
|
27
|
-
|
|
33
|
+
program
|
|
34
|
+
.name('pockethost')
|
|
35
|
+
.description('Multitenant PocketBase hosting')
|
|
36
|
+
.version(version)
|
|
37
|
+
|
|
38
|
+
const commands = await doCliCommandsFilter([
|
|
28
39
|
ServeCommand(),
|
|
29
40
|
ConfigCommand(),
|
|
30
41
|
PluginCommand(),
|
|
31
42
|
])
|
|
43
|
+
|
|
32
44
|
for (const command of commands) {
|
|
33
45
|
program.addCommand(command)
|
|
34
46
|
}
|
package/src/common/Logger.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import chalk from 'chalk'
|
|
4
4
|
import stringify from 'json-stringify-safe'
|
|
5
|
-
import {
|
|
5
|
+
import { doLogAction, mergeConfig, mkSingleton } from '.'
|
|
6
6
|
|
|
7
7
|
export type LoggerConfig = {
|
|
8
8
|
level: LogLevelName
|
|
@@ -74,7 +74,11 @@ export const createLogger = (config: Partial<LoggerConfig>) => {
|
|
|
74
74
|
.join(' ')
|
|
75
75
|
|
|
76
76
|
const _log = (levelIn: LogLevelName, ...args: any[]) => {
|
|
77
|
-
|
|
77
|
+
doLogAction({
|
|
78
|
+
currentLevel: _config.level,
|
|
79
|
+
levelIn,
|
|
80
|
+
args,
|
|
81
|
+
})
|
|
78
82
|
}
|
|
79
83
|
|
|
80
84
|
const raw = (...args: any[]) => {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { keys, map } from '@s-libs/micro-dash'
|
|
2
|
+
import { ClientResponseError } from 'pocketbase'
|
|
3
|
+
|
|
4
|
+
export const parseError = (e: any): string[] => {
|
|
5
|
+
if (e instanceof ClientResponseError) {
|
|
6
|
+
if (e.data.message && keys(e.data.data).length === 0)
|
|
7
|
+
return [e.data.message]
|
|
8
|
+
return map(e.data.data, (v, k) => (v ? v.message : undefined)).filter(
|
|
9
|
+
(v) => !!v,
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
if (e instanceof Error) {
|
|
13
|
+
return [e.message]
|
|
14
|
+
}
|
|
15
|
+
return [e.toString()]
|
|
16
|
+
}
|
package/src/common/index.ts
CHANGED
|
@@ -3,10 +3,10 @@ export * from './Logger'
|
|
|
3
3
|
export * from './TimerManager'
|
|
4
4
|
export * from './assert'
|
|
5
5
|
export * from './debug'
|
|
6
|
+
export * from './error'
|
|
6
7
|
export * from './events'
|
|
7
8
|
export * from './mergeConfig'
|
|
8
9
|
export * from './mkSingleton'
|
|
9
10
|
export * from './plugin'
|
|
10
|
-
export * from './pocketbase-client-helpers'
|
|
11
11
|
export * from './promiseSerializer'
|
|
12
12
|
export * from './schema'
|
|
@@ -1,60 +1,127 @@
|
|
|
1
|
+
import { Express, Request, Response } from 'express'
|
|
2
|
+
import { LogLevelName } from '../Logger'
|
|
1
3
|
import { DEBUG } from '../debug'
|
|
4
|
+
import { InstanceFields } from '../schema'
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
enum CoreActions {
|
|
7
|
+
Init = 'core_init',
|
|
8
|
+
AppMounted = 'core_on_app_mount',
|
|
9
|
+
AfterServeStart = 'core_after_serve_start',
|
|
4
10
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Core_OnAppMount = 'on_app_mount',
|
|
9
|
-
Core_AfterServeStart = 'after_serve_start',
|
|
11
|
+
Cli_Before_ServeCommand = 'core_before_serve',
|
|
12
|
+
Log = 'core_log',
|
|
13
|
+
InstanceLog = 'core_instance_log',
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
Core_Log = 'log',
|
|
13
|
-
Core_InstanceLog = 'instance_log',
|
|
15
|
+
StartInstance = 'core_start_instance',
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
BeforeInstanceStarted = 'core_before_instance_started',
|
|
18
|
+
AfterInstanceStarted = 'core_after_instance_started',
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
BeforeInstanceStopped = 'core_before_instance_stopped',
|
|
21
|
+
AfterInstanceStopped = 'core_after_instance_stopped',
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
Core_AfterInstanceStopped = 'after_instance_stopped',
|
|
23
|
+
AfterInstanceFound = 'core_after_instance_found',
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
Core_OnRequestError = 'edge_on_request_error',
|
|
26
|
-
Core_Request = 'edge_request',
|
|
27
|
-
|
|
28
|
-
/** Firewall actions */
|
|
29
|
-
Waf_OnRequestError = 'waf_on_request_error',
|
|
30
|
-
Waf_OnAppMiddleware = 'waf_on_app_middleware',
|
|
31
|
-
|
|
32
|
-
/** Edge actions */
|
|
33
|
-
/** Health actions */
|
|
34
|
-
Health_OnMessage = 'health_on_message',
|
|
35
|
-
Health_OnError = 'health_on_error',
|
|
25
|
+
RequestError = 'core_edge_on_request_error',
|
|
26
|
+
Request = 'core_edge_request',
|
|
36
27
|
}
|
|
37
28
|
|
|
38
29
|
const actions: {
|
|
39
|
-
[key
|
|
30
|
+
[key: string]: ActionHandler<any>[]
|
|
40
31
|
} = {}
|
|
41
32
|
|
|
42
|
-
export type ActionHandler =
|
|
33
|
+
export type ActionHandler<TContext = {}> = (
|
|
34
|
+
context: TContext extends never ? [] : TContext,
|
|
35
|
+
) => Promise<void>
|
|
43
36
|
|
|
44
|
-
|
|
45
|
-
action:
|
|
37
|
+
async function registerAction(
|
|
38
|
+
action: string,
|
|
46
39
|
handler: (...args: any[]) => Promise<void>,
|
|
47
40
|
) {
|
|
48
41
|
if (!(action in actions)) actions[action] = []
|
|
49
42
|
actions[action]!.push(handler)
|
|
50
43
|
}
|
|
51
44
|
|
|
52
|
-
|
|
53
|
-
if (DEBUG() && actionName !==
|
|
45
|
+
async function action(actionName: string, context: any) {
|
|
46
|
+
if (DEBUG() && actionName !== CoreActions.Log) {
|
|
54
47
|
console.log(`a:${actionName}`)
|
|
55
48
|
}
|
|
56
49
|
const action = actions[actionName]
|
|
57
50
|
if (!action) return false
|
|
58
|
-
await Promise.all(action.map((handler) => handler(
|
|
51
|
+
await Promise.all(action.map((handler) => handler(context)))
|
|
59
52
|
return true
|
|
60
53
|
}
|
|
54
|
+
|
|
55
|
+
export function createCustomActionWithContext<TContext extends {}>(
|
|
56
|
+
actionName: CoreActions,
|
|
57
|
+
) {
|
|
58
|
+
return [
|
|
59
|
+
async (context: TContext) => action(actionName, context),
|
|
60
|
+
async (handler: ActionHandler<TContext>) =>
|
|
61
|
+
registerAction(actionName, handler),
|
|
62
|
+
] as const
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function createCustomAction(actionName: CoreActions) {
|
|
66
|
+
return [
|
|
67
|
+
async () => action(actionName, {}),
|
|
68
|
+
async (handler: ActionHandler) => registerAction(actionName, handler),
|
|
69
|
+
] as const
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const [doRequestErrorAction, onRequestErrorAction] =
|
|
73
|
+
createCustomActionWithContext<{
|
|
74
|
+
err: Error
|
|
75
|
+
}>(CoreActions.RequestError)
|
|
76
|
+
|
|
77
|
+
export const [doAfterServerStartAction, onAfterServerStartAction] =
|
|
78
|
+
createCustomAction(CoreActions.AfterServeStart)
|
|
79
|
+
|
|
80
|
+
export const [doLogAction, onLogAction] = createCustomActionWithContext<{
|
|
81
|
+
currentLevel: LogLevelName
|
|
82
|
+
levelIn: LogLevelName
|
|
83
|
+
args: any[]
|
|
84
|
+
}>(CoreActions.Log)
|
|
85
|
+
|
|
86
|
+
export const [doInstanceLogAction, onInstanceLogAction] =
|
|
87
|
+
createCustomActionWithContext<{
|
|
88
|
+
instance: InstanceFields
|
|
89
|
+
type: 'stdout' | 'stderr'
|
|
90
|
+
data: string
|
|
91
|
+
}>(CoreActions.InstanceLog)
|
|
92
|
+
|
|
93
|
+
export const [doAppMountedAction, onAppMountedAction] =
|
|
94
|
+
createCustomActionWithContext<{ app: Express }>(CoreActions.AppMounted)
|
|
95
|
+
|
|
96
|
+
export const [doIncomingRequestAction, onIncomingRequestAction] =
|
|
97
|
+
createCustomActionWithContext<{ req: Request; res: Response; host: string }>(
|
|
98
|
+
CoreActions.Request,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
export const [doAfterInstanceFoundAction, onAfterInstanceFoundAction] =
|
|
102
|
+
createCustomActionWithContext<{
|
|
103
|
+
req: Request
|
|
104
|
+
res: Response
|
|
105
|
+
host: string
|
|
106
|
+
instance: InstanceFields
|
|
107
|
+
}>(CoreActions.AfterInstanceFound)
|
|
108
|
+
|
|
109
|
+
export const [doBeforeInstanceStartedAction, onBeforeInstanceStartedAction] =
|
|
110
|
+
createCustomActionWithContext<{ instance: InstanceFields }>(
|
|
111
|
+
CoreActions.BeforeInstanceStarted,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
export const [doAfterInstanceStartedAction, onAfterInstanceStartedAction] =
|
|
115
|
+
createCustomActionWithContext<{ instance: InstanceFields; url: string }>(
|
|
116
|
+
CoreActions.AfterInstanceStarted,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
export const [doBeforeInstanceStoppedAction, onBeforeInstanceStoppedAction] =
|
|
120
|
+
createCustomActionWithContext<{ instance: InstanceFields; url: string }>(
|
|
121
|
+
CoreActions.BeforeInstanceStopped,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
export const [doAfterInstanceStoppedAction, onAfterInstanceStoppedAction] =
|
|
125
|
+
createCustomActionWithContext<{ instance: InstanceFields; url: string }>(
|
|
126
|
+
CoreActions.AfterInstanceStopped,
|
|
127
|
+
)
|
|
@@ -1,56 +1,48 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { Request, Response } from 'express'
|
|
1
3
|
import { DEBUG } from '../debug'
|
|
4
|
+
import { InstanceFields, InstanceId } from '../schema'
|
|
2
5
|
|
|
3
|
-
export enum
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Core_NewInstanceRecord = 'core_new_instance_record',
|
|
16
|
-
|
|
17
|
-
/** Mothership Filters */
|
|
18
|
-
Mothership_MaintenanceMode_Message = 'mothership_maintenance_mode_message',
|
|
19
|
-
Mothership_UnverifiedAccountError_Message = 'mothership_unverified_account_error_message',
|
|
20
|
-
Mothership_PublicUrl = 'mothership_public_url',
|
|
21
|
-
|
|
22
|
-
/** Firewall Filters */
|
|
23
|
-
Waf_VHosts = 'waf_vhosts',
|
|
24
|
-
Waf_OnError_Message = 'waf_on_error_message',
|
|
25
|
-
Waf_AppMount = 'waf_app_mount',
|
|
26
|
-
|
|
27
|
-
/** Edge self checks */
|
|
28
|
-
EdgeSelfCheck_Checks = 'edge_self_check_checks',
|
|
6
|
+
export enum CoreFilters {
|
|
7
|
+
CliCommands = 'core_cli_commands',
|
|
8
|
+
SpawnConfig = 'core_spawn_config',
|
|
9
|
+
GetInstanceByRequestInfo = 'core_get_instance_by_request_info',
|
|
10
|
+
GetInstanceById = 'core_get_instance_by_id',
|
|
11
|
+
ErrorSpawningInstanceMessage = 'core_error_spawning_instance_message',
|
|
12
|
+
FailedToLaunchInstanceMessage = 'core_failed_to_launch_instance_message',
|
|
13
|
+
AuthenticateRequest = 'core_authenticate_request',
|
|
14
|
+
RequestError_Message = 'core_request_error_message',
|
|
15
|
+
GetOrProvisionInstanceUrl = 'core_get_or_provision_instance_url',
|
|
16
|
+
PocketBaseLaunchHandler = 'core_pocket_base_launch_handler',
|
|
17
|
+
NewInstanceRecord = 'core_new_instance_record',
|
|
29
18
|
}
|
|
30
19
|
|
|
31
|
-
export type FilterHandler<
|
|
20
|
+
export type FilterHandler<TCarry, TContext> = (
|
|
21
|
+
carry: TCarry,
|
|
22
|
+
context: TContext,
|
|
23
|
+
) => TCarry | Promise<TCarry>
|
|
32
24
|
type FilterEntry<T> = {
|
|
33
25
|
priority: number
|
|
34
|
-
handler: FilterHandler<T>
|
|
26
|
+
handler: FilterHandler<T, any>
|
|
35
27
|
}
|
|
36
28
|
const filters: {
|
|
37
|
-
[key
|
|
29
|
+
[key: string]: FilterEntry<any>[]
|
|
38
30
|
} = {}
|
|
39
31
|
|
|
40
|
-
|
|
41
|
-
filter:
|
|
42
|
-
handler: FilterHandler<
|
|
43
|
-
priority
|
|
32
|
+
async function registerFilter<TCarry, TContext extends {} = {}>(
|
|
33
|
+
filter: string,
|
|
34
|
+
handler: FilterHandler<TCarry, TContext>,
|
|
35
|
+
priority = 10,
|
|
44
36
|
) {
|
|
45
37
|
if (!(filter in filters)) filters[filter] = []
|
|
46
38
|
filters[filter]!.push({ priority, handler })
|
|
47
39
|
filters[filter]!.sort((a, b) => a.priority - b.priority)
|
|
48
40
|
}
|
|
49
41
|
|
|
50
|
-
|
|
51
|
-
filterName:
|
|
52
|
-
initialValue:
|
|
53
|
-
|
|
42
|
+
async function filter<TCarry, TContext extends {} = {}>(
|
|
43
|
+
filterName: string,
|
|
44
|
+
initialValue: TCarry,
|
|
45
|
+
context: TContext,
|
|
54
46
|
) {
|
|
55
47
|
if (DEBUG()) {
|
|
56
48
|
console.log(`f:${filterName}`)
|
|
@@ -59,7 +51,65 @@ export async function filter<TRet, TIn = TRet>(
|
|
|
59
51
|
if (!filter) return initialValue
|
|
60
52
|
return filter.reduce(
|
|
61
53
|
(carry, entry) =>
|
|
62
|
-
carry.then((carryRes) => entry.handler(carryRes,
|
|
54
|
+
carry.then((carryRes) => entry.handler(carryRes, context)),
|
|
63
55
|
Promise.resolve(initialValue),
|
|
64
|
-
) as
|
|
56
|
+
) as TCarry
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function createCustomFilterWithContext<TCarry, TContext extends {} = {}>(
|
|
60
|
+
filterName: CoreFilters,
|
|
61
|
+
) {
|
|
62
|
+
return [
|
|
63
|
+
async (initialValue: TCarry, context: TContext) =>
|
|
64
|
+
filter<TCarry, TContext>(filterName, initialValue, context),
|
|
65
|
+
async (handler: FilterHandler<TCarry, TContext>, priority = 10) =>
|
|
66
|
+
registerFilter<TCarry, TContext>(filterName, handler, priority),
|
|
67
|
+
] as const
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function createCustomFilter<TCarry>(filterName: string) {
|
|
71
|
+
return [
|
|
72
|
+
async (initialValue: TCarry) =>
|
|
73
|
+
filter<TCarry>(filterName, initialValue, {}),
|
|
74
|
+
async (handler: FilterHandler<TCarry, {}>, priority = 10) =>
|
|
75
|
+
registerFilter<TCarry, {}>(filterName, handler, priority),
|
|
76
|
+
] as const
|
|
65
77
|
}
|
|
78
|
+
|
|
79
|
+
export const [
|
|
80
|
+
doGetInstanceByRequestInfoFilter,
|
|
81
|
+
onGetInstanceByRequestInfoFilter,
|
|
82
|
+
] = createCustomFilterWithContext<
|
|
83
|
+
InstanceFields | undefined,
|
|
84
|
+
{ req: Request; res: Response; host: string; subdomain: string }
|
|
85
|
+
>(CoreFilters.GetInstanceByRequestInfo)
|
|
86
|
+
|
|
87
|
+
export const [doGetInstanceByIdFilter, onGetInstanceByIdFilter] =
|
|
88
|
+
createCustomFilterWithContext<
|
|
89
|
+
InstanceFields | undefined,
|
|
90
|
+
{ instanceId: InstanceId }
|
|
91
|
+
>(CoreFilters.GetInstanceById)
|
|
92
|
+
|
|
93
|
+
export const [doCliCommandsFilter, onCliCommandsFilter] = createCustomFilter<
|
|
94
|
+
Command[]
|
|
95
|
+
>(CoreFilters.CliCommands)
|
|
96
|
+
|
|
97
|
+
export const [doNewInstanceRecordFilter, onNewInstanceRecordFilter] =
|
|
98
|
+
createCustomFilter<InstanceFields>(CoreFilters.NewInstanceRecord)
|
|
99
|
+
|
|
100
|
+
export const [doRequestErrorMessageFilter, onRequestErrorMessageFilter] =
|
|
101
|
+
createCustomFilter<string>(CoreFilters.RequestError_Message)
|
|
102
|
+
|
|
103
|
+
export const [
|
|
104
|
+
doGetOrProvisionInstanceUrlFilter,
|
|
105
|
+
onGetOrProvisionInstanceUrlFilter,
|
|
106
|
+
] = createCustomFilterWithContext<
|
|
107
|
+
string,
|
|
108
|
+
{
|
|
109
|
+
req: Request
|
|
110
|
+
res: Response
|
|
111
|
+
host: string
|
|
112
|
+
subdomain: string
|
|
113
|
+
instance: InstanceFields
|
|
114
|
+
}
|
|
115
|
+
>(CoreFilters.GetOrProvisionInstanceUrl)
|
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import { isString } from '@s-libs/micro-dash'
|
|
2
|
-
import { registerAction } from './action'
|
|
3
|
-
import { registerFilter } from './filter'
|
|
4
2
|
export * from './action'
|
|
5
3
|
export * from './filter'
|
|
6
|
-
export * from './helpers'
|
|
7
4
|
|
|
8
5
|
export type PocketHostPlugin = (api: PocketHostPluginApi) => Promise<void>
|
|
9
6
|
|
|
10
|
-
export type PocketHostPluginApi = {
|
|
11
|
-
registerAction: typeof registerAction
|
|
12
|
-
registerFilter: typeof registerFilter
|
|
13
|
-
}
|
|
7
|
+
export type PocketHostPluginApi = {}
|
|
14
8
|
|
|
15
9
|
export const loadPlugins = async (plugins: (string | PocketHostPlugin)[]) => {
|
|
16
10
|
await Promise.all(
|
|
@@ -20,7 +14,7 @@ export const loadPlugins = async (plugins: (string | PocketHostPlugin)[]) => {
|
|
|
20
14
|
const plugin = await import(pluginPathOrModule)
|
|
21
15
|
return plugin.default
|
|
22
16
|
})()
|
|
23
|
-
await plugin({
|
|
17
|
+
await plugin({})
|
|
24
18
|
}),
|
|
25
19
|
)
|
|
26
20
|
return plugins
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { doNewInstanceRecordFilter } from '../plugin'
|
|
2
2
|
|
|
3
3
|
export type VersionId = string
|
|
4
4
|
|
|
@@ -13,33 +13,24 @@ export enum InstanceStatus {
|
|
|
13
13
|
Failed = 'failed',
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
export type InstanceId = string
|
|
16
17
|
export type InstanceSecretKey = string
|
|
17
18
|
export type InstanceSecretValue = string
|
|
18
19
|
export type InstanceSecretCollection = {
|
|
19
20
|
[name: InstanceSecretKey]: InstanceSecretValue
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
export type InstanceFields =
|
|
23
|
-
subdomain:
|
|
24
|
-
uid: UserId
|
|
25
|
-
status: InstanceStatus
|
|
23
|
+
export type InstanceFields = {
|
|
24
|
+
subdomain: string
|
|
26
25
|
version: VersionId
|
|
27
|
-
secrets: InstanceSecretCollection
|
|
28
|
-
|
|
29
|
-
suspension: string
|
|
30
|
-
syncAdmin: boolean
|
|
31
|
-
cname: string
|
|
32
|
-
dev: boolean
|
|
33
|
-
cname_active: boolean
|
|
34
|
-
notifyMaintenanceMode: boolean
|
|
26
|
+
secrets: InstanceSecretCollection
|
|
27
|
+
dev: boolean // Should instance run in --dev mode
|
|
35
28
|
}
|
|
36
29
|
|
|
37
|
-
export
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
export type InstanceRecordsById = { [_: RecordId]: InstanceFields }
|
|
30
|
+
export const mkInstance = (subdomain: string) =>
|
|
31
|
+
doNewInstanceRecordFilter({
|
|
32
|
+
subdomain,
|
|
33
|
+
version: '*',
|
|
34
|
+
secrets: {},
|
|
35
|
+
dev: false,
|
|
36
|
+
})
|
package/src/constants.ts
CHANGED
package/src/server.ts
CHANGED
|
@@ -2,36 +2,36 @@ import express, { ErrorRequestHandler } from 'express'
|
|
|
2
2
|
import 'express-async-errors'
|
|
3
3
|
import { default as httpProxy } from 'http-proxy'
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
APEX_DOMAIN,
|
|
6
6
|
LoggerService,
|
|
7
7
|
PORT,
|
|
8
|
-
PocketHostAction,
|
|
9
|
-
PocketHostFilter,
|
|
10
|
-
action,
|
|
11
8
|
asyncExitHook,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
doAfterInstanceFoundAction,
|
|
10
|
+
doAfterServerStartAction,
|
|
11
|
+
doAppMountedAction,
|
|
12
|
+
doGetInstanceByRequestInfoFilter,
|
|
13
|
+
doGetOrProvisionInstanceUrlFilter,
|
|
14
|
+
doIncomingRequestAction,
|
|
15
|
+
doRequestErrorAction,
|
|
16
|
+
doRequestErrorMessageFilter,
|
|
17
|
+
onInstanceLogAction,
|
|
15
18
|
} from '../core'
|
|
16
19
|
|
|
17
20
|
export const serve = async () => {
|
|
18
21
|
const _proxyLogger = LoggerService().create('ProxyService')
|
|
19
22
|
const { dbg, error, info, trace, warn } = _proxyLogger
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
},
|
|
33
|
-
99,
|
|
34
|
-
)
|
|
24
|
+
onInstanceLogAction(async ({ instance, type, data }) => {
|
|
25
|
+
dbg(data.toString())
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
// registerFilter(
|
|
29
|
+
// CoreFilters.Waf_VHosts,
|
|
30
|
+
// async (routes: { [_: string]: string }) => {
|
|
31
|
+
// return { ...routes, '*': `http://localhost:${PORT()}` }
|
|
32
|
+
// },
|
|
33
|
+
// 99,
|
|
34
|
+
// )
|
|
35
35
|
|
|
36
36
|
const proxy = httpProxy.createProxyServer({})
|
|
37
37
|
proxy.on('error', (err, req, res, target) => {
|
|
@@ -47,59 +47,55 @@ export const serve = async () => {
|
|
|
47
47
|
})
|
|
48
48
|
|
|
49
49
|
const errorHandler: ErrorRequestHandler = async (err, req, res, next) => {
|
|
50
|
-
|
|
51
|
-
res
|
|
52
|
-
.status(500)
|
|
53
|
-
.send(
|
|
54
|
-
await filter(
|
|
55
|
-
PocketHostFilter.Core_RequestError_Message,
|
|
56
|
-
err.toString(),
|
|
57
|
-
),
|
|
58
|
-
)
|
|
50
|
+
doRequestErrorAction({ err })
|
|
51
|
+
res.status(500).send(await doRequestErrorMessageFilter(err.toString()))
|
|
59
52
|
}
|
|
60
53
|
app.use(errorHandler)
|
|
61
54
|
|
|
62
|
-
|
|
55
|
+
doAppMountedAction({ app })
|
|
63
56
|
|
|
64
57
|
app.all(`*`, async (req, res, next) => {
|
|
65
58
|
const method = req.method
|
|
66
|
-
const fullUrl =
|
|
67
|
-
|
|
59
|
+
const fullUrl = new URL(
|
|
60
|
+
req.protocol + '://' + req.get('host') + req.originalUrl,
|
|
61
|
+
)
|
|
62
|
+
const host = fullUrl.hostname
|
|
68
63
|
if (!host) {
|
|
69
64
|
throw new Error(`Host not found`)
|
|
70
65
|
}
|
|
66
|
+
const subdomain = host.slice(0, -`.${APEX_DOMAIN()}`.length)
|
|
71
67
|
|
|
72
|
-
dbg(
|
|
68
|
+
dbg(
|
|
69
|
+
`${method} ${fullUrl} (${subdomain}) -> ${`http://localhost:${PORT()}`}`,
|
|
70
|
+
)
|
|
73
71
|
|
|
74
|
-
await
|
|
72
|
+
await doIncomingRequestAction({ req, res, host })
|
|
75
73
|
|
|
76
|
-
const instance = await
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
const instance = await doGetInstanceByRequestInfoFilter(undefined, {
|
|
75
|
+
req,
|
|
76
|
+
res,
|
|
77
|
+
host,
|
|
78
|
+
subdomain,
|
|
79
|
+
})
|
|
81
80
|
if (!instance) {
|
|
82
81
|
res.status(404).end(`${host} not found`)
|
|
83
82
|
return
|
|
84
83
|
}
|
|
85
84
|
|
|
86
|
-
await
|
|
85
|
+
await doAfterInstanceFoundAction({
|
|
87
86
|
instance,
|
|
88
87
|
req,
|
|
89
88
|
res,
|
|
90
89
|
host,
|
|
91
90
|
})
|
|
92
91
|
|
|
93
|
-
const url = await
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
host,
|
|
101
|
-
},
|
|
102
|
-
)
|
|
92
|
+
const url = await doGetOrProvisionInstanceUrlFilter('', {
|
|
93
|
+
req,
|
|
94
|
+
res,
|
|
95
|
+
instance,
|
|
96
|
+
host,
|
|
97
|
+
subdomain,
|
|
98
|
+
})
|
|
103
99
|
|
|
104
100
|
dbg(`Forwarding request for ${req.url} to instance ${url}`)
|
|
105
101
|
|
|
@@ -110,7 +106,7 @@ export const serve = async () => {
|
|
|
110
106
|
|
|
111
107
|
app.listen(PORT(), () => {
|
|
112
108
|
info(`pockethost listening on port ${PORT()}`)
|
|
113
|
-
|
|
109
|
+
doAfterServerStartAction()
|
|
114
110
|
})
|
|
115
111
|
|
|
116
112
|
asyncExitHook(async () => {
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { InstanceFields } from '../schema'
|
|
2
|
-
import { ActionHandler, PocketHostAction, registerAction } from './action'
|
|
3
|
-
import { PocketHostFilter, filter } from './filter'
|
|
4
|
-
|
|
5
|
-
export async function getInstanceById(instanceId: string) {
|
|
6
|
-
return await filter<InstanceFields | undefined>(
|
|
7
|
-
PocketHostFilter.Core_GetInstanceById,
|
|
8
|
-
undefined,
|
|
9
|
-
{ instanceId },
|
|
10
|
-
)
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export const onAppMount = (cb: ActionHandler) =>
|
|
14
|
-
registerAction(PocketHostAction.Core_OnAppMount, cb)
|
|
15
|
-
|
|
16
|
-
export const onInit = (cb: ActionHandler) =>
|
|
17
|
-
registerAction(PocketHostAction.Core_Init, cb)
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import Ajv, { JSONSchemaType } from 'ajv'
|
|
2
|
-
import PocketBase, { ClientResponseError } from 'pocketbase'
|
|
3
|
-
import type { JsonObject } from 'type-fest'
|
|
4
|
-
import { LoggerService, RestCommands, RestMethods } from '..'
|
|
5
|
-
|
|
6
|
-
export type RestHelperConfig = {
|
|
7
|
-
client: PocketBase
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export type RestHelper = ReturnType<typeof createRestHelper>
|
|
11
|
-
|
|
12
|
-
export const createRestHelper = (config: RestHelperConfig) => {
|
|
13
|
-
const _logger = LoggerService().create(`RestHelper`)
|
|
14
|
-
const { client } = config
|
|
15
|
-
|
|
16
|
-
const mkRest = <TPayload extends JsonObject, TResult extends JsonObject>(
|
|
17
|
-
cmd: RestCommands,
|
|
18
|
-
method: RestMethods,
|
|
19
|
-
schema: JSONSchemaType<TPayload>,
|
|
20
|
-
) => {
|
|
21
|
-
const validator = new Ajv().compile(schema)
|
|
22
|
-
return async (payload: TPayload): Promise<TResult> => {
|
|
23
|
-
const _restCallLogger = _logger.create(cmd)
|
|
24
|
-
const { dbg, error } = _restCallLogger
|
|
25
|
-
|
|
26
|
-
dbg(`Executing REST call`)
|
|
27
|
-
if (!validator(payload)) {
|
|
28
|
-
throw new Error(`Invalid REST payload: ${validator.errors}`)
|
|
29
|
-
}
|
|
30
|
-
const _payload = { ...payload }
|
|
31
|
-
|
|
32
|
-
const url = `/api/${cmd}${
|
|
33
|
-
method === RestMethods.Post ? '' : '/:id'
|
|
34
|
-
}`.replace(/:([a-zA-Z]+)/g, (_, key) => {
|
|
35
|
-
if (!(key in _payload)) {
|
|
36
|
-
throw new Error(`Payload must include '${key}`)
|
|
37
|
-
}
|
|
38
|
-
const value = _payload[key]!
|
|
39
|
-
delete _payload[key]
|
|
40
|
-
return encodeURIComponent(value.toString())
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
const options: any = {
|
|
44
|
-
method: method,
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (method !== RestMethods.Get) {
|
|
48
|
-
options.body = _payload
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
dbg({ url, options })
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
const res = await client.send(url, options)
|
|
55
|
-
dbg(res)
|
|
56
|
-
return res
|
|
57
|
-
} catch (e) {
|
|
58
|
-
if (e instanceof ClientResponseError) {
|
|
59
|
-
error(`REST error: ${e.originalError}`)
|
|
60
|
-
throw e.originalError
|
|
61
|
-
}
|
|
62
|
-
error(`REST error: ${e}`)
|
|
63
|
-
throw e
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return { mkRest }
|
|
69
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './RestHelper'
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { JSONSchemaType } from 'ajv'
|
|
2
|
-
import { InstanceFields, Subdomain } from '..'
|
|
3
|
-
|
|
4
|
-
export type CreateInstancePayload = {
|
|
5
|
-
subdomain: Subdomain
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export type CreateInstanceResult = {
|
|
9
|
-
instance: InstanceFields
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const CreateInstancePayloadSchema: JSONSchemaType<CreateInstancePayload> =
|
|
13
|
-
{
|
|
14
|
-
type: 'object',
|
|
15
|
-
properties: {
|
|
16
|
-
subdomain: { type: 'string' },
|
|
17
|
-
},
|
|
18
|
-
required: ['subdomain'],
|
|
19
|
-
additionalProperties: false,
|
|
20
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { JSONSchemaType } from 'ajv'
|
|
2
|
-
import { InstanceId } from '..'
|
|
3
|
-
|
|
4
|
-
export type DeleteInstancePayload = {
|
|
5
|
-
id: InstanceId
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export type DeleteInstanceResult = {
|
|
9
|
-
status: 'ok' | 'error'
|
|
10
|
-
message?: string
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export const DeleteInstancePayloadSchema: JSONSchemaType<DeleteInstancePayload> =
|
|
14
|
-
{
|
|
15
|
-
type: 'object',
|
|
16
|
-
properties: {
|
|
17
|
-
id: { type: 'string' },
|
|
18
|
-
},
|
|
19
|
-
required: ['id'],
|
|
20
|
-
additionalProperties: false,
|
|
21
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { JSONSchemaType } from 'ajv'
|
|
2
|
-
import { UserId } from '..'
|
|
3
|
-
|
|
4
|
-
export type GetUserTokenPayload = {
|
|
5
|
-
id: UserId
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export type GetUserTokenResult = {
|
|
9
|
-
id: string
|
|
10
|
-
email: string
|
|
11
|
-
tokenKey: string
|
|
12
|
-
passwordHash: string
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const GetUserTokenPayloadSchema: JSONSchemaType<GetUserTokenPayload> = {
|
|
16
|
-
type: 'object',
|
|
17
|
-
properties: {
|
|
18
|
-
id: { type: 'string' },
|
|
19
|
-
},
|
|
20
|
-
required: ['id'],
|
|
21
|
-
additionalProperties: false,
|
|
22
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { JSONSchemaType } from 'ajv'
|
|
2
|
-
import { InstanceFields, InstanceId } from '..'
|
|
3
|
-
|
|
4
|
-
export type UpdateInstancePayload = {
|
|
5
|
-
id: InstanceId
|
|
6
|
-
fields: Partial<
|
|
7
|
-
Pick<
|
|
8
|
-
InstanceFields,
|
|
9
|
-
| 'maintenance'
|
|
10
|
-
| 'secrets'
|
|
11
|
-
| 'subdomain'
|
|
12
|
-
| 'syncAdmin'
|
|
13
|
-
| 'version'
|
|
14
|
-
| 'dev'
|
|
15
|
-
| 'cname'
|
|
16
|
-
| 'notifyMaintenanceMode'
|
|
17
|
-
>
|
|
18
|
-
>
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const SECRET_KEY_REGEX = /^[A-Z][A-Z0-9_]*$/
|
|
22
|
-
|
|
23
|
-
export type UpdateInstanceResult = {
|
|
24
|
-
status: 'ok' | 'error'
|
|
25
|
-
message?: string
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export const UpdateInstancePayloadSchema: JSONSchemaType<UpdateInstancePayload> =
|
|
29
|
-
{
|
|
30
|
-
type: 'object',
|
|
31
|
-
properties: {
|
|
32
|
-
id: { type: 'string' },
|
|
33
|
-
fields: {
|
|
34
|
-
type: 'object',
|
|
35
|
-
properties: {
|
|
36
|
-
syncAdmin: { type: 'boolean', nullable: true },
|
|
37
|
-
subdomain: { type: 'string', nullable: true },
|
|
38
|
-
maintenance: { type: 'boolean', nullable: true },
|
|
39
|
-
version: {
|
|
40
|
-
type: 'string',
|
|
41
|
-
nullable: true,
|
|
42
|
-
},
|
|
43
|
-
secrets: {
|
|
44
|
-
type: 'object',
|
|
45
|
-
nullable: true,
|
|
46
|
-
patternProperties: {
|
|
47
|
-
[SECRET_KEY_REGEX.source]: {
|
|
48
|
-
anyOf: [{ type: 'string' }],
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
required: [],
|
|
52
|
-
},
|
|
53
|
-
dev: { type: 'boolean', nullable: true },
|
|
54
|
-
cname: { type: 'string', nullable: true },
|
|
55
|
-
notifyMaintenanceMode: { type: 'boolean', nullable: true },
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
required: ['id', 'fields'],
|
|
60
|
-
additionalProperties: false,
|
|
61
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import Ajv from 'ajv'
|
|
2
|
-
import { JsonObject } from 'type-fest'
|
|
3
|
-
|
|
4
|
-
export enum RestMethods {
|
|
5
|
-
Get = 'GET',
|
|
6
|
-
Post = 'POST',
|
|
7
|
-
Put = 'PUT',
|
|
8
|
-
Delete = 'DELETE',
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export enum RestCommands {
|
|
12
|
-
Instance = 'instance',
|
|
13
|
-
UserToken = 'userToken',
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export type RestPayloadBase = JsonObject
|
|
17
|
-
|
|
18
|
-
export const ajv = new Ajv()
|
|
19
|
-
|
|
20
|
-
export * from './CreateInstance'
|
|
21
|
-
export * from './DeleteInstance'
|
|
22
|
-
export * from './GetUserTokenInfo'
|
|
23
|
-
export * from './UpdateInstance'
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { BaseFields } from './types'
|
|
2
|
-
|
|
3
|
-
export enum SubscriptionType {
|
|
4
|
-
Legacy = 'legacy',
|
|
5
|
-
Free = 'free',
|
|
6
|
-
Premium = 'premium',
|
|
7
|
-
Lifetime = 'lifetime',
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const PLAN_NAMES = {
|
|
11
|
-
[SubscriptionType.Legacy]: 'Legacy',
|
|
12
|
-
[SubscriptionType.Free]: 'Hacker',
|
|
13
|
-
[SubscriptionType.Premium]: `Pro`,
|
|
14
|
-
[SubscriptionType.Lifetime]: `Founder's Edition`,
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export type UserFields = BaseFields & {
|
|
18
|
-
email: string
|
|
19
|
-
verified: boolean
|
|
20
|
-
isLegacy: boolean
|
|
21
|
-
isFounder: boolean
|
|
22
|
-
subscription: SubscriptionType
|
|
23
|
-
notifyMaintenanceMode: boolean
|
|
24
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export type RecordId = string
|
|
2
|
-
export type UserId = RecordId
|
|
3
|
-
export type InstanceId = RecordId
|
|
4
|
-
export type Semver = string
|
|
5
|
-
export type InternalInstanceId = RecordId
|
|
6
|
-
export type Subdomain = string
|
|
7
|
-
export type Port = number
|
|
8
|
-
export type IsoDate = string
|
|
9
|
-
export type ProcessId = number
|
|
10
|
-
export type Username = string
|
|
11
|
-
export type Password = string
|
|
12
|
-
export type CollectionName = string
|
|
13
|
-
export type Seconds = number
|
|
14
|
-
export type BaseFields = {
|
|
15
|
-
id: RecordId
|
|
16
|
-
created: IsoDate
|
|
17
|
-
updated: IsoDate
|
|
18
|
-
}
|