@pipeline-builder/api-core 0.0.1
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/LICENSE +202 -0
- package/README.md +98 -0
- package/lib/constants/ai-providers.d.ts +52 -0
- package/lib/constants/ai-providers.js +129 -0
- package/lib/constants/http-status.d.ts +20 -0
- package/lib/constants/http-status.js +26 -0
- package/lib/constants/index.d.ts +3 -0
- package/lib/constants/index.js +6 -0
- package/lib/constants/time.d.ts +10 -0
- package/lib/constants/time.js +13 -0
- package/lib/errors/app-errors.d.ts +30 -0
- package/lib/errors/app-errors.js +53 -0
- package/lib/errors/index.d.ts +1 -0
- package/lib/errors/index.js +4 -0
- package/lib/helpers/access-helpers.d.ts +63 -0
- package/lib/helpers/access-helpers.js +82 -0
- package/lib/helpers/crud-helpers.d.ts +9 -0
- package/lib/helpers/crud-helpers.js +23 -0
- package/lib/helpers/index.d.ts +6 -0
- package/lib/helpers/index.js +9 -0
- package/lib/helpers/org-hierarchy-http.d.ts +74 -0
- package/lib/helpers/org-hierarchy-http.js +144 -0
- package/lib/helpers/org-hierarchy.d.ts +57 -0
- package/lib/helpers/org-hierarchy.js +110 -0
- package/lib/helpers/restore-helpers.d.ts +49 -0
- package/lib/helpers/restore-helpers.js +78 -0
- package/lib/helpers/sse-helpers.d.ts +20 -0
- package/lib/helpers/sse-helpers.js +45 -0
- package/lib/index.d.ts +58 -0
- package/lib/index.js +71 -0
- package/lib/middleware/auth.d.ts +208 -0
- package/lib/middleware/auth.js +588 -0
- package/lib/middleware/index.d.ts +3 -0
- package/lib/middleware/index.js +6 -0
- package/lib/middleware/mongo-sanitize.d.ts +32 -0
- package/lib/middleware/mongo-sanitize.js +100 -0
- package/lib/middleware/step-up.d.ts +45 -0
- package/lib/middleware/step-up.js +138 -0
- package/lib/openapi/extend-zod.d.ts +1 -0
- package/lib/openapi/extend-zod.js +6 -0
- package/lib/openapi/index.d.ts +2 -0
- package/lib/openapi/index.js +5 -0
- package/lib/openapi/registry.d.ts +17 -0
- package/lib/openapi/registry.js +38 -0
- package/lib/openapi/routes/ask-routes.d.ts +7 -0
- package/lib/openapi/routes/ask-routes.js +62 -0
- package/lib/openapi/routes/billing-routes.d.ts +1 -0
- package/lib/openapi/routes/billing-routes.js +66 -0
- package/lib/openapi/routes/image-registry-routes.d.ts +7 -0
- package/lib/openapi/routes/image-registry-routes.js +98 -0
- package/lib/openapi/routes/index.d.ts +5 -0
- package/lib/openapi/routes/index.js +28 -0
- package/lib/openapi/routes/message-routes.d.ts +1 -0
- package/lib/openapi/routes/message-routes.js +105 -0
- package/lib/openapi/routes/pipeline-routes.d.ts +1 -0
- package/lib/openapi/routes/pipeline-routes.js +105 -0
- package/lib/openapi/routes/pipeline-template-routes.d.ts +1 -0
- package/lib/openapi/routes/pipeline-template-routes.js +61 -0
- package/lib/openapi/routes/plugin-routes.d.ts +1 -0
- package/lib/openapi/routes/plugin-routes.js +96 -0
- package/lib/openapi/routes/quota-routes.d.ts +1 -0
- package/lib/openapi/routes/quota-routes.js +62 -0
- package/lib/openapi/schema-registry.d.ts +25 -0
- package/lib/openapi/schema-registry.js +91 -0
- package/lib/routes/health.d.ts +41 -0
- package/lib/routes/health.js +82 -0
- package/lib/routes/index.d.ts +1 -0
- package/lib/routes/index.js +4 -0
- package/lib/services/audit-spool.d.ts +80 -0
- package/lib/services/audit-spool.js +172 -0
- package/lib/services/cache-service.d.ts +124 -0
- package/lib/services/cache-service.js +276 -0
- package/lib/services/circuit-breaker.d.ts +45 -0
- package/lib/services/circuit-breaker.js +138 -0
- package/lib/services/compliance-client.d.ts +46 -0
- package/lib/services/compliance-client.js +128 -0
- package/lib/services/compliance-event-subscriber.d.ts +16 -0
- package/lib/services/compliance-event-subscriber.js +72 -0
- package/lib/services/durable-event-bus.d.ts +96 -0
- package/lib/services/durable-event-bus.js +204 -0
- package/lib/services/entity-events.d.ts +48 -0
- package/lib/services/entity-events.js +79 -0
- package/lib/services/env-redis.d.ts +44 -0
- package/lib/services/env-redis.js +94 -0
- package/lib/services/http-client.d.ts +143 -0
- package/lib/services/http-client.js +335 -0
- package/lib/services/index.d.ts +18 -0
- package/lib/services/index.js +20 -0
- package/lib/services/leader-lock.d.ts +45 -0
- package/lib/services/leader-lock.js +85 -0
- package/lib/services/quota.d.ts +130 -0
- package/lib/services/quota.js +264 -0
- package/lib/services/remote-audit-client.d.ts +160 -0
- package/lib/services/remote-audit-client.js +390 -0
- package/lib/services/retry-strategy.d.ts +74 -0
- package/lib/services/retry-strategy.js +118 -0
- package/lib/services/scheduler.d.ts +41 -0
- package/lib/services/scheduler.js +86 -0
- package/lib/services/service-boot.d.ts +20 -0
- package/lib/services/service-boot.js +28 -0
- package/lib/services/sse-ticket-store.d.ts +51 -0
- package/lib/services/sse-ticket-store.js +130 -0
- package/lib/services/token-revocation.d.ts +52 -0
- package/lib/services/token-revocation.js +120 -0
- package/lib/testing/tier-mock.d.ts +44 -0
- package/lib/testing/tier-mock.js +65 -0
- package/lib/types/audit-events.d.ts +69 -0
- package/lib/types/audit-events.js +4 -0
- package/lib/types/catalog-metadata.d.ts +36 -0
- package/lib/types/catalog-metadata.js +21 -0
- package/lib/types/common.d.ts +255 -0
- package/lib/types/common.js +41 -0
- package/lib/types/error-codes.d.ts +54 -0
- package/lib/types/error-codes.js +107 -0
- package/lib/types/feature-flags.d.ts +36 -0
- package/lib/types/feature-flags.js +128 -0
- package/lib/types/http.d.ts +27 -0
- package/lib/types/http.js +4 -0
- package/lib/types/index.d.ts +11 -0
- package/lib/types/index.js +14 -0
- package/lib/types/permissions.d.ts +65 -0
- package/lib/types/permissions.js +131 -0
- package/lib/types/pipeline-template.d.ts +30 -0
- package/lib/types/pipeline-template.js +13 -0
- package/lib/types/pipeline.d.ts +59 -0
- package/lib/types/pipeline.js +31 -0
- package/lib/types/quota-tiers.d.ts +79 -0
- package/lib/types/quota-tiers.js +221 -0
- package/lib/types/visibility.d.ts +20 -0
- package/lib/types/visibility.js +22 -0
- package/lib/utils/alias-resolver.d.ts +37 -0
- package/lib/utils/alias-resolver.js +68 -0
- package/lib/utils/audit.d.ts +15 -0
- package/lib/utils/audit.js +39 -0
- package/lib/utils/aws-scrub.d.ts +11 -0
- package/lib/utils/aws-scrub.js +67 -0
- package/lib/utils/compliance-attributes.d.ts +6 -0
- package/lib/utils/compliance-attributes.js +69 -0
- package/lib/utils/concurrency.d.ts +9 -0
- package/lib/utils/concurrency.js +29 -0
- package/lib/utils/crash-handlers.d.ts +18 -0
- package/lib/utils/crash-handlers.js +36 -0
- package/lib/utils/env.d.ts +28 -0
- package/lib/utils/env.js +54 -0
- package/lib/utils/headers.d.ts +18 -0
- package/lib/utils/headers.js +21 -0
- package/lib/utils/identity.d.ts +40 -0
- package/lib/utils/identity.js +51 -0
- package/lib/utils/index.d.ts +17 -0
- package/lib/utils/index.js +20 -0
- package/lib/utils/logger.d.ts +38 -0
- package/lib/utils/logger.js +174 -0
- package/lib/utils/metric-emitter.d.ts +36 -0
- package/lib/utils/metric-emitter.js +32 -0
- package/lib/utils/object.d.ts +13 -0
- package/lib/utils/object.js +18 -0
- package/lib/utils/params.d.ts +150 -0
- package/lib/utils/params.js +206 -0
- package/lib/utils/response.d.ts +117 -0
- package/lib/utils/response.js +223 -0
- package/lib/utils/safe-require.d.ts +13 -0
- package/lib/utils/safe-require.js +19 -0
- package/lib/utils/secret-encryption.d.ts +176 -0
- package/lib/utils/secret-encryption.js +374 -0
- package/lib/utils/ssrf.d.ts +30 -0
- package/lib/utils/ssrf.js +106 -0
- package/lib/validation/ai-schemas.d.ts +56 -0
- package/lib/validation/ai-schemas.js +78 -0
- package/lib/validation/common-schemas.d.ts +133 -0
- package/lib/validation/common-schemas.js +94 -0
- package/lib/validation/index.d.ts +7 -0
- package/lib/validation/index.js +10 -0
- package/lib/validation/message-schemas.d.ts +160 -0
- package/lib/validation/message-schemas.js +156 -0
- package/lib/validation/middleware.d.ts +72 -0
- package/lib/validation/middleware.js +65 -0
- package/lib/validation/pipeline-schemas.d.ts +181 -0
- package/lib/validation/pipeline-schemas.js +101 -0
- package/lib/validation/pipeline-template-schemas.d.ts +155 -0
- package/lib/validation/pipeline-template-schemas.js +66 -0
- package/lib/validation/plugin-schemas.d.ts +171 -0
- package/lib/validation/plugin-schemas.js +81 -0
- package/openapi.yaml +292 -0
- package/package.json +167 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
export * from './auth.js';
|
|
4
|
+
export * from './mongo-sanitize.js';
|
|
5
|
+
export * from './step-up.js';
|
|
6
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbWlkZGxld2FyZS9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLGNBQWMsV0FBVyxDQUFDO0FBQzFCLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyxjQUFjLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuZXhwb3J0ICogZnJvbSAnLi9hdXRoLmpzJztcbmV4cG9ydCAqIGZyb20gJy4vbW9uZ28tc2FuaXRpemUuanMnO1xuZXhwb3J0ICogZnJvbSAnLi9zdGVwLXVwLmpzJztcbiJdfQ==
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
2
|
+
/** Maximum nesting depth accepted by {@link mongoSanitize}. */
|
|
3
|
+
export declare const MAX_SANITIZE_DEPTH = 10;
|
|
4
|
+
/**
|
|
5
|
+
* Thrown when a request payload nests deeper than {@link MAX_SANITIZE_DEPTH}.
|
|
6
|
+
* The middleware maps this to HTTP 400 instead of silently truncating the walk
|
|
7
|
+
* (which would let operator-shaped keys below the cap slip through unsanitized).
|
|
8
|
+
*/
|
|
9
|
+
export declare class PayloadTooDeepError extends Error {
|
|
10
|
+
readonly statusCode: 400;
|
|
11
|
+
constructor(message?: string);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* MongoDB operator-injection middleware.
|
|
15
|
+
*
|
|
16
|
+
* Recursively strips object keys starting with `$` (Mongo operators like
|
|
17
|
+
* `$ne`, `$gt`, `$where`) and keys containing `.` (dot-walks) from
|
|
18
|
+
* `req.body`, `req.query`, and `req.params`. Without this, a JSON request
|
|
19
|
+
* like `{"email": {"$ne": null}}` against a Mongo-backed service can match
|
|
20
|
+
* any document — Zod catches the wrong *type* but won't reach into nested
|
|
21
|
+
* objects to strip operator-shaped keys.
|
|
22
|
+
*
|
|
23
|
+
* Apply BEFORE any Mongo query handler (typically right after
|
|
24
|
+
* `express.json()`). Postgres-backed services don't need it.
|
|
25
|
+
*
|
|
26
|
+
* `req.body` and `req.params` are plain writable objects, so they're sanitized
|
|
27
|
+
* in place. On Express 5 `req.query` is a GETTER that re-parses the URL and
|
|
28
|
+
* returns a fresh object on every access — mutating it is a no-op — so we
|
|
29
|
+
* sanitize a snapshot and pin it onto the request instance with
|
|
30
|
+
* `Object.defineProperty`, shadowing the getter for all downstream reads.
|
|
31
|
+
*/
|
|
32
|
+
export declare function mongoSanitize(): (req: Request, res: Response, next: NextFunction) => void;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { HttpStatus } from '../constants/http-status.js';
|
|
4
|
+
import { ErrorCode } from '../types/error-codes.js';
|
|
5
|
+
import { sendError } from '../utils/response.js';
|
|
6
|
+
/** Maximum nesting depth accepted by {@link mongoSanitize}. */
|
|
7
|
+
export const MAX_SANITIZE_DEPTH = 10;
|
|
8
|
+
/**
|
|
9
|
+
* Thrown when a request payload nests deeper than {@link MAX_SANITIZE_DEPTH}.
|
|
10
|
+
* The middleware maps this to HTTP 400 instead of silently truncating the walk
|
|
11
|
+
* (which would let operator-shaped keys below the cap slip through unsanitized).
|
|
12
|
+
*/
|
|
13
|
+
export class PayloadTooDeepError extends Error {
|
|
14
|
+
statusCode = HttpStatus.BAD_REQUEST;
|
|
15
|
+
constructor(message = `Request payload nesting exceeds the maximum depth of ${MAX_SANITIZE_DEPTH}`) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = 'PayloadTooDeepError';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* MongoDB operator-injection middleware.
|
|
22
|
+
*
|
|
23
|
+
* Recursively strips object keys starting with `$` (Mongo operators like
|
|
24
|
+
* `$ne`, `$gt`, `$where`) and keys containing `.` (dot-walks) from
|
|
25
|
+
* `req.body`, `req.query`, and `req.params`. Without this, a JSON request
|
|
26
|
+
* like `{"email": {"$ne": null}}` against a Mongo-backed service can match
|
|
27
|
+
* any document — Zod catches the wrong *type* but won't reach into nested
|
|
28
|
+
* objects to strip operator-shaped keys.
|
|
29
|
+
*
|
|
30
|
+
* Apply BEFORE any Mongo query handler (typically right after
|
|
31
|
+
* `express.json()`). Postgres-backed services don't need it.
|
|
32
|
+
*
|
|
33
|
+
* `req.body` and `req.params` are plain writable objects, so they're sanitized
|
|
34
|
+
* in place. On Express 5 `req.query` is a GETTER that re-parses the URL and
|
|
35
|
+
* returns a fresh object on every access — mutating it is a no-op — so we
|
|
36
|
+
* sanitize a snapshot and pin it onto the request instance with
|
|
37
|
+
* `Object.defineProperty`, shadowing the getter for all downstream reads.
|
|
38
|
+
*/
|
|
39
|
+
export function mongoSanitize() {
|
|
40
|
+
return (req, res, next) => {
|
|
41
|
+
try {
|
|
42
|
+
if (req.body && typeof req.body === 'object')
|
|
43
|
+
sanitizeObject(req.body);
|
|
44
|
+
if (req.params && typeof req.params === 'object')
|
|
45
|
+
sanitizeObject(req.params);
|
|
46
|
+
if (req.query && typeof req.query === 'object') {
|
|
47
|
+
const q = req.query;
|
|
48
|
+
sanitizeObject(q);
|
|
49
|
+
// Pin the sanitized snapshot so the Express 5 getter can't re-parse a
|
|
50
|
+
// fresh (unsanitized) object on the next access.
|
|
51
|
+
try {
|
|
52
|
+
Object.defineProperty(req, 'query', { value: q, writable: true, configurable: true, enumerable: true });
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Express 4 (writable data prop) — the in-place sanitize above sufficed.
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
// Reject over-deep payloads with a 400 rather than silently truncating
|
|
61
|
+
// the sanitize walk (which risked leaving deep operator keys in place).
|
|
62
|
+
if (err instanceof PayloadTooDeepError) {
|
|
63
|
+
return sendError(res, err.statusCode, err.message, ErrorCode.VALIDATION_ERROR);
|
|
64
|
+
}
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
next();
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// Prototype-pollution keys — these never belong in JSON request bodies, and a
|
|
71
|
+
// downstream recursive merge/assign on the parsed object could otherwise walk
|
|
72
|
+
// `constructor.prototype` to inject Mongo operators or pollute Object.prototype.
|
|
73
|
+
const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
74
|
+
function sanitizeObject(obj, depth = 0) {
|
|
75
|
+
// Reject pathologically nested input rather than silently returning — a
|
|
76
|
+
// silent cap would leave any operator/dot/prototype keys deeper than the
|
|
77
|
+
// cap un-sanitized. The middleware maps this to a 400.
|
|
78
|
+
if (depth > MAX_SANITIZE_DEPTH) {
|
|
79
|
+
throw new PayloadTooDeepError();
|
|
80
|
+
}
|
|
81
|
+
for (const key of Object.keys(obj)) {
|
|
82
|
+
if (key.startsWith('$') || key.includes('.') || FORBIDDEN_KEYS.has(key)) {
|
|
83
|
+
delete obj[key];
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const v = obj[key];
|
|
87
|
+
if (v && typeof v === 'object') {
|
|
88
|
+
if (Array.isArray(v)) {
|
|
89
|
+
for (const item of v) {
|
|
90
|
+
if (item && typeof item === 'object')
|
|
91
|
+
sanitizeObject(item, depth + 1);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
sanitizeObject(v, depth + 1);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9uZ28tc2FuaXRpemUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbWlkZGxld2FyZS9tb25nby1zYW5pdGl6ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBR3RDLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUN6RCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDcEQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBRWpELCtEQUErRDtBQUMvRCxNQUFNLENBQUMsTUFBTSxrQkFBa0IsR0FBRyxFQUFFLENBQUM7QUFFckM7Ozs7R0FJRztBQUNILE1BQU0sT0FBTyxtQkFBb0IsU0FBUSxLQUFLO0lBQ25DLFVBQVUsR0FBRyxVQUFVLENBQUMsV0FBVyxDQUFDO0lBQzdDLFlBQVksT0FBTyxHQUFHLHdEQUF3RCxrQkFBa0IsRUFBRTtRQUNoRyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDZixJQUFJLENBQUMsSUFBSSxHQUFHLHFCQUFxQixDQUFDO0lBQ3BDLENBQUM7Q0FDRjtBQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FrQkc7QUFDSCxNQUFNLFVBQVUsYUFBYTtJQUMzQixPQUFPLENBQUMsR0FBWSxFQUFFLEdBQWEsRUFBRSxJQUFrQixFQUFFLEVBQUU7UUFDekQsSUFBSSxDQUFDO1lBQ0gsSUFBSSxHQUFHLENBQUMsSUFBSSxJQUFJLE9BQU8sR0FBRyxDQUFDLElBQUksS0FBSyxRQUFRO2dCQUFFLGNBQWMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDdkUsSUFBSSxHQUFHLENBQUMsTUFBTSxJQUFJLE9BQU8sR0FBRyxDQUFDLE1BQU0sS0FBSyxRQUFRO2dCQUFFLGNBQWMsQ0FBQyxHQUFHLENBQUMsTUFBaUMsQ0FBQyxDQUFDO1lBQ3hHLElBQUksR0FBRyxDQUFDLEtBQUssSUFBSSxPQUFPLEdBQUcsQ0FBQyxLQUFLLEtBQUssUUFBUSxFQUFFLENBQUM7Z0JBQy9DLE1BQU0sQ0FBQyxHQUFHLEdBQUcsQ0FBQyxLQUFnQyxDQUFDO2dCQUMvQyxjQUFjLENBQUMsQ0FBQyxDQUFDLENBQUM7Z0JBQ2xCLHNFQUFzRTtnQkFDdEUsaURBQWlEO2dCQUNqRCxJQUFJLENBQUM7b0JBQ0gsTUFBTSxDQUFDLGNBQWMsQ0FBQyxHQUFHLEVBQUUsT0FBTyxFQUFFLEVBQUUsS0FBSyxFQUFFLENBQUMsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLFlBQVksRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7Z0JBQzFHLENBQUM7Z0JBQUMsTUFBTSxDQUFDO29CQUNQLHlFQUF5RTtnQkFDM0UsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO1FBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztZQUNiLHVFQUF1RTtZQUN2RSx3RUFBd0U7WUFDeEUsSUFBSSxHQUFHLFlBQVksbUJBQW1CLEVBQUUsQ0FBQztnQkFDdkMsT0FBTyxTQUFTLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxVQUFVLEVBQUUsR0FBRyxDQUFDLE9BQU8sRUFBRSxTQUFTLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztZQUNqRixDQUFDO1lBQ0QsTUFBTSxHQUFHLENBQUM7UUFDWixDQUFDO1FBQ0QsSUFBSSxFQUFFLENBQUM7SUFDVCxDQUFDLENBQUM7QUFDSixDQUFDO0FBRUQsOEVBQThFO0FBQzlFLDhFQUE4RTtBQUM5RSxpRkFBaUY7QUFDakYsTUFBTSxjQUFjLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQyxXQUFXLEVBQUUsYUFBYSxFQUFFLFdBQVcsQ0FBQyxDQUFDLENBQUM7QUFFMUUsU0FBUyxjQUFjLENBQUMsR0FBNEIsRUFBRSxLQUFLLEdBQUcsQ0FBQztJQUM3RCx3RUFBd0U7SUFDeEUseUVBQXlFO0lBQ3pFLHVEQUF1RDtJQUN2RCxJQUFJLEtBQUssR0FBRyxrQkFBa0IsRUFBRSxDQUFDO1FBQy9CLE1BQU0sSUFBSSxtQkFBbUIsRUFBRSxDQUFDO0lBQ2xDLENBQUM7SUFDRCxLQUFLLE1BQU0sR0FBRyxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUNuQyxJQUFJLEdBQUcsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLElBQUksR0FBRyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxjQUFjLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDeEUsT0FBTyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDaEIsU0FBUztRQUNYLENBQUM7UUFDRCxNQUFNLENBQUMsR0FBRyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDbkIsSUFBSSxDQUFDLElBQUksT0FBTyxDQUFDLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDL0IsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7Z0JBQ3JCLEtBQUssTUFBTSxJQUFJLElBQUksQ0FBQyxFQUFFLENBQUM7b0JBQ3JCLElBQUksSUFBSSxJQUFJLE9BQU8sSUFBSSxLQUFLLFFBQVE7d0JBQUUsY0FBYyxDQUFDLElBQStCLEVBQUUsS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUNuRyxDQUFDO1lBQ0gsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLGNBQWMsQ0FBQyxDQUE0QixFQUFFLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQztZQUMxRCxDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7QUFDSCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB0eXBlIHsgTmV4dEZ1bmN0aW9uLCBSZXF1ZXN0LCBSZXNwb25zZSB9IGZyb20gJ2V4cHJlc3MnO1xuaW1wb3J0IHsgSHR0cFN0YXR1cyB9IGZyb20gJy4uL2NvbnN0YW50cy9odHRwLXN0YXR1cy5qcyc7XG5pbXBvcnQgeyBFcnJvckNvZGUgfSBmcm9tICcuLi90eXBlcy9lcnJvci1jb2Rlcy5qcyc7XG5pbXBvcnQgeyBzZW5kRXJyb3IgfSBmcm9tICcuLi91dGlscy9yZXNwb25zZS5qcyc7XG5cbi8qKiBNYXhpbXVtIG5lc3RpbmcgZGVwdGggYWNjZXB0ZWQgYnkge0BsaW5rIG1vbmdvU2FuaXRpemV9LiAqL1xuZXhwb3J0IGNvbnN0IE1BWF9TQU5JVElaRV9ERVBUSCA9IDEwO1xuXG4vKipcbiAqIFRocm93biB3aGVuIGEgcmVxdWVzdCBwYXlsb2FkIG5lc3RzIGRlZXBlciB0aGFuIHtAbGluayBNQVhfU0FOSVRJWkVfREVQVEh9LlxuICogVGhlIG1pZGRsZXdhcmUgbWFwcyB0aGlzIHRvIEhUVFAgNDAwIGluc3RlYWQgb2Ygc2lsZW50bHkgdHJ1bmNhdGluZyB0aGUgd2Fsa1xuICogKHdoaWNoIHdvdWxkIGxldCBvcGVyYXRvci1zaGFwZWQga2V5cyBiZWxvdyB0aGUgY2FwIHNsaXAgdGhyb3VnaCB1bnNhbml0aXplZCkuXG4gKi9cbmV4cG9ydCBjbGFzcyBQYXlsb2FkVG9vRGVlcEVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICByZWFkb25seSBzdGF0dXNDb2RlID0gSHR0cFN0YXR1cy5CQURfUkVRVUVTVDtcbiAgY29uc3RydWN0b3IobWVzc2FnZSA9IGBSZXF1ZXN0IHBheWxvYWQgbmVzdGluZyBleGNlZWRzIHRoZSBtYXhpbXVtIGRlcHRoIG9mICR7TUFYX1NBTklUSVpFX0RFUFRIfWApIHtcbiAgICBzdXBlcihtZXNzYWdlKTtcbiAgICB0aGlzLm5hbWUgPSAnUGF5bG9hZFRvb0RlZXBFcnJvcic7XG4gIH1cbn1cblxuLyoqXG4gKiBNb25nb0RCIG9wZXJhdG9yLWluamVjdGlvbiBtaWRkbGV3YXJlLlxuICpcbiAqIFJlY3Vyc2l2ZWx5IHN0cmlwcyBvYmplY3Qga2V5cyBzdGFydGluZyB3aXRoIGAkYCAoTW9uZ28gb3BlcmF0b3JzIGxpa2VcbiAqIGAkbmVgLCBgJGd0YCwgYCR3aGVyZWApIGFuZCBrZXlzIGNvbnRhaW5pbmcgYC5gIChkb3Qtd2Fsa3MpIGZyb21cbiAqIGByZXEuYm9keWAsIGByZXEucXVlcnlgLCBhbmQgYHJlcS5wYXJhbXNgLiBXaXRob3V0IHRoaXMsIGEgSlNPTiByZXF1ZXN0XG4gKiBsaWtlIGB7XCJlbWFpbFwiOiB7XCIkbmVcIjogbnVsbH19YCBhZ2FpbnN0IGEgTW9uZ28tYmFja2VkIHNlcnZpY2UgY2FuIG1hdGNoXG4gKiBhbnkgZG9jdW1lbnQg4oCUIFpvZCBjYXRjaGVzIHRoZSB3cm9uZyAqdHlwZSogYnV0IHdvbid0IHJlYWNoIGludG8gbmVzdGVkXG4gKiBvYmplY3RzIHRvIHN0cmlwIG9wZXJhdG9yLXNoYXBlZCBrZXlzLlxuICpcbiAqIEFwcGx5IEJFRk9SRSBhbnkgTW9uZ28gcXVlcnkgaGFuZGxlciAodHlwaWNhbGx5IHJpZ2h0IGFmdGVyXG4gKiBgZXhwcmVzcy5qc29uKClgKS4gUG9zdGdyZXMtYmFja2VkIHNlcnZpY2VzIGRvbid0IG5lZWQgaXQuXG4gKlxuICogYHJlcS5ib2R5YCBhbmQgYHJlcS5wYXJhbXNgIGFyZSBwbGFpbiB3cml0YWJsZSBvYmplY3RzLCBzbyB0aGV5J3JlIHNhbml0aXplZFxuICogaW4gcGxhY2UuIE9uIEV4cHJlc3MgNSBgcmVxLnF1ZXJ5YCBpcyBhIEdFVFRFUiB0aGF0IHJlLXBhcnNlcyB0aGUgVVJMIGFuZFxuICogcmV0dXJucyBhIGZyZXNoIG9iamVjdCBvbiBldmVyeSBhY2Nlc3Mg4oCUIG11dGF0aW5nIGl0IGlzIGEgbm8tb3Ag4oCUIHNvIHdlXG4gKiBzYW5pdGl6ZSBhIHNuYXBzaG90IGFuZCBwaW4gaXQgb250byB0aGUgcmVxdWVzdCBpbnN0YW5jZSB3aXRoXG4gKiBgT2JqZWN0LmRlZmluZVByb3BlcnR5YCwgc2hhZG93aW5nIHRoZSBnZXR0ZXIgZm9yIGFsbCBkb3duc3RyZWFtIHJlYWRzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gbW9uZ29TYW5pdGl6ZSgpIHtcbiAgcmV0dXJuIChyZXE6IFJlcXVlc3QsIHJlczogUmVzcG9uc2UsIG5leHQ6IE5leHRGdW5jdGlvbikgPT4ge1xuICAgIHRyeSB7XG4gICAgICBpZiAocmVxLmJvZHkgJiYgdHlwZW9mIHJlcS5ib2R5ID09PSAnb2JqZWN0Jykgc2FuaXRpemVPYmplY3QocmVxLmJvZHkpO1xuICAgICAgaWYgKHJlcS5wYXJhbXMgJiYgdHlwZW9mIHJlcS5wYXJhbXMgPT09ICdvYmplY3QnKSBzYW5pdGl6ZU9iamVjdChyZXEucGFyYW1zIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KTtcbiAgICAgIGlmIChyZXEucXVlcnkgJiYgdHlwZW9mIHJlcS5xdWVyeSA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgY29uc3QgcSA9IHJlcS5xdWVyeSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICAgICAgc2FuaXRpemVPYmplY3QocSk7XG4gICAgICAgIC8vIFBpbiB0aGUgc2FuaXRpemVkIHNuYXBzaG90IHNvIHRoZSBFeHByZXNzIDUgZ2V0dGVyIGNhbid0IHJlLXBhcnNlIGFcbiAgICAgICAgLy8gZnJlc2ggKHVuc2FuaXRpemVkKSBvYmplY3Qgb24gdGhlIG5leHQgYWNjZXNzLlxuICAgICAgICB0cnkge1xuICAgICAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShyZXEsICdxdWVyeScsIHsgdmFsdWU6IHEsIHdyaXRhYmxlOiB0cnVlLCBjb25maWd1cmFibGU6IHRydWUsIGVudW1lcmFibGU6IHRydWUgfSk7XG4gICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgIC8vIEV4cHJlc3MgNCAod3JpdGFibGUgZGF0YSBwcm9wKSDigJQgdGhlIGluLXBsYWNlIHNhbml0aXplIGFib3ZlIHN1ZmZpY2VkLlxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAvLyBSZWplY3Qgb3Zlci1kZWVwIHBheWxvYWRzIHdpdGggYSA0MDAgcmF0aGVyIHRoYW4gc2lsZW50bHkgdHJ1bmNhdGluZ1xuICAgICAgLy8gdGhlIHNhbml0aXplIHdhbGsgKHdoaWNoIHJpc2tlZCBsZWF2aW5nIGRlZXAgb3BlcmF0b3Iga2V5cyBpbiBwbGFjZSkuXG4gICAgICBpZiAoZXJyIGluc3RhbmNlb2YgUGF5bG9hZFRvb0RlZXBFcnJvcikge1xuICAgICAgICByZXR1cm4gc2VuZEVycm9yKHJlcywgZXJyLnN0YXR1c0NvZGUsIGVyci5tZXNzYWdlLCBFcnJvckNvZGUuVkFMSURBVElPTl9FUlJPUik7XG4gICAgICB9XG4gICAgICB0aHJvdyBlcnI7XG4gICAgfVxuICAgIG5leHQoKTtcbiAgfTtcbn1cblxuLy8gUHJvdG90eXBlLXBvbGx1dGlvbiBrZXlzIOKAlCB0aGVzZSBuZXZlciBiZWxvbmcgaW4gSlNPTiByZXF1ZXN0IGJvZGllcywgYW5kIGFcbi8vIGRvd25zdHJlYW0gcmVjdXJzaXZlIG1lcmdlL2Fzc2lnbiBvbiB0aGUgcGFyc2VkIG9iamVjdCBjb3VsZCBvdGhlcndpc2Ugd2Fsa1xuLy8gYGNvbnN0cnVjdG9yLnByb3RvdHlwZWAgdG8gaW5qZWN0IE1vbmdvIG9wZXJhdG9ycyBvciBwb2xsdXRlIE9iamVjdC5wcm90b3R5cGUuXG5jb25zdCBGT1JCSURERU5fS0VZUyA9IG5ldyBTZXQoWydfX3Byb3RvX18nLCAnY29uc3RydWN0b3InLCAncHJvdG90eXBlJ10pO1xuXG5mdW5jdGlvbiBzYW5pdGl6ZU9iamVjdChvYmo6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LCBkZXB0aCA9IDApOiB2b2lkIHtcbiAgLy8gUmVqZWN0IHBhdGhvbG9naWNhbGx5IG5lc3RlZCBpbnB1dCByYXRoZXIgdGhhbiBzaWxlbnRseSByZXR1cm5pbmcg4oCUIGFcbiAgLy8gc2lsZW50IGNhcCB3b3VsZCBsZWF2ZSBhbnkgb3BlcmF0b3IvZG90L3Byb3RvdHlwZSBrZXlzIGRlZXBlciB0aGFuIHRoZVxuICAvLyBjYXAgdW4tc2FuaXRpemVkLiBUaGUgbWlkZGxld2FyZSBtYXBzIHRoaXMgdG8gYSA0MDAuXG4gIGlmIChkZXB0aCA+IE1BWF9TQU5JVElaRV9ERVBUSCkge1xuICAgIHRocm93IG5ldyBQYXlsb2FkVG9vRGVlcEVycm9yKCk7XG4gIH1cbiAgZm9yIChjb25zdCBrZXkgb2YgT2JqZWN0LmtleXMob2JqKSkge1xuICAgIGlmIChrZXkuc3RhcnRzV2l0aCgnJCcpIHx8IGtleS5pbmNsdWRlcygnLicpIHx8IEZPUkJJRERFTl9LRVlTLmhhcyhrZXkpKSB7XG4gICAgICBkZWxldGUgb2JqW2tleV07XG4gICAgICBjb250aW51ZTtcbiAgICB9XG4gICAgY29uc3QgdiA9IG9ialtrZXldO1xuICAgIGlmICh2ICYmIHR5cGVvZiB2ID09PSAnb2JqZWN0Jykge1xuICAgICAgaWYgKEFycmF5LmlzQXJyYXkodikpIHtcbiAgICAgICAgZm9yIChjb25zdCBpdGVtIG9mIHYpIHtcbiAgICAgICAgICBpZiAoaXRlbSAmJiB0eXBlb2YgaXRlbSA9PT0gJ29iamVjdCcpIHNhbml0aXplT2JqZWN0KGl0ZW0gYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4sIGRlcHRoICsgMSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHNhbml0aXplT2JqZWN0KHYgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4sIGRlcHRoICsgMSk7XG4gICAgICB9XG4gICAgfVxuICB9XG59XG4iXX0=
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `requireStepUp` — a shared step-up (recent-password-reverify) gate for the
|
|
3
|
+
* stateless api services, mirroring the platform middleware of the same name.
|
|
4
|
+
*
|
|
5
|
+
* Flow: the UI hits `POST /api/auth/step-up` (platform) to re-verify the user's
|
|
6
|
+
* password; platform mints a short-lived (60s) `step-up` JWT signed with the
|
|
7
|
+
* SAME `JWT_SECRET` these services already verify access tokens with. The UI
|
|
8
|
+
* replays it as the `X-Step-Up-Token` header on the destructive/sensitive call;
|
|
9
|
+
* this middleware verifies it, binds it to the caller (`sub` match), and
|
|
10
|
+
* consumes its `jti` once (Redis-backed cross-instance when configured, else a
|
|
11
|
+
* bounded process-local set) so a replay inside the TTL is rejected.
|
|
12
|
+
*
|
|
13
|
+
* WHY here: restore endpoints on the api services (pipeline/plugin/...) are
|
|
14
|
+
* step-up-gated, but step-up previously lived only in `platform`. This ports the
|
|
15
|
+
* verify + single-use jti into api-core so any service can require it.
|
|
16
|
+
*/
|
|
17
|
+
import type { Request, Response, NextFunction } from 'express';
|
|
18
|
+
/** Payload of a platform-issued step-up token (see platform issueStepUpToken). */
|
|
19
|
+
export interface StepUpTokenPayload {
|
|
20
|
+
type: 'step-up';
|
|
21
|
+
sub: string;
|
|
22
|
+
jti: string;
|
|
23
|
+
iat: number;
|
|
24
|
+
exp: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Verify a step-up token: valid signature (primary `JWT_SECRET`, or
|
|
28
|
+
* `JWT_SECRET_PREVIOUS` during a rotation), allow-listed algorithm, AND the
|
|
29
|
+
* `type: 'step-up'` + `jti` claims — a plain access token shares the secret and
|
|
30
|
+
* `sub`, so without asserting the step-up shape it would bypass the gate.
|
|
31
|
+
* Throws on any failure. Does NOT bind to a caller — `requireStepUp` does the
|
|
32
|
+
* `sub` match and single-use consume.
|
|
33
|
+
*/
|
|
34
|
+
export declare function verifyStepUpToken(token: string): StepUpTokenPayload;
|
|
35
|
+
/**
|
|
36
|
+
* Consume a step-up `jti` exactly once. Returns true if this call claimed it
|
|
37
|
+
* (first use), false if it was already consumed (replay). `expEpochSeconds` is
|
|
38
|
+
* the token's `exp`, so the marker auto-expires with the token.
|
|
39
|
+
*/
|
|
40
|
+
export declare function consumeStepUpJti(jti: string, expEpochSeconds: number): Promise<boolean>;
|
|
41
|
+
/**
|
|
42
|
+
* Express middleware: require a valid, caller-bound, single-use step-up token in
|
|
43
|
+
* the `X-Step-Up-Token` header. Must run AFTER `requireAuth` (needs `req.user`).
|
|
44
|
+
*/
|
|
45
|
+
export declare function requireStepUp(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import jwt from 'jsonwebtoken';
|
|
4
|
+
import { isServicePrincipal } from './auth.js';
|
|
5
|
+
import { createEnvRedisClient } from '../services/env-redis.js';
|
|
6
|
+
import { getHeaderString } from '../utils/headers.js';
|
|
7
|
+
import { createLogger } from '../utils/logger.js';
|
|
8
|
+
import { sendError } from '../utils/response.js';
|
|
9
|
+
const logger = createLogger('step-up');
|
|
10
|
+
const HEADER = 'x-step-up-token';
|
|
11
|
+
function jwtAlgorithm() {
|
|
12
|
+
return (process.env.JWT_ALGORITHM || 'HS256');
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Verify a step-up token: valid signature (primary `JWT_SECRET`, or
|
|
16
|
+
* `JWT_SECRET_PREVIOUS` during a rotation), allow-listed algorithm, AND the
|
|
17
|
+
* `type: 'step-up'` + `jti` claims — a plain access token shares the secret and
|
|
18
|
+
* `sub`, so without asserting the step-up shape it would bypass the gate.
|
|
19
|
+
* Throws on any failure. Does NOT bind to a caller — `requireStepUp` does the
|
|
20
|
+
* `sub` match and single-use consume.
|
|
21
|
+
*/
|
|
22
|
+
export function verifyStepUpToken(token) {
|
|
23
|
+
const secret = process.env.JWT_SECRET;
|
|
24
|
+
if (!secret)
|
|
25
|
+
throw new Error('JWT_SECRET environment variable is required');
|
|
26
|
+
const opts = { algorithms: [jwtAlgorithm()] };
|
|
27
|
+
let payload;
|
|
28
|
+
try {
|
|
29
|
+
payload = jwt.verify(token, secret, opts);
|
|
30
|
+
}
|
|
31
|
+
catch (primaryErr) {
|
|
32
|
+
const previous = process.env.JWT_SECRET_PREVIOUS;
|
|
33
|
+
if (!previous)
|
|
34
|
+
throw primaryErr;
|
|
35
|
+
payload = jwt.verify(token, previous, opts);
|
|
36
|
+
}
|
|
37
|
+
if (payload.type !== 'step-up' || !payload.jti || !payload.sub) {
|
|
38
|
+
throw new Error('INVALID_STEP_UP_TOKEN');
|
|
39
|
+
}
|
|
40
|
+
return payload;
|
|
41
|
+
}
|
|
42
|
+
// -- Single-use jti store -----------------------------------------------------
|
|
43
|
+
// Redis SET NX EX when Redis is configured (cross-instance single-use); else a
|
|
44
|
+
// bounded process-local map with TTL sweep (per-process). A step-up token is
|
|
45
|
+
// 60s-lived. When Redis IS configured it's the authoritative store: a Redis
|
|
46
|
+
// error FAILS CLOSED (throws → STEP_UP_INVALID, user re-verifies) rather than
|
|
47
|
+
// degrading to the per-instance mem guard, which would accept a cross-instance
|
|
48
|
+
// replay during the outage. The mem guard is used ONLY when Redis is absent.
|
|
49
|
+
let _redis;
|
|
50
|
+
function redis() {
|
|
51
|
+
if (_redis === undefined) {
|
|
52
|
+
_redis = createEnvRedisClient('step-up-jti');
|
|
53
|
+
}
|
|
54
|
+
return _redis;
|
|
55
|
+
}
|
|
56
|
+
const memJti = new Map(); // jti -> expiry epoch ms
|
|
57
|
+
const memSweep = setInterval(() => {
|
|
58
|
+
const now = Date.now();
|
|
59
|
+
for (const [jti, exp] of memJti)
|
|
60
|
+
if (exp <= now)
|
|
61
|
+
memJti.delete(jti);
|
|
62
|
+
}, 30_000);
|
|
63
|
+
memSweep.unref?.();
|
|
64
|
+
/**
|
|
65
|
+
* Consume a step-up `jti` exactly once. Returns true if this call claimed it
|
|
66
|
+
* (first use), false if it was already consumed (replay). `expEpochSeconds` is
|
|
67
|
+
* the token's `exp`, so the marker auto-expires with the token.
|
|
68
|
+
*/
|
|
69
|
+
export async function consumeStepUpJti(jti, expEpochSeconds) {
|
|
70
|
+
const ttlSeconds = Math.max(1, expEpochSeconds - Math.floor(Date.now() / 1000));
|
|
71
|
+
const client = redis();
|
|
72
|
+
if (client) {
|
|
73
|
+
// Redis configured ⇒ authoritative cross-instance store. SET NX EX → 'OK'
|
|
74
|
+
// when newly set, null when already consumed. A thrown Redis error is NOT
|
|
75
|
+
// caught here: it propagates so `requireStepUp` fails closed (STEP_UP_INVALID)
|
|
76
|
+
// instead of silently degrading to the per-instance mem guard.
|
|
77
|
+
const res = await client.set(`stepup:jti:${jti}`, '1', 'EX', ttlSeconds, 'NX');
|
|
78
|
+
return res === 'OK';
|
|
79
|
+
}
|
|
80
|
+
// No Redis ⇒ single-instance deployment; the process-local map IS the store.
|
|
81
|
+
if (memJti.has(jti))
|
|
82
|
+
return false;
|
|
83
|
+
memJti.set(jti, Date.now() + ttlSeconds * 1000);
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Express middleware: require a valid, caller-bound, single-use step-up token in
|
|
88
|
+
* the `X-Step-Up-Token` header. Must run AFTER `requireAuth` (needs `req.user`).
|
|
89
|
+
*/
|
|
90
|
+
export async function requireStepUp(req, res, next) {
|
|
91
|
+
// Verified internal service principals are EXEMPT: step-up is a re-verify-the-
|
|
92
|
+
// HUMAN gate (they replay a password-reverify token), which a service token
|
|
93
|
+
// structurally cannot produce. Services already bypass `requirePermission` on
|
|
94
|
+
// the same trust basis (a signed service JWT), and internal callers of a
|
|
95
|
+
// step-up-gated route — e.g. the platform org-cascade's `DELETE /quotas/:orgId`
|
|
96
|
+
// or the billing→quota entitlement sync — would otherwise be hard-blocked.
|
|
97
|
+
// Safe: a service token can only be minted with `JWT_SECRET`, so this can't be
|
|
98
|
+
// spoofed by an external client to skip step-up.
|
|
99
|
+
if (isServicePrincipal(req)) {
|
|
100
|
+
next();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const callerSub = req.user?.sub;
|
|
104
|
+
if (!callerSub) {
|
|
105
|
+
sendError(res, 401, 'Authentication required', 'UNAUTHORIZED');
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const token = getHeaderString(req.headers[HEADER]);
|
|
109
|
+
if (!token) {
|
|
110
|
+
sendError(res, 401, 'Step-up verification required', 'STEP_UP_REQUIRED');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
let payload;
|
|
114
|
+
try {
|
|
115
|
+
payload = verifyStepUpToken(token);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
sendError(res, 401, 'Step-up token invalid or expired', 'STEP_UP_INVALID');
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (payload.sub !== callerSub) {
|
|
122
|
+
sendError(res, 401, 'Step-up token does not match session', 'STEP_UP_MISMATCH');
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
if (!(await consumeStepUpJti(payload.jti, payload.exp))) {
|
|
127
|
+
sendError(res, 401, 'Step-up token already used or expired', 'STEP_UP_REPLAY');
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
logger.error('Step-up jti consume errored', { error: String(err) });
|
|
133
|
+
sendError(res, 401, 'Step-up verification failed', 'STEP_UP_INVALID');
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
next();
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RlcC11cC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9taWRkbGV3YXJlL3N0ZXAtdXAudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQW9CdEMsT0FBTyxHQUFHLE1BQU0sY0FBYyxDQUFDO0FBQy9CLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUMvQyxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUNoRSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDdEQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ2xELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUVqRCxNQUFNLE1BQU0sR0FBRyxZQUFZLENBQUMsU0FBUyxDQUFDLENBQUM7QUFDdkMsTUFBTSxNQUFNLEdBQUcsaUJBQWlCLENBQUM7QUFXakMsU0FBUyxZQUFZO0lBQ25CLE9BQU8sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLGFBQWEsSUFBSSxPQUFPLENBQWtCLENBQUM7QUFDakUsQ0FBQztBQUVEOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLFVBQVUsaUJBQWlCLENBQUMsS0FBYTtJQUM3QyxNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQztJQUN0QyxJQUFJLENBQUMsTUFBTTtRQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsNkNBQTZDLENBQUMsQ0FBQztJQUM1RSxNQUFNLElBQUksR0FBc0IsRUFBRSxVQUFVLEVBQUUsQ0FBQyxZQUFZLEVBQUUsQ0FBQyxFQUFFLENBQUM7SUFFakUsSUFBSSxPQUEyQixDQUFDO0lBQ2hDLElBQUksQ0FBQztRQUNILE9BQU8sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsSUFBSSxDQUF1QixDQUFDO0lB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QkFBZ0IsQ0FBQyxDQUFDO1lBQy9FLE9BQU87UUFDVCxDQUFDO0lBQ0gsQ0FBQztJQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7UUFDYixNQUFNLENBQUMsS0FBSyxDQUFDLDZCQUE2QixFQUFFLEVBQUUsS0FBSyxFQUFFLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDcEUsU0FBUyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsNkJBQTZCLEVBQUUsaUJBQWlCLENBQUMsQ0FBQztRQUN0RSxPQUFPO0lBQ1QsQ0FBQztJQUVELElBQUksRUFBRSxDQUFDO0FBQ1QsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG4vKipcbiAqIGByZXF1aXJlU3RlcFVwYCDigJQgYSBzaGFyZWQgc3RlcC11cCAocmVjZW50LXBhc3N3b3JkLXJldmVyaWZ5KSBnYXRlIGZvciB0aGVcbiAqIHN0YXRlbGVzcyBhcGkgc2VydmljZXMsIG1pcnJvcmluZyB0aGUgcGxhdGZvcm0gbWlkZGxld2FyZSBvZiB0aGUgc2FtZSBuYW1lLlxuICpcbiAqIEZsb3c6IHRoZSBVSSBoaXRzIGBQT1NUIC9hcGkvYXV0aC9zdGVwLXVwYCAocGxhdGZvcm0pIHRvIHJlLXZlcmlmeSB0aGUgdXNlcidzXG4gKiBwYXNzd29yZDsgcGxhdGZvcm0gbWludHMgYSBzaG9ydC1saXZlZCAoNjBzKSBgc3RlcC11cGAgSldUIHNpZ25lZCB3aXRoIHRoZVxuICogU0FNRSBgSldUX1NFQ1JFVGAgdGhlc2Ugc2VydmljZXMgYWxyZWFkeSB2ZXJpZnkgYWNjZXNzIHRva2VucyB3aXRoLiBUaGUgVUlcbiAqIHJlcGxheXMgaXQgYXMgdGhlIGBYLVN0ZXAtVXAtVG9rZW5gIGhlYWRlciBvbiB0aGUgZGVzdHJ1Y3RpdmUvc2Vuc2l0aXZlIGNhbGw7XG4gKiB0aGlzIG1pZGRsZXdhcmUgdmVyaWZpZXMgaXQsIGJpbmRzIGl0IHRvIHRoZSBjYWxsZXIgKGBzdWJgIG1hdGNoKSwgYW5kXG4gKiBjb25zdW1lcyBpdHMgYGp0aWAgb25jZSAoUmVkaXMtYmFja2VkIGNyb3NzLWluc3RhbmNlIHdoZW4gY29uZmlndXJlZCwgZWxzZSBhXG4gKiBib3VuZGVkIHByb2Nlc3MtbG9jYWwgc2V0KSBzbyBhIHJlcGxheSBpbnNpZGUgdGhlIFRUTCBpcyByZWplY3RlZC5cbiAqXG4gKiBXSFkgaGVyZTogcmVzdG9yZSBlbmRwb2ludHMgb24gdGhlIGFwaSBzZXJ2aWNlcyAocGlwZWxpbmUvcGx1Z2luLy4uLikgYXJlXG4gKiBzdGVwLXVwLWdhdGVkLCBidXQgc3RlcC11cCBwcmV2aW91c2x5IGxpdmVkIG9ubHkgaW4gYHBsYXRmb3JtYC4gVGhpcyBwb3J0cyB0aGVcbiAqIHZlcmlmeSArIHNpbmdsZS11c2UganRpIGludG8gYXBpLWNvcmUgc28gYW55IHNlcnZpY2UgY2FuIHJlcXVpcmUgaXQuXG4gKi9cblxuaW1wb3J0IHR5cGUgeyBSZXF1ZXN0LCBSZXNwb25zZSwgTmV4dEZ1bmN0aW9uIH0gZnJvbSAnZXhwcmVzcyc7XG5pbXBvcnQgand0IGZyb20gJ2pzb253ZWJ0b2tlbic7XG5pbXBvcnQgeyBpc1NlcnZpY2VQcmluY2lwYWwgfSBmcm9tICcuL2F1dGguanMnO1xuaW1wb3J0IHsgY3JlYXRlRW52UmVkaXNDbGllbnQgfSBmcm9tICcuLi9zZXJ2aWNlcy9lbnYtcmVkaXMuanMnO1xuaW1wb3J0IHsgZ2V0SGVhZGVyU3RyaW5nIH0gZnJvbSAnLi4vdXRpbHMvaGVhZGVycy5qcyc7XG5pbXBvcnQgeyBjcmVhdGVMb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuaW1wb3J0IHsgc2VuZEVycm9yIH0gZnJvbSAnLi4vdXRpbHMvcmVzcG9uc2UuanMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ3N0ZXAtdXAnKTtcbmNvbnN0IEhFQURFUiA9ICd4LXN0ZXAtdXAtdG9rZW4nO1xuXG4vKiogUGF5bG9hZCBvZiBhIHBsYXRmb3JtLWlzc3VlZCBzdGVwLXVwIHRva2VuIChzZWUgcGxhdGZvcm0gaXNzdWVTdGVwVXBUb2tlbikuICovXG5leHBvcnQgaW50ZXJmYWNlIFN0ZXBVcFRva2VuUGF5bG9hZCB7XG4gIHR5cGU6ICdzdGVwLXVwJztcbiAgc3ViOiBzdHJpbmc7XG4gIGp0aTogc3RyaW5nO1xuICBpYXQ6IG51bWJlcjtcbiAgZXhwOiBudW1iZXI7XG59XG5cbmZ1bmN0aW9uIGp3dEFsZ29yaXRobSgpOiBqd3QuQWxnb3JpdGhtIHtcbiAgcmV0dXJuIChwcm9jZXNzLmVudi5KV1RfQUxHT1JJVEhNIHx8ICdIUzI1NicpIGFzIGp3dC5BbGdvcml0aG07XG59XG5cbi8qKlxuICogVmVyaWZ5IGEgc3RlcC11cCB0b2tlbjogdmFsaWQgc2lnbmF0dXJlIChwcmltYXJ5IGBKV1RfU0VDUkVUYCwgb3JcbiAqIGBKV1RfU0VDUkVUX1BSRVZJT1VTYCBkdXJpbmcgYSByb3RhdGlvbiksIGFsbG93LWxpc3RlZCBhbGdvcml0aG0sIEFORCB0aGVcbiAqIGB0eXBlOiAnc3RlcC11cCdgICsgYGp0aWAgY2xhaW1zIOKAlCBhIHBsYWluIGFjY2VzcyB0b2tlbiBzaGFyZXMgdGhlIHNlY3JldCBhbmRcbiAqIGBzdWJgLCBzbyB3aXRob3V0IGFzc2VydGluZyB0aGUgc3RlcC11cCBzaGFwZSBpdCB3b3VsZCBieXBhc3MgdGhlIGdhdGUuXG4gKiBUaHJvd3Mgb24gYW55IGZhaWx1cmUuIERvZXMgTk9UIGJpbmQgdG8gYSBjYWxsZXIg4oCUIGByZXF1aXJlU3RlcFVwYCBkb2VzIHRoZVxuICogYHN1YmAgbWF0Y2ggYW5kIHNpbmdsZS11c2UgY29uc3VtZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHZlcmlmeVN0ZXBVcFRva2VuKHRva2VuOiBzdHJpbmcpOiBTdGVwVXBUb2tlblBheWxvYWQge1xuICBjb25zdCBzZWNyZXQgPSBwcm9jZXNzLmVudi5KV1RfU0VDUkVUO1xuICBpZiAoIXNlY3JldCkgdGhyb3cgbmV3IEVycm9yKCdKV1RfU0VDUkVUIGVudmlyb25tZW50IHZhcmlhYmxlIGlzIHJlcXVpcmVkJyk7XG4gIGNvbnN0IG9wdHM6IGp3dC5WZXJpZnlPcHRpb25zID0geyBhbGdvcml0aG1zOiBband0QWxnb3JpdGhtKCldIH07XG5cbiAgbGV0IHBheWxvYWQ6IFN0ZXBVcFRva2VuUGF5bG9hZDtcbiAgdHJ5IHtcbiAgICBwYXlsb2FkID0gand0LnZlcmlmeSh0b2tlbiwgc2VjcmV0LCBvcHRzKSBhcyBTdGVwVXBUb2tlblBheWxvYWQ7XG4gIH0gY2F0Y2ggKHByaW1hcnlFcnIpIHtcbiAgICBjb25zdCBwcmV2aW91cyA9IHByb2Nlc3MuZW52LkpXVF9TRUNSRVRfUFJFVklPVVM7XG4gICAgaWYgKCFwcmV2aW91cykgdGhyb3cgcHJpbWFyeUVycjtcbiAgICBwYXlsb2FkID0gand0LnZlcmlmeSh0b2tlbiwgcHJldmlvdXMsIG9wdHMpIGFzIFN0ZXBVcFRva2VuUGF5bG9hZDtcbiAgfVxuXG4gIGlmIChwYXlsb2FkLnR5cGUgIT09ICdzdGVwLXVwJyB8fCAhcGF5bG9hZC5qdGkgfHwgIXBheWxvYWQuc3ViKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdJTlZBTElEX1NURVBfVVBfVE9LRU4nKTtcbiAgfVxuICByZXR1cm4gcGF5bG9hZDtcbn1cblxuLy8gLS0gU2luZ2xlLXVzZSBqdGkgc3RvcmUgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIFJlZGlzIFNFVCBOWCBFWCB3aGVuIFJlZGlzIGlzIGNvbmZpZ3VyZWQgKGNyb3NzLWluc3RhbmNlIHNpbmdsZS11c2UpOyBlbHNlIGFcbi8vIGJvdW5kZWQgcHJvY2Vzcy1sb2NhbCBtYXAgd2l0aCBUVEwgc3dlZXAgKHBlci1wcm9jZXNzKS4gQSBzdGVwLXVwIHRva2VuIGlzXG4vLyA2MHMtbGl2ZWQuIFdoZW4gUmVkaXMgSVMgY29uZmlndXJlZCBpdCdzIHRoZSBhdXRob3JpdGF0aXZlIHN0b3JlOiBhIFJlZGlzXG4vLyBlcnJvciBGQUlMUyBDTE9TRUQgKHRocm93cyDihpIgU1RFUF9VUF9JTlZBTElELCB1c2VyIHJlLXZlcmlmaWVzKSByYXRoZXIgdGhhblxuLy8gZGVncmFkaW5nIHRvIHRoZSBwZXItaW5zdGFuY2UgbWVtIGd1YXJkLCB3aGljaCB3b3VsZCBhY2NlcHQgYSBjcm9zcy1pbnN0YW5jZVxuLy8gcmVwbGF5IGR1cmluZyB0aGUgb3V0YWdlLiBUaGUgbWVtIGd1YXJkIGlzIHVzZWQgT05MWSB3aGVuIFJlZGlzIGlzIGFic2VudC5cblxubGV0IF9yZWRpczogeyBzZXQ6ICguLi5hcmdzOiB1bmtub3duW10pID0+IFByb21pc2U8dW5rbm93bj4gfSB8IG51bGwgfCB1bmRlZmluZWQ7XG5mdW5jdGlvbiByZWRpcygpOiB7IHNldDogKC4uLmFyZ3M6IHVua25vd25bXSkgPT4gUHJvbWlzZTx1bmtub3duPiB9IHwgbnVsbCB7XG4gIGlmIChfcmVkaXMgPT09IHVuZGVmaW5lZCkge1xuICAgIF9yZWRpcyA9IGNyZWF0ZUVudlJlZGlzQ2xpZW50PHsgc2V0OiAoLi4uYXJnczogdW5rbm93bltdKSA9PiBQcm9taXNlPHVua25vd24+IH0+KCdzdGVwLXVwLWp0aScpO1xuICB9XG4gIHJldHVybiBfcmVkaXM7XG59XG5cbmNvbnN0IG1lbUp0aSA9IG5ldyBNYXA8c3RyaW5nLCBudW1iZXI+KCk7IC8vIGp0aSAtPiBleHBpcnkgZXBvY2ggbXNcbmNvbnN0IG1lbVN3ZWVwID0gc2V0SW50ZXJ2YWwoKCkgPT4ge1xuICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICBmb3IgKGNvbnN0IFtqdGksIGV4cF0gb2YgbWVtSnRpKSBpZiAoZXhwIDw9IG5vdykgbWVtSnRpLmRlbGV0ZShqdGkpO1xufSwgMzBfMDAwKTtcbm1lbVN3ZWVwLnVucmVmPy4oKTtcblxuLyoqXG4gKiBDb25zdW1lIGEgc3RlcC11cCBganRpYCBleGFjdGx5IG9uY2UuIFJldHVybnMgdHJ1ZSBpZiB0aGlzIGNhbGwgY2xhaW1lZCBpdFxuICogKGZpcnN0IHVzZSksIGZhbHNlIGlmIGl0IHdhcyBhbHJlYWR5IGNvbnN1bWVkIChyZXBsYXkpLiBgZXhwRXBvY2hTZWNvbmRzYCBpc1xuICogdGhlIHRva2VuJ3MgYGV4cGAsIHNvIHRoZSBtYXJrZXIgYXV0by1leHBpcmVzIHdpdGggdGhlIHRva2VuLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY29uc3VtZVN0ZXBVcEp0aShqdGk6IHN0cmluZywgZXhwRXBvY2hTZWNvbmRzOiBudW1iZXIpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgY29uc3QgdHRsU2Vjb25kcyA9IE1hdGgubWF4KDEsIGV4cEVwb2NoU2Vjb25kcyAtIE1hdGguZmxvb3IoRGF0ZS5ub3coKSAvIDEwMDApKTtcbiAgY29uc3QgY2xpZW50ID0gcmVkaXMoKTtcbiAgaWYgKGNsaWVudCkge1xuICAgIC8vIFJlZGlzIGNvbmZpZ3VyZWQg4oeSIGF1dGhvcml0YXRpdmUgY3Jvc3MtaW5zdGFuY2Ugc3RvcmUuIFNFVCBOWCBFWCDihpIgJ09LJ1xuICAgIC8vIHdoZW4gbmV3bHkgc2V0LCBudWxsIHdoZW4gYWxyZWFkeSBjb25zdW1lZC4gQSB0aHJvd24gUmVkaXMgZXJyb3IgaXMgTk9UXG4gICAgLy8gY2F1Z2h0IGhlcmU6IGl0IHByb3BhZ2F0ZXMgc28gYHJlcXVpcmVTdGVwVXBgIGZhaWxzIGNsb3NlZCAoU1RFUF9VUF9JTlZBTElEKVxuICAgIC8vIGluc3RlYWQgb2Ygc2lsZW50bHkgZGVncmFkaW5nIHRvIHRoZSBwZXItaW5zdGFuY2UgbWVtIGd1YXJkLlxuICAgIGNvbnN0IHJlcyA9IGF3YWl0IGNsaWVudC5zZXQoYHN0ZXB1cDpqdGk6JHtqdGl9YCwgJzEnLCAnRVgnLCB0dGxTZWNvbmRzLCAnTlgnKTtcbiAgICByZXR1cm4gcmVzID09PSAnT0snO1xuICB9XG4gIC8vIE5vIFJlZGlzIOKHkiBzaW5nbGUtaW5zdGFuY2UgZGVwbG95bWVudDsgdGhlIHByb2Nlc3MtbG9jYWwgbWFwIElTIHRoZSBzdG9yZS5cbiAgaWYgKG1lbUp0aS5oYXMoanRpKSkgcmV0dXJuIGZhbHNlO1xuICBtZW1KdGkuc2V0KGp0aSwgRGF0ZS5ub3coKSArIHR0bFNlY29uZHMgKiAxMDAwKTtcbiAgcmV0dXJuIHRydWU7XG59XG5cbi8qKlxuICogRXhwcmVzcyBtaWRkbGV3YXJlOiByZXF1aXJlIGEgdmFsaWQsIGNhbGxlci1ib3VuZCwgc2luZ2xlLXVzZSBzdGVwLXVwIHRva2VuIGluXG4gKiB0aGUgYFgtU3RlcC1VcC1Ub2tlbmAgaGVhZGVyLiBNdXN0IHJ1biBBRlRFUiBgcmVxdWlyZUF1dGhgIChuZWVkcyBgcmVxLnVzZXJgKS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlcXVpcmVTdGVwVXAocmVxOiBSZXF1ZXN0LCByZXM6IFJlc3BvbnNlLCBuZXh0OiBOZXh0RnVuY3Rpb24pOiBQcm9taXNlPHZvaWQ+IHtcbiAgLy8gVmVyaWZpZWQgaW50ZXJuYWwgc2VydmljZSBwcmluY2lwYWxzIGFyZSBFWEVNUFQ6IHN0ZXAtdXAgaXMgYSByZS12ZXJpZnktdGhlLVxuICAvLyBIVU1BTiBnYXRlICh0aGV5IHJlcGxheSBhIHBhc3N3b3JkLXJldmVyaWZ5IHRva2VuKSwgd2hpY2ggYSBzZXJ2aWNlIHRva2VuXG4gIC8vIHN0cnVjdHVyYWxseSBjYW5ub3QgcHJvZHVjZS4gU2VydmljZXMgYWxyZWFkeSBieXBhc3MgYHJlcXVpcmVQZXJtaXNzaW9uYCBvblxuICAvLyB0aGUgc2FtZSB0cnVzdCBiYXNpcyAoYSBzaWduZWQgc2VydmljZSBKV1QpLCBhbmQgaW50ZXJuYWwgY2FsbGVycyBvZiBhXG4gIC8vIHN0ZXAtdXAtZ2F0ZWQgcm91dGUg4oCUIGUuZy4gdGhlIHBsYXRmb3JtIG9yZy1jYXNjYWRlJ3MgYERFTEVURSAvcXVvdGFzLzpvcmdJZGBcbiAgLy8gb3IgdGhlIGJpbGxpbmfihpJxdW90YSBlbnRpdGxlbWVudCBzeW5jIOKAlCB3b3VsZCBvdGhlcndpc2UgYmUgaGFyZC1ibG9ja2VkLlxuICAvLyBTYWZlOiBhIHNlcnZpY2UgdG9rZW4gY2FuIG9ubHkgYmUgbWludGVkIHdpdGggYEpXVF9TRUNSRVRgLCBzbyB0aGlzIGNhbid0IGJlXG4gIC8vIHNwb29mZWQgYnkgYW4gZXh0ZXJuYWwgY2xpZW50IHRvIHNraXAgc3RlcC11cC5cbiAgaWYgKGlzU2VydmljZVByaW5jaXBhbChyZXEpKSB7XG4gICAgbmV4dCgpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IGNhbGxlclN1YiA9IChyZXEgYXMgUmVxdWVzdCAmIHsgdXNlcj86IHsgc3ViPzogc3RyaW5nIH0gfSkudXNlcj8uc3ViO1xuICBpZiAoIWNhbGxlclN1Yikge1xuICAgIHNlbmRFcnJvcihyZXMsIDQwMSwgJ0F1dGhlbnRpY2F0aW9uIHJlcXVpcmVkJywgJ1VOQVVUSE9SSVpFRCcpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IHRva2VuID0gZ2V0SGVhZGVyU3RyaW5nKHJlcS5oZWFkZXJzW0hFQURFUl0pO1xuICBpZiAoIXRva2VuKSB7XG4gICAgc2VuZEVycm9yKHJlcywgNDAxLCAnU3RlcC11cCB2ZXJpZmljYXRpb24gcmVxdWlyZWQnLCAnU1RFUF9VUF9SRVFVSVJFRCcpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGxldCBwYXlsb2FkOiBTdGVwVXBUb2tlblBheWxvYWQ7XG4gIHRyeSB7XG4gICAgcGF5bG9hZCA9IHZlcmlmeVN0ZXBVcFRva2VuKHRva2VuKTtcbiAgfSBjYXRjaCB7XG4gICAgc2VuZEVycm9yKHJlcywgNDAxLCAnU3RlcC11cCB0b2tlbiBpbnZhbGlkIG9yIGV4cGlyZWQnLCAnU1RFUF9VUF9JTlZBTElEJyk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgaWYgKHBheWxvYWQuc3ViICE9PSBjYWxsZXJTdWIpIHtcbiAgICBzZW5kRXJyb3IocmVzLCA0MDEsICdTdGVwLXVwIHRva2VuIGRvZXMgbm90IG1hdGNoIHNlc3Npb24nLCAnU1RFUF9VUF9NSVNNQVRDSCcpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHRyeSB7XG4gICAgaWYgKCEoYXdhaXQgY29uc3VtZVN0ZXBVcEp0aShwYXlsb2FkLmp0aSwgcGF5bG9hZC5leHApKSkge1xuICAgICAgc2VuZEVycm9yKHJlcywgNDAxLCAnU3RlcC11cCB0b2tlbiBhbHJlYWR5IHVzZWQgb3IgZXhwaXJlZCcsICdTVEVQX1VQX1JFUExBWScpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgbG9nZ2VyLmVycm9yKCdTdGVwLXVwIGp0aSBjb25zdW1lIGVycm9yZWQnLCB7IGVycm9yOiBTdHJpbmcoZXJyKSB9KTtcbiAgICBzZW5kRXJyb3IocmVzLCA0MDEsICdTdGVwLXVwIHZlcmlmaWNhdGlvbiBmYWlsZWQnLCAnU1RFUF9VUF9JTlZBTElEJyk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgbmV4dCgpO1xufVxuIl19
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
extendZodWithOpenApi(z);
|
|
6
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXh0ZW5kLXpvZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9vcGVuYXBpL2V4dGVuZC16b2QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0QyxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN0RSxPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sS0FBSyxDQUFDO0FBRXhCLG9CQUFvQixDQUFDLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IGV4dGVuZFpvZFdpdGhPcGVuQXBpIH0gZnJvbSAnQGFzdGVhc29sdXRpb25zL3pvZC10by1vcGVuYXBpJztcbmltcG9ydCB7IHogfSBmcm9tICd6b2QnO1xuXG5leHRlbmRab2RXaXRoT3BlbkFwaSh6KTtcbiJdfQ==
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
// Public API
|
|
4
|
+
export { registry, generateOpenApiSpec } from './registry.js';
|
|
5
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb3BlbmFwaS9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLGFBQWE7QUFDYixPQUFPLEVBQUUsUUFBUSxFQUFFLG1CQUFtQixFQUFFLE1BQU0sZUFBZSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8vIFB1YmxpYyBBUElcbmV4cG9ydCB7IHJlZ2lzdHJ5LCBnZW5lcmF0ZU9wZW5BcGlTcGVjIH0gZnJvbSAnLi9yZWdpc3RyeS5qcyc7XG5leHBvcnQgdHlwZSB7IE9wZW5BcGlTcGVjT3B0aW9ucyB9IGZyb20gJy4vcmVnaXN0cnkuanMnO1xuIl19
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { OpenAPIRegistry, OpenApiGeneratorV31 } from '@asteasolutions/zod-to-openapi';
|
|
2
|
+
/** Shared OpenAPI registry for all schema and path registrations. */
|
|
3
|
+
export declare const registry: OpenAPIRegistry;
|
|
4
|
+
/** Options for customizing the generated OpenAPI spec. */
|
|
5
|
+
export interface OpenApiSpecOptions {
|
|
6
|
+
title?: string;
|
|
7
|
+
version?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
serverUrl?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Generate the complete OpenAPI 3.1.0 specification document.
|
|
13
|
+
*
|
|
14
|
+
* @param options - Optional overrides for spec metadata
|
|
15
|
+
* @returns OpenAPI 3.1 specification object
|
|
16
|
+
*/
|
|
17
|
+
export declare function generateOpenApiSpec(options?: OpenApiSpecOptions): ReturnType<OpenApiGeneratorV31['generateDocument']>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { OpenAPIRegistry, OpenApiGeneratorV31 } from '@asteasolutions/zod-to-openapi';
|
|
4
|
+
import { registerAllRoutes } from './routes/index.js';
|
|
5
|
+
import { registerSchemas } from './schema-registry.js';
|
|
6
|
+
/** Shared OpenAPI registry for all schema and path registrations. */
|
|
7
|
+
export const registry = new OpenAPIRegistry();
|
|
8
|
+
let _initialized = false;
|
|
9
|
+
/** Execute all registrations once. */
|
|
10
|
+
function ensureInitialized() {
|
|
11
|
+
if (_initialized)
|
|
12
|
+
return;
|
|
13
|
+
_initialized = true;
|
|
14
|
+
registerSchemas();
|
|
15
|
+
registerAllRoutes();
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Generate the complete OpenAPI 3.1.0 specification document.
|
|
19
|
+
*
|
|
20
|
+
* @param options - Optional overrides for spec metadata
|
|
21
|
+
* @returns OpenAPI 3.1 specification object
|
|
22
|
+
*/
|
|
23
|
+
export function generateOpenApiSpec(options) {
|
|
24
|
+
ensureInitialized();
|
|
25
|
+
const generator = new OpenApiGeneratorV31(registry.definitions);
|
|
26
|
+
return generator.generateDocument({
|
|
27
|
+
openapi: '3.1.0',
|
|
28
|
+
info: {
|
|
29
|
+
title: options?.title ?? 'Pipeline Builder API',
|
|
30
|
+
version: options?.version ?? '1.0.0',
|
|
31
|
+
description: options?.description ?? 'CI/CD Pipeline Builder Platform API — manage pipelines, plugins, messages, quotas, and billing.',
|
|
32
|
+
},
|
|
33
|
+
servers: [
|
|
34
|
+
{ url: options?.serverUrl ?? '/api', description: 'API base path' },
|
|
35
|
+
],
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVnaXN0cnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb3BlbmFwaS9yZWdpc3RyeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLE9BQU8sRUFBRSxlQUFlLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUV0RixPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUN0RCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFFdkQscUVBQXFFO0FBQ3JFLE1BQU0sQ0FBQyxNQUFNLFFBQVEsR0FBRyxJQUFJLGVBQWUsRUFBRSxDQUFDO0FBVTlDLElBQUksWUFBWSxHQUFHLEtBQUssQ0FBQztBQUV6QixzQ0FBc0M7QUFDdEMsU0FBUyxpQkFBaUI7SUFDeEIsSUFBSSxZQUFZO1FBQUUsT0FBTztJQUN6QixZQUFZLEdBQUcsSUFBSSxDQUFDO0lBQ3BCLGVBQWUsRUFBRSxDQUFDO0lBQ2xCLGlCQUFpQixFQUFFLENBQUM7QUFDdEIsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLG1CQUFtQixDQUNqQyxPQUE0QjtJQUU1QixpQkFBaUIsRUFBRSxDQUFDO0lBRXBCLE1BQU0sU0FBUyxHQUFHLElBQUksbUJBQW1CLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQ2hFLE9BQU8sU0FBUyxDQUFDLGdCQUFnQixDQUFDO1FBQ2hDLE9BQU8sRUFBRSxPQUFPO1FBQ2hCLElBQUksRUFBRTtZQUNKLEtBQUssRUFBRSxPQUFPLEVBQUUsS0FBSyxJQUFJLHNCQUFzQjtZQUMvQyxPQUFPLEVBQUUsT0FBTyxFQUFFLE9BQU8sSUFBSSxPQUFPO1lBQ3BDLFdBQVcsRUFBRSxPQUFPLEVBQUUsV0FBVyxJQUFJLGlHQUFpRztTQUN2STtRQUNELE9BQU8sRUFBRTtZQUNQLEVBQUUsR0FBRyxFQUFFLE9BQU8sRUFBRSxTQUFTLElBQUksTUFBTSxFQUFFLFdBQVcsRUFBRSxlQUFlLEVBQUU7U0FDcEU7S0FDRixDQUFDLENBQUM7QUFDTCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IE9wZW5BUElSZWdpc3RyeSwgT3BlbkFwaUdlbmVyYXRvclYzMSB9IGZyb20gJ0Bhc3RlYXNvbHV0aW9ucy96b2QtdG8tb3BlbmFwaSc7XG5cbmltcG9ydCB7IHJlZ2lzdGVyQWxsUm91dGVzIH0gZnJvbSAnLi9yb3V0ZXMvaW5kZXguanMnO1xuaW1wb3J0IHsgcmVnaXN0ZXJTY2hlbWFzIH0gZnJvbSAnLi9zY2hlbWEtcmVnaXN0cnkuanMnO1xuXG4vKiogU2hhcmVkIE9wZW5BUEkgcmVnaXN0cnkgZm9yIGFsbCBzY2hlbWEgYW5kIHBhdGggcmVnaXN0cmF0aW9ucy4gKi9cbmV4cG9ydCBjb25zdCByZWdpc3RyeSA9IG5ldyBPcGVuQVBJUmVnaXN0cnkoKTtcblxuLyoqIE9wdGlvbnMgZm9yIGN1c3RvbWl6aW5nIHRoZSBnZW5lcmF0ZWQgT3BlbkFQSSBzcGVjLiAqL1xuZXhwb3J0IGludGVyZmFjZSBPcGVuQXBpU3BlY09wdGlvbnMge1xuICB0aXRsZT86IHN0cmluZztcbiAgdmVyc2lvbj86IHN0cmluZztcbiAgZGVzY3JpcHRpb24/OiBzdHJpbmc7XG4gIHNlcnZlclVybD86IHN0cmluZztcbn1cblxubGV0IF9pbml0aWFsaXplZCA9IGZhbHNlO1xuXG4vKiogRXhlY3V0ZSBhbGwgcmVnaXN0cmF0aW9ucyBvbmNlLiAqL1xuZnVuY3Rpb24gZW5zdXJlSW5pdGlhbGl6ZWQoKTogdm9pZCB7XG4gIGlmIChfaW5pdGlhbGl6ZWQpIHJldHVybjtcbiAgX2luaXRpYWxpemVkID0gdHJ1ZTtcbiAgcmVnaXN0ZXJTY2hlbWFzKCk7XG4gIHJlZ2lzdGVyQWxsUm91dGVzKCk7XG59XG5cbi8qKlxuICogR2VuZXJhdGUgdGhlIGNvbXBsZXRlIE9wZW5BUEkgMy4xLjAgc3BlY2lmaWNhdGlvbiBkb2N1bWVudC5cbiAqXG4gKiBAcGFyYW0gb3B0aW9ucyAtIE9wdGlvbmFsIG92ZXJyaWRlcyBmb3Igc3BlYyBtZXRhZGF0YVxuICogQHJldHVybnMgT3BlbkFQSSAzLjEgc3BlY2lmaWNhdGlvbiBvYmplY3RcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdlbmVyYXRlT3BlbkFwaVNwZWMoXG4gIG9wdGlvbnM/OiBPcGVuQXBpU3BlY09wdGlvbnMsXG4pOiBSZXR1cm5UeXBlPE9wZW5BcGlHZW5lcmF0b3JWMzFbJ2dlbmVyYXRlRG9jdW1lbnQnXT4ge1xuICBlbnN1cmVJbml0aWFsaXplZCgpO1xuXG4gIGNvbnN0IGdlbmVyYXRvciA9IG5ldyBPcGVuQXBpR2VuZXJhdG9yVjMxKHJlZ2lzdHJ5LmRlZmluaXRpb25zKTtcbiAgcmV0dXJuIGdlbmVyYXRvci5nZW5lcmF0ZURvY3VtZW50KHtcbiAgICBvcGVuYXBpOiAnMy4xLjAnLFxuICAgIGluZm86IHtcbiAgICAgIHRpdGxlOiBvcHRpb25zPy50aXRsZSA/PyAnUGlwZWxpbmUgQnVpbGRlciBBUEknLFxuICAgICAgdmVyc2lvbjogb3B0aW9ucz8udmVyc2lvbiA/PyAnMS4wLjAnLFxuICAgICAgZGVzY3JpcHRpb246IG9wdGlvbnM/LmRlc2NyaXB0aW9uID8/ICdDSS9DRCBQaXBlbGluZSBCdWlsZGVyIFBsYXRmb3JtIEFQSSDigJQgbWFuYWdlIHBpcGVsaW5lcywgcGx1Z2lucywgbWVzc2FnZXMsIHF1b3RhcywgYW5kIGJpbGxpbmcuJyxcbiAgICB9LFxuICAgIHNlcnZlcnM6IFtcbiAgICAgIHsgdXJsOiBvcHRpb25zPy5zZXJ2ZXJVcmwgPz8gJy9hcGknLCBkZXNjcmlwdGlvbjogJ0FQSSBiYXNlIHBhdGgnIH0sXG4gICAgXSxcbiAgfSk7XG59XG4iXX0=
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAPI definitions for the "Ask" assistant service (api/ask), mounted at
|
|
3
|
+
* `/ask`. All routes are gated by the `ai_generation` feature and are READ /
|
|
4
|
+
* PROPOSE only — the agent never mutates; `propose_*` tool results are drafts the
|
|
5
|
+
* user confirms in the UI.
|
|
6
|
+
*/
|
|
7
|
+
export declare function registerAskRoutes(): void;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { registry } from '../registry.js';
|
|
4
|
+
const tags = ['Ask'];
|
|
5
|
+
const auth = [{ bearerAuth: [] }];
|
|
6
|
+
/**
|
|
7
|
+
* OpenAPI definitions for the "Ask" assistant service (api/ask), mounted at
|
|
8
|
+
* `/ask`. All routes are gated by the `ai_generation` feature and are READ /
|
|
9
|
+
* PROPOSE only — the agent never mutates; `propose_*` tool results are drafts the
|
|
10
|
+
* user confirms in the UI.
|
|
11
|
+
*/
|
|
12
|
+
export function registerAskRoutes() {
|
|
13
|
+
registry.registerPath({
|
|
14
|
+
method: 'get',
|
|
15
|
+
path: '/ask/providers',
|
|
16
|
+
summary: 'List configured AI providers',
|
|
17
|
+
description: 'The AI providers/models available to the Ask assistant (from server env — cloud keys and/or the OpenAI-compatible local endpoint).',
|
|
18
|
+
tags,
|
|
19
|
+
security: auth,
|
|
20
|
+
responses: { 200: { description: 'Configured providers' }, 403: { description: 'ai_generation feature required' } },
|
|
21
|
+
});
|
|
22
|
+
registry.registerPath({
|
|
23
|
+
method: 'post',
|
|
24
|
+
path: '/ask',
|
|
25
|
+
summary: 'Grounded how-to answer (non-streaming)',
|
|
26
|
+
description: 'Answer a platform how-to question grounded in the docs corpus. Reserves one aiCalls quota slot.',
|
|
27
|
+
tags,
|
|
28
|
+
security: auth,
|
|
29
|
+
responses: {
|
|
30
|
+
200: { description: 'Answer with grounded sources' },
|
|
31
|
+
403: { description: 'ai_generation feature required' },
|
|
32
|
+
429: { description: 'aiCalls quota exceeded or rate limited' },
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
registry.registerPath({
|
|
36
|
+
method: 'post',
|
|
37
|
+
path: '/ask/stream',
|
|
38
|
+
summary: 'Grounded how-to answer (SSE stream)',
|
|
39
|
+
description: 'Same as POST /ask but streams the grounded sources then answer tokens as Server-Sent Events.',
|
|
40
|
+
tags,
|
|
41
|
+
security: auth,
|
|
42
|
+
responses: {
|
|
43
|
+
200: { description: 'text/event-stream of sources + answer tokens' },
|
|
44
|
+
403: { description: 'ai_generation feature required' },
|
|
45
|
+
429: { description: 'aiCalls quota exceeded or rate limited' },
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
registry.registerPath({
|
|
49
|
+
method: 'post',
|
|
50
|
+
path: '/ask/agent/stream',
|
|
51
|
+
summary: 'Tool-calling Ask agent (SSE stream)',
|
|
52
|
+
description: 'Runs the tool-calling assistant loop and streams reasoning tokens, tool-call events, and reviewable `propose_*` drafts (pipeline/plugin/template) as SSE. Read-only: proposals are confirmed separately in the UI.',
|
|
53
|
+
tags,
|
|
54
|
+
security: auth,
|
|
55
|
+
responses: {
|
|
56
|
+
200: { description: 'text/event-stream of tokens, tool-calls, and proposal drafts' },
|
|
57
|
+
403: { description: 'ai_generation feature required' },
|
|
58
|
+
429: { description: 'aiCalls quota exceeded or rate limited' },
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXNrLXJvdXRlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9vcGVuYXBpL3JvdXRlcy9hc2stcm91dGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEMsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRTFDLE1BQU0sSUFBSSxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUM7QUFDckIsTUFBTSxJQUFJLEdBQUcsQ0FBQyxFQUFFLFVBQVUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBRWxDOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLGlCQUFpQjtJQUMvQixRQUFRLENBQUMsWUFBWSxDQUFDO1FBQ3BCLE1BQU0sRUFBRSxLQUFLO1FBQ2IsSUFBSSxFQUFFLGdCQUFnQjtRQUN0QixPQUFPLEVBQUUsOEJBQThCO1FBQ3ZDLFdBQVcsRUFBRSxvSUFBb0k7UUFDakosSUFBSTtRQUNKLFFBQVEsRUFBRSxJQUFJO1FBQ2QsU0FBUyxFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLHNCQUFzQixFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLGdDQUFnQyxFQUFFLEVBQUU7S0FDcEgsQ0FBQyxDQUFDO0lBRUgsUUFBUSxDQUFDLFlBQVksQ0FBQztRQUNwQixNQUFNLEVBQUUsTUFBTTtRQUNkLElBQUksRUFBRSxNQUFNO1FBQ1osT0FBTyxFQUFFLHdDQUF3QztRQUNqRCxXQUFXLEVBQUUsaUdBQWlHO1FBQzlHLElBQUk7UUFDSixRQUFRLEVBQUUsSUFBSTtRQUNkLFNBQVMsRUFBRTtZQUNULEdBQUcsRUFBRSxFQUFFLFdBQVcsRUFBRSw4QkFBOEIsRUFBRTtZQUNwRCxHQUFHLEVBQUUsRUFBRSxXQUFXLEVBQUUsZ0NBQWdDLEVBQUU7WUFDdEQsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLHdDQUF3QyxFQUFFO1NBQy9EO0tBQ0YsQ0FBQyxDQUFDO0lBRUgsUUFBUSxDQUFDLFlBQVksQ0FBQztRQUNwQixNQUFNLEVBQUUsTUFBTTtRQUNkLElBQUksRUFBRSxhQUFhO1FBQ25CLE9BQU8sRUFBRSxxQ0FBcUM7UUFDOUMsV0FBVyxFQUFFLDhGQUE4RjtRQUMzRyxJQUFJO1FBQ0osUUFBUSxFQUFFLElBQUk7UUFDZCxTQUFTLEVBQUU7WUFDVCxHQUFHLEVBQUUsRUFBRSxXQUFXLEVBQUUsOENBQThDLEVBQUU7WUFDcEUsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLGdDQUFnQyxFQUFFO1lBQ3RELEdBQUcsRUFBRSxFQUFFLFdBQVcsRUFBRSx3Q0FBd0MsRUFBRTtTQUMvRDtLQUNGLENBQUMsQ0FBQztJQUVILFFBQVEsQ0FBQyxZQUFZLENBQUM7UUFDcEIsTUFBTSxFQUFFLE1BQU07UUFDZCxJQUFJLEVBQUUsbUJBQW1CO1FBQ3pCLE9BQU8sRUFBRSxxQ0FBcUM7UUFDOUMsV0FBVyxFQUFFLG9OQUFvTjtRQUNqTyxJQUFJO1FBQ0osUUFBUSxFQUFFLElBQUk7UUFDZCxTQUFTLEVBQUU7WUFDVCxHQUFHLEVBQUUsRUFBRSxXQUFXLEVBQUUsOERBQThELEVBQUU7WUFDcEYsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLGdDQUFnQyxFQUFFO1lBQ3RELEdBQUcsRUFBRSxFQUFFLFdBQVcsRUFBRSx3Q0FBd0MsRUFBRTtTQUMvRDtLQUNGLENBQUMsQ0FBQztBQUNMLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgcmVnaXN0cnkgfSBmcm9tICcuLi9yZWdpc3RyeS5qcyc7XG5cbmNvbnN0IHRhZ3MgPSBbJ0FzayddO1xuY29uc3QgYXV0aCA9IFt7IGJlYXJlckF1dGg6IFtdIH1dO1xuXG4vKipcbiAqIE9wZW5BUEkgZGVmaW5pdGlvbnMgZm9yIHRoZSBcIkFza1wiIGFzc2lzdGFudCBzZXJ2aWNlIChhcGkvYXNrKSwgbW91bnRlZCBhdFxuICogYC9hc2tgLiBBbGwgcm91dGVzIGFyZSBnYXRlZCBieSB0aGUgYGFpX2dlbmVyYXRpb25gIGZlYXR1cmUgYW5kIGFyZSBSRUFEIC9cbiAqIFBST1BPU0Ugb25seSDigJQgdGhlIGFnZW50IG5ldmVyIG11dGF0ZXM7IGBwcm9wb3NlXypgIHRvb2wgcmVzdWx0cyBhcmUgZHJhZnRzIHRoZVxuICogdXNlciBjb25maXJtcyBpbiB0aGUgVUkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZWdpc3RlckFza1JvdXRlcygpOiB2b2lkIHtcbiAgcmVnaXN0cnkucmVnaXN0ZXJQYXRoKHtcbiAgICBtZXRob2Q6ICdnZXQnLFxuICAgIHBhdGg6ICcvYXNrL3Byb3ZpZGVycycsXG4gICAgc3VtbWFyeTogJ0xpc3QgY29uZmlndXJlZCBBSSBwcm92aWRlcnMnLFxuICAgIGRlc2NyaXB0aW9uOiAnVGhlIEFJIHByb3ZpZGVycy9tb2RlbHMgYXZhaWxhYmxlIHRvIHRoZSBBc2sgYXNzaXN0YW50IChmcm9tIHNlcnZlciBlbnYg4oCUIGNsb3VkIGtleXMgYW5kL29yIHRoZSBPcGVuQUktY29tcGF0aWJsZSBsb2NhbCBlbmRwb2ludCkuJyxcbiAgICB0YWdzLFxuICAgIHNlY3VyaXR5OiBhdXRoLFxuICAgIHJlc3BvbnNlczogeyAyMDA6IHsgZGVzY3JpcHRpb246ICdDb25maWd1cmVkIHByb3ZpZGVycycgfSwgNDAzOiB7IGRlc2NyaXB0aW9uOiAnYWlfZ2VuZXJhdGlvbiBmZWF0dXJlIHJlcXVpcmVkJyB9IH0sXG4gIH0pO1xuXG4gIHJlZ2lzdHJ5LnJlZ2lzdGVyUGF0aCh7XG4gICAgbWV0aG9kOiAncG9zdCcsXG4gICAgcGF0aDogJy9hc2snLFxuICAgIHN1bW1hcnk6ICdHcm91bmRlZCBob3ctdG8gYW5zd2VyIChub24tc3RyZWFtaW5nKScsXG4gICAgZGVzY3JpcHRpb246ICdBbnN3ZXIgYSBwbGF0Zm9ybSBob3ctdG8gcXVlc3Rpb24gZ3JvdW5kZWQgaW4gdGhlIGRvY3MgY29ycHVzLiBSZXNlcnZlcyBvbmUgYWlDYWxscyBxdW90YSBzbG90LicsXG4gICAgdGFncyxcbiAgICBzZWN1cml0eTogYXV0aCxcbiAgICByZXNwb25zZXM6IHtcbiAgICAgIDIwMDogeyBkZXNjcmlwdGlvbjogJ0Fuc3dlciB3aXRoIGdyb3VuZGVkIHNvdXJjZXMnIH0sXG4gICAgICA0MDM6IHsgZGVzY3JpcHRpb246ICdhaV9nZW5lcmF0aW9uIGZlYXR1cmUgcmVxdWlyZWQnIH0sXG4gICAgICA0Mjk6IHsgZGVzY3JpcHRpb246ICdhaUNhbGxzIHF1b3RhIGV4Y2VlZGVkIG9yIHJhdGUgbGltaXRlZCcgfSxcbiAgICB9LFxuICB9KTtcblxuICByZWdpc3RyeS5yZWdpc3RlclBhdGgoe1xuICAgIG1ldGhvZDogJ3Bvc3QnLFxuICAgIHBhdGg6ICcvYXNrL3N0cmVhbScsXG4gICAgc3VtbWFyeTogJ0dyb3VuZGVkIGhvdy10byBhbnN3ZXIgKFNTRSBzdHJlYW0pJyxcbiAgICBkZXNjcmlwdGlvbjogJ1NhbWUgYXMgUE9TVCAvYXNrIGJ1dCBzdHJlYW1zIHRoZSBncm91bmRlZCBzb3VyY2VzIHRoZW4gYW5zd2VyIHRva2VucyBhcyBTZXJ2ZXItU2VudCBFdmVudHMuJyxcbiAgICB0YWdzLFxuICAgIHNlY3VyaXR5OiBhdXRoLFxuICAgIHJlc3BvbnNlczoge1xuICAgICAgMjAwOiB7IGRlc2NyaXB0aW9uOiAndGV4dC9ldmVudC1zdHJlYW0gb2Ygc291cmNlcyArIGFuc3dlciB0b2tlbnMnIH0sXG4gICAgICA0MDM6IHsgZGVzY3JpcHRpb246ICdhaV9nZW5lcmF0aW9uIGZlYXR1cmUgcmVxdWlyZWQnIH0sXG4gICAgICA0Mjk6IHsgZGVzY3JpcHRpb246ICdhaUNhbGxzIHF1b3RhIGV4Y2VlZGVkIG9yIHJhdGUgbGltaXRlZCcgfSxcbiAgICB9LFxuICB9KTtcblxuICByZWdpc3RyeS5yZWdpc3RlclBhdGgoe1xuICAgIG1ldGhvZDogJ3Bvc3QnLFxuICAgIHBhdGg6ICcvYXNrL2FnZW50L3N0cmVhbScsXG4gICAgc3VtbWFyeTogJ1Rvb2wtY2FsbGluZyBBc2sgYWdlbnQgKFNTRSBzdHJlYW0pJyxcbiAgICBkZXNjcmlwdGlvbjogJ1J1bnMgdGhlIHRvb2wtY2FsbGluZyBhc3Npc3RhbnQgbG9vcCBhbmQgc3RyZWFtcyByZWFzb25pbmcgdG9rZW5zLCB0b29sLWNhbGwgZXZlbnRzLCBhbmQgcmV2aWV3YWJsZSBgcHJvcG9zZV8qYCBkcmFmdHMgKHBpcGVsaW5lL3BsdWdpbi90ZW1wbGF0ZSkgYXMgU1NFLiBSZWFkLW9ubHk6IHByb3Bvc2FscyBhcmUgY29uZmlybWVkIHNlcGFyYXRlbHkgaW4gdGhlIFVJLicsXG4gICAgdGFncyxcbiAgICBzZWN1cml0eTogYXV0aCxcbiAgICByZXNwb25zZXM6IHtcbiAgICAgIDIwMDogeyBkZXNjcmlwdGlvbjogJ3RleHQvZXZlbnQtc3RyZWFtIG9mIHRva2VucywgdG9vbC1jYWxscywgYW5kIHByb3Bvc2FsIGRyYWZ0cycgfSxcbiAgICAgIDQwMzogeyBkZXNjcmlwdGlvbjogJ2FpX2dlbmVyYXRpb24gZmVhdHVyZSByZXF1aXJlZCcgfSxcbiAgICAgIDQyOTogeyBkZXNjcmlwdGlvbjogJ2FpQ2FsbHMgcXVvdGEgZXhjZWVkZWQgb3IgcmF0ZSBsaW1pdGVkJyB9LFxuICAgIH0sXG4gIH0pO1xufVxuIl19
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function registerBillingRoutes(): void;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { registry } from '../registry.js';
|
|
4
|
+
const tags = ['Billing'];
|
|
5
|
+
const auth = [{ bearerAuth: [] }];
|
|
6
|
+
export function registerBillingRoutes() {
|
|
7
|
+
registry.registerPath({
|
|
8
|
+
method: 'get',
|
|
9
|
+
path: '/billing/plans',
|
|
10
|
+
summary: 'List active plans',
|
|
11
|
+
description: 'List all active billing plans. No authentication required.',
|
|
12
|
+
tags,
|
|
13
|
+
responses: { 200: { description: 'List of active billing plans' } },
|
|
14
|
+
});
|
|
15
|
+
registry.registerPath({
|
|
16
|
+
method: 'get',
|
|
17
|
+
path: '/billing/plans/{planId}',
|
|
18
|
+
summary: 'Get plan by ID',
|
|
19
|
+
tags,
|
|
20
|
+
responses: { 200: { description: 'Plan details' }, 404: { description: 'Plan not found' } },
|
|
21
|
+
});
|
|
22
|
+
registry.registerPath({
|
|
23
|
+
method: 'post',
|
|
24
|
+
path: '/billing/subscriptions',
|
|
25
|
+
summary: 'Create subscription',
|
|
26
|
+
description: 'Create a new billing subscription for the current organization.',
|
|
27
|
+
tags,
|
|
28
|
+
security: auth,
|
|
29
|
+
responses: { 201: { description: 'Subscription created' }, 400: { description: 'Validation error' } },
|
|
30
|
+
});
|
|
31
|
+
registry.registerPath({
|
|
32
|
+
method: 'get',
|
|
33
|
+
path: '/billing/subscriptions',
|
|
34
|
+
summary: 'List subscriptions',
|
|
35
|
+
description: 'List billing subscriptions for the current organization.',
|
|
36
|
+
tags,
|
|
37
|
+
security: auth,
|
|
38
|
+
responses: { 200: { description: 'List of subscriptions' } },
|
|
39
|
+
});
|
|
40
|
+
registry.registerPath({
|
|
41
|
+
method: 'put',
|
|
42
|
+
path: '/billing/subscriptions/{id}',
|
|
43
|
+
summary: 'Update subscription',
|
|
44
|
+
tags,
|
|
45
|
+
security: auth,
|
|
46
|
+
responses: { 200: { description: 'Subscription updated' }, 404: { description: 'Not found' } },
|
|
47
|
+
});
|
|
48
|
+
registry.registerPath({
|
|
49
|
+
method: 'delete',
|
|
50
|
+
path: '/billing/subscriptions/{id}',
|
|
51
|
+
summary: 'Cancel subscription',
|
|
52
|
+
tags,
|
|
53
|
+
security: auth,
|
|
54
|
+
responses: { 200: { description: 'Subscription cancelled' }, 404: { description: 'Not found' } },
|
|
55
|
+
});
|
|
56
|
+
registry.registerPath({
|
|
57
|
+
method: 'post',
|
|
58
|
+
path: '/billing/marketplace',
|
|
59
|
+
summary: 'AWS Marketplace integration',
|
|
60
|
+
description: 'Handle AWS Marketplace subscription events.',
|
|
61
|
+
tags,
|
|
62
|
+
security: auth,
|
|
63
|
+
responses: { 200: { description: 'Marketplace event processed' } },
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmlsbGluZy1yb3V0ZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvb3BlbmFwaS9yb3V0ZXMvYmlsbGluZy1yb3V0ZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0QyxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFMUMsTUFBTSxJQUFJLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUN6QixNQUFNLElBQUksR0FBRyxDQUFDLEVBQUUsVUFBVSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFFbEMsTUFBTSxVQUFVLHFCQUFxQjtJQUNuQyxRQUFRLENBQUMsWUFBWSxDQUFDO1FBQ3BCLE1BQU0sRUFBRSxLQUFLO1FBQ2IsSUFBSSxFQUFFLGdCQUFnQjtRQUN0QixPQUFPLEVBQUUsbUJBQW1CO1FBQzVCLFdBQVcsRUFBRSw0REFBNEQ7UUFDekUsSUFBSTtRQUNKLFNBQVMsRUFBRSxFQUFFLEdBQUcsRUFBRSxFQUFFLFdBQVcsRUFBRSw4QkFBOEIsRUFBRSxFQUFFO0tBQ3BFLENBQUMsQ0FBQztJQUVILFFBQVEsQ0FBQyxZQUFZLENBQUM7UUFDcEIsTUFBTSxFQUFFLEtBQUs7UUFDYixJQUFJLEVBQUUseUJBQXlCO1FBQy9CLE9BQU8sRUFBRSxnQkFBZ0I7UUFDekIsSUFBSTtRQUNKLFNBQVMsRUFBRSxFQUFFLEdBQUcsRUFBRSxFQUFFLFdBQVcsRUFBRSxjQUFjLEVBQUUsRUFBRSxHQUFHLEVBQUUsRUFBRSxXQUFXLEVBQUUsZ0JBQWdCLEVBQUUsRUFBRTtLQUM1RixDQUFDLENBQUM7SUFFSCxRQUFRLENBQUMsWUFBWSxDQUFDO1FBQ3BCLE1BQU0sRUFBRSxNQUFNO1FBQ2QsSUFBSSxFQUFFLHdCQUF3QjtRQUM5QixPQUFPLEVBQUUscUJBQXFCO1FBQzlCLFdBQVcsRUFBRSxpRUFBaUU7UUFDOUUsSUFBSTtRQUNKLFFBQVEsRUFBRSxJQUFJO1FBQ2QsU0FBUyxFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLHNCQUFzQixFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLGtCQUFrQixFQUFFLEVBQUU7S0FDdEcsQ0FBQyxDQUFDO0lBRUgsUUFBUSxDQUFDLFlBQVksQ0FBQztRQUNwQixNQUFNLEVBQUUsS0FBSztRQUNiLElBQUksRUFBRSx3QkFBd0I7UUFDOUIsT0FBTyxFQUFFLG9CQUFvQjtRQUM3QixXQUFXLEVBQUUsMERBQTBEO1FBQ3ZFLElBQUk7UUFDSixRQUFRLEVBQUUsSUFBSTtRQUNkLFNBQVMsRUFBRSxFQUFFLEdBQUcsRUFBRSxFQUFFLFdBQVcsRUFBRSx1QkFBdUIsRUFBRSxFQUFFO0tBQzdELENBQUMsQ0FBQztJQUVILFFBQVEsQ0FBQyxZQUFZLENBQUM7UUFDcEIsTUFBTSxFQUFFLEtBQUs7UUFDYixJQUFJLEVBQUUsNkJBQTZCO1FBQ25DLE9BQU8sRUFBRSxxQkFBcUI7UUFDOUIsSUFBSTtRQUNKLFFBQVEsRUFBRSxJQUFJO1FBQ2QsU0FBUyxFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLHNCQUFzQixFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLFdBQVcsRUFBRSxFQUFFO0tBQy9GLENBQUMsQ0FBQztJQUVILFFBQVEsQ0FBQyxZQUFZLENBQUM7UUFDcEIsTUFBTSxFQUFFLFFBQVE7UUFDaEIsSUFBSSxFQUFFLDZCQUE2QjtRQUNuQyxPQUFPLEVBQUUscUJBQXFCO1FBQzlCLElBQUk7UUFDSixRQUFRLEVBQUUsSUFBSTtRQUNkLFNBQVMsRUFBRSxFQUFFLEdBQUcsRUFBRSxFQUFFLFdBQVcsRUFBRSx3QkFBd0IsRUFBRSxFQUFFLEdBQUcsRUFBRSxFQUFFLFdBQVcsRUFBRSxXQUFXLEVBQUUsRUFBRTtLQUNqRyxDQUFDLENBQUM7SUFFSCxRQUFRLENBQUMsWUFBWSxDQUFDO1FBQ3BCLE1BQU0sRUFBRSxNQUFNO1FBQ2QsSUFBSSxFQUFFLHNCQUFzQjtRQUM1QixPQUFPLEVBQUUsNkJBQTZCO1FBQ3RDLFdBQVcsRUFBRSw2Q0FBNkM7UUFDMUQsSUFBSTtRQUNKLFFBQVEsRUFBRSxJQUFJO1FBQ2QsU0FBUyxFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLDZCQUE2QixFQUFFLEVBQUU7S0FDbkUsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5pbXBvcnQgeyByZWdpc3RyeSB9IGZyb20gJy4uL3JlZ2lzdHJ5LmpzJztcblxuY29uc3QgdGFncyA9IFsnQmlsbGluZyddO1xuY29uc3QgYXV0aCA9IFt7IGJlYXJlckF1dGg6IFtdIH1dO1xuXG5leHBvcnQgZnVuY3Rpb24gcmVnaXN0ZXJCaWxsaW5nUm91dGVzKCk6IHZvaWQge1xuICByZWdpc3RyeS5yZWdpc3RlclBhdGgoe1xuICAgIG1ldGhvZDogJ2dldCcsXG4gICAgcGF0aDogJy9iaWxsaW5nL3BsYW5zJyxcbiAgICBzdW1tYXJ5OiAnTGlzdCBhY3RpdmUgcGxhbnMnLFxuICAgIGRlc2NyaXB0aW9uOiAnTGlzdCBhbGwgYWN0aXZlIGJpbGxpbmcgcGxhbnMuIE5vIGF1dGhlbnRpY2F0aW9uIHJlcXVpcmVkLicsXG4gICAgdGFncyxcbiAgICByZXNwb25zZXM6IHsgMjAwOiB7IGRlc2NyaXB0aW9uOiAnTGlzdCBvZiBhY3RpdmUgYmlsbGluZyBwbGFucycgfSB9LFxuICB9KTtcblxuICByZWdpc3RyeS5yZWdpc3RlclBhdGgoe1xuICAgIG1ldGhvZDogJ2dldCcsXG4gICAgcGF0aDogJy9iaWxsaW5nL3BsYW5zL3twbGFuSWR9JyxcbiAgICBzdW1tYXJ5OiAnR2V0IHBsYW4gYnkgSUQnLFxuICAgIHRhZ3MsXG4gICAgcmVzcG9uc2VzOiB7IDIwMDogeyBkZXNjcmlwdGlvbjogJ1BsYW4gZGV0YWlscycgfSwgNDA0OiB7IGRlc2NyaXB0aW9uOiAnUGxhbiBub3QgZm91bmQnIH0gfSxcbiAgfSk7XG5cbiAgcmVnaXN0cnkucmVnaXN0ZXJQYXRoKHtcbiAgICBtZXRob2Q6ICdwb3N0JyxcbiAgICBwYXRoOiAnL2JpbGxpbmcvc3Vic2NyaXB0aW9ucycsXG4gICAgc3VtbWFyeTogJ0NyZWF0ZSBzdWJzY3JpcHRpb24nLFxuICAgIGRlc2NyaXB0aW9uOiAnQ3JlYXRlIGEgbmV3IGJpbGxpbmcgc3Vic2NyaXB0aW9uIGZvciB0aGUgY3VycmVudCBvcmdhbml6YXRpb24uJyxcbiAgICB0YWdzLFxuICAgIHNlY3VyaXR5OiBhdXRoLFxuICAgIHJlc3BvbnNlczogeyAyMDE6IHsgZGVzY3JpcHRpb246ICdTdWJzY3JpcHRpb24gY3JlYXRlZCcgfSwgNDAwOiB7IGRlc2NyaXB0aW9uOiAnVmFsaWRhdGlvbiBlcnJvcicgfSB9LFxuICB9KTtcblxuICByZWdpc3RyeS5yZWdpc3RlclBhdGgoe1xuICAgIG1ldGhvZDogJ2dldCcsXG4gICAgcGF0aDogJy9iaWxsaW5nL3N1YnNjcmlwdGlvbnMnLFxuICAgIHN1bW1hcnk6ICdMaXN0IHN1YnNjcmlwdGlvbnMnLFxuICAgIGRlc2NyaXB0aW9uOiAnTGlzdCBiaWxsaW5nIHN1YnNjcmlwdGlvbnMgZm9yIHRoZSBjdXJyZW50IG9yZ2FuaXphdGlvbi4nLFxuICAgIHRhZ3MsXG4gICAgc2VjdXJpdHk6IGF1dGgsXG4gICAgcmVzcG9uc2VzOiB7IDIwMDogeyBkZXNjcmlwdGlvbjogJ0xpc3Qgb2Ygc3Vic2NyaXB0aW9ucycgfSB9LFxuICB9KTtcblxuICByZWdpc3RyeS5yZWdpc3RlclBhdGgoe1xuICAgIG1ldGhvZDogJ3B1dCcsXG4gICAgcGF0aDogJy9iaWxsaW5nL3N1YnNjcmlwdGlvbnMve2lkfScsXG4gICAgc3VtbWFyeTogJ1VwZGF0ZSBzdWJzY3JpcHRpb24nLFxuICAgIHRhZ3MsXG4gICAgc2VjdXJpdHk6IGF1dGgsXG4gICAgcmVzcG9uc2VzOiB7IDIwMDogeyBkZXNjcmlwdGlvbjogJ1N1YnNjcmlwdGlvbiB1cGRhdGVkJyB9LCA0MDQ6IHsgZGVzY3JpcHRpb246ICdOb3QgZm91bmQnIH0gfSxcbiAgfSk7XG5cbiAgcmVnaXN0cnkucmVnaXN0ZXJQYXRoKHtcbiAgICBtZXRob2Q6ICdkZWxldGUnLFxuICAgIHBhdGg6ICcvYmlsbGluZy9zdWJzY3JpcHRpb25zL3tpZH0nLFxuICAgIHN1bW1hcnk6ICdDYW5jZWwgc3Vic2NyaXB0aW9uJyxcbiAgICB0YWdzLFxuICAgIHNlY3VyaXR5OiBhdXRoLFxuICAgIHJlc3BvbnNlczogeyAyMDA6IHsgZGVzY3JpcHRpb246ICdTdWJzY3JpcHRpb24gY2FuY2VsbGVkJyB9LCA0MDQ6IHsgZGVzY3JpcHRpb246ICdOb3QgZm91bmQnIH0gfSxcbiAgfSk7XG5cbiAgcmVnaXN0cnkucmVnaXN0ZXJQYXRoKHtcbiAgICBtZXRob2Q6ICdwb3N0JyxcbiAgICBwYXRoOiAnL2JpbGxpbmcvbWFya2V0cGxhY2UnLFxuICAgIHN1bW1hcnk6ICdBV1MgTWFya2V0cGxhY2UgaW50ZWdyYXRpb24nLFxuICAgIGRlc2NyaXB0aW9uOiAnSGFuZGxlIEFXUyBNYXJrZXRwbGFjZSBzdWJzY3JpcHRpb24gZXZlbnRzLicsXG4gICAgdGFncyxcbiAgICBzZWN1cml0eTogYXV0aCxcbiAgICByZXNwb25zZXM6IHsgMjAwOiB7IGRlc2NyaXB0aW9uOiAnTWFya2V0cGxhY2UgZXZlbnQgcHJvY2Vzc2VkJyB9IH0sXG4gIH0pO1xufVxuIl19
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAPI definitions for the image-registry service (api/image-registry): the
|
|
3
|
+
* Docker-registry token endpoint (`/token`), the org-scoped image API
|
|
4
|
+
* (`/api/images`), and the admin API (`/api/admin`). Image read/write is gated by
|
|
5
|
+
* the `registry:read` / `registry:write` permissions.
|
|
6
|
+
*/
|
|
7
|
+
export declare function registerImageRegistryRoutes(): void;
|