@camstack/server 1.2.683 → 1.2.684
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/CHANGELOG.md +6 -0
- package/dist/api/data-plane/form-body.js +54 -0
- package/dist/api/oauth2/oauth2-routes.js +4 -20
- package/dist/main.js +5 -5
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.2.684] - 2026-10-29
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- A form posted through an addon's data plane (a vendor sign-in page) reaches the addon unchanged instead of re-encoded as JSON
|
|
15
|
+
|
|
10
16
|
## [1.2.683] - 2026-10-28
|
|
11
17
|
|
|
12
18
|
### Changed
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseFormBody = parseFormBody;
|
|
4
|
+
exports.registerFormBodyParser = registerFormBodyParser;
|
|
5
|
+
exports.dataPlaneReplayBody = dataPlaneReplayBody;
|
|
6
|
+
const FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded';
|
|
7
|
+
const rawFormBodies = new WeakMap();
|
|
8
|
+
/** Parse a urlencoded body string into a plain object (duplicate keys collapse). */
|
|
9
|
+
function parseFormBody(raw) {
|
|
10
|
+
const result = {};
|
|
11
|
+
for (const [key, value] of new URLSearchParams(raw).entries()) {
|
|
12
|
+
result[key] = value;
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
/** Register the urlencoded parser: the parsed object for hub routes, the raw bytes on the side. */
|
|
17
|
+
function registerFormBodyParser(fastify) {
|
|
18
|
+
fastify.addContentTypeParser(FORM_CONTENT_TYPE, { parseAs: 'buffer' }, (req, body, done) => {
|
|
19
|
+
try {
|
|
20
|
+
const bytes = Buffer.isBuffer(body) ? body : Buffer.from(body);
|
|
21
|
+
rawFormBodies.set(req.raw, bytes);
|
|
22
|
+
done(null, parseFormBody(bytes.toString('utf8')));
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
done(err instanceof Error ? err : new Error(String(err)), undefined);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The bytes a data-plane upstream receives. Never a re-serialised object: a
|
|
31
|
+
* parsed body with no recorded raw bytes is a bug path, logged (no values) and
|
|
32
|
+
* refused with `null`.
|
|
33
|
+
*/
|
|
34
|
+
function dataPlaneReplayBody(raw, body, logger) {
|
|
35
|
+
if (body === undefined)
|
|
36
|
+
return undefined;
|
|
37
|
+
if (Buffer.isBuffer(body))
|
|
38
|
+
return body;
|
|
39
|
+
if (typeof body === 'string')
|
|
40
|
+
return Buffer.from(body);
|
|
41
|
+
const recorded = rawFormBodies.get(raw);
|
|
42
|
+
if (recorded !== undefined)
|
|
43
|
+
return recorded;
|
|
44
|
+
// A JSON body is re-serialised from its parsed object (Fastify's JSON parser
|
|
45
|
+
// keeps no bytes); every other parsed object has lost its original encoding.
|
|
46
|
+
const contentType = String(raw.headers['content-type'] ?? '');
|
|
47
|
+
if (/^application\/(?:[\w.+-]+\+)?json\b/i.test(contentType)) {
|
|
48
|
+
return Buffer.from(JSON.stringify(body));
|
|
49
|
+
}
|
|
50
|
+
logger.error('data-plane request body was parsed but its raw bytes were not recorded', {
|
|
51
|
+
tags: { contentType: String(raw.headers['content-type'] ?? ''), url: raw.url ?? '' },
|
|
52
|
+
});
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
@@ -9,6 +9,7 @@ exports.registerOauth2Routes = registerOauth2Routes;
|
|
|
9
9
|
const types_1 = require("@camstack/types");
|
|
10
10
|
const session_cookie_js_1 = require("../../auth/session-cookie.js");
|
|
11
11
|
const app_page_strings_js_1 = require("../../auth/app-page-strings.js");
|
|
12
|
+
const form_body_js_1 = require("../data-plane/form-body.js");
|
|
12
13
|
const consent_page_js_1 = require("./consent-page.js");
|
|
13
14
|
const private_host_js_1 = require("./private-host.js");
|
|
14
15
|
/**
|
|
@@ -328,27 +329,10 @@ async function readIntegrationRegistry(registry) {
|
|
|
328
329
|
.filter((addonId) => !registry.hasProvider('oauth-integration', addonId));
|
|
329
330
|
return { descriptors, pendingAddons };
|
|
330
331
|
}
|
|
331
|
-
/** Parse an application/x-www-form-urlencoded body string into a plain object. */
|
|
332
|
-
function parseFormBody(raw) {
|
|
333
|
-
const params = new URLSearchParams(raw);
|
|
334
|
-
const result = {};
|
|
335
|
-
for (const [key, value] of params.entries()) {
|
|
336
|
-
result[key] = value;
|
|
337
|
-
}
|
|
338
|
-
return result;
|
|
339
|
-
}
|
|
340
332
|
function registerOauth2Routes(fastify, deps) {
|
|
341
|
-
//
|
|
342
|
-
//
|
|
343
|
-
|
|
344
|
-
fastify.addContentTypeParser('application/x-www-form-urlencoded', { parseAs: 'string' }, (_req, body, done) => {
|
|
345
|
-
try {
|
|
346
|
-
done(null, parseFormBody(body));
|
|
347
|
-
}
|
|
348
|
-
catch (err) {
|
|
349
|
-
done(err, undefined);
|
|
350
|
-
}
|
|
351
|
-
});
|
|
333
|
+
// Form bodies (consent POST, token POST): parsed object for these routes, raw
|
|
334
|
+
// bytes recorded for the data plane (api/data-plane/form-body.ts).
|
|
335
|
+
(0, form_body_js_1.registerFormBodyParser)(fastify);
|
|
352
336
|
// ─── GET /api/oauth2/authorize ────────────────────────────────────────────
|
|
353
337
|
// Mounted under /api/* so it is naturally excluded from the SPA catch-all,
|
|
354
338
|
// the PWA service-worker navigate fallback, and the Vite dev proxy — no
|
package/dist/main.js
CHANGED
|
@@ -71,6 +71,7 @@ const release_identity_1 = require("./core/release-identity");
|
|
|
71
71
|
const runner_boot_check_1 = require("./core/runner-health/runner-boot-check");
|
|
72
72
|
const hub_cluster_url_1 = require("./api/hub-cluster-url");
|
|
73
73
|
const model_distributor_js_1 = require("./api/model-distributor.js");
|
|
74
|
+
const form_body_js_1 = require("./api/data-plane/form-body.js");
|
|
74
75
|
const oauth2_routes_js_1 = require("./api/oauth2/oauth2-routes.js");
|
|
75
76
|
const embed_auth_route_js_1 = require("./auth/embed-auth-route.js");
|
|
76
77
|
const live_user_js_1 = require("./auth/live-user.js");
|
|
@@ -1128,13 +1129,12 @@ async function bootstrap() {
|
|
|
1128
1129
|
}
|
|
1129
1130
|
if (shareHeaders !== null)
|
|
1130
1131
|
Object.assign(request.raw.headers, shareHeaders);
|
|
1132
|
+
const replayBody = (0, form_body_js_1.dataPlaneReplayBody)(request.raw, request.body, addonGateLogger);
|
|
1133
|
+
if (replayBody === null) {
|
|
1134
|
+
return reply.status(500).send({ error: 'Request body could not be forwarded' });
|
|
1135
|
+
}
|
|
1131
1136
|
// Take over the socket — `proxyToUpstream` drives the raw response.
|
|
1132
1137
|
reply.hijack();
|
|
1133
|
-
const replayBody = request.body === undefined
|
|
1134
|
-
? undefined
|
|
1135
|
-
: Buffer.isBuffer(request.body)
|
|
1136
|
-
? request.body
|
|
1137
|
-
: Buffer.from(typeof request.body === 'string' ? request.body : JSON.stringify(request.body));
|
|
1138
1138
|
(0, system_1.proxyToUpstream)({
|
|
1139
1139
|
baseUrl: dpMatch.endpoint.baseUrl,
|
|
1140
1140
|
secret: dpMatch.endpoint.secret,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/server",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.684",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
]
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@camstack/addon-admin-ui": "1.2.
|
|
52
|
+
"@camstack/addon-admin-ui": "1.2.512",
|
|
53
53
|
"@camstack/addon-agent-ui": "1.2.289",
|
|
54
54
|
"@camstack/addon-auth": "1.2.170",
|
|
55
55
|
"@camstack/addon-benchmark": "1.2.288",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"@camstack/sdk": "1.2.154",
|
|
63
63
|
"@camstack/system": "1.2.514",
|
|
64
64
|
"@camstack/types": "1.2.440",
|
|
65
|
-
"@camstack/ui-library": "1.2.
|
|
65
|
+
"@camstack/ui-library": "1.2.347",
|
|
66
66
|
"@fastify/compress": "^9.0.0",
|
|
67
67
|
"@fastify/cookie": "^11.0.2",
|
|
68
68
|
"@fastify/cors": "^11.2.0",
|