@platformatic/composer 2.41.0 → 2.43.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/index.js CHANGED
@@ -3,6 +3,7 @@
3
3
  const deepEqual = require('fast-deep-equal')
4
4
  const ConfigManager = require('@platformatic/config')
5
5
  const { platformaticService, buildServer, buildStackable } = require('@platformatic/service')
6
+ const { isKeyEnabled } = require('@platformatic/utils')
6
7
 
7
8
  const { schema } = require('./lib/schema')
8
9
  const serviceProxy = require('./lib/proxy')
@@ -25,7 +26,7 @@ async function platformaticComposer (app, opts) {
25
26
  let hasGraphqlServices, hasOpenapiServices
26
27
 
27
28
  // When no services are specified, get the list from the runtime.
28
- await ensureServices(config)
29
+ await ensureServices(opts.context?.stackable?.serviceId, config)
29
30
 
30
31
  const { services } = configManager.current.composer
31
32
 
@@ -48,6 +49,15 @@ async function platformaticComposer (app, opts) {
48
49
  generatedComposedOpenAPI = await openApiGenerator(app, config.composer)
49
50
  }
50
51
 
52
+ if (isKeyEnabled('healthCheck', config.server)) {
53
+ if (typeof config.server.healthCheck !== 'object') {
54
+ config.server.healthCheck = {}
55
+ }
56
+
57
+ const stackable = opts.context.stackable
58
+ config.server.healthCheck.fn = stackable.isHealthy.bind(stackable)
59
+ }
60
+
51
61
  app.register(serviceProxy, { ...config.composer, context: opts.context })
52
62
  await app.register(platformaticService, { config: { ...config, openapi: false }, context: opts.context })
53
63
 
package/lib/proxy.js CHANGED
@@ -1,8 +1,9 @@
1
1
  'use strict'
2
2
 
3
- const { getGlobalDispatcher } = require('undici')
4
3
  const httpProxy = require('@fastify/http-proxy')
5
4
  const fp = require('fastify-plugin')
5
+ const { workerData } = require('node:worker_threads')
6
+ const { getGlobalDispatcher } = require('undici')
6
7
 
7
8
  const kITC = Symbol.for('plt.runtime.itc')
8
9
  const kProxyRoute = Symbol('plt.composer.proxy.route')
@@ -14,12 +15,18 @@ async function resolveServiceProxyParameters (service) {
14
15
  const meta = (await globalThis[kITC]?.send('getServiceMeta', service.id))?.composer ?? { prefix: service.id }
15
16
 
16
17
  // If no prefix could be found, assume the service id
17
- const prefix = (service.proxy?.prefix ?? meta.prefix ?? service.id).replace(/(\/$)/g, '')
18
-
18
+ let prefix = (service.proxy?.prefix ?? meta.prefix ?? service.id).replace(/(\/$)/g, '')
19
19
  let rewritePrefix = ''
20
20
  let internalRewriteLocationHeader = true
21
21
 
22
22
  if (meta.wantsAbsoluteUrls) {
23
+ const basePath = workerData.config.basePath
24
+
25
+ // Strip the runtime basepath from the prefix when it comes from the service meta
26
+ if (basePath && !service.proxy?.prefix && prefix.startsWith(basePath)) {
27
+ prefix = prefix.substring(basePath.length)
28
+ }
29
+
23
30
  // The rewritePrefix purposely ignores service.proxy?.prefix to let
24
31
  // the service always being able to configure their value
25
32
  rewritePrefix = meta.prefix ?? service.id
@@ -138,7 +145,14 @@ module.exports = fp(async function (app, opts) {
138
145
  })
139
146
  }
140
147
 
141
- const toReplace = url ? new RegExp(url.replace(/127\.0\.0\.1/, 'localhost').replace(/\[::\]/, 'localhost').replace('http://', 'https?://')) : null
148
+ const toReplace = url
149
+ ? new RegExp(
150
+ url
151
+ .replace(/127\.0\.0\.1/, 'localhost')
152
+ .replace(/\[::\]/, 'localhost')
153
+ .replace('http://', 'https?://')
154
+ )
155
+ : null
142
156
 
143
157
  const proxyOptions = {
144
158
  websocket: true,
@@ -153,7 +167,7 @@ module.exports = fp(async function (app, opts) {
153
167
  },
154
168
  internalRewriteLocationHeader: false,
155
169
  replyOptions: {
156
- rewriteHeaders: (headers) => {
170
+ rewriteHeaders: headers => {
157
171
  let location = headers.location
158
172
  if (location) {
159
173
  if (toReplace) {
@@ -186,7 +200,7 @@ module.exports = fp(async function (app, opts) {
186
200
  ...telemetryHeaders,
187
201
  'x-forwarded-for': request.ip,
188
202
  'x-forwarded-host': request.host,
189
- 'x-forwarded-proto': request.protocol,
203
+ 'x-forwarded-proto': request.protocol
190
204
  }
191
205
 
192
206
  request.log.trace({ headers }, 'rewritten headers before proxying')
@@ -209,6 +223,7 @@ module.exports = fp(async function (app, opts) {
209
223
  if (host) {
210
224
  await app.register(httpProxy, {
211
225
  ...proxyOptions,
226
+ prefix: '/',
212
227
  constraints: { host }
213
228
  })
214
229
 
package/lib/stackable.js CHANGED
@@ -4,11 +4,12 @@ const { ServiceStackable } = require('@platformatic/service')
4
4
 
5
5
  const kITC = Symbol.for('plt.runtime.itc')
6
6
 
7
- async function ensureServices (config) {
7
+ async function ensureServices (composerId, config) {
8
8
  if (config.composer?.services?.length) {
9
9
  return
10
10
  }
11
11
 
12
+ composerId ??= globalThis.platformatic?.serviceId
12
13
  config.composer ??= {}
13
14
  config.composer.services ??= []
14
15
 
@@ -17,16 +18,17 @@ async function ensureServices (config) {
17
18
 
18
19
  if (services) {
19
20
  config.composer.services = services
20
- .filter(id => id !== globalThis.platformatic.serviceId) // Remove ourself
21
+ .filter(id => id !== composerId) // Remove ourself
21
22
  .map(id => ({ id, proxy: { prefix: `/${id}` } }))
22
23
  }
23
24
  }
24
25
 
25
26
  class ComposerStackable extends ServiceStackable {
26
27
  #meta
28
+ #dependencies
27
29
 
28
30
  async getBootstrapDependencies () {
29
- await ensureServices(this.configManager.current)
31
+ await ensureServices(this.serviceId, this.configManager.current)
30
32
 
31
33
  // We do not call init() on purpose, as we don't want to load the app just yet.
32
34
 
@@ -43,23 +45,10 @@ class ComposerStackable extends ServiceStackable {
43
45
  )
44
46
  }
45
47
 
48
+ this.#dependencies = dependencies
46
49
  return dependencies
47
50
  }
48
51
 
49
- async #parseDependency (id, urlString) {
50
- let url = this.#getServiceUrl(id)
51
-
52
- if (urlString) {
53
- const remoteUrl = await this.configManager.replaceEnv(urlString)
54
-
55
- if (remoteUrl) {
56
- url = remoteUrl
57
- }
58
- }
59
-
60
- return { id, url, local: url.endsWith('.plt.local') }
61
- }
62
-
63
52
  registerMeta (meta) {
64
53
  this.#meta = Object.assign(this.#meta ?? {}, meta)
65
54
  }
@@ -74,8 +63,36 @@ class ComposerStackable extends ServiceStackable {
74
63
  }
75
64
  }
76
65
 
77
- #getServiceUrl (id) {
78
- return `http://${id}.plt.local`
66
+ async isHealthy () {
67
+ // Still booting, assume healthy
68
+ if (!this.#dependencies) {
69
+ return true
70
+ }
71
+
72
+ const composedServices = this.#dependencies.map(dep => dep.id)
73
+ const workers = await globalThis[kITC].send('getWorkers')
74
+
75
+ for (const worker of Object.values(workers)) {
76
+ if (composedServices.includes(worker.service) && !worker.status.startsWith('start')) {
77
+ return false
78
+ }
79
+ }
80
+
81
+ return true
82
+ }
83
+
84
+ async #parseDependency (id, urlString) {
85
+ let url = `http://${id}.plt.local`
86
+
87
+ if (urlString) {
88
+ const remoteUrl = await this.configManager.replaceEnv(urlString)
89
+
90
+ if (remoteUrl) {
91
+ url = remoteUrl
92
+ }
93
+ }
94
+
95
+ return { id, url, local: url.endsWith('.plt.local') }
79
96
  }
80
97
  }
81
98
  module.exports = { ComposerStackable, ensureServices }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/composer",
3
- "version": "2.41.0",
3
+ "version": "2.43.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -31,9 +31,9 @@
31
31
  "typescript": "^5.5.4",
32
32
  "why-is-node-running": "2",
33
33
  "ws": "^8.16.0",
34
- "@platformatic/client": "2.41.0",
35
- "@platformatic/config": "2.41.0",
36
- "@platformatic/db": "2.41.0"
34
+ "@platformatic/client": "2.43.0",
35
+ "@platformatic/db": "2.43.0",
36
+ "@platformatic/config": "2.43.0"
37
37
  },
38
38
  "dependencies": {
39
39
  "@fastify/error": "^4.0.0",
@@ -67,12 +67,12 @@
67
67
  "rfdc": "^1.3.1",
68
68
  "semgrator": "^0.3.0",
69
69
  "undici": "^7.0.0",
70
- "@platformatic/config": "2.41.0",
71
- "@platformatic/scalar-theme": "2.41.0",
72
- "@platformatic/generators": "2.41.0",
73
- "@platformatic/utils": "^2.41.0",
74
- "@platformatic/telemetry": "2.41.0",
75
- "@platformatic/service": "2.41.0"
70
+ "@platformatic/config": "2.43.0",
71
+ "@platformatic/generators": "2.43.0",
72
+ "@platformatic/scalar-theme": "2.43.0",
73
+ "@platformatic/telemetry": "2.43.0",
74
+ "@platformatic/service": "2.43.0",
75
+ "@platformatic/utils": "^2.43.0"
76
76
  },
77
77
  "scripts": {
78
78
  "test": "pnpm run lint && borp -T --timeout=300000 -c 1 && tsd",
package/schema.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/@platformatic/composer/2.41.0.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/composer/2.43.0.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "title": "Platformatic Composer",
5
5
  "type": "object",