@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.
Files changed (126) hide show
  1. package/dist/src/start.js +16 -15
  2. package/dist/src/start.js.map +1 -1
  3. package/dist/src/tsconfig.tsbuildinfo +1 -1
  4. package/dist/tests/e2e/assume-identity.test.js.map +1 -1
  5. package/dist/tests/tsconfig.tsbuildinfo +1 -1
  6. package/package.json +6 -22
  7. package/src/start.ts +10 -8
  8. package/src/tsconfig.json +4 -45
  9. package/tests/e2e/assume-identity.test.ts +0 -1
  10. package/tests/tsconfig.json +2 -6
  11. package/tsconfig.json +2 -6
  12. package/dist/src/MasterContainer.d.ts +0 -125
  13. package/dist/src/MasterContainer.d.ts.map +0 -1
  14. package/dist/src/MasterContainer.js +0 -102
  15. package/dist/src/MasterContainer.js.map +0 -1
  16. package/dist/src/bootstrap/Initializer.d.ts +0 -7
  17. package/dist/src/bootstrap/Initializer.d.ts.map +0 -1
  18. package/dist/src/bootstrap/Initializer.js +0 -27
  19. package/dist/src/bootstrap/Initializer.js.map +0 -1
  20. package/dist/src/bootstrap/index.d.ts +0 -2
  21. package/dist/src/bootstrap/index.d.ts.map +0 -1
  22. package/dist/src/bootstrap/index.js +0 -18
  23. package/dist/src/bootstrap/index.js.map +0 -1
  24. package/dist/src/config/config.d.ts +0 -17
  25. package/dist/src/config/config.d.ts.map +0 -1
  26. package/dist/src/config/config.js +0 -30
  27. package/dist/src/config/config.js.map +0 -1
  28. package/dist/src/config/configSchema.d.ts +0 -201
  29. package/dist/src/config/configSchema.d.ts.map +0 -1
  30. package/dist/src/config/configSchema.js +0 -119
  31. package/dist/src/config/configSchema.js.map +0 -1
  32. package/dist/src/config/configTemplate.d.ts +0 -2
  33. package/dist/src/config/configTemplate.d.ts.map +0 -1
  34. package/dist/src/config/configTemplate.js +0 -90
  35. package/dist/src/config/configTemplate.js.map +0 -1
  36. package/dist/src/config/projectConfigResolver.d.ts +0 -8
  37. package/dist/src/config/projectConfigResolver.d.ts.map +0 -1
  38. package/dist/src/config/projectConfigResolver.js +0 -91
  39. package/dist/src/config/projectConfigResolver.js.map +0 -1
  40. package/dist/src/config/tenantConfigResolver.d.ts +0 -8
  41. package/dist/src/config/tenantConfigResolver.d.ts.map +0 -1
  42. package/dist/src/config/tenantConfigResolver.js +0 -31
  43. package/dist/src/config/tenantConfigResolver.js.map +0 -1
  44. package/dist/src/index.d.ts +0 -13
  45. package/dist/src/index.d.ts.map +0 -1
  46. package/dist/src/index.js +0 -33
  47. package/dist/src/index.js.map +0 -1
  48. package/dist/src/project/ProjectContainer.d.ts +0 -58
  49. package/dist/src/project/ProjectContainer.d.ts.map +0 -1
  50. package/dist/src/project/ProjectContainer.js +0 -66
  51. package/dist/src/project/ProjectContainer.js.map +0 -1
  52. package/dist/src/project/ProjectContainerResolver.d.ts +0 -24
  53. package/dist/src/project/ProjectContainerResolver.d.ts.map +0 -1
  54. package/dist/src/project/ProjectContainerResolver.js +0 -75
  55. package/dist/src/project/ProjectContainerResolver.js.map +0 -1
  56. package/dist/src/project/ProjectContainerStore.d.ts +0 -18
  57. package/dist/src/project/ProjectContainerStore.d.ts.map +0 -1
  58. package/dist/src/project/ProjectContainerStore.js +0 -30
  59. package/dist/src/project/ProjectContainerStore.js.map +0 -1
  60. package/dist/src/project/ProjectInitializer.d.ts +0 -14
  61. package/dist/src/project/ProjectInitializer.d.ts.map +0 -1
  62. package/dist/src/project/ProjectInitializer.js +0 -28
  63. package/dist/src/project/ProjectInitializer.js.map +0 -1
  64. package/dist/src/project/ProjectSchemaResolver.d.ts +0 -14
  65. package/dist/src/project/ProjectSchemaResolver.d.ts.map +0 -1
  66. package/dist/src/project/ProjectSchemaResolver.js +0 -30
  67. package/dist/src/project/ProjectSchemaResolver.js.map +0 -1
  68. package/dist/src/project/index.d.ts +0 -5
  69. package/dist/src/project/index.d.ts.map +0 -1
  70. package/dist/src/project/index.js +0 -21
  71. package/dist/src/project/index.js.map +0 -1
  72. package/dist/src/projectGroup/ProjectGroupContainer.d.ts +0 -26
  73. package/dist/src/projectGroup/ProjectGroupContainer.d.ts.map +0 -1
  74. package/dist/src/projectGroup/ProjectGroupContainer.js +0 -70
  75. package/dist/src/projectGroup/ProjectGroupContainer.js.map +0 -1
  76. package/dist/src/projectGroup/ProjectGroupResolver.d.ts +0 -7
  77. package/dist/src/projectGroup/ProjectGroupResolver.d.ts.map +0 -1
  78. package/dist/src/projectGroup/ProjectGroupResolver.js +0 -13
  79. package/dist/src/projectGroup/ProjectGroupResolver.js.map +0 -1
  80. package/dist/src/utils/index.d.ts +0 -3
  81. package/dist/src/utils/index.d.ts.map +0 -1
  82. package/dist/src/utils/index.js +0 -19
  83. package/dist/src/utils/index.js.map +0 -1
  84. package/dist/src/utils/sentry.d.ts +0 -10
  85. package/dist/src/utils/sentry.d.ts.map +0 -1
  86. package/dist/src/utils/sentry.js +0 -80
  87. package/dist/src/utils/sentry.js.map +0 -1
  88. package/dist/src/utils/serverStartup.d.ts +0 -33
  89. package/dist/src/utils/serverStartup.d.ts.map +0 -1
  90. package/dist/src/utils/serverStartup.js +0 -79
  91. package/dist/src/utils/serverStartup.js.map +0 -1
  92. package/dist/src/utils/serverTermination.d.ts +0 -13
  93. package/dist/src/utils/serverTermination.d.ts.map +0 -1
  94. package/dist/src/utils/serverTermination.js +0 -24
  95. package/dist/src/utils/serverTermination.js.map +0 -1
  96. package/dist/src/utils/strings.d.ts +0 -2
  97. package/dist/src/utils/strings.d.ts.map +0 -1
  98. package/dist/src/utils/strings.js +0 -6
  99. package/dist/src/utils/strings.js.map +0 -1
  100. package/dist/tests/cases/configResolver.test.d.ts +0 -2
  101. package/dist/tests/cases/configResolver.test.d.ts.map +0 -1
  102. package/dist/tests/cases/configResolver.test.js +0 -67
  103. package/dist/tests/cases/configResolver.test.js.map +0 -1
  104. package/src/MasterContainer.ts +0 -206
  105. package/src/bootstrap/Initializer.ts +0 -27
  106. package/src/bootstrap/index.ts +0 -1
  107. package/src/config/config.ts +0 -56
  108. package/src/config/configSchema.ts +0 -135
  109. package/src/config/configTemplate.ts +0 -86
  110. package/src/config/projectConfigResolver.ts +0 -86
  111. package/src/config/tenantConfigResolver.ts +0 -41
  112. package/src/index.ts +0 -23
  113. package/src/project/ProjectContainer.ts +0 -95
  114. package/src/project/ProjectContainerResolver.ts +0 -87
  115. package/src/project/ProjectContainerStore.ts +0 -33
  116. package/src/project/ProjectInitializer.ts +0 -28
  117. package/src/project/ProjectSchemaResolver.ts +0 -33
  118. package/src/project/index.ts +0 -4
  119. package/src/projectGroup/ProjectGroupContainer.ts +0 -134
  120. package/src/projectGroup/ProjectGroupResolver.ts +0 -12
  121. package/src/utils/index.ts +0 -2
  122. package/src/utils/sentry.ts +0 -56
  123. package/src/utils/serverStartup.ts +0 -78
  124. package/src/utils/serverTermination.ts +0 -19
  125. package/src/utils/strings.ts +0 -1
  126. package/tests/cases/configResolver.test.ts +0 -67
@@ -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
- }