@contember/engine-server 1.2.0-alpha.15 → 1.2.0-alpha.17
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/MasterContainer.d.ts +6 -6
- package/dist/src/MasterContainer.d.ts.map +1 -1
- package/dist/src/MasterContainer.js +9 -12
- package/dist/src/MasterContainer.js.map +1 -1
- package/dist/src/bootstrap/Initializer.d.ts.map +1 -1
- package/dist/src/bootstrap/Initializer.js +3 -15
- package/dist/src/bootstrap/Initializer.js.map +1 -1
- package/dist/src/config/configSchema.d.ts +69 -0
- package/dist/src/config/configSchema.d.ts.map +1 -1
- package/dist/src/config/configSchema.js +14 -2
- package/dist/src/config/configSchema.js.map +1 -1
- package/dist/src/config/configTemplate.d.ts.map +1 -1
- package/dist/src/config/configTemplate.js +18 -0
- package/dist/src/config/configTemplate.js.map +1 -1
- package/dist/src/project/ProjectContainer.d.ts +8 -2
- package/dist/src/project/ProjectContainer.d.ts.map +1 -1
- package/dist/src/project/ProjectContainer.js +20 -5
- package/dist/src/project/ProjectContainer.js.map +1 -1
- package/dist/src/project/ProjectContainerResolver.d.ts +2 -3
- package/dist/src/project/ProjectContainerResolver.d.ts.map +1 -1
- package/dist/src/project/ProjectContainerResolver.js +3 -4
- package/dist/src/project/ProjectContainerResolver.js.map +1 -1
- package/dist/src/project/ProjectInitializer.d.ts +3 -2
- package/dist/src/project/ProjectInitializer.d.ts.map +1 -1
- package/dist/src/project/ProjectInitializer.js +3 -3
- package/dist/src/project/ProjectInitializer.js.map +1 -1
- package/dist/src/project/ProjectSchemaResolver.d.ts +2 -7
- package/dist/src/project/ProjectSchemaResolver.d.ts.map +1 -1
- package/dist/src/project/ProjectSchemaResolver.js +2 -16
- package/dist/src/project/ProjectSchemaResolver.js.map +1 -1
- package/dist/src/projectGroup/ProjectGroupContainer.d.ts +3 -1
- package/dist/src/projectGroup/ProjectGroupContainer.d.ts.map +1 -1
- package/dist/src/projectGroup/ProjectGroupContainer.js +8 -6
- package/dist/src/projectGroup/ProjectGroupContainer.js.map +1 -1
- package/dist/src/start.js +16 -13
- package/dist/src/start.js.map +1 -1
- package/dist/src/tsconfig.tsbuildinfo +1 -1
- package/dist/src/utils/sentry.d.ts +8 -9
- package/dist/src/utils/sentry.d.ts.map +1 -1
- package/dist/src/utils/sentry.js +44 -29
- package/dist/src/utils/sentry.js.map +1 -1
- package/dist/src/utils/serverStartup.d.ts +3 -1
- package/dist/src/utils/serverStartup.d.ts.map +1 -1
- package/dist/src/utils/serverStartup.js +47 -10
- package/dist/src/utils/serverStartup.js.map +1 -1
- package/dist/src/utils/serverTermination.d.ts +2 -1
- package/dist/src/utils/serverTermination.d.ts.map +1 -1
- package/dist/src/utils/serverTermination.js +3 -5
- package/dist/src/utils/serverTermination.js.map +1 -1
- package/dist/tests/cases/configResolver.test.d.ts +2 -0
- package/dist/tests/cases/configResolver.test.d.ts.map +1 -0
- package/dist/tests/cases/configResolver.test.js +67 -0
- package/dist/tests/cases/configResolver.test.js.map +1 -0
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/package.json +22 -20
- package/src/MasterContainer.ts +16 -14
- package/src/bootstrap/Initializer.ts +3 -16
- package/src/config/configSchema.ts +42 -21
- package/src/config/configTemplate.ts +18 -0
- package/src/project/ProjectContainer.ts +23 -4
- package/src/project/ProjectContainerResolver.ts +4 -4
- package/src/project/ProjectInitializer.ts +4 -3
- package/src/project/ProjectSchemaResolver.ts +1 -18
- package/src/projectGroup/ProjectGroupContainer.ts +11 -6
- package/src/start.ts +24 -17
- package/src/tsconfig.json +3 -0
- package/src/utils/sentry.ts +45 -35
- package/src/utils/serverStartup.ts +45 -9
- package/src/utils/serverTermination.ts +4 -5
- package/tests/cases/configResolver.test.ts +67 -0
package/src/utils/sentry.ts
CHANGED
|
@@ -1,46 +1,56 @@
|
|
|
1
1
|
import * as Sentry from '@sentry/node'
|
|
2
|
+
import { LoggerRequestSymbol } from '@contember/engine-http'
|
|
3
|
+
import { LogEntry, LoggerHandler, LogLevel, LogLevels } from '@contember/logger'
|
|
2
4
|
|
|
3
|
-
export
|
|
5
|
+
export class SentryLoggerHandler implements LoggerHandler {
|
|
6
|
+
constructor(
|
|
7
|
+
private readonly logLevel: LogLevel,
|
|
8
|
+
) {
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
getMinLevel(): number {
|
|
12
|
+
return this.logLevel.value
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
handle(entry: LogEntry) {
|
|
16
|
+
if (!entry.error || entry.level.value < this.logLevel.value) {
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
Sentry.captureException(entry.error, scope => {
|
|
20
|
+
scope.setTag('project', entry.loggerAttributes.project ?? entry.ownAttributes.project)
|
|
21
|
+
scope.setTag('module', entry.loggerAttributes.module ?? entry.ownAttributes.module)
|
|
22
|
+
scope.setLevel('error')
|
|
23
|
+
scope.setUser({
|
|
24
|
+
id: entry.loggerAttributes.user ?? entry.ownAttributes.user,
|
|
25
|
+
})
|
|
26
|
+
scope.setExtra('requestId', entry.loggerAttributes.requestId)
|
|
27
|
+
|
|
28
|
+
scope.addEventProcessor(event => {
|
|
29
|
+
return {
|
|
30
|
+
...event,
|
|
31
|
+
request: {
|
|
32
|
+
url: entry.loggerAttributes.url ?? entry.ownAttributes.url,
|
|
33
|
+
data: entry.loggerAttributes[LoggerRequestSymbol]?.body,
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
return scope
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
close(): void {
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const createSentryLoggerHandler = (dsn?: string): null | LoggerHandler => {
|
|
4
46
|
if (!dsn) {
|
|
5
|
-
return
|
|
47
|
+
return null
|
|
6
48
|
}
|
|
7
|
-
// eslint-disable-next-line no-console
|
|
8
|
-
console.log(`Intializing sentry with dsn: ${dsn}`)
|
|
9
49
|
Sentry.init({
|
|
10
50
|
dsn: dsn,
|
|
11
51
|
integrations: integrations => {
|
|
12
52
|
return integrations.filter(integration => integration.name !== 'Console' && integration.name !== 'Http')
|
|
13
53
|
},
|
|
14
54
|
})
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
interface SentryErrorContext {
|
|
18
|
-
user: string
|
|
19
|
-
body: string
|
|
20
|
-
project?: string
|
|
21
|
-
url: string
|
|
22
|
-
module: string
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export const logSentryError = (error: any, context: SentryErrorContext) => {
|
|
26
|
-
Sentry.withScope(scope => {
|
|
27
|
-
if (context.project) {
|
|
28
|
-
scope.setTag('project', context.project)
|
|
29
|
-
}
|
|
30
|
-
scope.setTag('module', context.module)
|
|
31
|
-
scope.setLevel('error')
|
|
32
|
-
scope.setUser({
|
|
33
|
-
id: context.user,
|
|
34
|
-
})
|
|
35
|
-
scope.addEventProcessor(event => {
|
|
36
|
-
return {
|
|
37
|
-
...event,
|
|
38
|
-
request: {
|
|
39
|
-
url: context.url,
|
|
40
|
-
data: context.body,
|
|
41
|
-
},
|
|
42
|
-
}
|
|
43
|
-
})
|
|
44
|
-
Sentry.captureException(error)
|
|
45
|
-
})
|
|
55
|
+
return new SentryLoggerHandler(LogLevels.warn)
|
|
46
56
|
}
|
|
@@ -2,6 +2,7 @@ import { join } from 'path'
|
|
|
2
2
|
import { ConfigProcessor, Plugin } from '@contember/engine-plugins'
|
|
3
3
|
import { ConfigSource, readConfig, ServerConfig } from '../config/config'
|
|
4
4
|
import { Type } from '@contember/typesafe'
|
|
5
|
+
import { createLogger, JsonStreamLoggerHandler, Logger, LogLevels, PrettyPrintLoggerHandler } from '@contember/logger'
|
|
5
6
|
|
|
6
7
|
export const getServerVersion = (): string => {
|
|
7
8
|
const packageJsonFile = process.env.CONTEMBER_PACKAGE_JSON || join(__dirname, '../../../package.json')
|
|
@@ -12,16 +13,12 @@ export const getServerVersion = (): string => {
|
|
|
12
13
|
|
|
13
14
|
export const isDebugMode = (): boolean => process.env.NODE_ENV === 'development'
|
|
14
15
|
|
|
15
|
-
export const printStartInfo = ({ version, isDebug }: { version: string; isDebug: boolean }) => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// eslint-disable-next-line no-console
|
|
19
|
-
console.log(`NODE_ENV is set to ${process.env.NODE_ENV}`)
|
|
16
|
+
export const printStartInfo = ({ version, isDebug }: { version: string; isDebug: boolean }, logger: Logger) => {
|
|
17
|
+
logger.info(`Starting Contember ${version}`)
|
|
18
|
+
logger.info(`NODE_ENV is set to ${process.env.NODE_ENV}`)
|
|
20
19
|
if (isDebug) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// eslint-disable-next-line no-console
|
|
24
|
-
console.log('NEVER USE debug mode in production environment')
|
|
20
|
+
logger.warn('Starting Contember in debug mode')
|
|
21
|
+
logger.warn('NEVER USE debug mode in production environment')
|
|
25
22
|
}
|
|
26
23
|
}
|
|
27
24
|
|
|
@@ -40,3 +37,42 @@ export const resolveServerConfig = async <T extends ServerConfig>({ plugins, ser
|
|
|
40
37
|
|
|
41
38
|
return await readConfig(configSources, configProcessors, serverConfigSchema)
|
|
42
39
|
}
|
|
40
|
+
|
|
41
|
+
export const createDefaultLogger = (): Logger => {
|
|
42
|
+
const isDebug = isDebugMode()
|
|
43
|
+
const loggerType = process.env.CONTEMBER_LOGGER_FORMAT ?? (isDebug ? 'pretty' : 'json')
|
|
44
|
+
const stream = process.stderr
|
|
45
|
+
const handler = loggerType === 'pretty'
|
|
46
|
+
? new PrettyPrintLoggerHandler(stream, {
|
|
47
|
+
formatters: {
|
|
48
|
+
pid: () => undefined,
|
|
49
|
+
loggerId: () => undefined,
|
|
50
|
+
uri: ({ attributes, chalk }) => {
|
|
51
|
+
const color = attributes.status === undefined
|
|
52
|
+
? chalk.cyan
|
|
53
|
+
: attributes.status < 300
|
|
54
|
+
? chalk.green
|
|
55
|
+
: chalk.yellowBright
|
|
56
|
+
const timeInfo = attributes.totalTimeMs !== undefined ? ` ${attributes.totalTimeMs}ms` : ''
|
|
57
|
+
const statusInfo = attributes.status !== undefined ? ` [${attributes.status}]` : ''
|
|
58
|
+
return { line: color(`${attributes.method}${statusInfo} ${attributes.uri}${timeInfo} ${chalk.white.dim('#' + attributes.requestId)}`) }
|
|
59
|
+
},
|
|
60
|
+
method: () => undefined,
|
|
61
|
+
requestId: () => undefined,
|
|
62
|
+
timeLabel: () => undefined,
|
|
63
|
+
status: () => undefined,
|
|
64
|
+
totalTimeMs: () => undefined,
|
|
65
|
+
events: ({ formattedValue }) => formattedValue === '[]' ? undefined : formattedValue,
|
|
66
|
+
},
|
|
67
|
+
})
|
|
68
|
+
: new JsonStreamLoggerHandler(stream)
|
|
69
|
+
const logLevel = (process.env.CONTEMBER_LOGGER_LEVEL as keyof typeof LogLevels) ?? 'info'
|
|
70
|
+
return createLogger(handler, {
|
|
71
|
+
pid: process.pid,
|
|
72
|
+
loggerId: Math.random().toString().substring(2),
|
|
73
|
+
}, {
|
|
74
|
+
fingerCrossedOptions: {
|
|
75
|
+
logAlwaysLevel: LogLevels[logLevel],
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import cluster from 'cluster'
|
|
2
|
+
import { Logger } from '@contember/logger'
|
|
2
3
|
|
|
3
4
|
const signals = {
|
|
4
5
|
SIGHUP: 1,
|
|
@@ -6,14 +7,12 @@ const signals = {
|
|
|
6
7
|
SIGTERM: 15,
|
|
7
8
|
}
|
|
8
9
|
export type TerminationJob = (args: { signal: keyof typeof signals; code: number }) => Promise<void>
|
|
9
|
-
export const listenOnProcessTermination = (jobs: TerminationJob[]) => {
|
|
10
|
+
export const listenOnProcessTermination = (jobs: TerminationJob[], logger: Logger) => {
|
|
10
11
|
for (const [signal, code] of Object.entries(signals)) {
|
|
11
12
|
process.on(signal, async () => {
|
|
12
|
-
|
|
13
|
-
console.log(`Process ${process.pid} received a ${signal} signal, executing ${jobs.length} termination jobs`)
|
|
13
|
+
logger.info(`Process ${process.pid} received a ${signal} signal, executing ${jobs.length} termination jobs`)
|
|
14
14
|
await Promise.allSettled(jobs.map(it => it({ signal: signal as keyof typeof signals, code })))
|
|
15
|
-
|
|
16
|
-
console.log(cluster.isMaster ? `All terminated, exiting` : 'All terminated, exiting a worker')
|
|
15
|
+
logger.info(cluster.isMaster ? `All terminated, exiting` : 'All terminated, exiting a worker')
|
|
17
16
|
process.exit(128 + code)
|
|
18
17
|
})
|
|
19
18
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { assert, it } from 'vitest'
|
|
2
|
+
import { createTenantConfigResolver } from '../../src/config/tenantConfigResolver'
|
|
3
|
+
import { configTemplate } from '../../src/config/configTemplate'
|
|
4
|
+
import { createProjectConfigResolver } from '../../src/config/projectConfigResolver'
|
|
5
|
+
|
|
6
|
+
const env = {
|
|
7
|
+
DEFAULT_DB_POOL_IDLE_TIMEOUT_MS: '60000',
|
|
8
|
+
DEFAULT_DB_POOL_MAX_CONNECTIONS: '1000',
|
|
9
|
+
DEFAULT_DB_POOL_MAX_CONNECTING: '10',
|
|
10
|
+
DEFAULT_DB_CONNECTION_TIMEOUT_MS: '5000',
|
|
11
|
+
}
|
|
12
|
+
const tenantConfig = {
|
|
13
|
+
db: {
|
|
14
|
+
host: 'c0.cluster-abc.eu-west-1.rds.amazonaws.com',
|
|
15
|
+
port: 5432,
|
|
16
|
+
user: 'u_test_e',
|
|
17
|
+
password: 'PASSWD',
|
|
18
|
+
database: 'p_test_e',
|
|
19
|
+
read: { host: 'c0.cluster-ro-abc.eu-west-1.rds.amazonaws.com' },
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
it('resolves tenant config config', () => {
|
|
23
|
+
const tenantResolver = createTenantConfigResolver(env, configTemplate.tenant)
|
|
24
|
+
const resolvedTenantConfig = tenantResolver('test', tenantConfig)
|
|
25
|
+
assert.deepEqual(resolvedTenantConfig, {
|
|
26
|
+
db: {
|
|
27
|
+
host: 'c0.cluster-abc.eu-west-1.rds.amazonaws.com',
|
|
28
|
+
port: 5432,
|
|
29
|
+
user: 'u_test_e',
|
|
30
|
+
password: 'PASSWD',
|
|
31
|
+
database: 'p_test_e',
|
|
32
|
+
connectionTimeoutMs: 5000,
|
|
33
|
+
pool: { maxConnections: 1000, maxConnecting: 10, idleTimeoutMs: 60000 },
|
|
34
|
+
read: { host: 'c0.cluster-ro-abc.eu-west-1.rds.amazonaws.com' },
|
|
35
|
+
}, mailer: {}, credentials: {}, secrets: {},
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
it('resolves project config config', () => {
|
|
39
|
+
const tenantResolver = createTenantConfigResolver(env, configTemplate.tenant)
|
|
40
|
+
const resolvedTenantConfig = tenantResolver('test', tenantConfig)
|
|
41
|
+
|
|
42
|
+
const projectConfigResolver = createProjectConfigResolver(env, configTemplate, [])
|
|
43
|
+
const resolvedProjectConfig = projectConfigResolver('test-p', {
|
|
44
|
+
db: {
|
|
45
|
+
useTenantDb: true,
|
|
46
|
+
systemSchema: 'system_test_e',
|
|
47
|
+
}, s3: { prefix: 'test-e/test-e' }, stages: { live: { schema: 'content_test_e_live' } },
|
|
48
|
+
}, {}, resolvedTenantConfig)
|
|
49
|
+
|
|
50
|
+
assert.deepEqual(resolvedProjectConfig as any, {
|
|
51
|
+
name: 'Test p',
|
|
52
|
+
slug: 'test-p',
|
|
53
|
+
stages: [{ name: 'Live', slug: 'live', schema: 'content_test_e_live' }],
|
|
54
|
+
db: {
|
|
55
|
+
host: 'c0.cluster-abc.eu-west-1.rds.amazonaws.com',
|
|
56
|
+
port: 5432,
|
|
57
|
+
user: 'u_test_e',
|
|
58
|
+
password: 'PASSWD',
|
|
59
|
+
database: 'p_test_e',
|
|
60
|
+
connectionTimeoutMs: 5000,
|
|
61
|
+
pool: { maxConnections: 1000, maxConnecting: 10, idleTimeoutMs: 60000 },
|
|
62
|
+
read: { host: 'c0.cluster-ro-abc.eu-west-1.rds.amazonaws.com' },
|
|
63
|
+
useTenantDb: true,
|
|
64
|
+
systemSchema: 'system_test_e',
|
|
65
|
+
},
|
|
66
|
+
})
|
|
67
|
+
})
|