@platformatic/service 3.4.1 → 3.5.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/LICENSE +1 -1
- package/config.d.ts +450 -94
- package/eslint.config.js +4 -6
- package/index.d.ts +55 -48
- package/index.js +44 -179
- package/lib/application.js +35 -0
- package/lib/capability.js +281 -0
- package/lib/compile.js +2 -52
- package/lib/generator.js +426 -0
- package/lib/plugins/cors.js +5 -8
- package/lib/plugins/graphql.js +16 -14
- package/lib/plugins/health-check.js +6 -8
- package/lib/plugins/openapi.js +43 -32
- package/lib/plugins/plugins.js +6 -53
- package/lib/{root-endpoint/index.js → plugins/root.js} +9 -8
- package/lib/plugins/sandbox-wrapper.js +65 -63
- package/lib/schema.js +1075 -203
- package/lib/upgrade.js +6 -8
- package/lib/utils.js +30 -83
- package/lib/versions/0.16.0.js +14 -15
- package/lib/versions/{from-zero-twenty-eight-to-will-see.js → 0.28.0.js} +3 -6
- package/lib/versions/2.0.0.js +4 -7
- package/lib/versions/3.0.0.js +14 -0
- package/package.json +28 -36
- package/schema.json +1452 -165
- package/tsconfig.json +16 -6
- package/.c8rc +0 -6
- package/help/compile.txt +0 -19
- package/help/create.txt +0 -11
- package/help/help.txt +0 -8
- package/help/schema.txt +0 -9
- package/help/start.txt +0 -23
- package/index.test-d.ts +0 -107
- package/lib/create.mjs +0 -85
- package/lib/gen-schema.js +0 -15
- package/lib/gen-types.mjs +0 -38
- package/lib/generator/README.md +0 -31
- package/lib/generator/service-generator.d.ts +0 -11
- package/lib/generator/service-generator.js +0 -126
- package/lib/openapi-schema-defs.js +0 -1108
- package/lib/plugins/clients.js +0 -16
- package/lib/plugins/metrics.js +0 -244
- package/lib/plugins/typescript.js +0 -20
- package/lib/stackable.js +0 -306
- package/lib/start.js +0 -175
- package/service.mjs +0 -71
- /package/{lib/root-endpoint/public → public}/images/dark_mode.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/favicon.ico +0 -0
- /package/{lib/root-endpoint/public → public}/images/light_mode.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/platformatic-logo-dark.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/platformatic-logo-light.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/triangle_dark.svg +0 -0
- /package/{lib/root-endpoint/public → public}/images/triangle_light.svg +0 -0
- /package/{lib/root-endpoint/public → public}/index.html +0 -0
package/index.d.ts
CHANGED
|
@@ -1,66 +1,73 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { FastifyInstance, FastifyBaseLogger } from 'fastify'
|
|
4
|
-
import ConfigManager from '@platformatic/config'
|
|
5
|
-
import type { ConfigManagerConfig } from '@platformatic/config'
|
|
6
|
-
import type { Stackable as _Stackable, StackableInterface } from '@platformatic/config'
|
|
1
|
+
import { BaseCapability, BaseContext, BaseOptions } from '@platformatic/basic'
|
|
2
|
+
import { Configuration, ConfigurationOptions } from '@platformatic/foundation'
|
|
7
3
|
import { BaseGenerator } from '@platformatic/generators'
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
4
|
+
import { JSONSchemaType } from 'ajv'
|
|
5
|
+
import { FastifyInstance } from 'fastify'
|
|
6
|
+
import type { PlatformaticServiceConfig } from './config.d.ts'
|
|
11
7
|
|
|
12
|
-
export
|
|
8
|
+
export type { PlatformaticServiceConfig } from './config.d.ts'
|
|
13
9
|
|
|
14
|
-
export interface
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
export interface ServiceContext extends BaseContext {
|
|
11
|
+
applicationFactory?: typeof platformaticService
|
|
12
|
+
fastifyPlugins?: Function[]
|
|
17
13
|
}
|
|
18
14
|
|
|
19
|
-
export
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
export function start<ConfigType> (app: Stackable<ConfigType>, args: string[]): Promise<void>
|
|
15
|
+
export interface PlatformaticApplication<Config> {
|
|
16
|
+
config: Configuration<Config>
|
|
17
|
+
}
|
|
23
18
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
restart: () => Promise<void>
|
|
27
|
-
}
|
|
19
|
+
export type ServerInstance<Configuration = PlatformaticServiceConfig> = FastifyInstance & {
|
|
20
|
+
platformatic: PlatformaticApplication<Configuration>
|
|
28
21
|
}
|
|
29
22
|
|
|
30
|
-
type
|
|
23
|
+
export type ServiceConfiguration<T = {}> = Configuration<PlatformaticServiceConfig & T>
|
|
31
24
|
|
|
32
|
-
export
|
|
33
|
-
app: (app: FastifyInstance, opts: object) => Promise<void>
|
|
34
|
-
Generator?: Generator
|
|
35
|
-
version?: string
|
|
36
|
-
upgrade?: (config: any, version: string) => Promise<any>
|
|
37
|
-
transformConfig?: (config: any) => Promise<any>
|
|
38
|
-
buildStackable: (opts: { config: string }, app?: object) => Promise<StackableInterface>
|
|
39
|
-
}
|
|
25
|
+
export declare function transform (config: ServiceConfiguration): Promise<ServiceConfiguration>
|
|
40
26
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
27
|
+
export declare function loadConfiguration (
|
|
28
|
+
root: string | PlatformaticServiceConfig,
|
|
29
|
+
source?: string | PlatformaticServiceConfig,
|
|
30
|
+
context?: ConfigurationOptions
|
|
31
|
+
): Promise<Configuration<PlatformaticServiceConfig>>
|
|
32
|
+
|
|
33
|
+
export declare function create (
|
|
34
|
+
root: string | PlatformaticServiceConfig,
|
|
35
|
+
source?: string | PlatformaticServiceConfig,
|
|
36
|
+
context?: ConfigurationOptions
|
|
37
|
+
): Promise<ServiceCapability>
|
|
47
38
|
|
|
48
|
-
export const
|
|
49
|
-
export const configManagerConfig: ConfigManagerConfig<PlatformaticServiceConfig>
|
|
39
|
+
export declare const skipTelemetryHooks: boolean
|
|
50
40
|
|
|
51
|
-
export declare
|
|
41
|
+
export declare function platformaticService (app: FastifyInstance, capability: ServiceCapability): Promise<void>
|
|
52
42
|
|
|
53
|
-
export declare
|
|
43
|
+
export declare class Generator extends BaseGenerator {}
|
|
44
|
+
export declare function applyTestHelperCustomizations (
|
|
45
|
+
helper: string,
|
|
46
|
+
mod: string,
|
|
47
|
+
customizations: Record<string, string>
|
|
48
|
+
): string
|
|
54
49
|
|
|
55
|
-
export const
|
|
50
|
+
export declare const packageJson: Record<string, unknown>
|
|
56
51
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
52
|
+
export declare const schema: JSONSchemaType<PlatformaticServiceConfig>
|
|
53
|
+
|
|
54
|
+
export declare const schemaComponents: {
|
|
55
|
+
$defs: JSONSchemaType<object>
|
|
56
|
+
plugins: JSONSchemaType<object>
|
|
57
|
+
openApiBase: JSONSchemaType<object>
|
|
58
|
+
openapi: JSONSchemaType<object>
|
|
59
|
+
proxy: JSONSchemaType<object>
|
|
60
|
+
graphqlBase: JSONSchemaType<object>
|
|
61
|
+
graphql: JSONSchemaType<object>
|
|
62
|
+
application: JSONSchemaType<object>
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
export
|
|
65
|
+
export declare const version: string
|
|
65
66
|
|
|
66
|
-
export
|
|
67
|
+
export declare class ServiceCapability<Config = PlatformaticServiceConfig> extends BaseCapability<
|
|
68
|
+
Config,
|
|
69
|
+
BaseOptions<ServiceContext>
|
|
70
|
+
> {
|
|
71
|
+
constructor (root: string, config: Config, context?: object)
|
|
72
|
+
getApplication (): FastifyInstance
|
|
73
|
+
}
|
package/index.js
CHANGED
|
@@ -1,198 +1,63 @@
|
|
|
1
|
-
|
|
1
|
+
import { transform as basicTransform, resolve, validationOptions } from '@platformatic/basic'
|
|
2
|
+
import { kMetadata, loadConfiguration as utilsLoadConfiguration } from '@platformatic/foundation'
|
|
3
|
+
import { readFile } from 'node:fs/promises'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
import { ServiceCapability } from './lib/capability.js'
|
|
6
|
+
import { schema } from './lib/schema.js'
|
|
7
|
+
import { upgrade } from './lib/upgrade.js'
|
|
8
|
+
import { isDocker } from './lib/utils.js'
|
|
2
9
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const { readFile } = require('fs/promises')
|
|
6
|
-
const { join } = require('path')
|
|
10
|
+
export async function transform (config, schema, options) {
|
|
11
|
+
config = await basicTransform(config, schema, options)
|
|
7
12
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const setupGraphQL = require('./lib/plugins/graphql.js')
|
|
11
|
-
const setupClients = require('./lib/plugins/clients')
|
|
12
|
-
const setupMetrics = require('./lib/plugins/metrics')
|
|
13
|
-
const setupTsCompiler = require('./lib/plugins/typescript')
|
|
14
|
-
const setupHealthCheck = require('./lib/plugins/health-check')
|
|
15
|
-
const loadPlugins = require('./lib/plugins/plugins')
|
|
16
|
-
const upgrade = require('./lib/upgrade')
|
|
17
|
-
const { telemetry } = require('@platformatic/telemetry')
|
|
18
|
-
|
|
19
|
-
const { buildCompileCmd, extractTypeScriptCompileOptionsFromConfig } = require('./lib/compile')
|
|
20
|
-
const { schema } = require('./lib/schema')
|
|
21
|
-
const { addLoggerToTheConfig } = require('./lib/utils')
|
|
22
|
-
const { start, buildServer } = require('./lib/start')
|
|
23
|
-
const ServiceGenerator = require('./lib/generator/service-generator.js')
|
|
24
|
-
const { ServiceStackable } = require('./lib/stackable')
|
|
25
|
-
|
|
26
|
-
const { version } = require('./package.json')
|
|
27
|
-
|
|
28
|
-
// TODO(mcollina): arugments[2] is deprecated, remove it in the next major version.
|
|
29
|
-
async function platformaticService (app, opts) {
|
|
30
|
-
const configManager = app.platformatic.configManager
|
|
31
|
-
const config = configManager.current
|
|
32
|
-
const beforePlugins = opts.beforePlugins || arguments[2] || []
|
|
33
|
-
|
|
34
|
-
if (isKeyEnabled('metrics', config)) {
|
|
35
|
-
if (config.metrics.server === 'own' && parseInt(config.server.port) === parseInt(config.metrics.port)) {
|
|
36
|
-
app.log.warn('In order to serve metrics on the same port as the core applicaton, set metrics.server to "parent".')
|
|
37
|
-
config.metrics.server = 'parent'
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
app.register(setupMetrics, config.metrics)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// This must be done before loading the plugins, so they can inspect if the
|
|
44
|
-
// openTelemetry decorator exists and then configure accordingly.
|
|
45
|
-
if (isKeyEnabled('telemetry', config)) {
|
|
46
|
-
await app.register(telemetry, config.telemetry)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// This must be done before loading the plugins, so they can be
|
|
50
|
-
// configured accordingly
|
|
51
|
-
if (isKeyEnabled('clients', config)) {
|
|
52
|
-
app.register(setupClients, config.clients)
|
|
13
|
+
if (config.server && (await isDocker())) {
|
|
14
|
+
config.server.hostname = '0.0.0.0'
|
|
53
15
|
}
|
|
54
16
|
|
|
55
|
-
|
|
56
|
-
for (const plugin of beforePlugins) {
|
|
57
|
-
app.register(plugin)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
17
|
+
const typescript = config.plugins?.typescript
|
|
60
18
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const openapi = serviceConfig.openapi
|
|
65
|
-
app.register(setupOpenAPI, { openapi })
|
|
66
|
-
}
|
|
19
|
+
if (typescript) {
|
|
20
|
+
let { outDir, tsConfigFile } = typescript
|
|
21
|
+
tsConfigFile ??= 'tsconfig.json'
|
|
67
22
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const typescript = config.plugins.paths && config.plugins.typescript
|
|
75
|
-
/* c8 ignore next 6 */
|
|
76
|
-
if (typescript === true) {
|
|
77
|
-
registerTsCompiler = true
|
|
78
|
-
} else if (typeof typescript === 'object') {
|
|
79
|
-
registerTsCompiler = typescript.enabled === true || typescript.enabled === undefined
|
|
80
|
-
}
|
|
23
|
+
if (typeof outDir === 'undefined') {
|
|
24
|
+
try {
|
|
25
|
+
outDir = JSON.parse(await readFile(join(this.dirname, tsConfigFile), 'utf8')).compilerOptions.outDir
|
|
26
|
+
} catch {
|
|
27
|
+
// No-op
|
|
28
|
+
}
|
|
81
29
|
|
|
82
|
-
|
|
83
|
-
app.register(setupTsCompiler, { context: opts.context })
|
|
30
|
+
outDir ||= 'dist'
|
|
84
31
|
}
|
|
85
|
-
app.register(loadPlugins, { context: opts.context })
|
|
86
|
-
}
|
|
87
32
|
|
|
88
|
-
|
|
89
|
-
|
|
33
|
+
config.watch.ignore ??= []
|
|
34
|
+
config.watch.ignore.push(outDir + '/**/*')
|
|
90
35
|
}
|
|
91
36
|
|
|
92
|
-
|
|
93
|
-
app.register(setupHealthCheck, config.server.healthCheck)
|
|
94
|
-
}
|
|
37
|
+
return config
|
|
95
38
|
}
|
|
96
39
|
|
|
97
|
-
|
|
40
|
+
export async function loadConfiguration (configOrRoot, sourceOrConfig, context) {
|
|
41
|
+
const { root, source } = await resolve(configOrRoot, sourceOrConfig, 'service')
|
|
98
42
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
strict: false
|
|
108
|
-
},
|
|
109
|
-
async transformConfig () {
|
|
110
|
-
// Set watch to true by default. This is not possible
|
|
111
|
-
// to do in the schema, because it is uses an anyOf.
|
|
112
|
-
if (this.current.watch === undefined) {
|
|
113
|
-
this.current.watch = { enabled: false }
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (typeof this.current.watch !== 'object') {
|
|
117
|
-
this.current.watch = { enabled: this.current.watch || false }
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const typescript = this.current.plugins?.typescript
|
|
121
|
-
if (typescript) {
|
|
122
|
-
let outDir = typescript.outDir
|
|
123
|
-
if (outDir === undefined) {
|
|
124
|
-
let tsConfigFile = typescript.tsConfigFile || 'tsconfig.json'
|
|
125
|
-
tsConfigFile = join(this.dirname, tsConfigFile)
|
|
126
|
-
try {
|
|
127
|
-
const tsConfig = JSON.parse(await readFile(tsConfigFile, 'utf8'))
|
|
128
|
-
outDir = tsConfig.compilerOptions.outDir
|
|
129
|
-
} catch {}
|
|
130
|
-
outDir ||= 'dist'
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
this.current.watch.ignore ||= []
|
|
134
|
-
this.current.watch.ignore.push(outDir + '/**/*')
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
upgrade
|
|
43
|
+
return utilsLoadConfiguration(source, context?.schema ?? schema, {
|
|
44
|
+
validationOptions,
|
|
45
|
+
transform,
|
|
46
|
+
upgrade,
|
|
47
|
+
replaceEnv: true,
|
|
48
|
+
root,
|
|
49
|
+
...context
|
|
50
|
+
})
|
|
138
51
|
}
|
|
139
52
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
function _buildServer (options, app) {
|
|
145
|
-
return buildServer(options, app || module.exports)
|
|
53
|
+
export async function create (configOrRoot, sourceOrConfig, context) {
|
|
54
|
+
const config = await loadConfiguration(configOrRoot, sourceOrConfig, context)
|
|
55
|
+
return new ServiceCapability(config[kMetadata].root, config, context)
|
|
146
56
|
}
|
|
147
57
|
|
|
148
|
-
|
|
149
|
-
let configManager = options.configManager
|
|
150
|
-
|
|
151
|
-
if (configManager === undefined) {
|
|
152
|
-
if (typeof options.config === 'string') {
|
|
153
|
-
;({ configManager } = await loadConfig(
|
|
154
|
-
{},
|
|
155
|
-
['-c', options.config],
|
|
156
|
-
app,
|
|
157
|
-
{
|
|
158
|
-
onMissingEnv: options.onMissingEnv,
|
|
159
|
-
context: options.context
|
|
160
|
-
},
|
|
161
|
-
true
|
|
162
|
-
))
|
|
163
|
-
} else {
|
|
164
|
-
configManager = new ConfigManager({
|
|
165
|
-
...app.configManagerConfig,
|
|
166
|
-
source: options.config,
|
|
167
|
-
dirname: options.context?.directory
|
|
168
|
-
})
|
|
169
|
-
await configManager.parseAndValidate()
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const stackable = new Stackable({
|
|
174
|
-
init: () => buildServer({
|
|
175
|
-
configManager,
|
|
176
|
-
...configManager.current,
|
|
177
|
-
}, app, options.context),
|
|
178
|
-
stackable: app,
|
|
179
|
-
configManager,
|
|
180
|
-
context: options.context
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
return stackable
|
|
184
|
-
}
|
|
58
|
+
export const skipTelemetryHooks = true
|
|
185
59
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
module.exports.buildStackable = buildStackable
|
|
191
|
-
module.exports.schemas = require('./lib/schema')
|
|
192
|
-
module.exports.platformaticService = platformaticService
|
|
193
|
-
module.exports.addLoggerToTheConfig = addLoggerToTheConfig
|
|
194
|
-
module.exports.start = start
|
|
195
|
-
module.exports.Generator = ServiceGenerator
|
|
196
|
-
module.exports.ServiceStackable = ServiceStackable
|
|
197
|
-
module.exports.buildCompileCmd = buildCompileCmd
|
|
198
|
-
module.exports.extractTypeScriptCompileOptionsFromConfig = extractTypeScriptCompileOptionsFromConfig
|
|
60
|
+
export { platformaticService } from './lib/application.js'
|
|
61
|
+
export { ServiceCapability } from './lib/capability.js'
|
|
62
|
+
export { applyTestHelperCustomizations, Generator } from './lib/generator.js'
|
|
63
|
+
export { packageJson, schema, schemaComponents, version } from './lib/schema.js'
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { isKeyEnabled } from '@platformatic/foundation'
|
|
2
|
+
import { setupCors } from './plugins/cors.js'
|
|
3
|
+
import { setupGraphQL } from './plugins/graphql.js'
|
|
4
|
+
import { setupHealthCheck } from './plugins/health-check.js'
|
|
5
|
+
import { setupOpenAPI } from './plugins/openapi.js'
|
|
6
|
+
import { loadPlugins } from './plugins/plugins.js'
|
|
7
|
+
|
|
8
|
+
export async function platformaticService (app, capability) {
|
|
9
|
+
const config = await capability.getConfig()
|
|
10
|
+
|
|
11
|
+
const serviceConfig = config.service || {}
|
|
12
|
+
|
|
13
|
+
if (isKeyEnabled('openapi', serviceConfig)) {
|
|
14
|
+
const openapi = serviceConfig.openapi
|
|
15
|
+
await app.register(setupOpenAPI, { openapi })
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (isKeyEnabled('graphql', serviceConfig)) {
|
|
19
|
+
await app.register(setupGraphQL, serviceConfig.graphql)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (config.plugins) {
|
|
23
|
+
await app.register(loadPlugins, capability.context)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (isKeyEnabled('cors', config.server)) {
|
|
27
|
+
await app.register(setupCors, config.server.cors)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (isKeyEnabled('healthCheck', config.server)) {
|
|
31
|
+
await app.register(setupHealthCheck, config.server.healthCheck)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
platformaticService[Symbol.for('skip-override')] = true
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { BaseCapability, cleanBasePath, ensureTrailingSlash, getServerUrl } from '@platformatic/basic'
|
|
2
|
+
import { buildPinoFormatters, buildPinoTimestamp, deepmerge, features, isKeyEnabled } from '@platformatic/foundation'
|
|
3
|
+
import { telemetry } from '@platformatic/telemetry'
|
|
4
|
+
import fastify from 'fastify'
|
|
5
|
+
import { printSchema } from 'graphql'
|
|
6
|
+
import { randomUUID } from 'node:crypto'
|
|
7
|
+
import { hostname } from 'node:os'
|
|
8
|
+
import pino from 'pino'
|
|
9
|
+
import { platformaticService } from './application.js'
|
|
10
|
+
import { setupRoot } from './plugins/root.js'
|
|
11
|
+
import { version } from './schema.js'
|
|
12
|
+
import { sanitizeHTTPSArgument } from './utils.js'
|
|
13
|
+
|
|
14
|
+
export class ServiceCapability extends BaseCapability {
|
|
15
|
+
#app
|
|
16
|
+
#basePath
|
|
17
|
+
|
|
18
|
+
constructor (root, config, context) {
|
|
19
|
+
super('service', version, root, config, context)
|
|
20
|
+
this.applicationFactory = this.context.applicationFactory ?? platformaticService
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async init () {
|
|
24
|
+
await super.init()
|
|
25
|
+
|
|
26
|
+
if (this.#app) {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const config = this.config
|
|
31
|
+
this.#basePath = ensureTrailingSlash(cleanBasePath(config.basePath ?? this.applicationId))
|
|
32
|
+
|
|
33
|
+
// Create the application
|
|
34
|
+
this.#app = fastify({
|
|
35
|
+
...this.serverConfig,
|
|
36
|
+
...this.fastifyOptions,
|
|
37
|
+
genReqId () {
|
|
38
|
+
return randomUUID()
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
// This must be done before loading the plugins, so they can inspect if the
|
|
43
|
+
// openTelemetry decorator exists and then configure accordingly.
|
|
44
|
+
if (isKeyEnabled('telemetry', config)) {
|
|
45
|
+
await this.#app.register(telemetry, config.telemetry)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
this.#app.decorate('platformatic', { config: this.config })
|
|
49
|
+
|
|
50
|
+
await this.#app.register(this.applicationFactory, this)
|
|
51
|
+
|
|
52
|
+
if (Array.isArray(this.context.fastifyPlugins)) {
|
|
53
|
+
for (const plugin of this.context.fastifyPlugins) {
|
|
54
|
+
await this.#app.register(plugin)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!this.#app.hasRoute({ url: '/', method: 'GET' }) && !this.#app.hasRoute({ url: '/*', method: 'GET' })) {
|
|
59
|
+
await this.#app.register(setupRoot)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async start (startOptions) {
|
|
64
|
+
// Compatibility with v2 service
|
|
65
|
+
const { listen } = startOptions ?? { listen: true }
|
|
66
|
+
|
|
67
|
+
// Make this idempotent
|
|
68
|
+
if (this.url) {
|
|
69
|
+
return this.url
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Create the application if needed
|
|
73
|
+
if (!this.#app) {
|
|
74
|
+
await this.init()
|
|
75
|
+
await this.#app.ready()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (listen) {
|
|
79
|
+
await this._listen()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
await this._collectMetrics()
|
|
83
|
+
return this.url
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async stop () {
|
|
87
|
+
await super.stop()
|
|
88
|
+
await this.#app?.close()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async inject (injectParams, onInject) {
|
|
92
|
+
const response = await this.#app.inject(injectParams, onInject)
|
|
93
|
+
|
|
94
|
+
if (onInject) {
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const { statusCode, statusMessage, headers, body } = response
|
|
99
|
+
return { statusCode, statusMessage, headers, body }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
getApplication () {
|
|
103
|
+
return this.#app
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async getDispatchFunc () {
|
|
107
|
+
await this.init()
|
|
108
|
+
return this.#app
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async getConfig (includeMeta = false) {
|
|
112
|
+
let config = await super.getConfig(includeMeta)
|
|
113
|
+
const loggerInstance = this.serverConfig?.loggerInstance
|
|
114
|
+
|
|
115
|
+
if (loggerInstance) {
|
|
116
|
+
config = Object.assign({}, config)
|
|
117
|
+
const { loggerInstance: _, ...serverConfig } = this.serverConfig
|
|
118
|
+
config.server = { ...serverConfig, logger: { level: loggerInstance.level } }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return config
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async getWatchConfig () {
|
|
125
|
+
const config = this.config
|
|
126
|
+
|
|
127
|
+
const enabled = config.watch?.enabled !== false && config.plugins !== undefined
|
|
128
|
+
|
|
129
|
+
if (!enabled) {
|
|
130
|
+
return { enabled, path: this.root }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
enabled,
|
|
135
|
+
path: this.root,
|
|
136
|
+
allow: config.watch?.allow,
|
|
137
|
+
ignore: config.watch?.ignore
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
getMeta () {
|
|
142
|
+
return {
|
|
143
|
+
gateway: {
|
|
144
|
+
tcp: typeof this.url !== 'undefined',
|
|
145
|
+
url: this.url,
|
|
146
|
+
prefix: this.basePath ?? this.#basePath,
|
|
147
|
+
wantsAbsoluteUrls: false,
|
|
148
|
+
needsRootTrailingSlash: false
|
|
149
|
+
},
|
|
150
|
+
connectionStrings: [this.connectionString]
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async getOpenapiSchema () {
|
|
155
|
+
await this.init()
|
|
156
|
+
await this.#app.ready()
|
|
157
|
+
return this.#app.swagger ? this.#app.swagger() : null
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async getGraphqlSchema () {
|
|
161
|
+
await this.init()
|
|
162
|
+
await this.#app.ready()
|
|
163
|
+
return this.#app.graphql ? printSchema(this.#app.graphql.schema) : null
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async updateContext (context) {
|
|
167
|
+
super.updateContext(context)
|
|
168
|
+
|
|
169
|
+
this.context = { ...this.context, ...context }
|
|
170
|
+
|
|
171
|
+
if (!this.context) {
|
|
172
|
+
return
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const { telemetryConfig, serverConfig, isEntrypoint, isProduction, logger } = this.context
|
|
176
|
+
|
|
177
|
+
const config = { ...this.config }
|
|
178
|
+
|
|
179
|
+
if (telemetryConfig) {
|
|
180
|
+
config.telemetry = telemetryConfig
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const loggerInstance = logger ?? serverConfig?.loggerInstance ?? this.serverConfig?.loggerInstance
|
|
184
|
+
|
|
185
|
+
if (serverConfig) {
|
|
186
|
+
config.server = deepmerge(this.serverConfig, serverConfig ?? {})
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
config.server ??= {}
|
|
190
|
+
|
|
191
|
+
if (isProduction) {
|
|
192
|
+
if (config.plugins) {
|
|
193
|
+
config.plugins.typescript = false
|
|
194
|
+
}
|
|
195
|
+
config.watch = { enabled: false }
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Adjust server options
|
|
199
|
+
if (!isEntrypoint) {
|
|
200
|
+
config.server.trustProxy = true
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (config.server.https) {
|
|
204
|
+
config.server.https.key = await sanitizeHTTPSArgument(config.server.https.key)
|
|
205
|
+
config.server.https.cert = await sanitizeHTTPSArgument(config.server.https.cert)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Assign the logger instance if it exists
|
|
209
|
+
if (loggerInstance) {
|
|
210
|
+
config.server = { ...config.server }
|
|
211
|
+
config.server.loggerInstance = loggerInstance
|
|
212
|
+
delete config.server.logger
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
this.serverConfig = config.server
|
|
216
|
+
this.config = config
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
_initializeLogger () {
|
|
220
|
+
if (this.context?.logger) {
|
|
221
|
+
return this.context.logger
|
|
222
|
+
} else if (this.config.server?.loggerInstance) {
|
|
223
|
+
return this.config.server?.loggerInstance
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
this.serverConfig ??= {}
|
|
227
|
+
this.loggerConfig = deepmerge(this.context.loggerConfig ?? {}, this.serverConfig?.logger ?? {})
|
|
228
|
+
|
|
229
|
+
const pinoOptions = {
|
|
230
|
+
...(this.loggerConfig ?? {}),
|
|
231
|
+
level: this.loggerConfig?.level ?? 'trace'
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
this.registerGlobals({
|
|
235
|
+
logLevel: pinoOptions.level
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
if (this.context?.applicationId) {
|
|
239
|
+
pinoOptions.name = this.context.applicationId
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (this.context?.worker?.count > 1 && this.loggerConfig?.base !== null) {
|
|
243
|
+
pinoOptions.base = { pid: process.pid, hostname: hostname(), worker: this.context.worker.index }
|
|
244
|
+
} else if (this.loggerConfig?.base === null) {
|
|
245
|
+
pinoOptions.base = undefined
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (this.loggerConfig?.formatters) {
|
|
249
|
+
pinoOptions.formatters = buildPinoFormatters(this.loggerConfig?.formatters)
|
|
250
|
+
}
|
|
251
|
+
if (this.loggerConfig?.timestamp) {
|
|
252
|
+
pinoOptions.timestamp = buildPinoTimestamp(this.loggerConfig?.timestamp)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const logger = pino(pinoOptions)
|
|
256
|
+
|
|
257
|
+
// Only one of logger and loggerInstance should be set
|
|
258
|
+
this.serverConfig.loggerInstance = logger
|
|
259
|
+
delete this.serverConfig.logger
|
|
260
|
+
|
|
261
|
+
return logger
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async _listen () {
|
|
265
|
+
const serverOptions = this.serverConfig
|
|
266
|
+
const listenOptions = { host: serverOptions?.hostname || '127.0.0.1', port: serverOptions?.port || 0 }
|
|
267
|
+
|
|
268
|
+
if (this.isProduction && features.node.reusePort) {
|
|
269
|
+
listenOptions.reusePort = true
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
await this.#app.listen(listenOptions)
|
|
273
|
+
this.url = getServerUrl(this.#app.server)
|
|
274
|
+
|
|
275
|
+
if (this.serverConfig.http2 || this.serverConfig.https?.key) {
|
|
276
|
+
this.url = this.url.replace('http://', 'https://')
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return this.url
|
|
280
|
+
}
|
|
281
|
+
}
|