@platformatic/runtime 1.22.0 → 1.23.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/fixtures/configs/monorepo-with-management-api.json +21 -0
- package/fixtures/monorepo/serviceAppWithLogger/platformatic.service.json +4 -1
- package/lib/api-client.js +6 -0
- package/lib/api.js +7 -7
- package/lib/app.js +10 -0
- package/lib/errors.js +2 -1
- package/lib/generator/runtime-generator.js +1 -1
- package/lib/management-api.js +113 -0
- package/lib/schema.js +9 -0
- package/lib/start.js +50 -4
- package/package.json +10 -10
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://platformatic.dev/schemas/v1.21.0/runtime",
|
|
3
|
+
"entrypoint": "serviceApp",
|
|
4
|
+
"allowCycles": true,
|
|
5
|
+
"hotReload": false,
|
|
6
|
+
"autoload": {
|
|
7
|
+
"path": "../monorepo",
|
|
8
|
+
"exclude": ["docs", "composerApp"],
|
|
9
|
+
"mappings": {
|
|
10
|
+
"serviceAppWithLogger": {
|
|
11
|
+
"id": "with-logger",
|
|
12
|
+
"config": "platformatic.service.json"
|
|
13
|
+
},
|
|
14
|
+
"serviceAppWithMultiplePlugins": {
|
|
15
|
+
"id": "multi-plugin-service",
|
|
16
|
+
"config": "platformatic.service.json"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"managementApi": {}
|
|
21
|
+
}
|
package/lib/api-client.js
CHANGED
|
@@ -27,8 +27,14 @@ class RuntimeApiClient extends EventEmitter {
|
|
|
27
27
|
return this.#sendCommand('plt:start-services')
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
async stop () {
|
|
31
|
+
await this.#sendCommand('plt:stop-services')
|
|
32
|
+
}
|
|
33
|
+
|
|
30
34
|
async close () {
|
|
31
35
|
await this.#sendCommand('plt:stop-services')
|
|
36
|
+
|
|
37
|
+
this.worker.postMessage({ command: 'plt:close' })
|
|
32
38
|
await this.#exitPromise
|
|
33
39
|
}
|
|
34
40
|
|
package/lib/api.js
CHANGED
|
@@ -47,12 +47,13 @@ class RuntimeApi {
|
|
|
47
47
|
parentPort.on('message', async (message) => {
|
|
48
48
|
const command = message?.command
|
|
49
49
|
if (command) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (command === 'plt:stop-services') {
|
|
50
|
+
if (command === 'plt:close') {
|
|
51
|
+
await this.#dispatcher.close()
|
|
54
52
|
process.exit() // Exit the worker thread.
|
|
55
53
|
}
|
|
54
|
+
|
|
55
|
+
const res = await this.#executeCommand(message)
|
|
56
|
+
parentPort.postMessage(res)
|
|
56
57
|
return
|
|
57
58
|
}
|
|
58
59
|
await this.#handleProcessLevelEvent(message)
|
|
@@ -134,7 +135,7 @@ class RuntimeApi {
|
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
async stopServices () {
|
|
137
|
-
const stopServiceReqs = [
|
|
138
|
+
const stopServiceReqs = []
|
|
138
139
|
for (const service of this.#services.values()) {
|
|
139
140
|
const serviceStatus = service.getStatus()
|
|
140
141
|
if (serviceStatus === 'started') {
|
|
@@ -269,8 +270,7 @@ class RuntimeApi {
|
|
|
269
270
|
statusCode: res.statusCode,
|
|
270
271
|
statusMessage: res.statusMessage,
|
|
271
272
|
headers: res.headers,
|
|
272
|
-
body: res.body
|
|
273
|
-
payload: res.payload
|
|
273
|
+
body: res.body
|
|
274
274
|
}
|
|
275
275
|
}
|
|
276
276
|
}
|
package/lib/app.js
CHANGED
|
@@ -103,6 +103,16 @@ class PlatformaticApp {
|
|
|
103
103
|
})
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
if (!this.appConfig.entrypoint) {
|
|
107
|
+
configManager.update({
|
|
108
|
+
...configManager.current,
|
|
109
|
+
server: {
|
|
110
|
+
...(configManager.current.server || {}),
|
|
111
|
+
trustProxy: true
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
|
|
106
116
|
const config = configManager.current
|
|
107
117
|
|
|
108
118
|
this.#setuplogger(configManager)
|
package/lib/errors.js
CHANGED
|
@@ -21,5 +21,6 @@ module.exports = {
|
|
|
21
21
|
InspectorPortError: createError(`${ERROR_PREFIX}_INSPECTOR_PORT`, 'Inspector port must be 0 or in range 1024 to 65535'),
|
|
22
22
|
InspectorHostError: createError(`${ERROR_PREFIX}_INSPECTOR_HOST`, 'Inspector host cannot be empty'),
|
|
23
23
|
CannotMapSpecifierToAbsolutePathError: createError(`${ERROR_PREFIX}_CANNOT_MAP_SPECIFIER_TO_ABSOLUTE_PATH`, 'Cannot map "%s" to an absolute path'),
|
|
24
|
-
NodeInspectorFlagsNotSupportedError: createError(`${ERROR_PREFIX}_NODE_INSPECTOR_FLAGS_NOT_SUPPORTED`, 'The Node.js inspector flags are not supported. Please use \'platformatic start --inspect\' instead.')
|
|
24
|
+
NodeInspectorFlagsNotSupportedError: createError(`${ERROR_PREFIX}_NODE_INSPECTOR_FLAGS_NOT_SUPPORTED`, 'The Node.js inspector flags are not supported. Please use \'platformatic start --inspect\' instead.'),
|
|
25
|
+
FailedToUnlinkManagementApiSocket: createError(`${ERROR_PREFIX}_FAILED_TO_UNLINK_MANAGEMENT_API_SOCKET`, 'Failed to unlink management API socket "%s"')
|
|
25
26
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fastify = require('fastify')
|
|
4
|
+
const { isatty } = require('tty')
|
|
5
|
+
const platformaticVersion = require('../package.json').version
|
|
6
|
+
|
|
7
|
+
async function createManagementApi (config, runtimeApiClient) {
|
|
8
|
+
addManagementApiLogger(config)
|
|
9
|
+
const app = fastify(config)
|
|
10
|
+
app.log.warn(
|
|
11
|
+
'Runtime Management API is in the experimental stage. ' +
|
|
12
|
+
'The feature is not subject to semantic versioning rules. ' +
|
|
13
|
+
'Non-backward compatible changes or removal may occur in any future release. ' +
|
|
14
|
+
'Use of the feature is not recommended in production environments.'
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
app.register(async (app) => {
|
|
18
|
+
app.get('/metadata', async () => {
|
|
19
|
+
return {
|
|
20
|
+
pid: process.pid,
|
|
21
|
+
cwd: process.cwd(),
|
|
22
|
+
execPath: process.execPath,
|
|
23
|
+
nodeVersion: process.version,
|
|
24
|
+
platformaticVersion
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
app.get('/services', async () => {
|
|
29
|
+
return runtimeApiClient.getServices()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
app.post('/services/start', async () => {
|
|
33
|
+
app.log.debug('start services')
|
|
34
|
+
await runtimeApiClient.start()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
app.post('/services/stop', async () => {
|
|
38
|
+
app.log.debug('stop services')
|
|
39
|
+
await runtimeApiClient.stop()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
app.post('/services/restart', async () => {
|
|
43
|
+
app.log.debug('restart services')
|
|
44
|
+
await runtimeApiClient.restart()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
app.get('/services/:id', async (request) => {
|
|
48
|
+
const { id } = request.params
|
|
49
|
+
app.log.debug('get service details', { id })
|
|
50
|
+
return runtimeApiClient.getServiceDetails(id)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
app.get('/services/:id/config', async (request) => {
|
|
54
|
+
const { id } = request.params
|
|
55
|
+
app.log.debug('get service config', { id })
|
|
56
|
+
return runtimeApiClient.getServiceConfig(id)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
app.post('/services/:id/start', async (request) => {
|
|
60
|
+
const { id } = request.params
|
|
61
|
+
app.log.debug('start service', { id })
|
|
62
|
+
await runtimeApiClient.startService(id)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
app.post('/services/:id/stop', async (request) => {
|
|
66
|
+
const { id } = request.params
|
|
67
|
+
app.log.debug('stop service', { id })
|
|
68
|
+
await runtimeApiClient.stopService(id)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
app.all('/services/:id/proxy/*', async (request, reply) => {
|
|
72
|
+
const { id, '*': requestUrl } = request.params
|
|
73
|
+
app.log.debug('proxy request', { id, requestUrl })
|
|
74
|
+
|
|
75
|
+
const injectParams = {
|
|
76
|
+
method: request.method,
|
|
77
|
+
url: requestUrl || '/',
|
|
78
|
+
headers: request.headers,
|
|
79
|
+
query: request.query,
|
|
80
|
+
body: request.body
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const res = await runtimeApiClient.inject(id, injectParams)
|
|
84
|
+
|
|
85
|
+
reply
|
|
86
|
+
.code(res.statusCode)
|
|
87
|
+
.headers(res.headers)
|
|
88
|
+
.send(res.body)
|
|
89
|
+
})
|
|
90
|
+
}, { prefix: '/api' })
|
|
91
|
+
|
|
92
|
+
return app
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function addManagementApiLogger (config) {
|
|
96
|
+
let logger = config.logger
|
|
97
|
+
if (!logger) {
|
|
98
|
+
config.logger = {
|
|
99
|
+
level: 'info',
|
|
100
|
+
name: 'management-api'
|
|
101
|
+
}
|
|
102
|
+
logger = config.logger
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* c8 ignore next 5 */
|
|
106
|
+
if (isatty(1) && !logger.transport) {
|
|
107
|
+
logger.transport = {
|
|
108
|
+
target: 'pino-pretty'
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = { createManagementApi }
|
package/lib/schema.js
CHANGED
package/lib/start.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
+
|
|
3
|
+
const { tmpdir, platform } = require('node:os')
|
|
2
4
|
const { once } = require('node:events')
|
|
3
5
|
const inspector = require('node:inspector')
|
|
4
6
|
const { join, resolve, dirname } = require('node:path')
|
|
5
|
-
const
|
|
7
|
+
const { writeFile, unlink, mkdir } = require('node:fs/promises')
|
|
6
8
|
const { pathToFileURL } = require('node:url')
|
|
7
9
|
const { Worker } = require('node:worker_threads')
|
|
8
|
-
const closeWithGrace = require('close-with-grace')
|
|
9
10
|
const { start: serviceStart } = require('@platformatic/service')
|
|
11
|
+
const { printConfigValidationErrors } = require('@platformatic/config')
|
|
12
|
+
const closeWithGrace = require('close-with-grace')
|
|
10
13
|
const { loadConfig } = require('./load-config')
|
|
14
|
+
const { createManagementApi } = require('./management-api')
|
|
11
15
|
const { parseInspectorOptions, wrapConfigInRuntimeConfig } = require('./config')
|
|
12
16
|
const RuntimeApiClient = require('./api-client.js')
|
|
13
|
-
const { printConfigValidationErrors } = require('@platformatic/config')
|
|
14
17
|
const errors = require('./errors')
|
|
15
18
|
const pkg = require('../package.json')
|
|
16
19
|
|
|
@@ -22,6 +25,8 @@ const kWorkerExecArgv = [
|
|
|
22
25
|
kLoaderFile
|
|
23
26
|
]
|
|
24
27
|
|
|
28
|
+
const PLATFORMATIC_TMP_DIR = join(tmpdir(), 'platformatic', 'pids')
|
|
29
|
+
|
|
25
30
|
async function startWithConfig (configManager, env = process.env) {
|
|
26
31
|
const config = configManager.current
|
|
27
32
|
|
|
@@ -50,6 +55,8 @@ async function startWithConfig (configManager, env = process.env) {
|
|
|
50
55
|
env
|
|
51
56
|
})
|
|
52
57
|
|
|
58
|
+
let managementApi = null
|
|
59
|
+
|
|
53
60
|
let exited = null
|
|
54
61
|
let isWorkerAlive = true
|
|
55
62
|
worker.on('exit', (code) => {
|
|
@@ -57,6 +64,7 @@ async function startWithConfig (configManager, env = process.env) {
|
|
|
57
64
|
process.exitCode = code
|
|
58
65
|
isWorkerAlive = false
|
|
59
66
|
configManager.fileWatcher?.stopWatching()
|
|
67
|
+
managementApi?.close()
|
|
60
68
|
if (typeof exited === 'function') {
|
|
61
69
|
exited()
|
|
62
70
|
}
|
|
@@ -96,6 +104,12 @@ async function startWithConfig (configManager, env = process.env) {
|
|
|
96
104
|
await once(worker, 'message') // plt:init
|
|
97
105
|
|
|
98
106
|
const runtimeApiClient = new RuntimeApiClient(worker)
|
|
107
|
+
|
|
108
|
+
if (config.managementApi) {
|
|
109
|
+
managementApi = await startManagementApi(config.managementApi, runtimeApiClient)
|
|
110
|
+
runtimeApiClient.managementApi = managementApi
|
|
111
|
+
}
|
|
112
|
+
|
|
99
113
|
return runtimeApiClient
|
|
100
114
|
}
|
|
101
115
|
|
|
@@ -112,6 +126,38 @@ async function start (args) {
|
|
|
112
126
|
return serviceStart(config.app, args)
|
|
113
127
|
}
|
|
114
128
|
|
|
129
|
+
async function startManagementApi (managementApiConfig, runtimeApiClient) {
|
|
130
|
+
const runtimePID = process.pid
|
|
131
|
+
|
|
132
|
+
let socketPath = null
|
|
133
|
+
if (platform() === 'win32') {
|
|
134
|
+
socketPath = '\\\\.\\pipe\\' + join(PLATFORMATIC_TMP_DIR, `${runtimePID}.sock`)
|
|
135
|
+
} else {
|
|
136
|
+
await mkdir(PLATFORMATIC_TMP_DIR, { recursive: true })
|
|
137
|
+
socketPath = join(PLATFORMATIC_TMP_DIR, `${runtimePID}.sock`)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
await mkdir(PLATFORMATIC_TMP_DIR, { recursive: true })
|
|
142
|
+
await unlink(socketPath).catch((err) => {
|
|
143
|
+
if (err.code !== 'ENOENT') {
|
|
144
|
+
throw new errors.FailedToUnlinkManagementApiSocket(err.message)
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
const managementApi = await createManagementApi(
|
|
149
|
+
managementApiConfig,
|
|
150
|
+
runtimeApiClient
|
|
151
|
+
)
|
|
152
|
+
await managementApi.listen({ path: socketPath })
|
|
153
|
+
return managementApi
|
|
154
|
+
/* c8 ignore next 4 */
|
|
155
|
+
} catch (err) {
|
|
156
|
+
console.error(err)
|
|
157
|
+
process.exit(1)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
115
161
|
async function startCommand (args) {
|
|
116
162
|
try {
|
|
117
163
|
const config = await loadConfig({}, args)
|
|
@@ -148,7 +194,7 @@ async function startCommand (args) {
|
|
|
148
194
|
}
|
|
149
195
|
const toWrite = join(dirname(resolve(args[0])), 'platformatic.service.json')
|
|
150
196
|
console.log(`No config file found, creating ${join(dirname(args[0]), 'platformatic.service.json')}`)
|
|
151
|
-
await
|
|
197
|
+
await writeFile(toWrite, JSON.stringify(config, null, 2))
|
|
152
198
|
return startCommand(['--config', toWrite])
|
|
153
199
|
}
|
|
154
200
|
logErrorAndExit(err)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"tsd": "^0.30.4",
|
|
31
31
|
"typescript": "^5.3.3",
|
|
32
32
|
"undici-oauth-interceptor": "^0.4.2",
|
|
33
|
-
"@platformatic/sql-graphql": "1.
|
|
34
|
-
"@platformatic/sql-mapper": "1.
|
|
33
|
+
"@platformatic/sql-graphql": "1.23.0",
|
|
34
|
+
"@platformatic/sql-mapper": "1.23.0"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@fastify/error": "^3.4.1",
|
|
@@ -52,13 +52,13 @@
|
|
|
52
52
|
"pino-pretty": "^10.3.1",
|
|
53
53
|
"undici": "^6.6.0",
|
|
54
54
|
"why-is-node-running": "^2.2.2",
|
|
55
|
-
"@platformatic/composer": "1.
|
|
56
|
-
"@platformatic/
|
|
57
|
-
"@platformatic/
|
|
58
|
-
"@platformatic/
|
|
59
|
-
"@platformatic/
|
|
60
|
-
"@platformatic/telemetry": "1.
|
|
61
|
-
"@platformatic/utils": "1.
|
|
55
|
+
"@platformatic/composer": "1.23.0",
|
|
56
|
+
"@platformatic/config": "1.23.0",
|
|
57
|
+
"@platformatic/db": "1.23.0",
|
|
58
|
+
"@platformatic/service": "1.23.0",
|
|
59
|
+
"@platformatic/generators": "1.23.0",
|
|
60
|
+
"@platformatic/telemetry": "1.23.0",
|
|
61
|
+
"@platformatic/utils": "1.23.0"
|
|
62
62
|
},
|
|
63
63
|
"standard": {
|
|
64
64
|
"ignore": [
|