@stratum-hq/control-plane 0.1.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.
Files changed (133) hide show
  1. package/dist/__tests__/api-keys.test.d.ts +2 -0
  2. package/dist/__tests__/api-keys.test.d.ts.map +1 -0
  3. package/dist/__tests__/api-keys.test.js +240 -0
  4. package/dist/__tests__/api-keys.test.js.map +1 -0
  5. package/dist/__tests__/auth.test.d.ts +2 -0
  6. package/dist/__tests__/auth.test.d.ts.map +1 -0
  7. package/dist/__tests__/auth.test.js +227 -0
  8. package/dist/__tests__/auth.test.js.map +1 -0
  9. package/dist/__tests__/config.test.d.ts +2 -0
  10. package/dist/__tests__/config.test.d.ts.map +1 -0
  11. package/dist/__tests__/config.test.js +201 -0
  12. package/dist/__tests__/config.test.js.map +1 -0
  13. package/dist/__tests__/tenants.test.d.ts +2 -0
  14. package/dist/__tests__/tenants.test.d.ts.map +1 -0
  15. package/dist/__tests__/tenants.test.js +214 -0
  16. package/dist/__tests__/tenants.test.js.map +1 -0
  17. package/dist/__tests__/test-helpers.d.ts +71 -0
  18. package/dist/__tests__/test-helpers.d.ts.map +1 -0
  19. package/dist/__tests__/test-helpers.js +172 -0
  20. package/dist/__tests__/test-helpers.js.map +1 -0
  21. package/dist/app.d.ts +3 -0
  22. package/dist/app.d.ts.map +1 -0
  23. package/dist/app.js +114 -0
  24. package/dist/app.js.map +1 -0
  25. package/dist/config.d.ts +11 -0
  26. package/dist/config.d.ts.map +1 -0
  27. package/dist/config.js +26 -0
  28. package/dist/config.js.map +1 -0
  29. package/dist/db/connection.d.ts +4 -0
  30. package/dist/db/connection.d.ts.map +1 -0
  31. package/dist/db/connection.js +30 -0
  32. package/dist/db/connection.js.map +1 -0
  33. package/dist/db/migrate.d.ts +3 -0
  34. package/dist/db/migrate.d.ts.map +1 -0
  35. package/dist/db/migrate.js +56 -0
  36. package/dist/db/migrate.js.map +1 -0
  37. package/dist/db/regional-pool-router.d.ts +9 -0
  38. package/dist/db/regional-pool-router.d.ts.map +1 -0
  39. package/dist/db/regional-pool-router.js +29 -0
  40. package/dist/db/regional-pool-router.js.map +1 -0
  41. package/dist/index.d.ts +2 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +36 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/middleware/auth.d.ts +21 -0
  46. package/dist/middleware/auth.d.ts.map +1 -0
  47. package/dist/middleware/auth.js +70 -0
  48. package/dist/middleware/auth.js.map +1 -0
  49. package/dist/middleware/authorize.d.ts +3 -0
  50. package/dist/middleware/authorize.d.ts.map +1 -0
  51. package/dist/middleware/authorize.js +54 -0
  52. package/dist/middleware/authorize.js.map +1 -0
  53. package/dist/middleware/error-handler.d.ts +3 -0
  54. package/dist/middleware/error-handler.d.ts.map +1 -0
  55. package/dist/middleware/error-handler.js +48 -0
  56. package/dist/middleware/error-handler.js.map +1 -0
  57. package/dist/middleware/per-key-rate-limit.d.ts +3 -0
  58. package/dist/middleware/per-key-rate-limit.d.ts.map +1 -0
  59. package/dist/middleware/per-key-rate-limit.js +62 -0
  60. package/dist/middleware/per-key-rate-limit.js.map +1 -0
  61. package/dist/middleware/rate-limit-redis.d.ts +16 -0
  62. package/dist/middleware/rate-limit-redis.d.ts.map +1 -0
  63. package/dist/middleware/rate-limit-redis.js +118 -0
  64. package/dist/middleware/rate-limit-redis.js.map +1 -0
  65. package/dist/middleware/telemetry.d.ts +14 -0
  66. package/dist/middleware/telemetry.d.ts.map +1 -0
  67. package/dist/middleware/telemetry.js +81 -0
  68. package/dist/middleware/telemetry.js.map +1 -0
  69. package/dist/middleware/tenant-scope.d.ts +20 -0
  70. package/dist/middleware/tenant-scope.d.ts.map +1 -0
  71. package/dist/middleware/tenant-scope.js +60 -0
  72. package/dist/middleware/tenant-scope.js.map +1 -0
  73. package/dist/openapi.d.ts +3 -0
  74. package/dist/openapi.d.ts.map +1 -0
  75. package/dist/openapi.js +44 -0
  76. package/dist/openapi.js.map +1 -0
  77. package/dist/redis.d.ts +16 -0
  78. package/dist/redis.d.ts.map +1 -0
  79. package/dist/redis.js +71 -0
  80. package/dist/redis.js.map +1 -0
  81. package/dist/routes/api-keys.d.ts +4 -0
  82. package/dist/routes/api-keys.d.ts.map +1 -0
  83. package/dist/routes/api-keys.js +78 -0
  84. package/dist/routes/api-keys.js.map +1 -0
  85. package/dist/routes/audit-logs.d.ts +6 -0
  86. package/dist/routes/audit-logs.d.ts.map +1 -0
  87. package/dist/routes/audit-logs.js +43 -0
  88. package/dist/routes/audit-logs.js.map +1 -0
  89. package/dist/routes/config-diff.d.ts +4 -0
  90. package/dist/routes/config-diff.d.ts.map +1 -0
  91. package/dist/routes/config-diff.js +33 -0
  92. package/dist/routes/config-diff.js.map +1 -0
  93. package/dist/routes/config.d.ts +4 -0
  94. package/dist/routes/config.d.ts.map +1 -0
  95. package/dist/routes/config.js +54 -0
  96. package/dist/routes/config.js.map +1 -0
  97. package/dist/routes/consent.d.ts +4 -0
  98. package/dist/routes/consent.d.ts.map +1 -0
  99. package/dist/routes/consent.js +45 -0
  100. package/dist/routes/consent.js.map +1 -0
  101. package/dist/routes/health.d.ts +9 -0
  102. package/dist/routes/health.d.ts.map +1 -0
  103. package/dist/routes/health.js +32 -0
  104. package/dist/routes/health.js.map +1 -0
  105. package/dist/routes/maintenance.d.ts +4 -0
  106. package/dist/routes/maintenance.d.ts.map +1 -0
  107. package/dist/routes/maintenance.js +35 -0
  108. package/dist/routes/maintenance.js.map +1 -0
  109. package/dist/routes/permissions.d.ts +4 -0
  110. package/dist/routes/permissions.d.ts.map +1 -0
  111. package/dist/routes/permissions.js +36 -0
  112. package/dist/routes/permissions.js.map +1 -0
  113. package/dist/routes/regions.d.ts +4 -0
  114. package/dist/routes/regions.d.ts.map +1 -0
  115. package/dist/routes/regions.js +38 -0
  116. package/dist/routes/regions.js.map +1 -0
  117. package/dist/routes/roles.d.ts +4 -0
  118. package/dist/routes/roles.d.ts.map +1 -0
  119. package/dist/routes/roles.js +95 -0
  120. package/dist/routes/roles.js.map +1 -0
  121. package/dist/routes/tenants.d.ts +4 -0
  122. package/dist/routes/tenants.d.ts.map +1 -0
  123. package/dist/routes/tenants.js +112 -0
  124. package/dist/routes/tenants.js.map +1 -0
  125. package/dist/routes/webhooks.d.ts +4 -0
  126. package/dist/routes/webhooks.d.ts.map +1 -0
  127. package/dist/routes/webhooks.js +116 -0
  128. package/dist/routes/webhooks.js.map +1 -0
  129. package/dist/services/isolation-service.d.ts +22 -0
  130. package/dist/services/isolation-service.d.ts.map +1 -0
  131. package/dist/services/isolation-service.js +94 -0
  132. package/dist/services/isolation-service.js.map +1 -0
  133. package/package.json +48 -0
