@clef-sh/ui 0.1.28 → 0.1.30
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/dist/client/assets/index-BmfzUjLY.css +1 -0
- package/dist/client/assets/{index-DXZTFIp7.js → index-CGTwPvL1.js} +13 -13
- package/dist/client/index.html +2 -2
- package/dist/server/api.d.ts.map +1 -1
- package/dist/server/api.js +90 -105
- package/dist/server/api.js.map +1 -1
- package/package.json +10 -10
- package/src/client/useCapabilities.ts +66 -0
- package/dist/client/assets/index-DbfsjTUx.css +0 -1
package/dist/client/index.html
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
<link rel="shortcut icon" href="/favicon.ico" />
|
|
10
10
|
<!-- Fonts are bundled via @clef-sh/design/fonts.css (imported from
|
|
11
11
|
styles.css) — no runtime dependency on Google Fonts. -->
|
|
12
|
-
<script type="module" crossorigin src="/assets/index-
|
|
13
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
12
|
+
<script type="module" crossorigin src="/assets/index-CGTwPvL1.js"></script>
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BmfzUjLY.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body>
|
|
16
16
|
<div id="root"></div>
|
package/dist/server/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/server/api.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/server/api.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAGpD,OAAO,EASL,gBAAgB,EA4BjB,MAAM,eAAe,CAAC;AAIvB,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,gBAAgB,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CA0wDrD"}
|
package/dist/server/api.js
CHANGED
|
@@ -36,97 +36,35 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.createApiRouter = createApiRouter;
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
|
-
const os = __importStar(require("os"));
|
|
40
|
-
const child_process_1 = require("child_process");
|
|
41
39
|
const express_1 = require("express");
|
|
42
40
|
const express_rate_limit_1 = __importStar(require("express-rate-limit"));
|
|
43
41
|
const YAML = __importStar(require("yaml"));
|
|
44
|
-
// On Linux, libuv creates socketpairs for child stdio. Go's os.Open on
|
|
45
|
-
// /dev/stdin re-opens /proc/self/fd/0 which fails with ENXIO on socketpairs.
|
|
46
|
-
// Use a FIFO workaround on Linux, but not inside Jest (where the runner is
|
|
47
|
-
// mocked and real subprocesses are never spawned).
|
|
48
|
-
const _useStdinFifo = process.platform === "linux" && !process.env.JEST_WORKER_ID;
|
|
49
42
|
const core_1 = require("@clef-sh/core");
|
|
50
43
|
const envelope_1 = require("./envelope");
|
|
51
44
|
function createApiRouter(deps) {
|
|
52
45
|
const router = (0, express_1.Router)();
|
|
53
46
|
const parser = new core_1.ManifestParser();
|
|
54
47
|
const matrix = new core_1.MatrixManager();
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// Fix: when we see /dev/stdin in the args AND stdin content in opts,
|
|
63
|
-
// replace it with a FIFO (named pipe). A FIFO is an in-memory kernel
|
|
64
|
-
// buffer — plaintext never touches disk. The FIFO is cleaned up after
|
|
65
|
-
// the subprocess exits.
|
|
66
|
-
const sopsRunner = {
|
|
67
|
-
run: (cmd, args, opts) => {
|
|
68
|
-
const stdinIdx = args.indexOf("/dev/stdin");
|
|
69
|
-
// Only use the FIFO workaround in Linux SEA binaries where
|
|
70
|
-
// /dev/stdin → /proc/self/fd/0 fails with ENXIO on socketpairs.
|
|
71
|
-
// Normal Node.js processes (including Jest on Linux CI) work fine.
|
|
72
|
-
const needsFifo = stdinIdx >= 0 && opts?.stdin !== undefined && _useStdinFifo;
|
|
73
|
-
if (!needsFifo) {
|
|
74
|
-
return deps.runner.run(cmd, args, {
|
|
75
|
-
...opts,
|
|
76
|
-
cwd: opts?.cwd ?? deps.repoRoot,
|
|
77
|
-
env: opts?.env,
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
// Create a FIFO and feed stdin content through a background process
|
|
81
|
-
const fifoDir = (0, child_process_1.execFileSync)("mktemp", ["-d", path.join(os.tmpdir(), "clef-fifo-XXXXXX")])
|
|
82
|
-
.toString()
|
|
83
|
-
.trim();
|
|
84
|
-
const fifoPath = path.join(fifoDir, "input");
|
|
85
|
-
(0, child_process_1.execFileSync)("mkfifo", [fifoPath]);
|
|
86
|
-
// Background writer — blocks at OS level until sops opens the read end
|
|
87
|
-
const writer = (0, child_process_1.spawn)("dd", [`of=${fifoPath}`, "status=none"], {
|
|
88
|
-
stdio: ["pipe", "ignore", "ignore"],
|
|
89
|
-
});
|
|
90
|
-
writer.stdin.write(opts.stdin);
|
|
91
|
-
writer.stdin.end();
|
|
92
|
-
const patchedArgs = [...args];
|
|
93
|
-
patchedArgs[stdinIdx] = fifoPath;
|
|
94
|
-
const { stdin: _stdin, ...restOpts } = opts;
|
|
95
|
-
return deps.runner
|
|
96
|
-
.run(cmd, patchedArgs, {
|
|
97
|
-
...restOpts,
|
|
98
|
-
cwd: restOpts?.cwd ?? deps.repoRoot,
|
|
99
|
-
env: { SOPS_CONFIG: path.join(deps.repoRoot, ".sops.yaml"), ...restOpts?.env },
|
|
100
|
-
})
|
|
101
|
-
.finally(() => {
|
|
102
|
-
try {
|
|
103
|
-
writer.kill();
|
|
104
|
-
}
|
|
105
|
-
catch {
|
|
106
|
-
/* already exited */
|
|
107
|
-
}
|
|
108
|
-
try {
|
|
109
|
-
(0, child_process_1.execFileSync)("rm", ["-rf", fifoDir]);
|
|
110
|
-
}
|
|
111
|
-
catch {
|
|
112
|
-
/* best effort */
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
},
|
|
48
|
+
// Pin sops invocations to the repo root and apply the Linux FIFO
|
|
49
|
+
// workaround for /dev/stdin (see wrapWithLinuxStdinFifo).
|
|
50
|
+
const cwdRunner = {
|
|
51
|
+
run: (cmd, args, opts) => deps.runner.run(cmd, args, {
|
|
52
|
+
...opts,
|
|
53
|
+
cwd: opts?.cwd ?? deps.repoRoot,
|
|
54
|
+
}),
|
|
116
55
|
};
|
|
56
|
+
const sopsRunner = (0, core_1.wrapWithLinuxStdinFifo)(cwdRunner);
|
|
117
57
|
const sops = new core_1.SopsClient(sopsRunner, deps.ageKeyFile, deps.ageKey, deps.sopsPath);
|
|
118
58
|
const diffEngine = new core_1.DiffEngine();
|
|
119
59
|
const schemaValidator = new core_1.SchemaValidator();
|
|
120
|
-
const lintRunner = new core_1.LintRunner(matrix, schemaValidator, sops);
|
|
121
60
|
const git = new core_1.GitIntegration(deps.runner);
|
|
122
61
|
const tx = new core_1.TransactionManager(git);
|
|
123
62
|
const scanRunner = new core_1.ScanRunner(deps.runner);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
const
|
|
129
|
-
const structureManager = new core_1.StructureManager(matrix, sops, tx);
|
|
63
|
+
// Manifest-bound managers are constructed per-request so the manifest
|
|
64
|
+
// edits the user makes through other UI flows are picked up without
|
|
65
|
+
// restarting the server.
|
|
66
|
+
const buildSourceFor = (manifest) => (0, core_1.composeSecretSource)(new core_1.FilesystemStorageBackend(manifest, deps.repoRoot), sops, manifest);
|
|
67
|
+
const structureManager = new core_1.StructureManager(matrix, buildSourceFor, tx);
|
|
130
68
|
// In-session scan cache
|
|
131
69
|
let lastScanResult = null;
|
|
132
70
|
let lastScanAt = null;
|
|
@@ -259,6 +197,19 @@ function createApiRouter(deps) {
|
|
|
259
197
|
store: apiRateLimitStore,
|
|
260
198
|
});
|
|
261
199
|
router.use(apiLimiter);
|
|
200
|
+
// GET /api/capabilities — boolean trait descriptor for the composed
|
|
201
|
+
// source. Lets the UI client gate buttons (e.g. hide "Migrate backend"
|
|
202
|
+
// when the source can't migrate) without per-feature probing.
|
|
203
|
+
router.get("/capabilities", (_req, res) => {
|
|
204
|
+
try {
|
|
205
|
+
const manifest = loadManifest();
|
|
206
|
+
res.json((0, core_1.describeCapabilities)(buildSourceFor(manifest)));
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
const message = err instanceof Error ? err.message : "Failed to read capabilities";
|
|
210
|
+
res.status(500).json({ error: message, code: "CAPABILITIES_ERROR" });
|
|
211
|
+
}
|
|
212
|
+
});
|
|
262
213
|
// GET /api/manifest
|
|
263
214
|
router.get("/manifest", (_req, res) => {
|
|
264
215
|
try {
|
|
@@ -274,7 +225,7 @@ function createApiRouter(deps) {
|
|
|
274
225
|
router.get("/matrix", async (_req, res) => {
|
|
275
226
|
try {
|
|
276
227
|
const manifest = loadManifest();
|
|
277
|
-
const statuses = await matrix.getMatrixStatus(manifest, deps.repoRoot
|
|
228
|
+
const statuses = await matrix.getMatrixStatus(manifest, deps.repoRoot);
|
|
278
229
|
res.json(statuses);
|
|
279
230
|
}
|
|
280
231
|
catch (err) {
|
|
@@ -300,12 +251,14 @@ function createApiRouter(deps) {
|
|
|
300
251
|
});
|
|
301
252
|
return;
|
|
302
253
|
}
|
|
303
|
-
const
|
|
304
|
-
const
|
|
254
|
+
const source = buildSourceFor(manifest);
|
|
255
|
+
const ref = { namespace: ns, environment: env };
|
|
256
|
+
const decrypted = await source.readCell(ref);
|
|
305
257
|
// Read pending keys from metadata (plaintext sidecar)
|
|
306
258
|
let pending = [];
|
|
307
259
|
try {
|
|
308
|
-
|
|
260
|
+
const meta = await source.getPendingMetadata(ref);
|
|
261
|
+
pending = meta.pending.map((p) => p.key);
|
|
309
262
|
}
|
|
310
263
|
catch {
|
|
311
264
|
// Metadata unreadable — no pending info
|
|
@@ -358,8 +311,9 @@ function createApiRouter(deps) {
|
|
|
358
311
|
const relCellPath = manifest.file_pattern
|
|
359
312
|
.replace("{namespace}", ns)
|
|
360
313
|
.replace("{environment}", env);
|
|
361
|
-
const
|
|
362
|
-
const
|
|
314
|
+
const source = buildSourceFor(manifest);
|
|
315
|
+
const ref = { namespace: ns, environment: env };
|
|
316
|
+
const decrypted = await source.readCell(ref);
|
|
363
317
|
// Inside the mutate callback we do the actual encrypt + metadata
|
|
364
318
|
// update. When auto-commit is enabled this runs inside tx.run; when
|
|
365
319
|
// disabled (batched edit flow) it runs directly.
|
|
@@ -367,16 +321,16 @@ function createApiRouter(deps) {
|
|
|
367
321
|
const doWork = async () => {
|
|
368
322
|
if (random) {
|
|
369
323
|
decrypted.values[key] = (0, core_1.generateRandomValue)();
|
|
370
|
-
await
|
|
324
|
+
await source.writeCell(ref, decrypted.values);
|
|
371
325
|
// Metadata update failure used to trigger an in-method rollback
|
|
372
326
|
// here. Inside tx.run, that rollback comes for free via git
|
|
373
327
|
// reset, so we just let the throw propagate.
|
|
374
|
-
await
|
|
328
|
+
await source.markPending(ref, [key], "clef ui");
|
|
375
329
|
response = { success: true, key, pending: true };
|
|
376
330
|
}
|
|
377
331
|
else {
|
|
378
332
|
decrypted.values[key] = String(value);
|
|
379
|
-
await
|
|
333
|
+
await source.writeCell(ref, decrypted.values);
|
|
380
334
|
// Validate against schema if defined
|
|
381
335
|
const nsDef = manifest.namespaces.find((n) => n.name === ns);
|
|
382
336
|
if (nsDef?.schema) {
|
|
@@ -396,11 +350,11 @@ function createApiRouter(deps) {
|
|
|
396
350
|
// Schema load failed — skip validation, not fatal
|
|
397
351
|
}
|
|
398
352
|
}
|
|
399
|
-
// Real value set is a rotation event.
|
|
353
|
+
// Real value set is a rotation event. recordRotation also
|
|
400
354
|
// strips any matching pending entry, so this one call replaces
|
|
401
355
|
// the old markResolved + implicit pending cleanup.
|
|
402
356
|
try {
|
|
403
|
-
await
|
|
357
|
+
await source.recordRotation(ref, [key], "clef ui");
|
|
404
358
|
}
|
|
405
359
|
catch {
|
|
406
360
|
// Metadata update failed — non-fatal
|
|
@@ -452,8 +406,9 @@ function createApiRouter(deps) {
|
|
|
452
406
|
const relCellPath = manifest.file_pattern
|
|
453
407
|
.replace("{namespace}", ns)
|
|
454
408
|
.replace("{environment}", env);
|
|
455
|
-
const
|
|
456
|
-
const
|
|
409
|
+
const source = buildSourceFor(manifest);
|
|
410
|
+
const ref = { namespace: ns, environment: env };
|
|
411
|
+
const decrypted = await source.readCell(ref);
|
|
457
412
|
if (!(key in decrypted.values)) {
|
|
458
413
|
res.status(404).json({
|
|
459
414
|
error: `Key '${key}' not found in ${ns}/${env}.`,
|
|
@@ -463,12 +418,12 @@ function createApiRouter(deps) {
|
|
|
463
418
|
}
|
|
464
419
|
const doWork = async () => {
|
|
465
420
|
delete decrypted.values[key];
|
|
466
|
-
await
|
|
421
|
+
await source.writeCell(ref, decrypted.values);
|
|
467
422
|
// Strip both pending and rotation records — the key no longer
|
|
468
423
|
// exists, so stale metadata would mislead policy.
|
|
469
424
|
try {
|
|
470
|
-
await
|
|
471
|
-
await
|
|
425
|
+
await source.markResolved(ref, [key]);
|
|
426
|
+
await source.removeRotation(ref, [key]);
|
|
472
427
|
}
|
|
473
428
|
catch {
|
|
474
429
|
// Best effort — orphaned metadata is annoying but not dangerous
|
|
@@ -509,8 +464,9 @@ function createApiRouter(deps) {
|
|
|
509
464
|
const relCellPath = manifest.file_pattern
|
|
510
465
|
.replace("{namespace}", ns)
|
|
511
466
|
.replace("{environment}", env);
|
|
512
|
-
const
|
|
513
|
-
const
|
|
467
|
+
const source = buildSourceFor(manifest);
|
|
468
|
+
const ref = { namespace: ns, environment: env };
|
|
469
|
+
const decrypted = await source.readCell(ref);
|
|
514
470
|
const value = key in decrypted.values ? String(decrypted.values[key]) : undefined;
|
|
515
471
|
await tx.run(deps.repoRoot, {
|
|
516
472
|
description: `clef ui: accept ${ns}/${env}/${key}`,
|
|
@@ -521,7 +477,7 @@ function createApiRouter(deps) {
|
|
|
521
477
|
// rotation — it establishes a point-in-time "this is the
|
|
522
478
|
// current secret" assertion. recordRotation also strips the
|
|
523
479
|
// pending entry.
|
|
524
|
-
await
|
|
480
|
+
await source.recordRotation(ref, [key], "clef ui (accept)");
|
|
525
481
|
},
|
|
526
482
|
});
|
|
527
483
|
res.json({ success: true, key, value });
|
|
@@ -561,8 +517,11 @@ function createApiRouter(deps) {
|
|
|
561
517
|
});
|
|
562
518
|
return;
|
|
563
519
|
}
|
|
564
|
-
const
|
|
565
|
-
|
|
520
|
+
const cellSource = buildSourceFor(manifest);
|
|
521
|
+
const fromRef = { namespace: fromNs, environment: fromEnv };
|
|
522
|
+
const toRef = { namespace: toNs, environment: toEnv };
|
|
523
|
+
const sourceCell = await cellSource.readCell(fromRef);
|
|
524
|
+
if (!(key in sourceCell.values)) {
|
|
566
525
|
res.status(404).json({
|
|
567
526
|
error: `Key '${key}' not found in ${fromNs}/${fromEnv}.`,
|
|
568
527
|
code: "KEY_NOT_FOUND",
|
|
@@ -574,19 +533,19 @@ function createApiRouter(deps) {
|
|
|
574
533
|
description: `clef ui: copy ${key} from ${fromNs}/${fromEnv} to ${toNs}/${toEnv}`,
|
|
575
534
|
paths: [relToPath, relToPath.replace(/\.enc\.(yaml|json)$/, ".clef-meta.yaml")],
|
|
576
535
|
mutate: async () => {
|
|
577
|
-
const dest = await
|
|
578
|
-
const valueChanged = dest.values[key] !==
|
|
579
|
-
dest.values[key] =
|
|
580
|
-
await
|
|
536
|
+
const dest = await cellSource.readCell(toRef);
|
|
537
|
+
const valueChanged = dest.values[key] !== sourceCell.values[key];
|
|
538
|
+
dest.values[key] = sourceCell.values[key];
|
|
539
|
+
await cellSource.writeCell(toRef, dest.values);
|
|
581
540
|
// Only a real value change is a rotation. Copying an identical
|
|
582
541
|
// value re-encrypts the ciphertext but does not rotate — matches
|
|
583
542
|
// the import semantics agreed in the design.
|
|
584
543
|
try {
|
|
585
544
|
if (valueChanged) {
|
|
586
|
-
await
|
|
545
|
+
await cellSource.recordRotation(toRef, [key], "clef ui (copy)");
|
|
587
546
|
}
|
|
588
547
|
else {
|
|
589
|
-
await
|
|
548
|
+
await cellSource.markResolved(toRef, [key]);
|
|
590
549
|
}
|
|
591
550
|
}
|
|
592
551
|
catch {
|
|
@@ -617,7 +576,13 @@ function createApiRouter(deps) {
|
|
|
617
576
|
});
|
|
618
577
|
return;
|
|
619
578
|
}
|
|
620
|
-
|
|
579
|
+
// Compose a per-request SecretSource. The manifest is parsed
|
|
580
|
+
// per-request (it can change while the server runs), so the
|
|
581
|
+
// source is built here rather than at router construction.
|
|
582
|
+
const storage = new core_1.FilesystemStorageBackend(manifest, deps.repoRoot);
|
|
583
|
+
const encryption = sops;
|
|
584
|
+
const source = (0, core_1.composeSecretSource)(storage, encryption, manifest);
|
|
585
|
+
const result = await diffEngine.diffCells(ns, envA, envB, source);
|
|
621
586
|
// Mask values by default — only reveal when client explicitly requests it
|
|
622
587
|
if (req.query.showValues !== "true") {
|
|
623
588
|
for (const row of result.rows) {
|
|
@@ -646,6 +611,8 @@ function createApiRouter(deps) {
|
|
|
646
611
|
});
|
|
647
612
|
return;
|
|
648
613
|
}
|
|
614
|
+
const lintSource = (0, core_1.composeSecretSource)(new core_1.FilesystemStorageBackend(manifest, deps.repoRoot), sops, manifest);
|
|
615
|
+
const lintRunner = new core_1.LintRunner(matrix, schemaValidator, lintSource);
|
|
649
616
|
const result = await lintRunner.run(manifest, deps.repoRoot);
|
|
650
617
|
const filtered = result.issues.filter((issue) => {
|
|
651
618
|
const issueNs = issue.file.split("/")[0];
|
|
@@ -782,6 +749,8 @@ function createApiRouter(deps) {
|
|
|
782
749
|
router.get("/lint", async (_req, res) => {
|
|
783
750
|
try {
|
|
784
751
|
const manifest = loadManifest();
|
|
752
|
+
const lintSource = (0, core_1.composeSecretSource)(new core_1.FilesystemStorageBackend(manifest, deps.repoRoot), sops, manifest);
|
|
753
|
+
const lintRunner = new core_1.LintRunner(matrix, schemaValidator, lintSource);
|
|
785
754
|
const result = await lintRunner.run(manifest, deps.repoRoot);
|
|
786
755
|
res.json(result);
|
|
787
756
|
}
|
|
@@ -794,6 +763,8 @@ function createApiRouter(deps) {
|
|
|
794
763
|
router.post("/lint/fix", async (_req, res) => {
|
|
795
764
|
try {
|
|
796
765
|
const manifest = loadManifest();
|
|
766
|
+
const lintSource = (0, core_1.composeSecretSource)(new core_1.FilesystemStorageBackend(manifest, deps.repoRoot), sops, manifest);
|
|
767
|
+
const lintRunner = new core_1.LintRunner(matrix, schemaValidator, lintSource);
|
|
797
768
|
const result = await lintRunner.fix(manifest, deps.repoRoot);
|
|
798
769
|
res.json(result);
|
|
799
770
|
}
|
|
@@ -1014,7 +985,8 @@ function createApiRouter(deps) {
|
|
|
1014
985
|
});
|
|
1015
986
|
return;
|
|
1016
987
|
}
|
|
1017
|
-
const
|
|
988
|
+
const importSource = (0, core_1.composeSecretSource)(new core_1.FilesystemStorageBackend(manifest, deps.repoRoot), sops, manifest);
|
|
989
|
+
const importRunner = new core_1.ImportRunner(importSource, tx);
|
|
1018
990
|
const result = await importRunner.import(target, null, content, manifest, deps.repoRoot, {
|
|
1019
991
|
format,
|
|
1020
992
|
dryRun: true,
|
|
@@ -1067,7 +1039,8 @@ function createApiRouter(deps) {
|
|
|
1067
1039
|
res.json({ imported: [], skipped: [], failed: [] });
|
|
1068
1040
|
return;
|
|
1069
1041
|
}
|
|
1070
|
-
const
|
|
1042
|
+
const importSource = (0, core_1.composeSecretSource)(new core_1.FilesystemStorageBackend(manifest, deps.repoRoot), sops, manifest);
|
|
1043
|
+
const importRunner = new core_1.ImportRunner(importSource, tx);
|
|
1071
1044
|
const result = await importRunner.import(target, null, content, manifest, deps.repoRoot, {
|
|
1072
1045
|
format,
|
|
1073
1046
|
keys,
|
|
@@ -1088,6 +1061,7 @@ function createApiRouter(deps) {
|
|
|
1088
1061
|
router.get("/recipients", async (_req, res) => {
|
|
1089
1062
|
try {
|
|
1090
1063
|
const manifest = loadManifest();
|
|
1064
|
+
const recipientManager = new core_1.RecipientManager(buildSourceFor(manifest), matrix, tx);
|
|
1091
1065
|
const recipients = await recipientManager.list(manifest, deps.repoRoot);
|
|
1092
1066
|
const cells = matrix.resolveMatrix(manifest, deps.repoRoot);
|
|
1093
1067
|
const totalFiles = cells.filter((c) => c.exists).length;
|
|
@@ -1113,6 +1087,7 @@ function createApiRouter(deps) {
|
|
|
1113
1087
|
try {
|
|
1114
1088
|
const manifest = loadManifest();
|
|
1115
1089
|
const { key, label } = req.body;
|
|
1090
|
+
const recipientManager = new core_1.RecipientManager(buildSourceFor(manifest), matrix, tx);
|
|
1116
1091
|
const result = await recipientManager.add(key, label, manifest, deps.repoRoot);
|
|
1117
1092
|
res.json(result);
|
|
1118
1093
|
}
|
|
@@ -1126,6 +1101,7 @@ function createApiRouter(deps) {
|
|
|
1126
1101
|
try {
|
|
1127
1102
|
const manifest = loadManifest();
|
|
1128
1103
|
const { key } = req.body;
|
|
1104
|
+
const recipientManager = new core_1.RecipientManager(buildSourceFor(manifest), matrix, tx);
|
|
1129
1105
|
const result = await recipientManager.remove(key, manifest, deps.repoRoot);
|
|
1130
1106
|
const cells = matrix.resolveMatrix(manifest, deps.repoRoot);
|
|
1131
1107
|
const targets = cells.filter((c) => c.exists).map((c) => `${c.namespace}/${c.environment}`);
|
|
@@ -1209,6 +1185,7 @@ function createApiRouter(deps) {
|
|
|
1209
1185
|
};
|
|
1210
1186
|
}
|
|
1211
1187
|
}
|
|
1188
|
+
const serviceIdManager = new core_1.ServiceIdentityManager(buildSourceFor(manifest), matrix, tx);
|
|
1212
1189
|
const result = await serviceIdManager.create(name, namespaces, description ?? "", manifest, deps.repoRoot, {
|
|
1213
1190
|
kmsEnvConfigs: typedKmsConfigs,
|
|
1214
1191
|
sharedRecipient: sharedRecipient === true,
|
|
@@ -1238,6 +1215,7 @@ function createApiRouter(deps) {
|
|
|
1238
1215
|
res.status(404).json({ error: `Service identity '${name}' not found.`, code: "NOT_FOUND" });
|
|
1239
1216
|
return;
|
|
1240
1217
|
}
|
|
1218
|
+
const serviceIdManager = new core_1.ServiceIdentityManager(buildSourceFor(manifest), matrix, tx);
|
|
1241
1219
|
await serviceIdManager.delete(name, manifest, deps.repoRoot);
|
|
1242
1220
|
res.json({ ok: true });
|
|
1243
1221
|
}
|
|
@@ -1269,6 +1247,7 @@ function createApiRouter(deps) {
|
|
|
1269
1247
|
}
|
|
1270
1248
|
typedKmsConfigs[envName] = { provider: cfg.provider, keyId: cfg.keyId };
|
|
1271
1249
|
}
|
|
1250
|
+
const serviceIdManager = new core_1.ServiceIdentityManager(buildSourceFor(manifest), matrix, tx);
|
|
1272
1251
|
await serviceIdManager.updateEnvironments(name, typedKmsConfigs, manifest, deps.repoRoot);
|
|
1273
1252
|
res.json({ ok: true });
|
|
1274
1253
|
}
|
|
@@ -1283,6 +1262,7 @@ function createApiRouter(deps) {
|
|
|
1283
1262
|
const name = req.params.name;
|
|
1284
1263
|
const { environment } = req.body;
|
|
1285
1264
|
const manifest = loadManifest();
|
|
1265
|
+
const serviceIdManager = new core_1.ServiceIdentityManager(buildSourceFor(manifest), matrix, tx);
|
|
1286
1266
|
const privateKeys = await serviceIdManager.rotateKey(name, manifest, deps.repoRoot, environment);
|
|
1287
1267
|
setNoCacheHeaders(res);
|
|
1288
1268
|
res.json({ privateKeys });
|
|
@@ -1471,6 +1451,7 @@ function createApiRouter(deps) {
|
|
|
1471
1451
|
return;
|
|
1472
1452
|
}
|
|
1473
1453
|
const events = [];
|
|
1454
|
+
const backendMigrator = new core_1.BackendMigrator(buildSourceFor, matrix, tx);
|
|
1474
1455
|
const result = await backendMigrator.migrate(manifest, deps.repoRoot, { target, environment, dryRun: true }, (event) => events.push(event));
|
|
1475
1456
|
res.json({ success: !result.rolledBack, result, events });
|
|
1476
1457
|
}
|
|
@@ -1501,6 +1482,7 @@ function createApiRouter(deps) {
|
|
|
1501
1482
|
return;
|
|
1502
1483
|
}
|
|
1503
1484
|
const events = [];
|
|
1485
|
+
const backendMigrator = new core_1.BackendMigrator(buildSourceFor, matrix, tx);
|
|
1504
1486
|
const result = await backendMigrator.migrate(manifest, deps.repoRoot, { target, environment, dryRun: false }, (event) => events.push(event));
|
|
1505
1487
|
res.json({ success: !result.rolledBack, result, events });
|
|
1506
1488
|
}
|
|
@@ -1539,6 +1521,7 @@ function createApiRouter(deps) {
|
|
|
1539
1521
|
res.status(404).json({ error: message, code: "NOT_FOUND" });
|
|
1540
1522
|
return;
|
|
1541
1523
|
}
|
|
1524
|
+
const resetManager = new core_1.ResetManager(matrix, buildSourceFor, schemaValidator, tx);
|
|
1542
1525
|
const result = await resetManager.reset({ scope, backend, key, keys }, manifest, deps.repoRoot);
|
|
1543
1526
|
res.json({ success: true, result });
|
|
1544
1527
|
}
|
|
@@ -1570,6 +1553,7 @@ function createApiRouter(deps) {
|
|
|
1570
1553
|
return;
|
|
1571
1554
|
}
|
|
1572
1555
|
}
|
|
1556
|
+
const syncManager = new core_1.SyncManager(matrix, buildSourceFor(manifest), tx);
|
|
1573
1557
|
const plan = await syncManager.plan(manifest, deps.repoRoot, { namespace });
|
|
1574
1558
|
res.json(plan);
|
|
1575
1559
|
}
|
|
@@ -1593,6 +1577,7 @@ function createApiRouter(deps) {
|
|
|
1593
1577
|
return;
|
|
1594
1578
|
}
|
|
1595
1579
|
}
|
|
1580
|
+
const syncManager = new core_1.SyncManager(matrix, buildSourceFor(manifest), tx);
|
|
1596
1581
|
const result = await syncManager.sync(manifest, deps.repoRoot, { namespace });
|
|
1597
1582
|
res.json({ success: true, result });
|
|
1598
1583
|
}
|