@platformatic/service 0.13.0 → 0.14.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
@@ -90,6 +90,10 @@ async function platformaticService (app, opts, toLoad = []) {
90
90
  healthCheck: opts.healthCheck.fn
91
91
  })
92
92
  }
93
+
94
+ if (!app.hasRoute({ url: '/', method: 'GET' }) && !Array.isArray(toLoad)) {
95
+ await app.register(require('./lib/root-endpoint'), opts)
96
+ }
93
97
  }
94
98
 
95
99
  async function loadPlugin (app, config, pluginOptions) {
@@ -179,10 +183,13 @@ function adjustConfigAfterMerge (options, stash) {
179
183
  }
180
184
  }
181
185
 
182
- async function buildServer (options, app = platformaticService) {
186
+ async function buildServer (options, app, ConfigManagerContructor) {
187
+ app = app || platformaticService
188
+ ConfigManagerContructor = ConfigManagerContructor || ConfigManager
189
+
183
190
  if (!options.configManager) {
184
191
  // instantiate a new config manager from current options
185
- const cm = new ConfigManager({
192
+ const cm = new ConfigManagerContructor({
186
193
  source: options,
187
194
  schema: app?.schema ?? schema
188
195
  })
@@ -87,7 +87,7 @@ module.exports = fp(async function (app, opts) {
87
87
  const metricsEndpointOptions = {
88
88
  url: '/metrics',
89
89
  method: 'GET',
90
- logLevel: 'trace',
90
+ logLevel: 'warn',
91
91
  handler: async (req, reply) => {
92
92
  const promRegistry = app.metrics.client.register
93
93
  const accepts = req.accepts()
@@ -0,0 +1,26 @@
1
+ 'use strict'
2
+ const path = require('path')
3
+ const fastifyStatic = require('@fastify/static')
4
+ const userAgentParser = require('ua-parser-js')
5
+
6
+ module.exports = async (app, opts) => {
7
+ app.register(fastifyStatic, {
8
+ root: path.join(__dirname, 'public')
9
+ })
10
+ // root endpoint
11
+ app.route({
12
+ method: 'GET',
13
+ path: '/',
14
+ schema: { hide: true },
15
+ handler: (req, reply) => {
16
+ const uaString = req.headers['user-agent']
17
+ if (uaString) {
18
+ const parsed = userAgentParser(uaString)
19
+ if (parsed.browser.name !== undefined) {
20
+ return reply.sendFile('./index.html')
21
+ }
22
+ }
23
+ return { message: 'Welcome to Platformatic! Please visit https://oss.platformatic.dev' }
24
+ }
25
+ })
26
+ }
@@ -0,0 +1,99 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/images/favicon.ico" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Platformatic DB</title>
8
+ <style>
9
+ :root {
10
+ width: 100%;
11
+ font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
12
+ font-size: 16px;
13
+ line-height: 24px;
14
+ font-weight: 400;
15
+
16
+ color-scheme: light dark;
17
+ color: rgba(255, 255, 255, 0.87);
18
+ background-color: #092339;
19
+
20
+ font-synthesis: none;
21
+ text-rendering: optimizeLegibility;
22
+ -webkit-font-smoothing: antialiased;
23
+ -moz-osx-font-smoothing: grayscale;
24
+ -webkit-text-size-adjust: 100%;
25
+ }
26
+
27
+ a {
28
+ font-weight: 500;
29
+ color: #21f190;
30
+ text-decoration: inherit;
31
+ }
32
+ a:hover {
33
+ color: #007B82;
34
+ }
35
+
36
+ body {
37
+ width: 50%;
38
+ display: flex;
39
+ align-items: center;
40
+ justify-content: center;
41
+ flex-direction: column;
42
+ margin: auto;
43
+ min-height: 100vh;
44
+ }
45
+
46
+ h1 {
47
+ font-size: 3.2em;
48
+ line-height: 1.1;
49
+ }
50
+
51
+ button {
52
+ border-radius: 8px;
53
+ border: 1px solid transparent;
54
+ padding: 0.6em 1.2em;
55
+ font-size: 1em;
56
+ font-weight: 500;
57
+ font-family: inherit;
58
+ background-color: #1a1a1a;
59
+ cursor: pointer;
60
+ transition: border-color 0.25s;
61
+ }
62
+ button:hover {
63
+ border-color: #646cff;
64
+ }
65
+ button:focus,
66
+ button:focus-visible {
67
+ outline: 4px auto -webkit-focus-ring-color;
68
+ }
69
+
70
+ @media (prefers-color-scheme: light) {
71
+ :root {
72
+ color: #213547;
73
+ background-color: #ffffff;
74
+ }
75
+ a:hover {
76
+ color: #747bff;
77
+ }
78
+ button {
79
+ background-color: #f9f9f9;
80
+ }
81
+ }
82
+
83
+ #root {
84
+ display: flex;
85
+ flex-direction: column;
86
+ align-items: center;
87
+ justify-content: center;
88
+ }
89
+
90
+ </style>
91
+ </head>
92
+ <body>
93
+ <div id="root">
94
+ <img height="256" src="logo-512x512.png"/>
95
+ <h1>Welcome to Platformatic Service</h1>
96
+ <h2><a href="https://oss.platformatic.dev" target="_blank">Documentation</a></h2>
97
+ </div>
98
+ </body>
99
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/service",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "author": "Matteo Collina <hello@matteocollina.com>",
@@ -49,14 +49,13 @@
49
49
  "fastify-sandbox": "^0.11.0",
50
50
  "graphql": "^16.6.0",
51
51
  "help-me": "^4.2.0",
52
- "mercurius": "^11.4.0",
53
- "minimatch": "^5.1.1",
54
52
  "minimist": "^1.2.7",
55
53
  "pino": "^8.8.0",
56
54
  "pino-pretty": "^9.1.1",
57
55
  "rfdc": "^1.3.0",
58
- "@platformatic/config": "0.13.0",
59
- "@platformatic/utils": "0.13.0"
56
+ "ua-parser-js": "^1.0.32",
57
+ "@platformatic/config": "0.14.0",
58
+ "@platformatic/utils": "0.14.0"
60
59
  },
61
60
  "standard": {
62
61
  "ignore": [
@@ -2,7 +2,7 @@
2
2
 
3
3
  require('./helper')
4
4
  const { test } = require('tap')
5
- const { buildServer } = require('..')
5
+ const { buildServer, ConfigManager } = require('..')
6
6
  const { request } = require('undici')
7
7
  const { join } = require('path')
8
8
  const os = require('os')
@@ -212,3 +212,115 @@ test('config is adjusted to handle custom loggers', async (t) => {
212
212
  await buildServer(options)
213
213
  t.equal(called, true)
214
214
  })
215
+
216
+ test('custom ConfigManager', async ({ teardown, equal, pass, same }) => {
217
+ const file = join(os.tmpdir(), `${process.pid}-2.js`)
218
+
219
+ await writeFile(file, `
220
+ module.exports = async function (app, options) {
221
+ app.get('/', () => options.message)
222
+ }`)
223
+
224
+ class MyConfigManager extends ConfigManager {
225
+ _transformConfig () {
226
+ super._transformConfig.call(this)
227
+ this.current.plugin = {
228
+ path: file,
229
+ options: {
230
+ message: 'hello'
231
+ }
232
+ }
233
+ }
234
+ }
235
+
236
+ const server = await buildServer({
237
+ server: {
238
+ hostname: '127.0.0.1',
239
+ port: 0
240
+ },
241
+ metrics: false
242
+ }, null, MyConfigManager)
243
+ teardown(server.stop)
244
+ await server.listen()
245
+
246
+ {
247
+ const res = await request(`${server.url}/`)
248
+ equal(res.statusCode, 200, 'add status code')
249
+ same(await res.body.text(), 'hello', 'response')
250
+ }
251
+
252
+ await server.app.platformatic.configManager.update({
253
+ server: {
254
+ hostname: '127.0.0.1',
255
+ port: 0
256
+ },
257
+ plugin: {
258
+ path: file,
259
+ options: {
260
+ message: 'ciao mondo'
261
+ }
262
+ },
263
+ metrics: false
264
+ })
265
+
266
+ await server.restart()
267
+
268
+ {
269
+ const res = await request(`${server.url}/`)
270
+ equal(res.statusCode, 200, 'add status code')
271
+ same(await res.body.text(), 'ciao mondo', 'response')
272
+ }
273
+ })
274
+
275
+ test('config reloads', async ({ teardown, equal, pass, same }) => {
276
+ const file = join(os.tmpdir(), `${process.pid}-1.js`)
277
+
278
+ await writeFile(file, `
279
+ module.exports = async function (app, options) {
280
+ app.get('/', () => options.message)
281
+ }`)
282
+
283
+ const server = await buildServer({
284
+ server: {
285
+ hostname: '127.0.0.1',
286
+ port: 0
287
+ },
288
+ plugin: {
289
+ path: file,
290
+ options: {
291
+ message: 'hello'
292
+ }
293
+ },
294
+ metrics: false
295
+ })
296
+ teardown(server.stop)
297
+ await server.listen()
298
+
299
+ {
300
+ const res = await request(`${server.url}/`)
301
+ equal(res.statusCode, 200, 'add status code')
302
+ same(await res.body.text(), 'hello', 'response')
303
+ }
304
+
305
+ await server.app.platformatic.configManager.update({
306
+ server: {
307
+ hostname: '127.0.0.1',
308
+ port: 0
309
+ },
310
+ plugin: {
311
+ path: file,
312
+ options: {
313
+ message: 'ciao mondo'
314
+ }
315
+ },
316
+ metrics: false
317
+ })
318
+
319
+ await server.restart()
320
+
321
+ {
322
+ const res = await request(`${server.url}/`)
323
+ equal(res.statusCode, 200, 'add status code')
324
+ same(await res.body.text(), 'ciao mondo', 'response')
325
+ }
326
+ })
@@ -0,0 +1,8 @@
1
+ 'use strict'
2
+
3
+ module.exports = async function (app) {
4
+ console.log('plugin loaded')
5
+ app.get('/', async function () {
6
+ return { message: 'Root Plugin' }
7
+ })
8
+ }
package/test/helper.js CHANGED
@@ -18,3 +18,14 @@ const agent = new Agent({
18
18
  }
19
19
  })
20
20
  setGlobalDispatcher(agent)
21
+
22
+ function buildConfig (options) {
23
+ const base = {
24
+ server: {},
25
+ core: {}
26
+ }
27
+
28
+ return Object.assign(base, options)
29
+ }
30
+
31
+ module.exports.buildConfig = buildConfig
@@ -31,7 +31,9 @@ test('load and reload', async ({ teardown, equal, pass, same }) => {
31
31
 
32
32
  {
33
33
  const res = await request(`${server.url}/`)
34
- equal(res.statusCode, 404, 'status code')
34
+ equal(res.statusCode, 200, 'status code')
35
+ const data = await res.body.json()
36
+ same(data, { message: 'Welcome to Platformatic! Please visit https://oss.platformatic.dev' })
35
37
  }
36
38
 
37
39
  await writeFile(file, `
@@ -97,7 +99,9 @@ test('update config', async ({ teardown, equal, pass, same }) => {
97
99
 
98
100
  {
99
101
  const res = await request(`${server.url}/`)
100
- equal(res.statusCode, 404, 'status code')
102
+ equal(res.statusCode, 200, 'status code')
103
+ const data = await res.body.json()
104
+ same(data, { message: 'Welcome to Platformatic! Please visit https://oss.platformatic.dev' })
101
105
  }
102
106
 
103
107
  const file2 = join(os.tmpdir(), `some-plugin-${process.pid}-2.js`)
@@ -184,7 +188,9 @@ test('load and reload with the fallback', async ({ teardown, equal, pass, same }
184
188
 
185
189
  {
186
190
  const res = await request(`${server.url}/`)
187
- equal(res.statusCode, 404, 'status code')
191
+ equal(res.statusCode, 200, 'status code')
192
+ const data = await res.body.json()
193
+ same(data, { message: 'Welcome to Platformatic! Please visit https://oss.platformatic.dev' })
188
194
  }
189
195
 
190
196
  await writeFile(file, `
@@ -223,7 +229,9 @@ test('load and reload ESM', async ({ teardown, equal, pass, same }) => {
223
229
 
224
230
  {
225
231
  const res = await request(`${server.url}/`)
226
- equal(res.statusCode, 404, 'status code')
232
+ equal(res.statusCode, 200, 'status code')
233
+ const data = await res.body.json()
234
+ same(data, { message: 'Welcome to Platformatic! Please visit https://oss.platformatic.dev' })
227
235
  }
228
236
 
229
237
  await writeFile(file, `
@@ -240,7 +248,7 @@ test('load and reload ESM', async ({ teardown, equal, pass, same }) => {
240
248
  }
241
249
  })
242
250
 
243
- test('server should be available after reload a compromised plugin', async ({ teardown, equal, pass }) => {
251
+ test('server should be available after reload a compromised plugin', async ({ teardown, equal, pass, same }) => {
244
252
  const file = join(os.tmpdir(), `some-plugin-${process.pid}.js`)
245
253
 
246
254
  const workingModule = `
@@ -271,7 +279,9 @@ test('server should be available after reload a compromised plugin', async ({ te
271
279
 
272
280
  {
273
281
  const res = await request(`${server.url}/`, { method: 'GET' })
274
- equal(res.statusCode, 404, 'status code')
282
+ equal(res.statusCode, 200, 'status code')
283
+ const data = await res.body.json()
284
+ same(data, { message: 'Welcome to Platformatic! Please visit https://oss.platformatic.dev' })
275
285
  }
276
286
 
277
287
  await writeFile(file, workingModule)
@@ -279,7 +289,9 @@ test('server should be available after reload a compromised plugin', async ({ te
279
289
 
280
290
  {
281
291
  const res = await request(`${server.url}/`, { method: 'GET' })
282
- equal(res.statusCode, 404, 'add status code')
292
+ equal(res.statusCode, 200, 'add status code')
293
+ const data = await res.body.json()
294
+ same(data, { message: 'Welcome to Platformatic! Please visit https://oss.platformatic.dev' })
283
295
  }
284
296
 
285
297
  teardown(server.stop)
@@ -0,0 +1,79 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('tap')
4
+ const { buildServer } = require('..')
5
+ const { buildConfig, connInfo } = require('./helper')
6
+ const { request } = require('undici')
7
+ const { join } = require('path')
8
+
9
+ test('should respond 200 on root endpoint', async ({ teardown, equal, same }) => {
10
+ const server = await buildServer(buildConfig({
11
+ server: {
12
+ hostname: '127.0.0.1',
13
+ port: 0,
14
+ healthCheck: {
15
+ enabled: true,
16
+ interval: 2000
17
+ }
18
+ },
19
+ core: {
20
+ ...connInfo
21
+ },
22
+ authorization: {
23
+ adminSecret: 'secret'
24
+ },
25
+ dashboard: false
26
+ }))
27
+ teardown(server.stop)
28
+
29
+ await server.listen()
30
+ {
31
+ // No browser (i.e. curl)
32
+ const res = await (request(`${server.url}/`))
33
+ equal(res.statusCode, 200)
34
+ const body = await res.body.json()
35
+ same(body, { message: 'Welcome to Platformatic! Please visit https://oss.platformatic.dev' })
36
+ }
37
+
38
+ {
39
+ // browser
40
+ const res = await (request(`${server.url}/`, {
41
+ headers: {
42
+ 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36'
43
+ }
44
+ }))
45
+
46
+ equal(res.statusCode, 200)
47
+ equal(res.headers['content-type'], 'text/html; charset=UTF-8')
48
+ }
49
+ })
50
+
51
+ test('should not overwrite a plugin which define a root endpoint', async ({ teardown, equal, same }) => {
52
+ const server = await buildServer(buildConfig({
53
+ server: {
54
+ hostname: '127.0.0.1',
55
+ port: 0,
56
+ healthCheck: {
57
+ enabled: true,
58
+ interval: 2000
59
+ }
60
+ },
61
+ core: {
62
+ ...connInfo
63
+ },
64
+ authorization: {
65
+ adminSecret: 'secret'
66
+ },
67
+ dashboard: false,
68
+ plugin: {
69
+ path: join(__dirname, 'fixtures', 'root-endpoint-plugin.js')
70
+ }
71
+ }))
72
+ teardown(server.stop)
73
+
74
+ await server.listen()
75
+ const res = await (request(`${server.url}/`))
76
+ equal(res.statusCode, 200)
77
+ const body = await res.body.json()
78
+ same(body, { message: 'Root Plugin' })
79
+ })