@@ -0,0 +1,44 @@
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.registerOpenApi = void 0;
7
+ const swagger_1 = __importDefault(require("@fastify/swagger"));
8
+ const swagger_ui_1 = __importDefault(require("@fastify/swagger-ui"));
9
+ async function registerOpenApi(app) {
10
+ await app.register(swagger_1.default, {
11
+ openapi: {
12
+ info: {
13
+ title: "Stratum Control Plane API",
14
+ version: "0.1.0",
15
+ description: "Universal Tenant Context Engine - Control Plane",
16
+ },
17
+ components: {
18
+ securitySchemes: {
19
+ apiKey: {
20
+ type: "apiKey",
21
+ in: "header",
22
+ name: "X-API-Key",
23
+ },
24
+ bearerAuth: {
25
+ type: "http",
26
+ scheme: "bearer",
27
+ bearerFormat: "JWT",
28
+ },
29
+ },
30
+ },
31
+ security: [{ apiKey: [] }, { bearerAuth: [] }],
32
+ },
33
+ });
34
+ await app.register(swagger_ui_1.default, {
35
+ routePrefix: "/api/docs",
36
+ uiConfig: {
37
+ docExpansion: "list",
38
+ deepLinking: true,
39
+ },
40
+ staticCSP: false,
41
+ });
42
+ }
43
+ exports.registerOpenApi = registerOpenApi;
44
+ //# sourceMappingURL=openapi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi.js","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":";;;;;;AACA,+DAAuC;AACvC,qEAA4C;AAErC,KAAK,UAAU,eAAe,CAAC,GAAoB;IACxD,MAAM,GAAG,CAAC,QAAQ,CAAC,iBAAc,EAAE;QACjC,OAAO,EAAE;YACP,IAAI,EAAE;gBACJ,KAAK,EAAE,2BAA2B;gBAClC,OAAO,EAAE,OAAO;gBAChB,WAAW,EAAE,iDAAiD;aAC/D;YACD,UAAU,EAAE;gBACV,eAAe,EAAE;oBACf,MAAM,EAAE;wBACN,IAAI,EAAE,QAAQ;wBACd,EAAE,EAAE,QAAQ;wBACZ,IAAI,EAAE,WAAW;qBAClB;oBACD,UAAU,EAAE;wBACV,IAAI,EAAE,MAAM;wBACZ,MAAM,EAAE,QAAQ;wBAChB,YAAY,EAAE,KAAK;qBACpB;iBACF;aACF;YACD,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;SAC/C;KACF,CAAC,CAAC;IAEH,MAAM,GAAG,CAAC,QAAQ,CAAC,oBAAgB,EAAE;QACnC,WAAW,EAAE,WAAW;QACxB,QAAQ,EAAE;YACR,YAAY,EAAE,MAAM;YACpB,WAAW,EAAE,IAAI;SAClB;QACD,SAAS,EAAE,KAAK;KACjB,CAAC,CAAC;AACL,CAAC;AAlCD,0CAkCC"}
@@ -0,0 +1,16 @@
1
+ import Redis from "ioredis";
2
+ /**
3
+ * Create a Redis client from the REDIS_URL env var.
4
+ * Returns null if REDIS_URL is not configured.
5
+ * Connection errors are logged but never crash the process (fail-open).
6
+ */
7
+ export declare function createRedisClient(): Redis | null;
8
+ /**
9
+ * Get the current Redis client singleton (may be null).
10
+ */
11
+ export declare function getRedisClient(): Redis | null;
12
+ /**
13
+ * Health check: returns "connected", "disconnected", or "not_configured".
14
+ */
15
+ export declare function checkRedisHealth(): Promise<"connected" | "disconnected" | "not_configured">;
16
+ //# sourceMappingURL=redis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../src/redis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,SAAS,CAAC;AAK5B;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,KAAK,GAAG,IAAI,CAiChD;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,KAAK,GAAG,IAAI,CAE7C;AAED;;GAEG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,WAAW,GAAG,cAAc,GAAG,gBAAgB,CAAC,CAgBjG"}
package/dist/redis.js ADDED
@@ -0,0 +1,71 @@
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.checkRedisHealth = exports.getRedisClient = exports.createRedisClient = void 0;
7
+ const ioredis_1 = __importDefault(require("ioredis"));
8
+ const config_js_1 = require("./config.js");
9
+ let redisClient = null;
10
+ /**
11
+ * Create a Redis client from the REDIS_URL env var.
12
+ * Returns null if REDIS_URL is not configured.
13
+ * Connection errors are logged but never crash the process (fail-open).
14
+ */
15
+ function createRedisClient() {
16
+ if (!config_js_1.config.redisUrl) {
17
+ return null;
18
+ }
19
+ const client = new ioredis_1.default(config_js_1.config.redisUrl, {
20
+ maxRetriesPerRequest: 1,
21
+ retryStrategy(times) {
22
+ if (times > 3) {
23
+ // Stop retrying after 3 attempts — the app can run without Redis
24
+ return null;
25
+ }
26
+ return Math.min(times * 200, 2000);
27
+ },
28
+ enableOfflineQueue: false,
29
+ lazyConnect: true,
30
+ });
31
+ client.on("error", (err) => {
32
+ console.warn("[stratum] Redis connection error (rate limiting will fall back to in-memory):", err.message);
33
+ });
34
+ client.on("connect", () => {
35
+ console.info("[stratum] Redis connected — using Redis-backed rate limiting.");
36
+ });
37
+ // Attempt to connect; failures are non-fatal
38
+ client.connect().catch((err) => {
39
+ console.warn("[stratum] Redis initial connection failed (continuing without Redis):", err.message);
40
+ });
41
+ redisClient = client;
42
+ return client;
43
+ }
44
+ exports.createRedisClient = createRedisClient;
45
+ /**
46
+ * Get the current Redis client singleton (may be null).
47
+ */
48
+ function getRedisClient() {
49
+ return redisClient;
50
+ }
51
+ exports.getRedisClient = getRedisClient;
52
+ /**
53
+ * Health check: returns "connected", "disconnected", or "not_configured".
54
+ */
55
+ async function checkRedisHealth() {
56
+ if (!redisClient) {
57
+ return "not_configured";
58
+ }
59
+ try {
60
+ const result = await Promise.race([
61
+ redisClient.ping(),
62
+ new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), 1000)),
63
+ ]);
64
+ return result === "PONG" ? "connected" : "disconnected";
65
+ }
66
+ catch {
67
+ return "disconnected";
68
+ }
69
+ }
70
+ exports.checkRedisHealth = checkRedisHealth;
71
+ //# sourceMappingURL=redis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis.js","sourceRoot":"","sources":["../src/redis.ts"],"names":[],"mappings":";;;;;;AAAA,sDAA4B;AAC5B,2CAAqC;AAErC,IAAI,WAAW,GAAiB,IAAI,CAAC;AAErC;;;;GAIG;AACH,SAAgB,iBAAiB;IAC/B,IAAI,CAAC,kBAAM,CAAC,QAAQ,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,iBAAK,CAAC,kBAAM,CAAC,QAAQ,EAAE;QACxC,oBAAoB,EAAE,CAAC;QACvB,aAAa,CAAC,KAAa;YACzB,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACd,iEAAiE;gBACjE,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,GAAG,EAAE,IAAI,CAAC,CAAC;QACrC,CAAC;QACD,kBAAkB,EAAE,KAAK;QACzB,WAAW,EAAE,IAAI;KAClB,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QACzB,OAAO,CAAC,IAAI,CAAC,+EAA+E,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IAC7G,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;QACxB,OAAO,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC;IAEH,6CAA6C;IAC7C,MAAM,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QAC7B,OAAO,CAAC,IAAI,CAAC,uEAAuE,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACrG,CAAC,CAAC,CAAC;IAEH,WAAW,GAAG,MAAM,CAAC;IACrB,OAAO,MAAM,CAAC;AAChB,CAAC;AAjCD,8CAiCC;AAED;;GAEG;AACH,SAAgB,cAAc;IAC5B,OAAO,WAAW,CAAC;AACrB,CAAC;AAFD,wCAEC;AAED;;GAEG;AACI,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;YAChC,WAAW,CAAC,IAAI,EAAE;YAClB,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC/B,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,CACrD;SACF,CAAC,CAAC;QACH,OAAO,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,cAAc,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,cAAc,CAAC;IACxB,CAAC;AACH,CAAC;AAhBD,4CAgBC"}
@@ -0,0 +1,4 @@
1
+ import { FastifyInstance } from "fastify";
2
+ import { Stratum } from "@stratum-hq/lib";
3
+ export declare function createApiKeyRoutes(stratum: Stratum): (app: FastifyInstance) => Promise<void>;
4
+ //# sourceMappingURL=api-keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-keys.d.ts","sourceRoot":"","sources":["../../src/routes/api-keys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAW1C,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,SACT,eAAe,KAAG,QAAQ,IAAI,CAAC,CAuExE"}
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createApiKeyRoutes = void 0;
4
+ const zod_1 = require("zod");
5
+ const tenant_scope_js_1 = require("../middleware/tenant-scope.js");
6
+ const createApiKeySchema = zod_1.z.object({
7
+ tenant_id: zod_1.z.string().uuid(),
8
+ name: zod_1.z.string().optional(),
9
+ rate_limit_max: zod_1.z.number().int().min(1).max(100_000).optional(),
10
+ rate_limit_window: zod_1.z.string().regex(/^\d+\s+(second|minute|hour|day)s?$/i).optional(),
11
+ });
12
+ function createApiKeyRoutes(stratum) {
13
+ return async function apiKeyRoutes(app) {
14
+ // POST /api/v1/api-keys — Create a new API key (plaintext returned once)
15
+ app.post("/", { preHandler: (0, tenant_scope_js_1.createTenantScopeGuard)(stratum, tenant_scope_js_1.fromBodyTenantId) }, async (request, reply) => {
16
+ const parsed = createApiKeySchema.safeParse(request.body);
17
+ if (!parsed.success) {
18
+ reply.status(400).send({ error: { code: "VALIDATION_ERROR", message: "Invalid request body" }, details: parsed.error.issues });
19
+ return;
20
+ }
21
+ const { tenant_id, name, rate_limit_max, rate_limit_window } = parsed.data;
22
+ const result = await stratum.createApiKey(tenant_id, {
23
+ name,
24
+ rateLimitMax: rate_limit_max,
25
+ rateLimitWindow: rate_limit_window,
26
+ });
27
+ reply.status(201).send(result);
28
+ });
29
+ // GET /api/v1/api-keys — List API keys (optional ?tenant_id=)
30
+ app.get("/", async (request, reply) => {
31
+ // Scoped keys can only list keys for their own tenant
32
+ const tenantId = request.apiKey?.tenant_id ?? request.query.tenant_id;
33
+ const keys = await stratum.listApiKeys(tenantId);
34
+ reply.status(200).send(keys);
35
+ });
36
+ // GET /api/v1/api-keys/dormant — List dormant API keys (must be before /:id)
37
+ app.get("/dormant", async (request, reply) => {
38
+ const raw = request.query.days ? parseInt(request.query.days, 10) : 90;
39
+ const days = Number.isNaN(raw) || raw < 1 ? 90 : Math.min(raw, 365);
40
+ const keys = await stratum.listDormantKeys(days);
41
+ reply.status(200).send(keys);
42
+ });
43
+ // POST /api/v1/api-keys/:id/rotate — Rotate an API key
44
+ app.post("/:id/rotate", async (request, reply) => {
45
+ // Verify caller has access to this key's tenant
46
+ if (request.apiKey?.tenant_id) {
47
+ const keys = await stratum.listApiKeys(request.apiKey.tenant_id);
48
+ if (!keys.some(k => k.id === request.params.id)) {
49
+ reply.status(404).send({ error: { code: "NOT_FOUND", message: "API key not found or already revoked" } });
50
+ return;
51
+ }
52
+ }
53
+ const rotateSchema = zod_1.z.object({ name: zod_1.z.string().max(255).optional() });
54
+ const parsed = rotateSchema.parse(request.body ?? {});
55
+ const result = await stratum.rotateApiKey(request.params.id, parsed.name);
56
+ reply.status(201).send(result);
57
+ });
58
+ // DELETE /api/v1/api-keys/:id — Revoke an API key
59
+ app.delete("/:id", async (request, reply) => {
60
+ // Verify caller has access to this key's tenant
61
+ if (request.apiKey?.tenant_id) {
62
+ const keys = await stratum.listApiKeys(request.apiKey.tenant_id);
63
+ if (!keys.some(k => k.id === request.params.id)) {
64
+ reply.status(404).send({ error: { code: "NOT_FOUND", message: "API key not found or already revoked" } });
65
+ return;
66
+ }
67
+ }
68
+ const revoked = await stratum.revokeApiKey(request.params.id);
69
+ if (!revoked) {
70
+ reply.status(404).send({ error: { code: "NOT_FOUND", message: "API key not found or already revoked" } });
71
+ return;
72
+ }
73
+ reply.status(204).send();
74
+ });
75
+ };
76
+ }
77
+ exports.createApiKeyRoutes = createApiKeyRoutes;
78
+ //# sourceMappingURL=api-keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-keys.js","sourceRoot":"","sources":["../../src/routes/api-keys.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { FastifyInstance, FastifyRequest } from "fastify";
2
+ import type { AuditContext } from "@stratum-hq/core";
3
+ import { Stratum } from "@stratum-hq/lib";
4
+ export declare function buildAuditContext(request: FastifyRequest): AuditContext;
5
+ export declare function createAuditLogRoutes(stratum: Stratum): (app: FastifyInstance) => Promise<void>;
6
+ //# sourceMappingURL=audit-logs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-logs.d.ts","sourceRoot":"","sources":["../../src/routes/audit-logs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,YAAY,CAOvE;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,SACT,eAAe,KAAG,QAAQ,IAAI,CAAC,CA2B1E"}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAuditLogRoutes = exports.buildAuditContext = void 0;
4
+ const core_1 = require("@stratum-hq/core");
5
+ function buildAuditContext(request) {
6
+ return {
7
+ actor_id: request.apiKey?.id ?? "unknown",
8
+ actor_type: (request.authMethod || "api_key"),
9
+ source_ip: request.ip,
10
+ request_id: request.id,
11
+ };
12
+ }
13
+ exports.buildAuditContext = buildAuditContext;
14
+ function createAuditLogRoutes(stratum) {
15
+ return async function auditLogRoutes(app) {
16
+ // GET /api/v1/audit-logs — List audit logs with filters
17
+ app.get("/", async (request, reply) => {
18
+ const query = core_1.AuditLogQuerySchema.parse(request.query);
19
+ // Scoped keys can only see logs for their own tenant
20
+ if (request.apiKey?.tenant_id) {
21
+ query.tenant_id = request.apiKey.tenant_id;
22
+ }
23
+ const entries = await stratum.queryAuditLogs(query);
24
+ reply.status(200).send(entries);
25
+ });
26
+ // GET /api/v1/audit-logs/:id — Get single audit entry
27
+ app.get("/:id", async (request, reply) => {
28
+ const entry = await stratum.getAuditEntry(request.params.id);
29
+ if (!entry) {
30
+ reply.status(404).send({ error: { code: "NOT_FOUND", message: "Audit entry not found" } });
31
+ return;
32
+ }
33
+ // Enforce tenant isolation for scoped keys
34
+ if (request.apiKey?.tenant_id && entry.tenant_id !== request.apiKey.tenant_id) {
35
+ reply.status(404).send({ error: { code: "NOT_FOUND", message: "Audit entry not found" } });
36
+ return;
37
+ }
38
+ reply.status(200).send(entry);
39
+ });
40
+ };
41
+ }
42
+ exports.createAuditLogRoutes = createAuditLogRoutes;
43
+ //# sourceMappingURL=audit-logs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-logs.js","sourceRoot":"","sources":["../../src/routes/audit-logs.ts"],"names":[],"mappings":";;;AACA,2CAAuD;AAIvD,SAAgB,iBAAiB,CAAC,OAAuB;IACvD,OAAO;QACL,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,SAAS;QACzC,UAAU,EAAE,CAAC,OAAO,CAAC,UAAU,IAAI,SAAS,CAAsB;QAClE,SAAS,EAAE,OAAO,CAAC,EAAE;QACrB,UAAU,EAAE,OAAO,CAAC,EAAE;KACvB,CAAC;AACJ,CAAC;AAPD,8CAOC;AAED,SAAgB,oBAAoB,CAAC,OAAgB;IACnD,OAAO,KAAK,UAAU,cAAc,CAAC,GAAoB;QACvD,wDAAwD;QACxD,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YACpC,MAAM,KAAK,GAAG,0BAAmB,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACvD,qDAAqD;YACrD,IAAI,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC;gBAC9B,KAAK,CAAC,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC;YAC7C,CAAC;YACD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;YACpD,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,sDAAsD;QACtD,GAAG,CAAC,GAAG,CAA6B,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YACnE,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC7D,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,uBAAuB,EAAE,EAAE,CAAC,CAAC;gBAC3F,OAAO;YACT,CAAC;YACD,2CAA2C;YAC3C,IAAI,OAAO,CAAC,MAAM,EAAE,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;gBAC9E,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,uBAAuB,EAAE,EAAE,CAAC,CAAC;gBAC3F,OAAO;YACT,CAAC;YACD,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AA5BD,oDA4BC"}
@@ -0,0 +1,4 @@
1
+ import { FastifyInstance } from "fastify";
2
+ import { Stratum } from "@stratum-hq/lib";
3
+ export declare function createConfigDiffRoutes(stratum: Stratum): (app: FastifyInstance) => Promise<void>;
4
+ //# sourceMappingURL=config-diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-diff.d.ts","sourceRoot":"","sources":["../../src/routes/config-diff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,SACT,eAAe,KAAG,QAAQ,IAAI,CAAC,CAgC5E"}
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createConfigDiffRoutes = void 0;
4
+ function createConfigDiffRoutes(stratum) {
5
+ return async function configDiffRoutes(app) {
6
+ // GET /api/v1/config/diff?tenant_a=UUID&tenant_b=UUID — Compare resolved config between two tenants
7
+ app.get("/diff", async (request, reply) => {
8
+ const { tenant_a, tenant_b } = request.query;
9
+ if (!tenant_a || !tenant_b) {
10
+ reply.status(400).send({
11
+ error: {
12
+ code: "VALIDATION_ERROR",
13
+ message: "Both tenant_a and tenant_b query parameters are required",
14
+ },
15
+ });
16
+ return;
17
+ }
18
+ if (tenant_a === tenant_b) {
19
+ reply.status(400).send({
20
+ error: {
21
+ code: "VALIDATION_ERROR",
22
+ message: "tenant_a and tenant_b must be different",
23
+ },
24
+ });
25
+ return;
26
+ }
27
+ const diff = await stratum.diffConfig(tenant_a, tenant_b);
28
+ reply.status(200).send(diff);
29
+ });
30
+ };
31
+ }
32
+ exports.createConfigDiffRoutes = createConfigDiffRoutes;
33
+ //# sourceMappingURL=config-diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-diff.js","sourceRoot":"","sources":["../../src/routes/config-diff.ts"],"names":[],"mappings":";;;AAGA,SAAgB,sBAAsB,CAAC,OAAgB;IACrD,OAAO,KAAK,UAAU,gBAAgB,CAAC,GAAoB;QACzD,oGAAoG;QACpG,GAAG,CAAC,GAAG,CACL,OAAO,EACP,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YACvB,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC;YAE7C,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAC3B,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBACrB,KAAK,EAAE;wBACL,IAAI,EAAE,kBAAkB;wBACxB,OAAO,EAAE,0DAA0D;qBACpE;iBACF,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YAED,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBAC1B,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBACrB,KAAK,EAAE;wBACL,IAAI,EAAE,kBAAkB;wBACxB,OAAO,EAAE,yCAAyC;qBACnD;iBACF,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC1D,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC,CACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAjCD,wDAiCC"}
@@ -0,0 +1,4 @@
1
+ import { FastifyInstance } from "fastify";
2
+ import { Stratum } from "@stratum-hq/lib";
3
+ export declare function createConfigRoutes(stratum: Stratum): (app: FastifyInstance) => Promise<void>;
4
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1C,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAI1C,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,SACT,eAAe,KAAG,QAAQ,IAAI,CAAC,CAgDxE"}
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createConfigRoutes = void 0;
4
+ const core_1 = require("@stratum-hq/core");
5
+ const audit_logs_js_1 = require("./audit-logs.js");
6
+ const tenant_scope_js_1 = require("../middleware/tenant-scope.js");
7
+ function createConfigRoutes(stratum) {
8
+ return async function configRoutes(app) {
9
+ // Tenant-scoped keys can only access config for their own tenant subtree
10
+ app.addHook("preHandler", (0, tenant_scope_js_1.createTenantScopeGuard)(stratum, tenant_scope_js_1.fromParamId));
11
+ // GET /api/v1/tenants/:id/config — Get resolved config
12
+ app.get("/", async (request, reply) => {
13
+ const resolved = await stratum.resolveConfig(request.params.id);
14
+ reply.status(200).send(resolved);
15
+ });
16
+ // PUT /api/v1/tenants/:id/config/:key — Set config value
17
+ app.put("/:key", async (request, reply) => {
18
+ const input = core_1.SetConfigInputSchema.parse(request.body);
19
+ const entry = await stratum.setConfig(request.params.id, request.params.key, input, (0, audit_logs_js_1.buildAuditContext)(request));
20
+ reply.status(200).send(entry);
21
+ });
22
+ // DELETE /api/v1/tenants/:id/config/:key — Delete config override
23
+ app.delete("/:key", async (request, reply) => {
24
+ await stratum.deleteConfig(request.params.id, request.params.key, (0, audit_logs_js_1.buildAuditContext)(request));
25
+ reply.status(204).send();
26
+ });
27
+ // PUT /api/v1/tenants/:id/config/batch — Set multiple config keys atomically
28
+ app.put("/batch", async (request, reply) => {
29
+ const body = request.body;
30
+ if (!Array.isArray(body?.entries) || body.entries.length === 0) {
31
+ reply.status(400).send({ error: { code: "VALIDATION_ERROR", message: "Body must contain a non-empty 'entries' array" } });
32
+ return;
33
+ }
34
+ if (body.entries.length > 200) {
35
+ reply.status(400).send({ error: { code: "VALIDATION_ERROR", message: "Batch limited to 200 entries" } });
36
+ return;
37
+ }
38
+ const entries = body.entries.map((e) => {
39
+ const parsed = core_1.SetConfigInputSchema.parse(e);
40
+ const entry = e;
41
+ return { key: entry.key, value: parsed.value, locked: parsed.locked, sensitive: parsed.sensitive };
42
+ });
43
+ const batchResult = await stratum.batchSetConfig(request.params.id, entries, (0, audit_logs_js_1.buildAuditContext)(request));
44
+ reply.status(200).send(batchResult);
45
+ });
46
+ // GET /api/v1/tenants/:id/config/inheritance — Get full inheritance view
47
+ app.get("/inheritance", async (request, reply) => {
48
+ const inheritance = await stratum.getConfigWithInheritance(request.params.id);
49
+ reply.status(200).send(inheritance);
50
+ });
51
+ };
52
+ }
53
+ exports.createConfigRoutes = createConfigRoutes;
54
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { FastifyInstance } from "fastify";
2
+ import { Stratum } from "@stratum-hq/lib";
3
+ export declare function createConsentRoutes(stratum: Stratum): (app: FastifyInstance) => Promise<void>;
4
+ //# sourceMappingURL=consent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent.d.ts","sourceRoot":"","sources":["../../src/routes/consent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1C,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAI1C,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,SACT,eAAe,KAAG,QAAQ,IAAI,CAAC,CA2CzE"}
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createConsentRoutes = void 0;
4
+ const core_1 = require("@stratum-hq/core");
5
+ const audit_logs_js_1 = require("./audit-logs.js");
6
+ const tenant_scope_js_1 = require("../middleware/tenant-scope.js");
7
+ function createConsentRoutes(stratum) {
8
+ return async function consentRoutes(app) {
9
+ // Tenant-scoped keys can only access consent for their own tenant subtree
10
+ app.addHook("preHandler", (0, tenant_scope_js_1.createTenantScopeGuard)(stratum, tenant_scope_js_1.fromParamTenantId));
11
+ // POST /api/v1/tenants/:tenantId/consent — Grant consent
12
+ app.post("/", async (request, reply) => {
13
+ const { tenantId } = request.params;
14
+ const input = core_1.GrantConsentInputSchema.parse(request.body);
15
+ const audit = (0, audit_logs_js_1.buildAuditContext)(request);
16
+ const record = await stratum.grantConsent(tenantId, input, audit);
17
+ reply.status(201).send(record);
18
+ });
19
+ // GET /api/v1/tenants/:tenantId/consent — List consent records
20
+ app.get("/", async (request, reply) => {
21
+ const { tenantId } = request.params;
22
+ const subjectId = request.query.subject_id;
23
+ const records = await stratum.listConsent(tenantId, subjectId);
24
+ reply.status(200).send(records);
25
+ });
26
+ // DELETE /api/v1/tenants/:tenantId/consent/:purpose — Revoke consent
27
+ app.delete("/:purpose", async (request, reply) => {
28
+ const { tenantId, purpose } = request.params;
29
+ const subjectId = request.query.subject_id;
30
+ if (!subjectId) {
31
+ reply.status(400).send({ error: { code: "VALIDATION_ERROR", message: "subject_id query parameter is required" } });
32
+ return;
33
+ }
34
+ const audit = (0, audit_logs_js_1.buildAuditContext)(request);
35
+ const revoked = await stratum.revokeConsent(tenantId, subjectId, purpose, audit);
36
+ if (!revoked) {
37
+ reply.status(404).send({ error: { code: "NOT_FOUND", message: "Consent record not found" } });
38
+ return;
39
+ }
40
+ reply.status(200).send({ success: true });
41
+ });
42
+ };
43
+ }
44
+ exports.createConsentRoutes = createConsentRoutes;
45
+ //# sourceMappingURL=consent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent.js","sourceRoot":"","sources":["../../src/routes/consent.ts"],"names":[],"mappings":";;;AACA,2CAA2D;AAE3D,mDAAoD;AACpD,mEAA0F;AAE1F,SAAgB,mBAAmB,CAAC,OAAgB;IAClD,OAAO,KAAK,UAAU,aAAa,CAAC,GAAoB;QACtD,0EAA0E;QAC1E,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,IAAA,wCAAsB,EAAC,OAAO,EAAE,mCAAiB,CAAC,CAAC,CAAC;QAC9E,yDAAyD;QACzD,GAAG,CAAC,IAAI,CAAmC,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YACvE,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;YACpC,MAAM,KAAK,GAAG,8BAAuB,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC1D,MAAM,KAAK,GAAG,IAAA,iCAAiB,EAAC,OAAO,CAAC,CAAC;YACzC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;YAClE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,+DAA+D;QAC/D,GAAG,CAAC,GAAG,CACL,GAAG,EACH,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YACvB,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;YACpC,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC;YAC3C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;YAC/D,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAClC,CAAC,CACF,CAAC;QAEF,qEAAqE;QACrE,GAAG,CAAC,MAAM,CACR,WAAW,EACX,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YACvB,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;YAC7C,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC;YAC3C,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,wCAAwC,EAAE,EAAE,CAAC,CAAC;gBACnH,OAAO;YACT,CAAC;YACD,MAAM,KAAK,GAAG,IAAA,iCAAiB,EAAC,OAAO,CAAC,CAAC;YACzC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;YACjF,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,0BAA0B,EAAE,EAAE,CAAC,CAAC;gBAC9F,OAAO;YACT,CAAC;YACD,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5C,CAAC,CACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AA5CD,kDA4CC"}
@@ -0,0 +1,9 @@
1
+ import { FastifyInstance } from "fastify";
2
+ type RedisHealthChecker = () => Promise<"connected" | "disconnected" | "not_configured">;
3
+ /**
4
+ * Health routes factory. Accepts an optional Redis health checker so the
5
+ * health endpoint can report Redis status without a hard dependency on it.
6
+ */
7
+ export declare function healthRoutes(checkRedisHealth: RedisHealthChecker): (app: FastifyInstance) => Promise<void>;
8
+ export {};
9
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAG1C,KAAK,kBAAkB,GAAG,MAAM,OAAO,CAAC,WAAW,GAAG,cAAc,GAAG,gBAAgB,CAAC,CAAC;AAEzF;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,gBAAgB,EAAE,kBAAkB,SAEU,eAAe,KAAG,QAAQ,IAAI,CAAC,CAsB9E"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.healthRoutes = void 0;
4
+ const connection_js_1 = require("../db/connection.js");
5
+ /**
6
+ * Health routes factory. Accepts an optional Redis health checker so the
7
+ * health endpoint can report Redis status without a hard dependency on it.
8
+ */
9
+ function healthRoutes(checkRedisHealth) {
10
+ return async function healthRoutesPlugin(app) {
11
+ app.get("/api/v1/health", async (_request, reply) => {
12
+ let dbStatus = "disconnected";
13
+ try {
14
+ const pool = (0, connection_js_1.getPool)();
15
+ await pool.query("SELECT 1");
16
+ dbStatus = "connected";
17
+ }
18
+ catch {
19
+ dbStatus = "disconnected";
20
+ }
21
+ const redisStatus = await checkRedisHealth();
22
+ reply.status(200).send({
23
+ status: "ok",
24
+ timestamp: new Date().toISOString(),
25
+ db: dbStatus,
26
+ redis: redisStatus,
27
+ });
28
+ });
29
+ };
30
+ }
31
+ exports.healthRoutes = healthRoutes;
32
+ //# sourceMappingURL=health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":";;;AACA,uDAA8C;AAI9C;;;GAGG;AACH,SAAgB,YAAY,CAC1B,gBAAoC;IAEpC,OAAO,KAAK,UAAU,kBAAkB,CAAC,GAAoB;QAC3D,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;YAClD,IAAI,QAAQ,GAAiC,cAAc,CAAC;YAE5D,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,IAAA,uBAAO,GAAE,CAAC;gBACvB,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;gBAC7B,QAAQ,GAAG,WAAW,CAAC;YACzB,CAAC;YAAC,MAAM,CAAC;gBACP,QAAQ,GAAG,cAAc,CAAC;YAC5B,CAAC;YAED,MAAM,WAAW,GAAG,MAAM,gBAAgB,EAAE,CAAC;YAE7C,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACrB,MAAM,EAAE,IAAI;gBACZ,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,EAAE,EAAE,QAAQ;gBACZ,KAAK,EAAE,WAAW;aACnB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AAzBD,oCAyBC"}
@@ -0,0 +1,4 @@
1
+ import { FastifyInstance } from "fastify";
2
+ import { Stratum } from "@stratum-hq/lib";
3
+ export declare function createMaintenanceRoutes(stratum: Stratum): (app: FastifyInstance) => Promise<void>;
4
+ //# sourceMappingURL=maintenance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"maintenance.d.ts","sourceRoot":"","sources":["../../src/routes/maintenance.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAG1C,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,SACT,eAAe,KAAG,QAAQ,IAAI,CAAC,CA4B7E"}
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createMaintenanceRoutes = void 0;
4
+ const audit_logs_js_1 = require("./audit-logs.js");
5
+ function createMaintenanceRoutes(stratum) {
6
+ return async function maintenanceRoutes(app) {
7
+ // POST /api/v1/maintenance/purge-expired — Purge expired data
8
+ app.post("/purge-expired", async (request, reply) => {
9
+ const rawDays = request.query.retention_days
10
+ ? parseInt(request.query.retention_days, 10)
11
+ : undefined;
12
+ const retentionDays = rawDays !== undefined && (Number.isNaN(rawDays) || rawDays < 1)
13
+ ? undefined
14
+ : rawDays !== undefined ? Math.min(rawDays, 3650) : undefined;
15
+ const result = await stratum.purgeExpiredData(retentionDays);
16
+ reply.status(200).send(result);
17
+ });
18
+ // POST /api/v1/maintenance/rotate-encryption-key — Re-encrypt all sensitive data
19
+ app.post("/rotate-encryption-key", async (request, reply) => {
20
+ const body = request.body;
21
+ if (!body?.old_key || !body?.new_key) {
22
+ reply.status(400).send({ error: { code: "VALIDATION_ERROR", message: "Both 'old_key' and 'new_key' are required" } });
23
+ return;
24
+ }
25
+ if (body.old_key === body.new_key) {
26
+ reply.status(400).send({ error: { code: "VALIDATION_ERROR", message: "'new_key' must differ from 'old_key'" } });
27
+ return;
28
+ }
29
+ const result = await stratum.rotateEncryptionKey(body.old_key, body.new_key, (0, audit_logs_js_1.buildAuditContext)(request));
30
+ reply.status(200).send(result);
31
+ });
32
+ };
33
+ }
34
+ exports.createMaintenanceRoutes = createMaintenanceRoutes;
35
+ //# sourceMappingURL=maintenance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"maintenance.js","sourceRoot":"","sources":["../../src/routes/maintenance.ts"],"names":[],"mappings":";;;AAEA,mDAAoD;AAEpD,SAAgB,uBAAuB,CAAC,OAAgB;IACtD,OAAO,KAAK,UAAU,iBAAiB,CAAC,GAAoB;QAC1D,8DAA8D;QAC9D,GAAG,CAAC,IAAI,CAA+C,gBAAgB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YAChG,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,cAAc;gBAC1C,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,EAAE,CAAC;gBAC5C,CAAC,CAAC,SAAS,CAAC;YACd,MAAM,aAAa,GAAG,OAAO,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC;gBACnF,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAChE,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC;YAC7D,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,iFAAiF;QACjF,GAAG,CAAC,IAAI,CAAC,wBAAwB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YAC1D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAqD,CAAC;YAC3E,IAAI,CAAC,IAAI,EAAE,OAAO,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC;gBACrC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,2CAA2C,EAAE,EAAE,CAAC,CAAC;gBACtH,OAAO;YACT,CAAC;YACD,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,sCAAsC,EAAE,EAAE,CAAC,CAAC;gBACjH,OAAO;YACT,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,mBAAmB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAA,iCAAiB,EAAC,OAAO,CAAC,CAAC,CAAC;YACzG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AA7BD,0DA6BC"}
@@ -0,0 +1,4 @@
1
+ import { FastifyInstance } from "fastify";
2
+ import { Stratum } from "@stratum-hq/lib";
3
+ export declare function createPermissionRoutes(stratum: Stratum): (app: FastifyInstance) => Promise<void>;
4
+ //# sourceMappingURL=permissions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../src/routes/permissions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1C,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAI1C,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,SACT,eAAe,KAAG,QAAQ,IAAI,CAAC,CAkC5E"}