@forestadmin/agent-bff 1.21.0 → 1.23.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/README.md +40 -3
- package/dist/action/action-routes-middleware.js +12 -7
- package/dist/action/agent-action-client.d.ts +7 -1
- package/dist/action/agent-action-client.js +3 -3
- package/dist/ai/ai-proxy-client.d.ts +26 -0
- package/dist/ai/ai-proxy-client.js +74 -0
- package/dist/ai/ai-proxy-timeout-error.d.ts +5 -0
- package/dist/ai/ai-proxy-timeout-error.js +11 -0
- package/dist/ai/ai-routes-middleware.d.ts +15 -0
- package/dist/ai/ai-routes-middleware.js +96 -0
- package/dist/api-key/agent-token.js +4 -7
- package/dist/auth/auth-mode.d.ts +2 -0
- package/dist/auth/auth-mode.js +9 -1
- package/dist/cli-core.d.ts +9 -0
- package/dist/cli-core.js +55 -7
- package/dist/cli-dispatch.js +15 -3
- package/dist/config/env-config.d.ts +3 -1
- package/dist/config/env-config.js +10 -8
- package/dist/docs/docs-samples.js +24 -16
- package/dist/http/bff-local-errors.d.ts +4 -0
- package/dist/http/bff-local-errors.js +19 -1
- package/dist/http/body-limit.d.ts +1 -0
- package/dist/http/body-limit.js +3 -1
- package/dist/oauth/oauth-error.d.ts +3 -2
- package/dist/oauth/oauth-error.js +5 -4
- package/dist/oauth/session-lifecycle.js +2 -2
- package/dist/openapi/openapi-document.d.ts +5 -1
- package/dist/openapi/openapi-document.js +133 -17
- package/dist/openapi/openapi-routes.d.ts +2 -1
- package/dist/openapi/openapi-routes.js +9 -4
- package/dist/openapi/schemas.d.ts +1 -0
- package/dist/openapi/schemas.js +10 -2
- package/dist/openapi/unfolded-document.d.ts +5 -1
- package/dist/openapi/unfolded-document.js +6 -3
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -93,6 +93,7 @@ yarn start:dev # node --env-file=.env dist/cli.js
|
|
|
93
93
|
| `HTTP_PORT` | no | Server port, integer 0–65535. Defaults to `3450`. `0` binds an OS-assigned ephemeral port. |
|
|
94
94
|
| `BFF_ALLOWED_ORIGINS`| no | Comma-separated CORS allow-list of exact origins (scheme + host + port). No wildcard. Empty ⇒ no cross-origin browser access. |
|
|
95
95
|
| `BFF_DEFAULT_TIMEZONE`| no | Fallback IANA timezone used when a request carries neither an `X-Forest-Timezone` header nor a body `timezone`. |
|
|
96
|
+
| `BFF_AI_TIMEOUT_MS` | no | How long `POST /agent/v1/ai/query` waits for the Forest server on the relay itself, in milliseconds. Defaults to `120000` — an AI generation is slow, and neither the app nor the Forest server bounds it. Past it the route answers `504`. It is not the route's end-to-end cap: an expired session is refreshed first, under a separate hard-coded 60 s ceiling that answers `502`, so a request can exceed this value. A malformed value (non-integer, `0`, or above 2147483647) fails the boot, unlike an absent one which takes the default. |
|
|
96
97
|
| `BFF_OPENAPI_ENABLED` | no | Serve `GET/HEAD /agent/openapi.json` (auth-gated) when `true`. Defaults to `true`. Set to `false` for customers who do not want the HTTP surface exposed: an authenticated `GET`/`HEAD` then gets `404 openapi_disabled`, other methods fall through to the agent routes exactly as they do when enabled, and `forest-bff openapi` keeps working either way. Accepted values: `true`/`false`. **The served document is unfolded and is not filtered per caller**: any authenticated caller, whatever their role, reads the name of every exposed collection, relation and field. Set this to `false` if that surface must not be reachable over HTTP. |
|
|
97
98
|
|
|
98
99
|
### Config validation
|
|
@@ -111,6 +112,13 @@ Every agent call flows through a request edge that enforces three cross-cutting
|
|
|
111
112
|
call is proxied to the agent. Errors use a structured, type-first contract — `{ error: { type, status,
|
|
112
113
|
message, details? } }` — so consumers branch on `error.type`, never on message text.
|
|
113
114
|
|
|
115
|
+
Two paths sit before the timezone layer and so skip it: `/agent/v1/context`, and the AI relay
|
|
116
|
+
described below. Neither reaches an agent, so neither has a timezone to resolve. This holds only
|
|
117
|
+
where they are actually mounted — `/agent/v1/context` needs the read-model bundle and the AI relay
|
|
118
|
+
needs an OAuth session. In a deployment missing either, the request falls through to the timezone
|
|
119
|
+
layer instead, so a caller sending no timezone against a BFF with no `BFF_DEFAULT_TIMEZONE` gets
|
|
120
|
+
`400 missing_timezone` rather than the `404` it would expect from an unmounted route.
|
|
121
|
+
|
|
114
122
|
### Auth-mode precedence
|
|
115
123
|
|
|
116
124
|
| Presented credentials | Result |
|
|
@@ -142,9 +150,38 @@ normalized away, no trailing slash, no wildcard, no subdomain matching):
|
|
|
142
150
|
|
|
143
151
|
### Timezone
|
|
144
152
|
|
|
145
|
-
|
|
146
|
-
header, (2) body `timezone` field, (3) `BFF_DEFAULT_TIMEZONE`. None →
|
|
147
|
-
non-IANA value → `400 invalid_timezone`.
|
|
153
|
+
On every route that reaches an agent, the BFF forwards an explicit `timezone`, resolved in order:
|
|
154
|
+
(1) `X-Forest-Timezone` header, (2) body `timezone` field, (3) `BFF_DEFAULT_TIMEZONE`. None →
|
|
155
|
+
`400 missing_timezone`; a non-IANA value → `400 invalid_timezone`. The two paths listed above are
|
|
156
|
+
mounted ahead of this layer and answer without it.
|
|
157
|
+
|
|
158
|
+
### AI query relay
|
|
159
|
+
|
|
160
|
+
`POST /agent/v1/ai/query` relays its body to the Forest AI proxy under the OAuth session the BFF
|
|
161
|
+
holds, and relays the answer back:
|
|
162
|
+
|
|
163
|
+
- **Session only.** An API key is refused with `403 oauth_required`: only the OAuth flow yields the
|
|
164
|
+
Forest access token this route forwards. The environment and rendering are derived server-side, so
|
|
165
|
+
sending `forest-*` headers has no effect.
|
|
166
|
+
- **Body.** `1mb` instead of the `16kb` of every other route, and `application/json` only — any other
|
|
167
|
+
content type arrives as an empty body, is relayed upstream as `{}`, and the Forest server rejects
|
|
168
|
+
the query. Over the limit → `413`. The parser is selected on the request path alone and runs ahead
|
|
169
|
+
of authentication, so an unauthenticated caller can make the BFF buffer and JSON-parse 1 MB on this
|
|
170
|
+
path — 64x the pre-auth surface of every other route. Bounding that is a deployment concern (rate
|
|
171
|
+
limiting or a body cap at the reverse proxy), not a BFF one.
|
|
172
|
+
- **Timeout.** `BFF_AI_TIMEOUT_MS` (default `120000`) caps the relay itself; past it → `504`. It is
|
|
173
|
+
not the only cap on the request: when the session's access token has expired the route first
|
|
174
|
+
refreshes it against the Forest server, and that hop has its own hard-coded 60 s ceiling which
|
|
175
|
+
answers `502 network_error`, not `504`.
|
|
176
|
+
- **Error shape.** Below 500 the upstream JSON body is relayed unchanged, so its shape is the Forest
|
|
177
|
+
AI proxy's contract and not the BFF's; when the upstream answer carried no JSON body the BFF
|
|
178
|
+
substitutes its own envelope, keeping the status only when it was 4xx and reporting `502` for a
|
|
179
|
+
2xx or 3xx that carried nothing to relay. At 500 and above the body is always the BFF
|
|
180
|
+
envelope, so no upstream infrastructure detail reaches the caller. **This is the one route where a
|
|
181
|
+
sub-500 body is not guaranteed to be `{ error: { type, … } }`**, so a consumer cannot branch on
|
|
182
|
+
`error.type` here without checking that the field exists.
|
|
183
|
+
- **Mounted only with OAuth.** Without a complete OAuth configuration the route is absent (`404`) and
|
|
184
|
+
`forest-bff openapi` omits it from the document.
|
|
148
185
|
|
|
149
186
|
## Sessions & token rotation
|
|
150
187
|
|
|
@@ -78,17 +78,15 @@ function describePayloadShape(raw) {
|
|
|
78
78
|
return raw.constructor?.name ?? 'object';
|
|
79
79
|
return `object{${Object.keys(raw).join(',')}}`;
|
|
80
80
|
}
|
|
81
|
-
async function handleForm(ctx,
|
|
81
|
+
async function handleForm({ ctx, action, values, logger, }) {
|
|
82
82
|
// Each agent-hitting call is wrapped on its own; getFields/extractRawLayout/mapping stay outside
|
|
83
83
|
// callAgent so a local BFF bug surfaces as a 500, not a mislabelled agent error. Fields and
|
|
84
84
|
// layout are read AFTER tryToSetFields because a change hook rebuilds them in place.
|
|
85
|
-
const action = await (0, agent_route_helpers_1.callAgent)(() => client.loadAction(collection, actionName, recordIds), logger);
|
|
86
85
|
const skippedFields = await (0, agent_route_helpers_1.callAgent)(() => action.tryToSetFields(values), logger);
|
|
87
86
|
ctx.status = 200;
|
|
88
87
|
ctx.body = (0, action_form_mapper_1.mapActionForm)(action, skippedFields, (0, agent_action_client_1.extractRawLayout)(action));
|
|
89
88
|
}
|
|
90
|
-
async function handleExecute(ctx,
|
|
91
|
-
const action = await (0, agent_route_helpers_1.callAgent)(() => client.loadAction(collection, actionName, recordIds), logger);
|
|
89
|
+
async function handleExecute({ ctx, action, values, logger, }) {
|
|
92
90
|
// setFields is strict: an unknown submitted field is a client error (400), not a 500. A transport
|
|
93
91
|
// failure from the change-hook it triggers is a genuine agent error, so it goes to the mapper.
|
|
94
92
|
try {
|
|
@@ -157,12 +155,19 @@ function createActionRoutesMiddleware({ store, agentUrl, timeoutMs, logger, crea
|
|
|
157
155
|
actionEndpoints: readModel.getActionEndpoints(),
|
|
158
156
|
timeoutMs,
|
|
159
157
|
});
|
|
158
|
+
const action = await (0, agent_route_helpers_1.callAgent)(() => client.loadAction({
|
|
159
|
+
collection,
|
|
160
|
+
actionName,
|
|
161
|
+
recordIds,
|
|
162
|
+
timezone: ctx.state.timezone,
|
|
163
|
+
}), logger);
|
|
164
|
+
const handlerArgs = { ctx, action, values, logger };
|
|
160
165
|
if (verb === 'execute') {
|
|
161
|
-
await handleExecute(
|
|
166
|
+
await handleExecute(handlerArgs);
|
|
162
167
|
}
|
|
163
168
|
else {
|
|
164
|
-
await handleForm(
|
|
169
|
+
await handleForm(handlerArgs);
|
|
165
170
|
}
|
|
166
171
|
};
|
|
167
172
|
}
|
|
168
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
173
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWN0aW9uLXJvdXRlcy1taWRkbGV3YXJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FjdGlvbi9hY3Rpb24tcm91dGVzLW1pZGRsZXdhcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUErSkEsK0NBNkRDO0FBbE5ELDREQUltQztBQUVuQyxtRUFBaUU7QUFDakUsNkRBQXFEO0FBQ3JELDZFQUF5RjtBQUN6RixtRUFBMkQ7QUFDM0QscUVBS3FDO0FBQ3JDLCtEQUtrQztBQUVsQyxNQUFNLFlBQVksR0FBRywwREFBMEQsQ0FBQztBQU9oRixvR0FBb0c7QUFDcEcsK0ZBQStGO0FBQy9GLHdFQUF3RTtBQUN4RSxxR0FBcUc7QUFDckcscUdBQXFHO0FBQ3JHLFNBQVMsY0FBYyxDQUFDLEdBQVk7SUFDbEMsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUN4QixNQUFNLElBQUEsaUNBQWMsRUFBQyw0Q0FBNEMsQ0FBQyxDQUFDO0lBQ3JFLENBQUM7SUFFRCxPQUFPLEdBQUcsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztBQUNuQyxDQUFDO0FBRUQsU0FBUyxXQUFXLENBQUMsR0FBWTtJQUMvQixJQUFJLEdBQUcsS0FBSyxTQUFTO1FBQUUsT0FBTyxFQUFFLENBQUM7SUFFakMsSUFBSSxPQUFPLEdBQUcsS0FBSyxRQUFRLElBQUksR0FBRyxLQUFLLElBQUksSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDbEUsTUFBTSxJQUFBLGlDQUFjLEVBQUMsMEJBQTBCLENBQUMsQ0FBQztJQUNuRCxDQUFDO0lBRUQsT0FBTyxHQUE4QixDQUFDO0FBQ3hDLENBQUM7QUFFRCwrRkFBK0Y7QUFDL0YsNEZBQTRGO0FBQzVGLFNB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
|
|
@@ -19,8 +19,14 @@ export interface Action extends ActionForm {
|
|
|
19
19
|
setFields(values: Record<string, unknown>): Promise<void>;
|
|
20
20
|
execute(): Promise<unknown>;
|
|
21
21
|
}
|
|
22
|
+
export interface LoadActionParams {
|
|
23
|
+
collection: string;
|
|
24
|
+
actionName: string;
|
|
25
|
+
recordIds: string[];
|
|
26
|
+
timezone: string;
|
|
27
|
+
}
|
|
22
28
|
export interface AgentActionClient {
|
|
23
|
-
loadAction(
|
|
29
|
+
loadAction(params: LoadActionParams): Promise<Action>;
|
|
24
30
|
}
|
|
25
31
|
export interface AgentActionClientOptions {
|
|
26
32
|
agentUrl: string;
|
|
@@ -9,7 +9,7 @@ const agent_client_1 = require("@forestadmin/agent-client");
|
|
|
9
9
|
const create_agent_http_requester_1 = __importDefault(require("../agent/create-agent-http-requester"));
|
|
10
10
|
// The raw layout must be read AFTER tryToSetFields: a change hook rebuilds fields+layout in place.
|
|
11
11
|
// agent-client's `Action.getLayout()` only returns an `ActionLayoutRoot` wrapper whose element array
|
|
12
|
-
// lives in a protected field. The rollback contract
|
|
12
|
+
// lives in a protected field. The rollback contract forbade agent-client changes, so we read it
|
|
13
13
|
// through a cast rather than adding a public accessor. `extract-raw-layout.test.ts` builds a real
|
|
14
14
|
// `ActionLayoutRoot` and asserts this unwraps it, so a rename of that field fails a test.
|
|
15
15
|
function extractRawLayout(action) {
|
|
@@ -29,7 +29,7 @@ function createAgentActionClient({ agentUrl, token, actionEndpoints, timeoutMs,
|
|
|
29
29
|
httpRequester: (0, create_agent_http_requester_1.default)(token, agentUrl, timeoutMs),
|
|
30
30
|
});
|
|
31
31
|
return {
|
|
32
|
-
loadAction: (collection,
|
|
32
|
+
loadAction: ({ collection, actionName, recordIds, timezone }) => client.collection(collection).action(actionName, { recordIds, timezone }),
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
35
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQtYWN0aW9uLWNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hY3Rpb24vYWdlbnQtYWN0aW9uLWNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQXNEQSw0Q0FJQztBQU9ELDBDQWlCQztBQS9FRCw0REFBb0U7QUFFcEUsdUdBQTRFO0FBNEM1RSxtR0FBbUc7QUFDbkcscUdBQXFHO0FBQ3JHLGdHQUFnRztBQUNoRyxrR0FBa0c7QUFDbEcsMEZBQTBGO0FBQzFGLFNBQWdCLGdCQUFnQixDQUFDLE1BQWtCO0lBQ2pELE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxTQUFTLEVBQXdELENBQUM7SUFFdEYsT0FBTyxJQUFJLENBQUMsTUFBTSxJQUFJLEVBQUUsQ0FBQztBQUMzQixDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILFNBQXdCLHVCQUF1QixDQUFDLEVBQzlDLFFBQVEsRUFDUixLQUFLLEVBQ0wsZUFBZSxFQUNmLFNBQVMsR0FDZ0I7SUFDekIsTUFBTSxNQUFNLEdBQUcsSUFBQSxzQ0FBdUIsRUFBQztRQUNyQyxHQUFHLEVBQUUsUUFBUTtRQUNiLEtBQUs7UUFDTCxlQUFlO1FBQ2YsYUFBYSxFQUFFLElBQUEscUNBQXdCLEVBQUMsS0FBSyxFQUFFLFFBQVEsRUFBRSxTQUFTLENBQUM7S0FDcEUsQ0FBQyxDQUFDO0lBRUgsT0FBTztRQUNMLFVBQVUsRUFBRSxDQUFDLEVBQUUsVUFBVSxFQUFFLFVBQVUsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLEVBQUUsRUFBRSxDQUM5RCxNQUFNLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxVQUFVLEVBQUUsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLENBQUM7S0FDNUUsQ0FBQztBQUNKLENBQUMifQ==
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export { default as AiProxyTimeoutError } from './ai-proxy-timeout-error';
|
|
2
|
+
export interface AiProxyClientOptions {
|
|
3
|
+
forestServerUrl: string;
|
|
4
|
+
timeoutMs: number;
|
|
5
|
+
}
|
|
6
|
+
export interface AiQueryParams {
|
|
7
|
+
saasAccessToken: string;
|
|
8
|
+
environmentId?: number;
|
|
9
|
+
renderingId: number;
|
|
10
|
+
body: unknown;
|
|
11
|
+
}
|
|
12
|
+
export interface AiProxyResponse {
|
|
13
|
+
status: number;
|
|
14
|
+
body: unknown;
|
|
15
|
+
isJson: boolean;
|
|
16
|
+
unparseableBodyError?: unknown;
|
|
17
|
+
}
|
|
18
|
+
export default class AiProxyClient {
|
|
19
|
+
private readonly forestServerUrl;
|
|
20
|
+
private readonly timeoutMs;
|
|
21
|
+
constructor({ forestServerUrl, timeoutMs }: AiProxyClientOptions);
|
|
22
|
+
private url;
|
|
23
|
+
private static headers;
|
|
24
|
+
query(params: AiQueryParams): Promise<AiProxyResponse>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=ai-proxy-client.d.ts.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AiProxyTimeoutError = void 0;
|
|
7
|
+
const ai_proxy_timeout_error_1 = __importDefault(require("./ai-proxy-timeout-error"));
|
|
8
|
+
var ai_proxy_timeout_error_2 = require("./ai-proxy-timeout-error");
|
|
9
|
+
Object.defineProperty(exports, "AiProxyTimeoutError", { enumerable: true, get: function () { return __importDefault(ai_proxy_timeout_error_2).default; } });
|
|
10
|
+
const AI_NAME = 'zendesk';
|
|
11
|
+
const AI_QUERY_PATH = '/api/ai-proxy/ai-query';
|
|
12
|
+
const JSON_CONTENT_TYPE = 'application/json';
|
|
13
|
+
const TIMEOUT_ERROR_NAME = 'TimeoutError';
|
|
14
|
+
function isTimeout(error) {
|
|
15
|
+
if (typeof error !== 'object' || error === null)
|
|
16
|
+
return false;
|
|
17
|
+
return error.name === TIMEOUT_ERROR_NAME;
|
|
18
|
+
}
|
|
19
|
+
class AiProxyClient {
|
|
20
|
+
constructor({ forestServerUrl, timeoutMs }) {
|
|
21
|
+
this.forestServerUrl = forestServerUrl;
|
|
22
|
+
this.timeoutMs = timeoutMs;
|
|
23
|
+
}
|
|
24
|
+
url() {
|
|
25
|
+
const url = new URL(AI_QUERY_PATH, this.forestServerUrl);
|
|
26
|
+
url.searchParams.set('ai-name', AI_NAME);
|
|
27
|
+
return url.toString();
|
|
28
|
+
}
|
|
29
|
+
static headers({ saasAccessToken, environmentId, renderingId }) {
|
|
30
|
+
const headers = {
|
|
31
|
+
Authorization: `Bearer ${saasAccessToken}`,
|
|
32
|
+
'Content-Type': JSON_CONTENT_TYPE,
|
|
33
|
+
'forest-rendering-id': String(renderingId),
|
|
34
|
+
};
|
|
35
|
+
if (environmentId !== undefined)
|
|
36
|
+
headers['forest-environment-id'] = String(environmentId);
|
|
37
|
+
return headers;
|
|
38
|
+
}
|
|
39
|
+
async query(params) {
|
|
40
|
+
let response;
|
|
41
|
+
try {
|
|
42
|
+
response = await fetch(this.url(), {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: AiProxyClient.headers(params),
|
|
45
|
+
body: JSON.stringify(params.body),
|
|
46
|
+
signal: AbortSignal.timeout(this.timeoutMs),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (isTimeout(error))
|
|
51
|
+
throw new ai_proxy_timeout_error_1.default(error);
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
const contentType = response.headers.get('content-type')?.toLowerCase() ?? '';
|
|
55
|
+
const declaresJson = contentType.includes(JSON_CONTENT_TYPE);
|
|
56
|
+
if (!declaresJson)
|
|
57
|
+
return { status: response.status, body: undefined, isJson: false };
|
|
58
|
+
try {
|
|
59
|
+
return { status: response.status, body: await response.json(), isJson: true };
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (isTimeout(error))
|
|
63
|
+
throw new ai_proxy_timeout_error_1.default(error);
|
|
64
|
+
return {
|
|
65
|
+
status: response.status,
|
|
66
|
+
body: undefined,
|
|
67
|
+
isJson: false,
|
|
68
|
+
unparseableBodyError: error,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.default = AiProxyClient;
|
|
74
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWktcHJveHktY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FpL2FpLXByb3h5LWNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxzRkFBMkQ7QUFFM0QsbUVBQTBFO0FBQWpFLDhJQUFBLE9BQU8sT0FBdUI7QUFFdkMsTUFBTSxPQUFPLEdBQUcsU0FBUyxDQUFDO0FBQzFCLE1BQU0sYUFBYSxHQUFHLHdCQUF3QixDQUFDO0FBQy9DLE1BQU0saUJBQWlCLEdBQUcsa0JBQWtCLENBQUM7QUFDN0MsTUFBTSxrQkFBa0IsR0FBRyxjQUFjLENBQUM7QUFFMUMsU0FBUyxTQUFTLENBQUMsS0FBYztJQUMvQixJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxLQUFLLEtBQUssSUFBSTtRQUFFLE9BQU8sS0FBSyxDQUFDO0lBRTlELE9BQVEsS0FBNEIsQ0FBQyxJQUFJLEtBQUssa0JBQWtCLENBQUM7QUFDbkUsQ0FBQztBQXFCRCxNQUFxQixhQUFhO0lBSWhDLFlBQVksRUFBRSxlQUFlLEVBQUUsU0FBUyxFQUF3QjtRQUM5RCxJQUFJLENBQUMsZUFBZSxHQUFHLGVBQWUsQ0FBQztRQUN2QyxJQUFJLENBQUMsU0FBUyxHQUFHLFNBQVMsQ0FBQztJQUM3QixDQUFDO0lBRU8sR0FBRztRQUNULE1BQU0sR0FBRyxHQUFHLElBQUksR0FBRyxDQUFDLGFBQWEsRUFBRSxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7UUFDekQsR0FBRyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsU0FBUyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBRXpDLE9BQU8sR0FBRyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQ3hCLENBQUM7SUFFTyxNQUFNLENBQUMsT0FBTyxDQUFDLEVBQUUsZUFBZSxFQUFFLGFBQWEsRUFBRSxXQUFXLEVBQWlCO1FBQ25GLE1BQU0sT0FBTyxHQUEyQjtZQUN0QyxhQUFhLEVBQUUsVUFBVSxlQUFlLEVBQUU7WUFDMUMsY0FBYyxFQUFFLGlCQUFpQjtZQUNqQyxxQkFBcUIsRUFBRSxNQUFNLENBQUMsV0FBVyxDQUFDO1NBQzNDLENBQUM7UUFFRixJQUFJLGFBQWEsS0FBSyxTQUFTO1lBQUUsT0FBTyxDQUFDLHVCQUF1QixDQUFDLEdBQUcsTUFBTSxDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBRTFGLE9BQU8sT0FBTyxDQUFDO0lBQ2pCLENBQUM7SUFFRCxLQUFLLENBQUMsS0FBSyxDQUFDLE1BQXFCO1FBQy9CLElBQUksUUFBa0IsQ0FBQztRQUV2QixJQUFJLENBQUM7WUFDSCxRQUFRLEdBQUcsTUFBTSxLQUFLLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxFQUFFO2dCQUNqQyxNQUFNLEVBQUUsTUFBTTtnQkFDZCxPQUFPLEVBQUUsYUFBYSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUM7Z0JBQ3RDLElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUM7Z0JBQ2pDLE1BQU0sRUFBRSxXQUFXLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUM7YUFDNUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixJQUFJLFNBQVMsQ0FBQyxLQUFLLENBQUM7Z0JBQUUsTUFBTSxJQUFJLGdDQUFtQixDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQzNELE1BQU0sS0FBSyxDQUFDO1FBQ2QsQ0FBQztRQUVELE1BQU0sV0FBVyxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLGNBQWMsQ0FBQyxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsQ0FBQztRQUM5RSxNQUFNLFlBQVksR0FBRyxXQUFXLENBQUMsUUFBUSxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFFN0QsSUFBSSxDQUFDLFlBQVk7WUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLFFBQVEsQ0FBQyxNQUFNLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLENBQUM7UUFFdEYsSUFBSSxDQUFDO1lBQ0gsT0FBTyxFQUFFLE1BQU0sRUFBRSxRQUFRLENBQUMsTUFBTSxFQUFFLElBQUksRUFBRSxNQUFNLFFBQVEsQ0FBQyxJQUFJLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLENBQUM7UUFDaEYsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixJQUFJLFNBQVMsQ0FBQyxLQUFLLENBQUM7Z0JBQUUsTUFBTSxJQUFJLGdDQUFtQixDQUFDLEtBQUssQ0FBQyxDQUFDO1lBRTNELE9BQU87Z0JBQ0wsTUFBTSxFQUFFLFFBQVEsQ0FBQyxNQUFNO2dCQUN2QixJQUFJLEVBQUUsU0FBUztnQkFDZixNQUFNLEVBQUUsS0FBSztnQkFDYixvQkFBb0IsRUFBRSxLQUFLO2FBQzVCLENBQUM7UUFDSixDQUFDO0lBQ0gsQ0FBQztDQUNGO0FBN0RELGdDQTZEQyJ9
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class AiProxyTimeoutError extends Error {
|
|
4
|
+
constructor(cause) {
|
|
5
|
+
super('The Forest server did not respond in time');
|
|
6
|
+
this.name = 'AiProxyTimeoutError';
|
|
7
|
+
this.cause = cause;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
exports.default = AiProxyTimeoutError;
|
|
11
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWktcHJveHktdGltZW91dC1lcnJvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9haS9haS1wcm94eS10aW1lb3V0LWVycm9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsTUFBcUIsbUJBQW9CLFNBQVEsS0FBSztJQUdwRCxZQUFZLEtBQWU7UUFDekIsS0FBSyxDQUFDLDJDQUEyQyxDQUFDLENBQUM7UUFDbkQsSUFBSSxDQUFDLElBQUksR0FBRyxxQkFBcUIsQ0FBQztRQUNsQyxJQUFJLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztJQUNyQixDQUFDO0NBQ0Y7QUFSRCxzQ0FRQyJ9
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type AiProxyClient from './ai-proxy-client';
|
|
2
|
+
import type ForestServerClient from '../oauth/forest-server-client';
|
|
3
|
+
import type { SessionStore } from '../oauth/session-store';
|
|
4
|
+
import type { Logger } from '../ports/logger-port';
|
|
5
|
+
import type { Middleware } from 'koa';
|
|
6
|
+
export declare const AI_QUERY_ROUTE = "/agent/v1/ai/query";
|
|
7
|
+
export interface AiRoutesMiddlewareOptions {
|
|
8
|
+
client: AiProxyClient;
|
|
9
|
+
sessionStore: SessionStore;
|
|
10
|
+
serverClient: ForestServerClient;
|
|
11
|
+
environmentId?: number;
|
|
12
|
+
logger: Logger;
|
|
13
|
+
}
|
|
14
|
+
export default function createAiRoutesMiddleware({ client, sessionStore, serverClient, environmentId, logger, }: AiRoutesMiddlewareOptions): Middleware;
|
|
15
|
+
//# sourceMappingURL=ai-routes-middleware.d.ts.map
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AI_QUERY_ROUTE = void 0;
|
|
7
|
+
exports.default = createAiRoutesMiddleware;
|
|
8
|
+
const ai_proxy_client_1 = require("./ai-proxy-client");
|
|
9
|
+
const auth_mode_1 = require("../auth/auth-mode");
|
|
10
|
+
const bff_http_error_1 = require("../http/bff-http-error");
|
|
11
|
+
const bff_local_errors_1 = require("../http/bff-local-errors");
|
|
12
|
+
const oauth_error_1 = require("../oauth/oauth-error");
|
|
13
|
+
const session_lifecycle_1 = __importDefault(require("../oauth/session-lifecycle"));
|
|
14
|
+
exports.AI_QUERY_ROUTE = '/agent/v1/ai/query';
|
|
15
|
+
const JSON_CONTENT_TYPE = 'application/json';
|
|
16
|
+
const MAX_CAUSE_DEPTH = 3;
|
|
17
|
+
const MAX_CAUSE_MESSAGE = 512;
|
|
18
|
+
function requireSessionPrincipal(ctx) {
|
|
19
|
+
if (ctx.state.authMode === 'api-key')
|
|
20
|
+
throw (0, bff_local_errors_1.oauthRequired)();
|
|
21
|
+
const principal = ctx.state.principal;
|
|
22
|
+
if (!principal)
|
|
23
|
+
throw (0, bff_http_error_1.unauthorized)('No session for this request');
|
|
24
|
+
return principal;
|
|
25
|
+
}
|
|
26
|
+
function clamp(message) {
|
|
27
|
+
return message.length > MAX_CAUSE_MESSAGE ? `${message.slice(0, MAX_CAUSE_MESSAGE)}…` : message;
|
|
28
|
+
}
|
|
29
|
+
function describeFailure(error, depthLeft = MAX_CAUSE_DEPTH) {
|
|
30
|
+
if (!(error instanceof Error))
|
|
31
|
+
return clamp(String(error));
|
|
32
|
+
const { cause } = error;
|
|
33
|
+
const walkable = depthLeft > 0 && cause !== undefined;
|
|
34
|
+
const tail = walkable ? ` (cause: ${describeFailure(cause, depthLeft - 1)})` : '';
|
|
35
|
+
return `${error.name}: ${clamp(error.message)}${tail}`;
|
|
36
|
+
}
|
|
37
|
+
async function resolveSessionAccessToken(principal, store, serverClient, logger) {
|
|
38
|
+
try {
|
|
39
|
+
return await (0, session_lifecycle_1.default)({ sid: principal.sid, store, serverClient });
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (error instanceof oauth_error_1.OAuthRequestError && error.status >= 500) {
|
|
43
|
+
logger('Warn', 'AI query refused: the Forest server could not refresh the session', {
|
|
44
|
+
cause: describeFailure(error),
|
|
45
|
+
});
|
|
46
|
+
throw (0, bff_local_errors_1.upstreamUnreachable)();
|
|
47
|
+
}
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function relayJsonBodyOrGenericError(ctx, response, logger) {
|
|
52
|
+
const bodyWouldLeakInfrastructure = response.status >= 500 || !response.isJson;
|
|
53
|
+
if (bodyWouldLeakInfrastructure) {
|
|
54
|
+
logger('Warn', 'AI proxy upstream body withheld; client message is generic', {
|
|
55
|
+
status: response.status,
|
|
56
|
+
isJson: response.isJson,
|
|
57
|
+
...(response.unparseableBodyError !== undefined
|
|
58
|
+
? { cause: describeFailure(response.unparseableBodyError) }
|
|
59
|
+
: {}),
|
|
60
|
+
});
|
|
61
|
+
throw (0, bff_local_errors_1.upstreamError)(response.status);
|
|
62
|
+
}
|
|
63
|
+
ctx.status = response.status;
|
|
64
|
+
ctx.type = JSON_CONTENT_TYPE;
|
|
65
|
+
ctx.body = JSON.stringify(response.body);
|
|
66
|
+
}
|
|
67
|
+
function createAiRoutesMiddleware({ client, sessionStore, serverClient, environmentId, logger, }) {
|
|
68
|
+
return async function aiRoutesMiddleware(ctx, next) {
|
|
69
|
+
if (ctx.path !== exports.AI_QUERY_ROUTE || ctx.method !== 'POST') {
|
|
70
|
+
await next();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const principal = requireSessionPrincipal(ctx);
|
|
74
|
+
const renderingId = (0, auth_mode_1.requireRenderingId)(principal);
|
|
75
|
+
const saasAccessToken = await resolveSessionAccessToken(principal, sessionStore, serverClient, logger);
|
|
76
|
+
let response;
|
|
77
|
+
try {
|
|
78
|
+
response = await client.query({
|
|
79
|
+
saasAccessToken,
|
|
80
|
+
environmentId,
|
|
81
|
+
renderingId,
|
|
82
|
+
body: ctx.request.body,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
logger('Warn', 'AI query failed against the Forest server', {
|
|
87
|
+
cause: describeFailure(error),
|
|
88
|
+
});
|
|
89
|
+
if (error instanceof ai_proxy_client_1.AiProxyTimeoutError)
|
|
90
|
+
throw (0, bff_local_errors_1.upstreamTimeout)();
|
|
91
|
+
throw (0, bff_local_errors_1.upstreamUnreachable)();
|
|
92
|
+
}
|
|
93
|
+
relayJsonBodyOrGenericError(ctx, response, logger);
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWktcm91dGVzLW1pZGRsZXdhcmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWkvYWktcm91dGVzLW1pZGRsZXdhcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBc0dBLDJDQTZDQztBQTNJRCx1REFBd0Q7QUFDeEQsaURBQXVEO0FBQ3ZELDJEQUFzRDtBQUN0RCwrREFLa0M7QUFDbEMsc0RBQXlEO0FBQ3pELG1GQUFpRTtBQUVwRCxRQUFBLGNBQWMsR0FBRyxvQkFBb0IsQ0FBQztBQUVuRCxNQUFNLGlCQUFpQixHQUFHLGtCQUFrQixDQUFDO0FBQzdDLE1BQU0sZUFBZSxHQUFHLENBQUMsQ0FBQztBQUMxQixNQUFNLGlCQUFpQixHQUFHLEdBQUcsQ0FBQztBQVU5QixTQUFTLHVCQUF1QixDQUFDLEdBQVk7SUFDM0MsSUFBSSxHQUFHLENBQUMsS0FBSyxDQUFDLFFBQVEsS0FBSyxTQUFTO1FBQUUsTUFBTSxJQUFBLGdDQUFhLEdBQUUsQ0FBQztJQUU1RCxNQUFNLFNBQVMsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLFNBQThDLENBQUM7SUFDM0UsSUFBSSxDQUFDLFNBQVM7UUFBRSxNQUFNLElBQUEsNkJBQVksRUFBQyw2QkFBNkIsQ0FBQyxDQUFDO0lBRWxFLE9BQU8sU0FBUyxDQUFDO0FBQ25CLENBQUM7QUFFRCxTQUFTLEtBQUssQ0FBQyxPQUFlO0lBQzVCLE9BQU8sT0FBTyxDQUFDLE1BQU0sR0FBRyxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQztBQUNsRyxDQUFDO0FBRUQsU0FBUyxlQUFlLENBQUMsS0FBYyxFQUFFLFNBQVMsR0FBRyxlQUFlO0lBQ2xFLElBQUksQ0FBQyxDQUFDLEtBQUssWUFBWSxLQUFLLENBQUM7UUFBRSxPQUFPLEtBQUssQ0FBQyxNQUFNLENBQUMs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
|
|
@@ -7,9 +7,8 @@ exports.AGENT_TOKEN_EXPIRES_IN = void 0;
|
|
|
7
7
|
exports.issueAgentToken = issueAgentToken;
|
|
8
8
|
exports.issueAgentTokenFromPrincipal = issueAgentTokenFromPrincipal;
|
|
9
9
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
10
|
-
const
|
|
10
|
+
const auth_mode_1 = require("../auth/auth-mode");
|
|
11
11
|
exports.AGENT_TOKEN_EXPIRES_IN = '5m';
|
|
12
|
-
const POSITIVE_INTEGER = /^[1-9]\d*$/;
|
|
13
12
|
function tagsToRecord(tags) {
|
|
14
13
|
return tags.reduce((memo, { key, value }) => ({ ...memo, [key]: value }), {});
|
|
15
14
|
}
|
|
@@ -40,19 +39,17 @@ function issueAgentToken({ identity, authSecret }) {
|
|
|
40
39
|
}, authSecret);
|
|
41
40
|
}
|
|
42
41
|
function issueAgentTokenFromPrincipal({ principal, authSecret, }) {
|
|
43
|
-
|
|
44
|
-
throw (0, bff_http_error_1.unauthorized)('The session carries no usable rendering');
|
|
45
|
-
}
|
|
42
|
+
const renderingId = (0, auth_mode_1.requireRenderingId)(principal);
|
|
46
43
|
return signWithSnakeCaseAliasesForRubyAndPythonAgents({
|
|
47
44
|
id: principal.id,
|
|
48
45
|
email: principal.email,
|
|
49
46
|
firstName: principal.first_name,
|
|
50
47
|
lastName: principal.last_name,
|
|
51
48
|
team: principal.team,
|
|
52
|
-
renderingId
|
|
49
|
+
renderingId,
|
|
53
50
|
tags: principal.tags ?? {},
|
|
54
51
|
permissionLevel: principal.permission_level,
|
|
55
52
|
role: principal.role,
|
|
56
53
|
}, authSecret);
|
|
57
54
|
}
|
|
58
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
55
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQtdG9rZW4uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXBpLWtleS9hZ2VudC10b2tlbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUF5REEsMENBZ0JDO0FBRUQsb0VBb0JDO0FBNUZELGdFQUF3QztBQUV4QyxpREFBdUQ7QUFFMUMsUUFBQSxzQkFBc0IsR0FBRyxJQUFJLENBQUM7QUFZM0MsU0FBUyxZQUFZLENBQUMsSUFBc0M7SUFDMUQsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsR0FBRyxFQUFFLEtBQUssRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDLEVBQUUsR0FBRyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQ2hGLENBQUM7QUFjRCxTQUFTLDhDQUE4QyxDQUNyRCxNQUF5QixFQUN6QixVQUFrQjtJQUVsQixNQUFNLFNBQVMsR0FBRyxNQUFNLENBQUMsU0FBUyxJQUFJLEVBQUUsQ0FBQztJQUN6QyxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsUUFBUSxJQUFJLEVBQUUsQ0FBQztJQUV2QyxPQUFPLHNCQUFZLENBQUMsSUFBSSxDQUN0QjtRQUNFLEdBQUcsTUFBTTtRQUNULFNBQVM7UUFDVCxRQUFRO1FBQ1IsVUFBVSxFQUFFLFNBQVM7UUFDckIsU0FBUyxFQUFFLFFBQVE7UUFDbkIsWUFBWSxFQUFFLE1BQU0sQ0FBQyxXQUFXO1FBQ2hDLGdCQUFnQixFQUFFLE1BQU0sQ0FBQyxlQUFlO0tBQ3pDLEVBQ0QsVUFBVSxFQUNWLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsOEJBQXNCLEVBQUUsQ0FDMUQsQ0FBQztBQUNKLENBQUM7QUFFRCxTQUFnQixlQUFlLENBQUMsRUFBRSxRQUFRLEVBQUUsVUFBVSxFQUF5QjtJQUM3RSxNQUFNLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxHQUFHLFFBQVEsQ0FBQztJQUV2QyxPQUFPLDhDQUE4QyxDQUNuRDtRQUNFLEVBQUUsRUFBRSxJQUFJLENBQUMsRUFBRTtRQUNYLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSztRQUNqQixTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVM7UUFDekIsUUFBUSxFQUFFLElBQUksQ0FBQyxRQUFRO1FBQ3ZCLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSTtRQUNmLFdBQVc7UUFDWCxJQUFJLEVBQUUsWUFBWSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUM7UUFDN0IsZUFBZSxFQUFFLElBQUksQ0FBQyxlQUFlO0tBQ3RDLEVBQ0QsVUFBVSxDQUNYLENBQUM7QUFDSixDQUFDO0FBRUQsU0FBZ0IsNEJBQTRCLENBQUMsRUFDM0MsU0FBUyxFQUNULFVBQVUsR0FDeUI7SUFDbkMsTUFBTSxXQUFXLEdBQUcsSUFBQSw4QkFBa0IsRUFBQyxTQUFTLENBQUMsQ0FBQztJQUVsRCxPQUFPLDhDQUE4QyxDQUNuRDtRQUNFLEVBQUUsRUFBRSxTQUFTLENBQUMsRUFBRTtRQUNoQixLQUFLLEVBQUUsU0FBUyxDQUFDLEtBQUs7UUFDdEIsU0FBUyxFQUFFLFNBQVMsQ0FBQyxVQUFVO1FBQy9CLFFBQVEsRUFBRSxTQUFTLENBQUMsU0FBUztRQUM3QixJQUFJLEVBQUUsU0FBUyxDQUFDLElBQUk7UUFDcEIsV0FBVztRQUNYLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSSxJQUFJLEVBQUU7UUFDMUIsZUFBZSxFQUFFLFNBQVMsQ0FBQyxnQkFBZ0I7UUFDM0MsSUFBSSxFQUFFLFNBQVMsQ0FBQyxJQUFJO0tBQ3JCLEVBQ0QsVUFBVSxDQUNYLENBQUM7QUFDSixDQUFDIn0=
|
package/dist/auth/auth-mode.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import type { BffAccessTokenPayload } from '../oauth/bff-token';
|
|
1
2
|
export type AuthMode = 'oauth' | 'api-key';
|
|
3
|
+
export declare function requireRenderingId(principal: BffAccessTokenPayload): number;
|
|
2
4
|
export declare function extractBearerToken(authorization: string | undefined): string | undefined;
|
|
3
5
|
export declare function resolveAuthMode({ hasBearer, hasApiKey, }: {
|
|
4
6
|
hasBearer: boolean;
|
package/dist/auth/auth-mode.js
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.requireRenderingId = requireRenderingId;
|
|
3
4
|
exports.extractBearerToken = extractBearerToken;
|
|
4
5
|
exports.resolveAuthMode = resolveAuthMode;
|
|
5
6
|
const bff_http_error_1 = require("../http/bff-http-error");
|
|
6
7
|
const BEARER_PATTERN = /^Bearer[ \t]+(.+)$/i;
|
|
8
|
+
const POSITIVE_INTEGER = /^[1-9]\d*$/;
|
|
9
|
+
function requireRenderingId(principal) {
|
|
10
|
+
if (!POSITIVE_INTEGER.test(String(principal.rendering_id))) {
|
|
11
|
+
throw (0, bff_http_error_1.unauthorized)('The session carries no usable rendering');
|
|
12
|
+
}
|
|
13
|
+
return Number(principal.rendering_id);
|
|
14
|
+
}
|
|
7
15
|
function extractBearerToken(authorization) {
|
|
8
16
|
if (!authorization)
|
|
9
17
|
return undefined;
|
|
@@ -22,4 +30,4 @@ function resolveAuthMode({ hasBearer, hasApiKey, }) {
|
|
|
22
30
|
return 'oauth';
|
|
23
31
|
throw (0, bff_http_error_1.unauthorized)();
|
|
24
32
|
}
|
|
25
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
33
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aC1tb2RlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2F1dGgvYXV0aC1tb2RlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBU0EsZ0RBTUM7QUFFRCxnREFTQztBQUVELDBDQVlDO0FBdENELDJEQUE0RTtBQUk1RSxNQUFNLGNBQWMsR0FBRyxxQkFBcUIsQ0FBQztBQUM3QyxNQUFNLGdCQUFnQixHQUFHLFlBQVksQ0FBQztBQUV0QyxTQUFnQixrQkFBa0IsQ0FBQyxTQUFnQztJQUNqRSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsWUFBWSxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQzNELE1BQU0sSUFBQSw2QkFBWSxFQUFDLHlDQUF5QyxDQUFDLENBQUM7SUFDaEUsQ0FBQztJQUVELE9BQU8sTUFBTSxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsQ0FBQztBQUN4QyxDQUFDO0FBRUQsU0FBZ0Isa0JBQWtCLENBQUMsYUFBaUM7SUFDbEUsSUFBSSxDQUFDLGFBQWE7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUVyQyxNQUFNLEtBQUssR0FBRyxjQUFjLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3hELElBQUksQ0FBQyxLQUFLO1FBQUUsT0FBTyxTQUFTLENBQUM7SUFFN0IsTUFBTSxLQUFLLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO0lBRTlCLE9BQU8sS0FBSyxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7QUFDMUMsQ0FBQztBQUVELFNBQWdCLGVBQWUsQ0FBQyxFQUM5QixTQUFTLEVBQ1QsU0FBUyxHQUlWO0lBQ0MsSUFBSSxTQUFTLElBQUksU0FBUztRQUFFLE1BQU0sSUFBQSxxQ0FBb0IsR0FBRSxDQUFDO0lBQ3pELElBQUksU0FBUztRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQ2hDLElBQUksU0FBUztRQUFFLE9BQU8sT0FBTyxDQUFDO0lBRTlCLE1BQU0sSUFBQSw2QkFBWSxHQUFFLENBQUM7QUFDdkIsQ0FBQyJ9
|
package/dist/cli-core.d.ts
CHANGED
|
@@ -2,7 +2,16 @@ import type { BFFConfig } from './config/env-config';
|
|
|
2
2
|
import type { UnfoldSource } from './openapi/unfolded-document';
|
|
3
3
|
import type { Logger } from './ports/logger-port';
|
|
4
4
|
import BFFHttpServer from './http/bff-http-server';
|
|
5
|
+
interface ResolvedOAuthConfig {
|
|
6
|
+
forestServerUrl: string;
|
|
7
|
+
forestEnvSecret: string;
|
|
8
|
+
forestAppUrl: string;
|
|
9
|
+
forestAuthSecret: string;
|
|
10
|
+
tokenEncryptionKey: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function resolveOAuthConfig(config: BFFConfig): ResolvedOAuthConfig | undefined;
|
|
5
13
|
export declare function resolveUnfoldSource(config: BFFConfig, logger: Logger): UnfoldSource | undefined;
|
|
6
14
|
export default function runCli(env: NodeJS.ProcessEnv, logger?: Logger): Promise<BFFHttpServer>;
|
|
7
15
|
export declare function reportFatalError(err: unknown): void;
|
|
16
|
+
export {};
|
|
8
17
|
//# sourceMappingURL=cli-core.d.ts.map
|