@platformatic/runtime 1.19.0 → 1.20.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/interceptors/external.js +27 -0
- package/fixtures/interceptors/idp.js +45 -0
- package/fixtures/interceptors/platformatic.runtime.json +26 -0
- package/fixtures/interceptors/services/a/platformatic.service.json +17 -0
- package/fixtures/interceptors/services/a/plugin.js +15 -0
- package/lib/api.js +5 -7
- package/lib/schema.js +22 -0
- package/lib/start.js +4 -1
- package/lib/worker.js +65 -20
- package/package.json +24 -21
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fastify = require('fastify')
|
|
4
|
+
|
|
5
|
+
async function start (opts) {
|
|
6
|
+
const app = fastify()
|
|
7
|
+
|
|
8
|
+
// Dumb bearer token implementation
|
|
9
|
+
app.get('/hello', async (req, res) => {
|
|
10
|
+
// We just check if there is an authorization header,
|
|
11
|
+
// which is insecure,
|
|
12
|
+
if (req.headers.authorization) {
|
|
13
|
+
return { hello: 'world' }
|
|
14
|
+
} else {
|
|
15
|
+
res.code(401)
|
|
16
|
+
return { error: 'Unauthorized' }
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
return app
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = start
|
|
24
|
+
|
|
25
|
+
if (require.main === module) {
|
|
26
|
+
start({ logger: { name: 'external' } }).then(app => app.listen({ port: process.env.PORT || 3001 }))
|
|
27
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fastify = require('fastify')
|
|
4
|
+
const { createSigner } = require('fast-jwt')
|
|
5
|
+
|
|
6
|
+
async function start (opts) {
|
|
7
|
+
const app = fastify(opts)
|
|
8
|
+
let port = opts.port
|
|
9
|
+
|
|
10
|
+
const signSync = createSigner({
|
|
11
|
+
key: 'secret',
|
|
12
|
+
expiresIn: '1h'
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
app.decorate('refreshToken', '')
|
|
16
|
+
app.decorate('signSync', signSync)
|
|
17
|
+
|
|
18
|
+
// Dump bearer token implementation
|
|
19
|
+
app.post('/token', async (req, res) => {
|
|
20
|
+
return { access_token: signSync({}) }
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
await app.listen({ port })
|
|
24
|
+
|
|
25
|
+
port = app.server.address().port
|
|
26
|
+
|
|
27
|
+
const refreshToken = signSync({
|
|
28
|
+
iss: `http://localhost:${port}`
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
app.refreshToken = refreshToken
|
|
32
|
+
|
|
33
|
+
app.log.info({ refreshToken }, 'refresh token')
|
|
34
|
+
|
|
35
|
+
return app
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = start
|
|
39
|
+
|
|
40
|
+
if (require.main === module) {
|
|
41
|
+
start({ logger: { name: 'idp' }, port: process.env.PORT || 3000 }).catch((err) => {
|
|
42
|
+
console.error(err)
|
|
43
|
+
process.exit(1)
|
|
44
|
+
})
|
|
45
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://platformatic.dev/schemas/v1.20.0/runtime",
|
|
3
|
+
"entrypoint": "a",
|
|
4
|
+
"autoload": {
|
|
5
|
+
"path": "./services"
|
|
6
|
+
},
|
|
7
|
+
"server": {
|
|
8
|
+
"hostname": "127.0.0.1",
|
|
9
|
+
"port": "{{PORT}}",
|
|
10
|
+
"logger": {
|
|
11
|
+
"level": "info"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"undici": {
|
|
15
|
+
"interceptors": {
|
|
16
|
+
"Agent": [{
|
|
17
|
+
"module": "undici-oauth-interceptor",
|
|
18
|
+
"options": {
|
|
19
|
+
"refreshToken": "{{PLT_REFRESH_TOKEN}}",
|
|
20
|
+
"origins": ["{{PLT_EXTERNAL_SERVICE}}"],
|
|
21
|
+
"clientId": "my-client-id"
|
|
22
|
+
}
|
|
23
|
+
}]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://platformatic.dev/schemas/v1.3.0/service",
|
|
3
|
+
"server": {
|
|
4
|
+
"logger": {
|
|
5
|
+
"level": "warn"
|
|
6
|
+
}
|
|
7
|
+
},
|
|
8
|
+
"plugins": {
|
|
9
|
+
"paths": [{
|
|
10
|
+
"path": "plugin.js",
|
|
11
|
+
"encapsulate": false,
|
|
12
|
+
"options": {
|
|
13
|
+
"externalService": "{{PLT_EXTERNAL_SERVICE}}"
|
|
14
|
+
}
|
|
15
|
+
}]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { request } = require('undici')
|
|
4
|
+
|
|
5
|
+
module.exports = async function (fastify, options) {
|
|
6
|
+
fastify.get('/hello', async (_, reply) => {
|
|
7
|
+
const res = await request(`${options.externalService}/hello`)
|
|
8
|
+
if (res.statusCode !== 200) {
|
|
9
|
+
reply.code(res.statusCode)
|
|
10
|
+
}
|
|
11
|
+
reply.log.info('response received')
|
|
12
|
+
const data = await res.body.json()
|
|
13
|
+
return data
|
|
14
|
+
})
|
|
15
|
+
}
|
package/lib/api.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { getGlobalDispatcher, setGlobalDispatcher } = require('undici')
|
|
3
4
|
const FastifyUndiciDispatcher = require('fastify-undici-dispatcher')
|
|
4
|
-
const { setGlobalDispatcher, getGlobalDispatcher } = require('undici')
|
|
5
5
|
const { PlatformaticApp } = require('./app')
|
|
6
6
|
const errors = require('./errors')
|
|
7
7
|
const { printSchema } = require('graphql')
|
|
@@ -29,20 +29,18 @@ class RuntimeApi {
|
|
|
29
29
|
keepAliveTimeout: 5000
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
+
|
|
32
33
|
const app = new PlatformaticApp(service, loaderPort, logger, serviceTelemetryConfig, serverConfig)
|
|
33
34
|
|
|
34
35
|
this.#services.set(service.id, app)
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
dispatcher: globalAgent,
|
|
38
|
+
this.#dispatcher = new FastifyUndiciDispatcher({
|
|
39
|
+
dispatcher: getGlobalDispatcher(),
|
|
40
40
|
// setting the domain here allows for fail-fast scenarios
|
|
41
41
|
domain: '.plt.local'
|
|
42
42
|
})
|
|
43
|
-
|
|
44
|
-
setGlobalDispatcher(globalDispatcher)
|
|
45
|
-
this.#dispatcher = globalDispatcher
|
|
43
|
+
setGlobalDispatcher(this.#dispatcher)
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
async startListening (parentPort) {
|
package/lib/schema.js
CHANGED
|
@@ -105,6 +105,28 @@ const platformaticRuntimeSchema = {
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
},
|
|
108
|
+
undici: {
|
|
109
|
+
agentOptions: {
|
|
110
|
+
type: 'object',
|
|
111
|
+
additionalProperties: true
|
|
112
|
+
},
|
|
113
|
+
interceptors: {
|
|
114
|
+
type: 'array',
|
|
115
|
+
items: {
|
|
116
|
+
type: 'object',
|
|
117
|
+
properties: {
|
|
118
|
+
module: {
|
|
119
|
+
type: 'string'
|
|
120
|
+
},
|
|
121
|
+
options: {
|
|
122
|
+
type: 'object',
|
|
123
|
+
additionalProperties: true
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
required: ['module', 'options']
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
},
|
|
108
130
|
$schema: {
|
|
109
131
|
type: 'string'
|
|
110
132
|
}
|
package/lib/start.js
CHANGED
|
@@ -36,6 +36,9 @@ async function startWithConfig (configManager, env = process.env) {
|
|
|
36
36
|
if (config.hotReload) {
|
|
37
37
|
config.loaderFile = kLoaderFile
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
const dirname = configManager.dirname
|
|
41
|
+
|
|
39
42
|
// The configManager cannot be transferred to the worker, so remove it.
|
|
40
43
|
delete config.configManager
|
|
41
44
|
|
|
@@ -43,7 +46,7 @@ async function startWithConfig (configManager, env = process.env) {
|
|
|
43
46
|
/* c8 ignore next */
|
|
44
47
|
execArgv: config.hotReload ? kWorkerExecArgv : [],
|
|
45
48
|
transferList: config.loggingPort ? [config.loggingPort] : [],
|
|
46
|
-
workerData: { config },
|
|
49
|
+
workerData: { config, dirname },
|
|
47
50
|
env
|
|
48
51
|
})
|
|
49
52
|
|
package/lib/worker.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const inspector = require('node:inspector')
|
|
4
|
-
const { register } = require('node:module')
|
|
4
|
+
const { register, createRequire } = require('node:module')
|
|
5
5
|
const { isatty } = require('node:tty')
|
|
6
|
+
const { pathToFileURL } = require('node:url')
|
|
7
|
+
const { join } = require('node:path')
|
|
6
8
|
const {
|
|
7
9
|
MessageChannel,
|
|
8
10
|
parentPort,
|
|
@@ -10,6 +12,7 @@ const {
|
|
|
10
12
|
} = require('node:worker_threads')
|
|
11
13
|
const undici = require('undici')
|
|
12
14
|
const pino = require('pino')
|
|
15
|
+
const { setGlobalDispatcher, Agent } = require('undici')
|
|
13
16
|
const RuntimeApi = require('./api')
|
|
14
17
|
const { MessagePortWritable } = require('./message-port-writable')
|
|
15
18
|
let loaderPort
|
|
@@ -58,7 +61,48 @@ if (config.server) {
|
|
|
58
61
|
config.server.logger = logger
|
|
59
62
|
}
|
|
60
63
|
|
|
61
|
-
|
|
64
|
+
let stop
|
|
65
|
+
|
|
66
|
+
/* c8 ignore next 4 */
|
|
67
|
+
process.on('uncaughtException', (err) => {
|
|
68
|
+
logger.error({ err }, 'runtime uncaught exception')
|
|
69
|
+
|
|
70
|
+
if (stop) {
|
|
71
|
+
stop().then(() => {
|
|
72
|
+
process.exit(1)
|
|
73
|
+
})
|
|
74
|
+
} else {
|
|
75
|
+
process.exit(1)
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// Tested by test/cli/start.test.mjs by C8 does not see it.
|
|
80
|
+
/* c8 ignore next 4 */
|
|
81
|
+
process.on('unhandledRejection', (err) => {
|
|
82
|
+
logger.error({ err }, 'runtime unhandled rejection')
|
|
83
|
+
|
|
84
|
+
if (stop) {
|
|
85
|
+
stop().then(() => {
|
|
86
|
+
process.exit(1)
|
|
87
|
+
})
|
|
88
|
+
} else {
|
|
89
|
+
process.exit(1)
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
async function loadInterceptor (_require, module, options) {
|
|
94
|
+
const url = pathToFileURL(_require.resolve(module))
|
|
95
|
+
const interceptor = (await import(url)).default
|
|
96
|
+
return interceptor(options)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function loadInterceptors (_require, interceptors) {
|
|
100
|
+
return Promise.all(interceptors.map(async ({ module, options }) => {
|
|
101
|
+
return loadInterceptor(_require, module, options)
|
|
102
|
+
}))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function main () {
|
|
62
106
|
const { inspectorOptions } = workerData.config
|
|
63
107
|
|
|
64
108
|
if (inspectorOptions) {
|
|
@@ -70,6 +114,23 @@ function main () {
|
|
|
70
114
|
inspector.open(inspectorOptions.port, inspectorOptions.host, inspectorOptions.breakFirstLine)
|
|
71
115
|
}
|
|
72
116
|
|
|
117
|
+
const interceptors = {}
|
|
118
|
+
|
|
119
|
+
if (config.undici?.interceptors) {
|
|
120
|
+
const _require = createRequire(join(workerData.dirname, 'package.json'))
|
|
121
|
+
for (const key of ['Agent', 'Pool', 'Client']) {
|
|
122
|
+
if (config.undici.interceptors[key]) {
|
|
123
|
+
interceptors[key] = await loadInterceptors(_require, config.undici.interceptors[key])
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const globalDispatcher = new Agent({
|
|
129
|
+
...config.undici,
|
|
130
|
+
interceptors
|
|
131
|
+
})
|
|
132
|
+
setGlobalDispatcher(globalDispatcher)
|
|
133
|
+
|
|
73
134
|
const runtime = new RuntimeApi(workerData.config, logger, loaderPort)
|
|
74
135
|
runtime.startListening(parentPort)
|
|
75
136
|
|
|
@@ -77,7 +138,7 @@ function main () {
|
|
|
77
138
|
|
|
78
139
|
let stopping = false
|
|
79
140
|
|
|
80
|
-
async function
|
|
141
|
+
stop = async function () {
|
|
81
142
|
if (stopping) {
|
|
82
143
|
return
|
|
83
144
|
}
|
|
@@ -89,23 +150,7 @@ function main () {
|
|
|
89
150
|
logger.error({ err }, 'error while stopping services')
|
|
90
151
|
}
|
|
91
152
|
}
|
|
92
|
-
|
|
93
|
-
/* c8 ignore next 4 */
|
|
94
|
-
process.on('uncaughtException', (err) => {
|
|
95
|
-
logger.error({ err }, 'runtime error')
|
|
96
|
-
stop().then(() => {
|
|
97
|
-
process.exit(1)
|
|
98
|
-
})
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
// Tested by test/cli/start.test.mjs by C8 does not see it.
|
|
102
|
-
/* c8 ignore next 4 */
|
|
103
|
-
process.on('unhandledRejection', (err) => {
|
|
104
|
-
logger.error({ err }, 'runtime error')
|
|
105
|
-
stop().then(() => {
|
|
106
|
-
process.exit(1)
|
|
107
|
-
})
|
|
108
|
-
})
|
|
109
153
|
}
|
|
110
154
|
|
|
155
|
+
// No need to catch this because there is the unhadledRejection handler on top.
|
|
111
156
|
main()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -18,45 +18,48 @@
|
|
|
18
18
|
"homepage": "https://github.com/platformatic/platformatic#readme",
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@fastify/express": "^2.3.0",
|
|
21
|
-
"
|
|
21
|
+
"borp": "^0.9.0",
|
|
22
|
+
"c8": "^9.1.0",
|
|
22
23
|
"execa": "^8.0.1",
|
|
23
24
|
"express": "^4.18.2",
|
|
25
|
+
"fast-jwt": "^3.3.3",
|
|
24
26
|
"glob": "^10.3.10",
|
|
25
27
|
"pino-abstract-transport": "^1.1.0",
|
|
26
28
|
"snazzy": "^9.0.0",
|
|
27
29
|
"split2": "^4.2.0",
|
|
28
30
|
"standard": "^17.1.0",
|
|
29
|
-
"tsd": "^0.30.
|
|
30
|
-
"typescript": "^5.
|
|
31
|
-
"
|
|
32
|
-
"@platformatic/sql-graphql": "1.
|
|
31
|
+
"tsd": "^0.30.4",
|
|
32
|
+
"typescript": "^5.3.3",
|
|
33
|
+
"undici-oauth-interceptor": "^0.4.2",
|
|
34
|
+
"@platformatic/sql-graphql": "1.20.0",
|
|
35
|
+
"@platformatic/sql-mapper": "1.20.0"
|
|
33
36
|
},
|
|
34
37
|
"dependencies": {
|
|
35
|
-
"@fastify/error": "^3.4.
|
|
38
|
+
"@fastify/error": "^3.4.1",
|
|
36
39
|
"@hapi/topo": "^6.0.2",
|
|
37
40
|
"boring-name-generator": "^1.0.3",
|
|
38
41
|
"close-with-grace": "^1.2.0",
|
|
39
42
|
"commist": "^3.2.0",
|
|
40
43
|
"debounce": "^2.0.0",
|
|
41
|
-
"desm": "^1.3.
|
|
44
|
+
"desm": "^1.3.1",
|
|
42
45
|
"es-main": "^1.3.0",
|
|
43
46
|
"fastest-levenshtein": "^1.0.16",
|
|
44
|
-
"fastify": "^4.
|
|
47
|
+
"fastify": "^4.26.0",
|
|
45
48
|
"fastify-undici-dispatcher": "^0.5.0",
|
|
46
49
|
"graphql": "^16.8.1",
|
|
47
50
|
"help-me": "^5.0.0",
|
|
48
51
|
"minimist": "^1.2.8",
|
|
49
|
-
"pino": "^8.
|
|
50
|
-
"pino-pretty": "^10.
|
|
51
|
-
"undici": "^6.
|
|
52
|
+
"pino": "^8.17.2",
|
|
53
|
+
"pino-pretty": "^10.3.1",
|
|
54
|
+
"undici": "^6.6.0",
|
|
52
55
|
"why-is-node-running": "^2.2.2",
|
|
53
|
-
"@platformatic/
|
|
54
|
-
"@platformatic/config": "1.
|
|
55
|
-
"@platformatic/generators": "1.
|
|
56
|
-
"@platformatic/
|
|
57
|
-
"@platformatic/
|
|
58
|
-
"@platformatic/
|
|
59
|
-
"@platformatic/
|
|
56
|
+
"@platformatic/composer": "1.20.0",
|
|
57
|
+
"@platformatic/config": "1.20.0",
|
|
58
|
+
"@platformatic/generators": "1.20.0",
|
|
59
|
+
"@platformatic/db": "1.20.0",
|
|
60
|
+
"@platformatic/service": "1.20.0",
|
|
61
|
+
"@platformatic/telemetry": "1.20.0",
|
|
62
|
+
"@platformatic/utils": "1.20.0"
|
|
60
63
|
},
|
|
61
64
|
"standard": {
|
|
62
65
|
"ignore": [
|
|
@@ -65,8 +68,8 @@
|
|
|
65
68
|
]
|
|
66
69
|
},
|
|
67
70
|
"scripts": {
|
|
68
|
-
"test": "npm run lint &&
|
|
69
|
-
"coverage": "npm run lint &&
|
|
71
|
+
"test": "npm run lint && borp --concurrency=1 --timeout=120000 && tsd",
|
|
72
|
+
"coverage": "npm run lint && borp -X=fixtures -X=test -C --concurrency=1 --timeout=120000 && tsd",
|
|
70
73
|
"lint": "standard | snazzy"
|
|
71
74
|
}
|
|
72
75
|
}
|