@ossy/platform 3.0.8 → 3.0.9
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/README.md +10 -0
- package/package.json +13 -13
- package/src/health.js +24 -0
- package/src/health.spec.js +53 -0
- package/src/runtime.js +3 -0
- package/src/server.js +3 -0
package/README.md
CHANGED
|
@@ -61,6 +61,16 @@ Optional environment variables used by the platform itself:
|
|
|
61
61
|
| `API_URL` + `OSSY_API_KEY` | SDK configuration. When set, tasks receive a pre-configured SDK instance. |
|
|
62
62
|
| `PORT` | HTTP port. |
|
|
63
63
|
|
|
64
|
+
## Health check
|
|
65
|
+
|
|
66
|
+
Both `startServer` (website / app images) and `startRuntime` (CMS multi-tenant image) expose an unauthenticated liveness probe:
|
|
67
|
+
|
|
68
|
+
| Method | Path | Response |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| `GET` / `HEAD` | `/health` | `200` with `{ ok: true, status: "ok", service }` |
|
|
71
|
+
|
|
72
|
+
The route is mounted before auth and before CMS site loading, so ALB target groups and ECS container health checks can use path `/health` without cookies, API tokens, or a resolvable hostname.
|
|
73
|
+
|
|
64
74
|
## Exported API
|
|
65
75
|
|
|
66
76
|
```js
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ossy/platform",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.9",
|
|
4
4
|
"description": "Ossy application server runtime",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -44,17 +44,17 @@
|
|
|
44
44
|
"@aws-sdk/util-create-request": "^3.972.26",
|
|
45
45
|
"@aws-sdk/util-format-url": "^3.972.17",
|
|
46
46
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
47
|
-
"@ossy/config": "^3.0.
|
|
48
|
-
"@ossy/event-store": "^3.0.
|
|
49
|
-
"@ossy/locale": "^3.0.
|
|
50
|
-
"@ossy/manifest": "^3.0.
|
|
51
|
-
"@ossy/observability": "^3.0.
|
|
52
|
-
"@ossy/policies": "^3.0.
|
|
53
|
-
"@ossy/schema": "^3.0.
|
|
54
|
-
"@ossy/sdk": "^3.0.
|
|
55
|
-
"@ossy/tokens": "^3.0.
|
|
56
|
-
"@ossy/users": "^3.0.
|
|
57
|
-
"@ossy/workspaces": "^3.0.
|
|
47
|
+
"@ossy/config": "^3.0.9",
|
|
48
|
+
"@ossy/event-store": "^3.0.9",
|
|
49
|
+
"@ossy/locale": "^3.0.9",
|
|
50
|
+
"@ossy/manifest": "^3.0.9",
|
|
51
|
+
"@ossy/observability": "^3.0.9",
|
|
52
|
+
"@ossy/policies": "^3.0.9",
|
|
53
|
+
"@ossy/schema": "^3.0.9",
|
|
54
|
+
"@ossy/sdk": "^3.0.9",
|
|
55
|
+
"@ossy/tokens": "^3.0.9",
|
|
56
|
+
"@ossy/users": "^3.0.9",
|
|
57
|
+
"@ossy/workspaces": "^3.0.9",
|
|
58
58
|
"cookie-parser": "^1.4.7",
|
|
59
59
|
"dotenv": ">=16.0.0 <18.0.0",
|
|
60
60
|
"express": ">=5.0.0 <6.0.0",
|
|
@@ -74,5 +74,5 @@
|
|
|
74
74
|
"src",
|
|
75
75
|
"Dockerfile"
|
|
76
76
|
],
|
|
77
|
-
"gitHead": "
|
|
77
|
+
"gitHead": "908fae28675f6c3c179d4c2eecb451e8a1af886e"
|
|
78
78
|
}
|
package/src/health.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable liveness probe for ALB / ECS health checks.
|
|
3
|
+
*
|
|
4
|
+
* Mounted before auth and site loading so probes succeed whenever the
|
|
5
|
+
* process can accept HTTP traffic. No authentication required.
|
|
6
|
+
*/
|
|
7
|
+
export const HEALTH_PATH = '/health'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {import('express').Express} app
|
|
11
|
+
* @param {{ service?: string }} [options]
|
|
12
|
+
*/
|
|
13
|
+
export function mountHealthEndpoint (app, { service = 'ossy' } = {}) {
|
|
14
|
+
const handler = (_req, res) => {
|
|
15
|
+
res.status(200).json({
|
|
16
|
+
ok: true,
|
|
17
|
+
status: 'ok',
|
|
18
|
+
service,
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
app.get(HEALTH_PATH, handler)
|
|
23
|
+
app.head(HEALTH_PATH, handler)
|
|
24
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, expect, it, jest } from '@jest/globals'
|
|
2
|
+
import express from 'express'
|
|
3
|
+
import { HEALTH_PATH, mountHealthEndpoint } from './health.js'
|
|
4
|
+
|
|
5
|
+
function createMockRes () {
|
|
6
|
+
const res = {
|
|
7
|
+
statusCode: 200,
|
|
8
|
+
body: undefined,
|
|
9
|
+
status (code) {
|
|
10
|
+
this.statusCode = code
|
|
11
|
+
return this
|
|
12
|
+
},
|
|
13
|
+
json (body) {
|
|
14
|
+
this.body = body
|
|
15
|
+
return this
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
return res
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe('mountHealthEndpoint', () => {
|
|
22
|
+
it('registers GET /health that returns 200 without auth', () => {
|
|
23
|
+
const app = express()
|
|
24
|
+
const get = jest.spyOn(app, 'get')
|
|
25
|
+
const head = jest.spyOn(app, 'head')
|
|
26
|
+
|
|
27
|
+
mountHealthEndpoint(app, { service: 'platform-runtime' })
|
|
28
|
+
|
|
29
|
+
expect(get).toHaveBeenCalledWith(HEALTH_PATH, expect.any(Function))
|
|
30
|
+
expect(head).toHaveBeenCalledWith(HEALTH_PATH, expect.any(Function))
|
|
31
|
+
|
|
32
|
+
const handler = get.mock.calls[0][1]
|
|
33
|
+
const res = createMockRes()
|
|
34
|
+
handler({}, res)
|
|
35
|
+
|
|
36
|
+
expect(res.statusCode).toBe(200)
|
|
37
|
+
expect(res.body).toEqual({
|
|
38
|
+
ok: true,
|
|
39
|
+
status: 'ok',
|
|
40
|
+
service: 'platform-runtime',
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('defaults service name to ossy', () => {
|
|
45
|
+
const app = express()
|
|
46
|
+
const get = jest.spyOn(app, 'get')
|
|
47
|
+
mountHealthEndpoint(app)
|
|
48
|
+
const handler = get.mock.calls[0][1]
|
|
49
|
+
const res = createMockRes()
|
|
50
|
+
handler({}, res)
|
|
51
|
+
expect(res.body.service).toBe('ossy')
|
|
52
|
+
})
|
|
53
|
+
})
|
package/src/runtime.js
CHANGED
|
@@ -10,6 +10,7 @@ import { buildManifestSummary } from '@ossy/manifest/build-manifest-summary'
|
|
|
10
10
|
import { ProxyInternal } from './proxy-internal.js'
|
|
11
11
|
import { loadSite, invalidateSite } from './site-loader.js'
|
|
12
12
|
import { createLogger } from '@ossy/observability'
|
|
13
|
+
import { mountHealthEndpoint } from './health.js'
|
|
13
14
|
|
|
14
15
|
const log = createLogger('platform')
|
|
15
16
|
|
|
@@ -76,6 +77,8 @@ export async function startRuntime ({ port } = {}) {
|
|
|
76
77
|
const resolvedPort = port ?? resolvePort()
|
|
77
78
|
|
|
78
79
|
const app = express()
|
|
80
|
+
// Liveness for ALB/ECS — before site loading so probes never depend on CMS/domain.
|
|
81
|
+
mountHealthEndpoint(app, { service: 'platform-runtime' })
|
|
79
82
|
app.use(morgan('tiny'))
|
|
80
83
|
app.use(express.json({ strict: false }))
|
|
81
84
|
app.use(cookieParser(process.env.OSSY_COOKIE_SECRET || 'default_secret'))
|
package/src/server.js
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
createWorkspaceLoader,
|
|
28
28
|
} from './entitlements/action-entitlement.js'
|
|
29
29
|
import { closePushSseConnections, mountPushSse } from './push/mount-push-sse.js'
|
|
30
|
+
import { mountHealthEndpoint } from './health.js'
|
|
30
31
|
import {
|
|
31
32
|
createSlowRequestLogger,
|
|
32
33
|
createTimedOperation,
|
|
@@ -320,6 +321,8 @@ export async function startServer (options = {}) {
|
|
|
320
321
|
}
|
|
321
322
|
|
|
322
323
|
const app = express()
|
|
324
|
+
// Liveness for ALB/ECS — before auth so probes never depend on cookies/Mongo.
|
|
325
|
+
mountHealthEndpoint(app, { service: 'platform' })
|
|
323
326
|
app.use(morgan('tiny'))
|
|
324
327
|
app.use(express.json({ strict: false }))
|
|
325
328
|
app.use(cookieParser(ConfigService.TokenSecret))
|