@contember/engine-server 1.2.1 → 1.3.0-alpha.2
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/dist/src/start.js +16 -15
- package/dist/src/start.js.map +1 -1
- package/dist/src/tsconfig.tsbuildinfo +1 -1
- package/dist/tests/e2e/assume-identity.test.js.map +1 -1
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -22
- package/src/start.ts +10 -8
- package/src/tsconfig.json +4 -45
- package/tests/e2e/assume-identity.test.ts +0 -1
- package/tests/tsconfig.json +2 -6
- package/tsconfig.json +2 -6
- package/dist/src/MasterContainer.d.ts +0 -125
- package/dist/src/MasterContainer.d.ts.map +0 -1
- package/dist/src/MasterContainer.js +0 -102
- package/dist/src/MasterContainer.js.map +0 -1
- package/dist/src/bootstrap/Initializer.d.ts +0 -7
- package/dist/src/bootstrap/Initializer.d.ts.map +0 -1
- package/dist/src/bootstrap/Initializer.js +0 -27
- package/dist/src/bootstrap/Initializer.js.map +0 -1
- package/dist/src/bootstrap/index.d.ts +0 -2
- package/dist/src/bootstrap/index.d.ts.map +0 -1
- package/dist/src/bootstrap/index.js +0 -18
- package/dist/src/bootstrap/index.js.map +0 -1
- package/dist/src/config/config.d.ts +0 -17
- package/dist/src/config/config.d.ts.map +0 -1
- package/dist/src/config/config.js +0 -30
- package/dist/src/config/config.js.map +0 -1
- package/dist/src/config/configSchema.d.ts +0 -201
- package/dist/src/config/configSchema.d.ts.map +0 -1
- package/dist/src/config/configSchema.js +0 -119
- package/dist/src/config/configSchema.js.map +0 -1
- package/dist/src/config/configTemplate.d.ts +0 -2
- package/dist/src/config/configTemplate.d.ts.map +0 -1
- package/dist/src/config/configTemplate.js +0 -90
- package/dist/src/config/configTemplate.js.map +0 -1
- package/dist/src/config/projectConfigResolver.d.ts +0 -8
- package/dist/src/config/projectConfigResolver.d.ts.map +0 -1
- package/dist/src/config/projectConfigResolver.js +0 -91
- package/dist/src/config/projectConfigResolver.js.map +0 -1
- package/dist/src/config/tenantConfigResolver.d.ts +0 -8
- package/dist/src/config/tenantConfigResolver.d.ts.map +0 -1
- package/dist/src/config/tenantConfigResolver.js +0 -31
- package/dist/src/config/tenantConfigResolver.js.map +0 -1
- package/dist/src/index.d.ts +0 -13
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js +0 -33
- package/dist/src/index.js.map +0 -1
- package/dist/src/project/ProjectContainer.d.ts +0 -58
- package/dist/src/project/ProjectContainer.d.ts.map +0 -1
- package/dist/src/project/ProjectContainer.js +0 -66
- package/dist/src/project/ProjectContainer.js.map +0 -1
- package/dist/src/project/ProjectContainerResolver.d.ts +0 -24
- package/dist/src/project/ProjectContainerResolver.d.ts.map +0 -1
- package/dist/src/project/ProjectContainerResolver.js +0 -75
- package/dist/src/project/ProjectContainerResolver.js.map +0 -1
- package/dist/src/project/ProjectContainerStore.d.ts +0 -18
- package/dist/src/project/ProjectContainerStore.d.ts.map +0 -1
- package/dist/src/project/ProjectContainerStore.js +0 -30
- package/dist/src/project/ProjectContainerStore.js.map +0 -1
- package/dist/src/project/ProjectInitializer.d.ts +0 -14
- package/dist/src/project/ProjectInitializer.d.ts.map +0 -1
- package/dist/src/project/ProjectInitializer.js +0 -28
- package/dist/src/project/ProjectInitializer.js.map +0 -1
- package/dist/src/project/ProjectSchemaResolver.d.ts +0 -14
- package/dist/src/project/ProjectSchemaResolver.d.ts.map +0 -1
- package/dist/src/project/ProjectSchemaResolver.js +0 -30
- package/dist/src/project/ProjectSchemaResolver.js.map +0 -1
- package/dist/src/project/index.d.ts +0 -5
- package/dist/src/project/index.d.ts.map +0 -1
- package/dist/src/project/index.js +0 -21
- package/dist/src/project/index.js.map +0 -1
- package/dist/src/projectGroup/ProjectGroupContainer.d.ts +0 -26
- package/dist/src/projectGroup/ProjectGroupContainer.d.ts.map +0 -1
- package/dist/src/projectGroup/ProjectGroupContainer.js +0 -70
- package/dist/src/projectGroup/ProjectGroupContainer.js.map +0 -1
- package/dist/src/projectGroup/ProjectGroupResolver.d.ts +0 -7
- package/dist/src/projectGroup/ProjectGroupResolver.d.ts.map +0 -1
- package/dist/src/projectGroup/ProjectGroupResolver.js +0 -13
- package/dist/src/projectGroup/ProjectGroupResolver.js.map +0 -1
- package/dist/src/utils/index.d.ts +0 -3
- package/dist/src/utils/index.d.ts.map +0 -1
- package/dist/src/utils/index.js +0 -19
- package/dist/src/utils/index.js.map +0 -1
- package/dist/src/utils/sentry.d.ts +0 -10
- package/dist/src/utils/sentry.d.ts.map +0 -1
- package/dist/src/utils/sentry.js +0 -80
- package/dist/src/utils/sentry.js.map +0 -1
- package/dist/src/utils/serverStartup.d.ts +0 -33
- package/dist/src/utils/serverStartup.d.ts.map +0 -1
- package/dist/src/utils/serverStartup.js +0 -79
- package/dist/src/utils/serverStartup.js.map +0 -1
- package/dist/src/utils/serverTermination.d.ts +0 -13
- package/dist/src/utils/serverTermination.d.ts.map +0 -1
- package/dist/src/utils/serverTermination.js +0 -24
- package/dist/src/utils/serverTermination.js.map +0 -1
- package/dist/src/utils/strings.d.ts +0 -2
- package/dist/src/utils/strings.d.ts.map +0 -1
- package/dist/src/utils/strings.js +0 -6
- package/dist/src/utils/strings.js.map +0 -1
- package/dist/tests/cases/configResolver.test.d.ts +0 -2
- package/dist/tests/cases/configResolver.test.d.ts.map +0 -1
- package/dist/tests/cases/configResolver.test.js +0 -67
- package/dist/tests/cases/configResolver.test.js.map +0 -1
- package/src/MasterContainer.ts +0 -206
- package/src/bootstrap/Initializer.ts +0 -27
- package/src/bootstrap/index.ts +0 -1
- package/src/config/config.ts +0 -56
- package/src/config/configSchema.ts +0 -135
- package/src/config/configTemplate.ts +0 -86
- package/src/config/projectConfigResolver.ts +0 -86
- package/src/config/tenantConfigResolver.ts +0 -41
- package/src/index.ts +0 -23
- package/src/project/ProjectContainer.ts +0 -95
- package/src/project/ProjectContainerResolver.ts +0 -87
- package/src/project/ProjectContainerStore.ts +0 -33
- package/src/project/ProjectInitializer.ts +0 -28
- package/src/project/ProjectSchemaResolver.ts +0 -33
- package/src/project/index.ts +0 -4
- package/src/projectGroup/ProjectGroupContainer.ts +0 -134
- package/src/projectGroup/ProjectGroupResolver.ts +0 -12
- package/src/utils/index.ts +0 -2
- package/src/utils/sentry.ts +0 -56
- package/src/utils/serverStartup.ts +0 -78
- package/src/utils/serverTermination.ts +0 -19
- package/src/utils/strings.ts +0 -1
- package/tests/cases/configResolver.test.ts +0 -67
package/src/config/config.ts
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { ConfigLoader, createObjectParametersResolver, Merger, resolveParameters } from '@contember/config-loader'
|
|
2
|
-
import { ConfigProcessor } from '@contember/engine-plugins'
|
|
3
|
-
import { serverConfigSchema as defaultServerConfigSchema, tenantConfigSchema } from './configSchema'
|
|
4
|
-
import { configTemplate } from './configTemplate'
|
|
5
|
-
import { createProjectConfigResolver, ProjectConfigResolver } from './projectConfigResolver'
|
|
6
|
-
import { createTenantConfigResolver, TenantConfigResolver } from './tenantConfigResolver'
|
|
7
|
-
import { Type } from '@contember/typesafe'
|
|
8
|
-
|
|
9
|
-
export type ServerConfig = ReturnType<typeof defaultServerConfigSchema>
|
|
10
|
-
export type TenantConfig = ReturnType<typeof tenantConfigSchema>
|
|
11
|
-
|
|
12
|
-
export type ConfigSource = { data: string; type: 'file' | 'json' | 'yaml' }
|
|
13
|
-
|
|
14
|
-
type Env = Record<string, string>
|
|
15
|
-
|
|
16
|
-
export async function readConfig<T extends ServerConfig>(
|
|
17
|
-
configSources: ConfigSource[] = [],
|
|
18
|
-
configProcessors: ConfigProcessor[] = [],
|
|
19
|
-
serverConfigSchema: Type<T> = defaultServerConfigSchema as Type<T>,
|
|
20
|
-
): Promise<{
|
|
21
|
-
serverConfig: T
|
|
22
|
-
projectConfigResolver: ProjectConfigResolver
|
|
23
|
-
tenantConfigResolver: TenantConfigResolver
|
|
24
|
-
}> {
|
|
25
|
-
const loader = new ConfigLoader()
|
|
26
|
-
const configs = await Promise.all(
|
|
27
|
-
configSources.map(it => (it.type === 'file' ? loader.load(it.data) : loader.loadString(it.data, it.type))),
|
|
28
|
-
)
|
|
29
|
-
const env: Env = {
|
|
30
|
-
...configProcessors.reduce((acc, curr) => ({ ...acc, ...(curr.getDefaultEnv?.() || {}) }), {
|
|
31
|
-
DEFAULT_DB_PORT: '5432',
|
|
32
|
-
}),
|
|
33
|
-
...Object.fromEntries(Object.entries(process.env).filter((it): it is [string, string] => it[1] !== undefined)),
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const template = configProcessors.reduce(
|
|
37
|
-
(tpl, processor) => processor.prepareConfigTemplate?.(tpl, { env }) || tpl,
|
|
38
|
-
configTemplate,
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
const rawConfig = Merger.merge(template, ...configs)
|
|
42
|
-
|
|
43
|
-
const parametersResolver = createObjectParametersResolver({ env })
|
|
44
|
-
const config = resolveParameters(rawConfig.server, parametersResolver)
|
|
45
|
-
|
|
46
|
-
const serverConfig = serverConfigSchema(config)
|
|
47
|
-
return {
|
|
48
|
-
serverConfig,
|
|
49
|
-
projectConfigResolver: createProjectConfigResolver(env, rawConfig, configProcessors),
|
|
50
|
-
tenantConfigResolver: createTenantConfigResolver(env, rawConfig.tenant),
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import * as Typesafe from '@contember/typesafe'
|
|
2
|
-
import { MailerOptions } from '@contember/engine-tenant-api'
|
|
3
|
-
import { upperCaseFirst } from '../utils'
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const dbConfigRequired = {
|
|
7
|
-
host: Typesafe.string,
|
|
8
|
-
port: Typesafe.number,
|
|
9
|
-
user: Typesafe.string,
|
|
10
|
-
password: Typesafe.string,
|
|
11
|
-
database: Typesafe.string,
|
|
12
|
-
}
|
|
13
|
-
const dbConfigOptional = {
|
|
14
|
-
ssl: Typesafe.boolean,
|
|
15
|
-
queryTimeoutMs: Typesafe.number,
|
|
16
|
-
statementTimeoutMs: Typesafe.number,
|
|
17
|
-
connectionTimeoutMs: Typesafe.number,
|
|
18
|
-
pool: Typesafe.partial({
|
|
19
|
-
maxConnections: Typesafe.number,
|
|
20
|
-
maxConnecting: Typesafe.number,
|
|
21
|
-
maxIdle: Typesafe.number,
|
|
22
|
-
reconnectIntervalMs: Typesafe.number,
|
|
23
|
-
rateLimitCount: Typesafe.number,
|
|
24
|
-
rateLimitPeriodMs: Typesafe.number,
|
|
25
|
-
idleTimeoutMs: Typesafe.number,
|
|
26
|
-
acquireTimeoutMs: Typesafe.number,
|
|
27
|
-
maxUses: Typesafe.number,
|
|
28
|
-
maxAgeMs: Typesafe.number,
|
|
29
|
-
}),
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const dbConfigSchemaInner = Typesafe.intersection(
|
|
33
|
-
Typesafe.object(dbConfigRequired),
|
|
34
|
-
Typesafe.partial(dbConfigOptional),
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
const readDbSchema = Typesafe.intersection(
|
|
38
|
-
Typesafe.partial(dbConfigRequired),
|
|
39
|
-
Typesafe.partial(dbConfigOptional),
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
export const dbConfigSchema = Typesafe.intersection(
|
|
43
|
-
dbConfigSchemaInner,
|
|
44
|
-
Typesafe.partial({
|
|
45
|
-
read: (val: unknown) => {
|
|
46
|
-
const readDb = readDbSchema(val)
|
|
47
|
-
if ('host' in readDb) {
|
|
48
|
-
return readDb
|
|
49
|
-
}
|
|
50
|
-
return undefined
|
|
51
|
-
},
|
|
52
|
-
}),
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
export const tenantConfigSchema = Typesafe.intersection(
|
|
56
|
-
Typesafe.object({
|
|
57
|
-
db: dbConfigSchema,
|
|
58
|
-
mailer: Typesafe.transform(Typesafe.partial({
|
|
59
|
-
from: Typesafe.string,
|
|
60
|
-
host: Typesafe.string,
|
|
61
|
-
port: Typesafe.number,
|
|
62
|
-
user: Typesafe.string,
|
|
63
|
-
password: Typesafe.string,
|
|
64
|
-
}), (value, input): MailerOptions & Typesafe.JsonObject => {
|
|
65
|
-
return {
|
|
66
|
-
...(input as any),
|
|
67
|
-
...value,
|
|
68
|
-
...(value.user ? { auth: { user: value.user, pass: value.password } } : {}),
|
|
69
|
-
}
|
|
70
|
-
}),
|
|
71
|
-
credentials: Typesafe.partial({
|
|
72
|
-
rootToken: Typesafe.string,
|
|
73
|
-
rootTokenHash: Typesafe.string,
|
|
74
|
-
rootPassword: Typesafe.string,
|
|
75
|
-
rootEmail: Typesafe.string,
|
|
76
|
-
loginToken: Typesafe.string,
|
|
77
|
-
}),
|
|
78
|
-
}),
|
|
79
|
-
Typesafe.object({
|
|
80
|
-
secrets: Typesafe.partial({
|
|
81
|
-
encryptionKey: Typesafe.string,
|
|
82
|
-
}),
|
|
83
|
-
}),
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
export const serverConfigSchema = Typesafe.object({
|
|
87
|
-
port: Typesafe.number,
|
|
88
|
-
http: Typesafe.partial({
|
|
89
|
-
requestBodySize: Typesafe.string,
|
|
90
|
-
}),
|
|
91
|
-
logging: Typesafe.union(
|
|
92
|
-
(val): { sentry?: { dsn: string } } => Typesafe.valueAt(val, ['sentry', 'dsn']) === undefined ? {} : Typesafe.fail([]),
|
|
93
|
-
Typesafe.partial({
|
|
94
|
-
sentry: Typesafe.object({
|
|
95
|
-
dsn: Typesafe.string,
|
|
96
|
-
}),
|
|
97
|
-
}),
|
|
98
|
-
),
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
export const stageConfig = Typesafe.record(
|
|
102
|
-
Typesafe.string,
|
|
103
|
-
Typesafe.intersection(
|
|
104
|
-
Typesafe.object({
|
|
105
|
-
name: Typesafe.union(
|
|
106
|
-
Typesafe.string,
|
|
107
|
-
(_: unknown, path: PropertyKey[] = []) => upperCaseFirst(String(path[path.length - 2])),
|
|
108
|
-
),
|
|
109
|
-
slug: (_: unknown, path: PropertyKey[] = []) => String(path[path.length - 2]),
|
|
110
|
-
}),
|
|
111
|
-
Typesafe.partial({
|
|
112
|
-
schema: Typesafe.string,
|
|
113
|
-
}),
|
|
114
|
-
),
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
export const projectConfigSchema = Typesafe.object({
|
|
118
|
-
name: Typesafe.union(
|
|
119
|
-
Typesafe.string,
|
|
120
|
-
(_: unknown, path: PropertyKey[] = []) => upperCaseFirst(String(path[path.length - 2])).replace(/-/g, ' '),
|
|
121
|
-
),
|
|
122
|
-
slug: (_, path = []) => String(path[path.length - 2]),
|
|
123
|
-
stages: (input, path = []) => Object.values(stageConfig(input, path)),
|
|
124
|
-
db: Typesafe.intersection(
|
|
125
|
-
Typesafe.union(
|
|
126
|
-
Typesafe.object({
|
|
127
|
-
useTenantDb: Typesafe.literal(true),
|
|
128
|
-
}),
|
|
129
|
-
dbConfigSchema,
|
|
130
|
-
),
|
|
131
|
-
Typesafe.partial({
|
|
132
|
-
systemSchema: Typesafe.string,
|
|
133
|
-
}),
|
|
134
|
-
),
|
|
135
|
-
})
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
const createDbConfigTemplate = (prefix: string) => {
|
|
2
|
-
return {
|
|
3
|
-
ssl: `%?${prefix}_SSL::bool%`,
|
|
4
|
-
queryTimeoutMs: `%?${prefix}_QUERY_TIMEOUT_MS::number%`,
|
|
5
|
-
statementTimeoutMs: `%?${prefix}_STATEMENT_TIMEOUT_MS::number%`,
|
|
6
|
-
connectionTimeoutMs: `%?${prefix}_CONNECTION_TIMEOUT_MS::number%`,
|
|
7
|
-
pool: {
|
|
8
|
-
maxConnections: `%?${prefix}_POOL_MAX_CONNECTIONS::number%`,
|
|
9
|
-
maxConnecting: `%?${prefix}_POOL_MAX_CONNECTING::number%`,
|
|
10
|
-
maxIdle: `%?${prefix}_POOL_MAX_IDLE::number%`,
|
|
11
|
-
reconnectIntervalMs: `%?${prefix}_POOL_RECONNECT_INTERVAL_MS::number%`,
|
|
12
|
-
idleTimeoutMs: `%?${prefix}_POOL_IDLE_TIMEOUT_MS::number%`,
|
|
13
|
-
acquireTimeoutMs: `%?${prefix}_POOL_ACQUIRE_TIMEOUT_MS::number%`,
|
|
14
|
-
maxUses: `%?${prefix}_POOL_MAX_USES::number%`,
|
|
15
|
-
maxAgeMs: `%?${prefix}_POOL_MAX_AGE_MS::number%`,
|
|
16
|
-
rateLimitCount: `%?${prefix}_POOL_RATE_LIMIT_COUNT::number%`,
|
|
17
|
-
rateLimitPeriodMs: `%?${prefix}_POOL_RATE_LIMIT_PERIOD_MS::number%`,
|
|
18
|
-
},
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
export const configTemplate: any = {
|
|
22
|
-
tenant: {
|
|
23
|
-
db: {
|
|
24
|
-
host: `%tenant.env.DB_HOST%`,
|
|
25
|
-
port: `%tenant.env.DB_PORT::number%`,
|
|
26
|
-
user: `%tenant.env.DB_USER%`,
|
|
27
|
-
password: `%tenant.env.DB_PASSWORD%`,
|
|
28
|
-
database: `%tenant.env.DB_NAME%`,
|
|
29
|
-
...createDbConfigTemplate('tenant.env.DB'),
|
|
30
|
-
},
|
|
31
|
-
mailer: {
|
|
32
|
-
from: '%?tenant.env.MAILER_FROM%',
|
|
33
|
-
host: '%?tenant.env.MAILER_HOST::string%',
|
|
34
|
-
port: '%?tenant.env.MAILER_PORT::number%',
|
|
35
|
-
secure: '%?tenant.env.MAILER_SECURE::bool%',
|
|
36
|
-
user: '%?tenant.env.MAILER_USER%',
|
|
37
|
-
password: '%?tenant.env.MAILER_PASSWORD%',
|
|
38
|
-
},
|
|
39
|
-
credentials: {
|
|
40
|
-
rootEmail: '%?env.CONTEMBER_ROOT_EMAIL%',
|
|
41
|
-
rootToken: '%?env.CONTEMBER_ROOT_TOKEN%',
|
|
42
|
-
rootTokenHash: '%?env.CONTEMBER_ROOT_TOKEN_HASH%',
|
|
43
|
-
rootPassword: '%?env.CONTEMBER_ROOT_PASSWORD%',
|
|
44
|
-
loginToken: '%?env.CONTEMBER_LOGIN_TOKEN%',
|
|
45
|
-
},
|
|
46
|
-
secrets: {
|
|
47
|
-
encryptionKey: '%?env.CONTEMBER_ENCRYPTION_KEY%',
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
projectDefaults: {
|
|
51
|
-
db: {
|
|
52
|
-
host: `%?project.env.DB_HOST%`,
|
|
53
|
-
port: `%?project.env.DB_PORT::number%`,
|
|
54
|
-
user: `%?project.env.DB_USER%`,
|
|
55
|
-
password: `%?project.secret.db.password||project.env.DB_PASSWORD%`,
|
|
56
|
-
database: `%?project.env.DB_NAME||project.slug%`,
|
|
57
|
-
...createDbConfigTemplate('project.env.DB'),
|
|
58
|
-
read: {
|
|
59
|
-
host: `%?project.env.DB_READ_HOST%`,
|
|
60
|
-
port: `%?project.env.DB_READ_PORT::number%`,
|
|
61
|
-
user: `%?project.env.DB_READ_USER%`,
|
|
62
|
-
password: `%?project.secret.db.read.password||project.env.DB_READ_PASSWORD%`,
|
|
63
|
-
database: `%?project.env.DB_READ_NAME%`,
|
|
64
|
-
...createDbConfigTemplate('project.env.DB_READ'),
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
server: {
|
|
69
|
-
port: '%env.CONTEMBER_PORT::number%',
|
|
70
|
-
monitoringPort: '%env.CONTEMBER_MONITORING_PORT::number%',
|
|
71
|
-
workerCount: '%?env.CONTEMBER_WORKER_COUNT::string%',
|
|
72
|
-
http: {
|
|
73
|
-
requestBodySize: '%?env.CONTEMBER_HTTP_REQUEST_BODY_SIZE::string%',
|
|
74
|
-
},
|
|
75
|
-
projectGroup: {
|
|
76
|
-
domainMapping: '%?env.CONTEMBER_PROJECT_GROUP_DOMAIN_MAPPING%',
|
|
77
|
-
configHeader: '%?env.CONTEMBER_PROJECT_GROUP_CONFIG_HEADER%',
|
|
78
|
-
configEncryptionKey: '%?env.CONTEMBER_PROJECT_GROUP_CONFIG_ENCRYPTION_KEY%',
|
|
79
|
-
},
|
|
80
|
-
logging: {
|
|
81
|
-
sentry: {
|
|
82
|
-
dsn: '%?env.SENTRY_DSN%',
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { ProjectConfig, ProjectSecrets } from '@contember/engine-http'
|
|
2
|
-
import { ConfigProcessor } from '@contember/engine-plugins'
|
|
3
|
-
import { Merger, ParametersResolver, resolveParameters, UndefinedParameterError } from '@contember/config-loader'
|
|
4
|
-
import * as Typesafe from '@contember/typesafe'
|
|
5
|
-
import { projectConfigSchema } from './configSchema'
|
|
6
|
-
import { TenantConfig } from './config'
|
|
7
|
-
|
|
8
|
-
export type ProjectConfigResolver = (
|
|
9
|
-
slug: string,
|
|
10
|
-
additionalConfig: any,
|
|
11
|
-
secrets: ProjectSecrets,
|
|
12
|
-
tenantConfig: TenantConfig,
|
|
13
|
-
) => ProjectConfig
|
|
14
|
-
|
|
15
|
-
type Env = Record<string, string>
|
|
16
|
-
|
|
17
|
-
export const createProjectConfigResolver = (env: Env, config: any, configProcessors: ConfigProcessor[]): ProjectConfigResolver =>
|
|
18
|
-
(slug, additionalConfig, secrets, tenantConfig): ProjectConfig => {
|
|
19
|
-
const mergedConfig = Merger.merge(
|
|
20
|
-
config.projectDefaults as any,
|
|
21
|
-
(config?.projects as any)?.[slug] as any,
|
|
22
|
-
additionalConfig,
|
|
23
|
-
)
|
|
24
|
-
if (!mergedConfig.stages) {
|
|
25
|
-
mergedConfig.stages = { live: {} }
|
|
26
|
-
}
|
|
27
|
-
const resolvedConfig = resolveParameters(mergedConfig, createProjectParametersResolver(slug, env, secrets))
|
|
28
|
-
|
|
29
|
-
const projectConfigSchemaWithPlugins = configProcessors.reduce(
|
|
30
|
-
(schema, current) => {
|
|
31
|
-
if (!current.getProjectConfigSchema) {
|
|
32
|
-
return schema
|
|
33
|
-
}
|
|
34
|
-
return Typesafe.intersection(schema, current.getProjectConfigSchema(slug))
|
|
35
|
-
},
|
|
36
|
-
projectConfigSchema,
|
|
37
|
-
)
|
|
38
|
-
const projectConfig = projectConfigSchemaWithPlugins(resolvedConfig, ['project', slug])
|
|
39
|
-
if (!('host' in projectConfig.db)) {
|
|
40
|
-
return {
|
|
41
|
-
...projectConfig,
|
|
42
|
-
db: {
|
|
43
|
-
...tenantConfig.db,
|
|
44
|
-
...projectConfig.db,
|
|
45
|
-
},
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return {
|
|
49
|
-
...projectConfig,
|
|
50
|
-
db: projectConfig.db,
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const createProjectParametersResolver = (slug: string, env: Env, secrets: ProjectSecrets): ParametersResolver =>
|
|
56
|
-
parts => {
|
|
57
|
-
if (parts[0] === 'project') {
|
|
58
|
-
if (parts[1] === 'env') {
|
|
59
|
-
const envName = parts[2]
|
|
60
|
-
const projectEnvName = projectNameToEnvName(slug)
|
|
61
|
-
const envValue = env[projectEnvName + '_' + envName] || env['DEFAULT_' + envName]
|
|
62
|
-
if (envValue === undefined) {
|
|
63
|
-
throw new UndefinedParameterError(`ENV variable "${projectEnvName + '_' + envName}" not found.`)
|
|
64
|
-
}
|
|
65
|
-
return envValue
|
|
66
|
-
} else if (parts[1] === 'slug') {
|
|
67
|
-
return slug
|
|
68
|
-
} else if (parts[1] === 'secret') {
|
|
69
|
-
const key = parts.slice(2).join('.')
|
|
70
|
-
const value = secrets[key]
|
|
71
|
-
if (value === undefined) {
|
|
72
|
-
throw new UndefinedParameterError(`Project secret ${parts[2]} not found`)
|
|
73
|
-
}
|
|
74
|
-
return value
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
throw new UndefinedParameterError(`Parameter "${parts.join('.')}" not found.`)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const projectNameToEnvName = (projectName: string): string => {
|
|
81
|
-
const envName = projectName.toUpperCase().replace(/-/g, '_')
|
|
82
|
-
if (envName === 'TENANT') {
|
|
83
|
-
throw new Error('Forbidden project name')
|
|
84
|
-
}
|
|
85
|
-
return envName
|
|
86
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { TenantConfig } from './config'
|
|
2
|
-
import {
|
|
3
|
-
createObjectParametersResolver,
|
|
4
|
-
Merger,
|
|
5
|
-
ParametersResolver,
|
|
6
|
-
resolveParameters,
|
|
7
|
-
UndefinedParameterError,
|
|
8
|
-
} from '@contember/config-loader'
|
|
9
|
-
import { tenantConfigSchema } from './configSchema'
|
|
10
|
-
|
|
11
|
-
export type TenantConfigResolver = (slug: string | undefined, additionalConfig: any) => TenantConfig
|
|
12
|
-
|
|
13
|
-
type Env = Record<string, string>
|
|
14
|
-
|
|
15
|
-
export const createTenantConfigResolver = (env: Env, tenantConfig: any): TenantConfigResolver =>
|
|
16
|
-
(slug, additionalConfig) => {
|
|
17
|
-
const mergedConfig = Merger.merge(tenantConfig, additionalConfig)
|
|
18
|
-
const parametersResolver = createObjectParametersResolver({ env })
|
|
19
|
-
const tenantParametersResolver = createTenantParametersResolver(env)
|
|
20
|
-
const resolvedConfig = resolveParameters(mergedConfig, (parts, path, dataResolver) => {
|
|
21
|
-
if (parts[0] === 'tenant') {
|
|
22
|
-
return tenantParametersResolver(parts, path, dataResolver)
|
|
23
|
-
}
|
|
24
|
-
return parametersResolver(parts, path, dataResolver)
|
|
25
|
-
})
|
|
26
|
-
return tenantConfigSchema(resolvedConfig)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
export const createTenantParametersResolver = (env: Env): ParametersResolver =>
|
|
31
|
-
parts => {
|
|
32
|
-
if (parts[1] === 'env') {
|
|
33
|
-
const envName = parts[2]
|
|
34
|
-
const envValue = env['TENANT_' + envName] || env['DEFAULT_' + envName]
|
|
35
|
-
if (envValue === undefined) {
|
|
36
|
-
throw new UndefinedParameterError(`ENV variable "${'TENANT_' + envName}" not found.`)
|
|
37
|
-
}
|
|
38
|
-
return envValue
|
|
39
|
-
}
|
|
40
|
-
throw new UndefinedParameterError(`Parameter "${parts.join('.')}" not found.`)
|
|
41
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { MasterContainerFactory, MasterContainerArgs } from './MasterContainer'
|
|
2
|
-
import { ProjectConfig } from '@contember/engine-http'
|
|
3
|
-
import { readConfig } from './config/config'
|
|
4
|
-
import { serverConfigSchema } from './config/configSchema'
|
|
5
|
-
import { TenantConfigResolver } from './config/tenantConfigResolver'
|
|
6
|
-
import { ProjectGroupContainerFactory } from './projectGroup/ProjectGroupContainer'
|
|
7
|
-
|
|
8
|
-
export * from './utils/serverStartup'
|
|
9
|
-
export * from './utils/serverTermination'
|
|
10
|
-
export * from './utils/sentry'
|
|
11
|
-
export * from './MasterContainer'
|
|
12
|
-
|
|
13
|
-
export {
|
|
14
|
-
ProjectConfig,
|
|
15
|
-
readConfig,
|
|
16
|
-
serverConfigSchema,
|
|
17
|
-
TenantConfigResolver,
|
|
18
|
-
ProjectGroupContainerFactory,
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const createContainer = (args: MasterContainerArgs) => {
|
|
22
|
-
return new MasterContainerFactory().create(args)
|
|
23
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { Builder } from '@contember/dic'
|
|
2
|
-
import { Connection } from '@contember/database'
|
|
3
|
-
import { DatabaseContextFactory, SchemaVersionBuilder } from '@contember/engine-system-api'
|
|
4
|
-
import { GraphQlSchemaBuilderFactory, PermissionsByIdentityFactory } from '@contember/engine-content-api'
|
|
5
|
-
import { GraphQLSchemaContributor, Plugin } from '@contember/engine-plugins'
|
|
6
|
-
import {
|
|
7
|
-
ContentSchemaResolver,
|
|
8
|
-
GraphQlSchemaFactory,
|
|
9
|
-
ProjectConfig,
|
|
10
|
-
ProjectContainer,
|
|
11
|
-
Providers,
|
|
12
|
-
} from '@contember/engine-http'
|
|
13
|
-
import { Logger } from '@contember/logger'
|
|
14
|
-
|
|
15
|
-
export class ProjectContainerFactoryFactory {
|
|
16
|
-
constructor(
|
|
17
|
-
private readonly debug: boolean,
|
|
18
|
-
private readonly plugins: Plugin[],
|
|
19
|
-
private readonly providers: Providers,
|
|
20
|
-
) {
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
create(schemaVersionBuilder: SchemaVersionBuilder, logger: Logger): ProjectContainerFactory {
|
|
24
|
-
return new ProjectContainerFactory(this.debug, this.plugins, schemaVersionBuilder, this.providers, logger)
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
interface ProjectContainerFactoryArgs {
|
|
29
|
-
project: ProjectConfig
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export class ProjectContainerFactory {
|
|
33
|
-
constructor(
|
|
34
|
-
private readonly debug: boolean,
|
|
35
|
-
private readonly plugins: Plugin[],
|
|
36
|
-
private readonly schemaVersionBuilder: SchemaVersionBuilder,
|
|
37
|
-
private readonly providers: Providers,
|
|
38
|
-
private readonly logger: Logger,
|
|
39
|
-
) {}
|
|
40
|
-
|
|
41
|
-
public createContainer(args: ProjectContainerFactoryArgs): ProjectContainer {
|
|
42
|
-
return this.createBuilder(args)
|
|
43
|
-
.build()
|
|
44
|
-
.pick(
|
|
45
|
-
'project',
|
|
46
|
-
'connection',
|
|
47
|
-
'readConnection',
|
|
48
|
-
'systemDatabaseContextFactory',
|
|
49
|
-
'contentSchemaResolver',
|
|
50
|
-
'graphQlSchemaFactory',
|
|
51
|
-
'logger',
|
|
52
|
-
)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
protected createBuilder({ project }: ProjectContainerFactoryArgs) {
|
|
56
|
-
return new Builder({})
|
|
57
|
-
.addService('providers', () =>
|
|
58
|
-
this.providers)
|
|
59
|
-
.addService('schemaVersionBuilder', () =>
|
|
60
|
-
this.schemaVersionBuilder)
|
|
61
|
-
.addService('logger', () =>
|
|
62
|
-
this.logger.child({ project: project.slug }))
|
|
63
|
-
.addService('project', () =>
|
|
64
|
-
project)
|
|
65
|
-
.addService('connection', ({ project, logger }) =>
|
|
66
|
-
Connection.create(project.db, err => logger.error(err)))
|
|
67
|
-
.addService('readConnection', ({ project, logger, connection }) => {
|
|
68
|
-
if (!project.db.read) {
|
|
69
|
-
return connection
|
|
70
|
-
}
|
|
71
|
-
return Connection.create({
|
|
72
|
-
...project.db,
|
|
73
|
-
...project.db.read,
|
|
74
|
-
pool: {
|
|
75
|
-
...project.db.pool,
|
|
76
|
-
...project.db.read.pool,
|
|
77
|
-
},
|
|
78
|
-
}, err => logger.error(err))
|
|
79
|
-
})
|
|
80
|
-
.addService('graphQlSchemaBuilderFactory', () =>
|
|
81
|
-
new GraphQlSchemaBuilderFactory())
|
|
82
|
-
.addService('permissionsByIdentityFactory', ({}) =>
|
|
83
|
-
new PermissionsByIdentityFactory())
|
|
84
|
-
.addService('graphQlSchemaFactory', ({ project, permissionsByIdentityFactory, graphQlSchemaBuilderFactory, providers }) => {
|
|
85
|
-
const contributors = this.plugins
|
|
86
|
-
.map(it => (it.getSchemaContributor ? it.getSchemaContributor({ project, providers }) : null))
|
|
87
|
-
.filter((it): it is GraphQLSchemaContributor => !!it)
|
|
88
|
-
return new GraphQlSchemaFactory(graphQlSchemaBuilderFactory, permissionsByIdentityFactory, contributors)
|
|
89
|
-
})
|
|
90
|
-
.addService('contentSchemaResolver', ({ schemaVersionBuilder }) =>
|
|
91
|
-
new ContentSchemaResolver(schemaVersionBuilder))
|
|
92
|
-
.addService('systemDatabaseContextFactory', ({ connection, providers, project }) =>
|
|
93
|
-
new DatabaseContextFactory(connection.createClient(project.db.systemSchema ?? 'system', { module: 'system' }), providers))
|
|
94
|
-
}
|
|
95
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { ProjectContainerFactory } from './ProjectContainer'
|
|
2
|
-
import { ProjectContainer } from '@contember/engine-http'
|
|
3
|
-
import { DatabaseContext, ProjectManager, ProjectWithSecrets } from '@contember/engine-tenant-api'
|
|
4
|
-
import { ProjectInitializer as SystemProjectInitializer } from '@contember/engine-system-api'
|
|
5
|
-
import { ProjectContainerStore } from './ProjectContainerStore'
|
|
6
|
-
import { ProjectConfigResolver } from '../config/projectConfigResolver'
|
|
7
|
-
import { TenantConfig } from '../config/config'
|
|
8
|
-
import { Logger } from '@contember/logger'
|
|
9
|
-
|
|
10
|
-
export class ProjectContainerResolver {
|
|
11
|
-
private projectContainers = new ProjectContainerStore()
|
|
12
|
-
|
|
13
|
-
public readonly onCreate: ((container: ProjectContainer) => void | (() => void))[] = []
|
|
14
|
-
|
|
15
|
-
constructor(
|
|
16
|
-
private readonly projectContainerFactory: ProjectContainerFactory,
|
|
17
|
-
private readonly projectConfigResolver: ProjectConfigResolver,
|
|
18
|
-
private readonly projectManager: ProjectManager,
|
|
19
|
-
private readonly systemProjectInitializer: SystemProjectInitializer,
|
|
20
|
-
private readonly tenantDatabase: DatabaseContext,
|
|
21
|
-
private readonly tenantConfig: TenantConfig,
|
|
22
|
-
) {}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
public async getProjectContainer(slug: string, { alias = false }: { alias?: boolean } = {}): Promise<ProjectContainer | undefined> {
|
|
26
|
-
const realSlug = this.projectContainers.resolveAlias(slug)
|
|
27
|
-
if (realSlug) {
|
|
28
|
-
slug = realSlug
|
|
29
|
-
}
|
|
30
|
-
const existing = this.projectContainers.getContainer(slug)
|
|
31
|
-
if (existing) {
|
|
32
|
-
const existingAwaited = await existing
|
|
33
|
-
const state = await this.projectManager.getProjectState(this.tenantDatabase, slug, existingAwaited.timestamp)
|
|
34
|
-
if (state === 'valid') {
|
|
35
|
-
return (await existingAwaited).container
|
|
36
|
-
}
|
|
37
|
-
await this.destroyContainer(slug)
|
|
38
|
-
if (state === 'not_found') {
|
|
39
|
-
return undefined
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
const project = await this.projectManager.getProjectWithSecretsBySlug(this.tenantDatabase, slug, alias)
|
|
43
|
-
if (!project) {
|
|
44
|
-
return undefined
|
|
45
|
-
}
|
|
46
|
-
const container = await this.createProjectContainer(project)
|
|
47
|
-
if (slug !== project.slug) {
|
|
48
|
-
this.projectContainers.setAlias(project.slug, slug)
|
|
49
|
-
}
|
|
50
|
-
return container
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
public async createProjectContainer(project: ProjectWithSecrets, logger?: Logger): Promise<ProjectContainer> {
|
|
54
|
-
const projectConfig = this.projectConfigResolver(project.slug, project.config, project.secrets, this.tenantConfig)
|
|
55
|
-
return (await this.projectContainers.fetchContainer(project.slug, async slug => {
|
|
56
|
-
const projectContainer = this.projectContainerFactory.createContainer({
|
|
57
|
-
project: projectConfig,
|
|
58
|
-
})
|
|
59
|
-
await this.systemProjectInitializer.initialize(
|
|
60
|
-
projectContainer.systemDatabaseContextFactory,
|
|
61
|
-
projectContainer.project,
|
|
62
|
-
logger ?? projectContainer.logger,
|
|
63
|
-
)
|
|
64
|
-
const cleanups = this.onCreate.map(it => it(projectContainer) || (() => null))
|
|
65
|
-
return {
|
|
66
|
-
container: projectContainer,
|
|
67
|
-
cleanups,
|
|
68
|
-
timestamp: project.updatedAt,
|
|
69
|
-
}
|
|
70
|
-
})).container
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
public async destroyContainer(slug: string): Promise<void> {
|
|
74
|
-
const existing = this.projectContainers.getContainer(slug)
|
|
75
|
-
if (existing) {
|
|
76
|
-
const existingAwaited = await existing
|
|
77
|
-
existingAwaited.cleanups.forEach(it => it())
|
|
78
|
-
existingAwaited.container.project.alias?.forEach(it => this.projectContainers.removeAlias(it))
|
|
79
|
-
this.projectContainers.removeContainer(slug)
|
|
80
|
-
}
|
|
81
|
-
process.nextTick(() => {
|
|
82
|
-
if (global.gc) {
|
|
83
|
-
global.gc()
|
|
84
|
-
}
|
|
85
|
-
})
|
|
86
|
-
}
|
|
87
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { ProjectContainer } from '@contember/engine-http'
|
|
2
|
-
import { PromiseMap } from '@contember/engine-common'
|
|
3
|
-
|
|
4
|
-
type ContainerWithMeta = { container: ProjectContainer; cleanups: (() => void)[]; timestamp: Date }
|
|
5
|
-
|
|
6
|
-
export class ProjectContainerStore {
|
|
7
|
-
private containers = new PromiseMap<string, ContainerWithMeta>()
|
|
8
|
-
private aliasMapping = new Map<string, string>()
|
|
9
|
-
|
|
10
|
-
public resolveAlias(slug: string): string | undefined {
|
|
11
|
-
return this.aliasMapping.get(slug)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
public setAlias(slug: string, alias: string): void {
|
|
15
|
-
this.aliasMapping.set(alias, slug)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
public removeAlias(alias: string): void {
|
|
19
|
-
this.aliasMapping.delete(alias)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
public getContainer(slug: string): Promise<ContainerWithMeta> | undefined {
|
|
23
|
-
return this.containers.get(slug)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
public async fetchContainer(slug: string, factory: (slug: string) => Promise<ContainerWithMeta>): Promise<ContainerWithMeta> {
|
|
27
|
-
return await this.containers.fetch(slug, factory)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
public removeContainer(slug: string): void {
|
|
31
|
-
this.containers.delete(slug)
|
|
32
|
-
}
|
|
33
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { ProjectInitializer as ProjectInitializerInterface, ProjectWithSecrets } from '@contember/engine-tenant-api'
|
|
2
|
-
import { ProjectContainerResolver } from './ProjectContainerResolver'
|
|
3
|
-
import { Logger } from '@contember/logger'
|
|
4
|
-
|
|
5
|
-
export class ProjectInitializer implements ProjectInitializerInterface {
|
|
6
|
-
constructor(
|
|
7
|
-
private readonly projectContainerResolver: ProjectContainerResolver,
|
|
8
|
-
) {}
|
|
9
|
-
|
|
10
|
-
async initializeProject(project: ProjectWithSecrets, logger: Logger) {
|
|
11
|
-
await this.projectContainerResolver.createProjectContainer(project)
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export class ProjectInitializerProxy implements ProjectInitializerInterface {
|
|
16
|
-
private initializer: ProjectInitializerInterface | undefined = undefined
|
|
17
|
-
|
|
18
|
-
setInitializer(initializer: ProjectInitializerInterface): void {
|
|
19
|
-
this.initializer = initializer
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
initializeProject(project: ProjectWithSecrets, logger: Logger) {
|
|
23
|
-
if (!this.initializer) {
|
|
24
|
-
throw new Error('Initializer is not set')
|
|
25
|
-
}
|
|
26
|
-
return this.initializer.initializeProject(project, logger)
|
|
27
|
-
}
|
|
28
|
-
}
|