delegation-blob-store.mjs
96 lines 2.8 KB
Raw
sha256:c541f54879f8110bb25bb9786cdb8ff7a4aec19f9f2a2789a55bc74e6f7fe095 fix(7C-L1c): persist hosted delegation indexes in Netlify Blobs Human minor ⚠ breaking 32 days ago
1 /**
2 * Hosted bridge: persist delegation index JSON in Netlify Blobs (7C-L1c).
3 *
4 * Self-hosted bridge uses DATA_DIR files only. On Netlify, DATA_DIR is ephemeral;
5 * this module hydrates files from Blobs before delegation handlers and persists after writes.
6 */
7
8 import fs from 'fs';
9 import path from 'path';
10 import {
11 DELEGATION_POLICY_FILE,
12 DELEGATION_IDENTITIES_FILE,
13 DELEGATION_CONSENTS_FILE,
14 DELEGATION_GRANTS_FILE,
15 DELEGATION_AUDIT_FILE,
16 } from '../../lib/agent/delegation.mjs';
17
18 /** @typedef {{ get: (key: string, opts?: { type?: string }) => Promise<string|ArrayBuffer|null>, set: (key: string, value: string) => Promise<void> }} BlobStore */
19
20 export const DELEGATION_BLOB_FILES = [
21 DELEGATION_POLICY_FILE,
22 DELEGATION_IDENTITIES_FILE,
23 DELEGATION_CONSENTS_FILE,
24 DELEGATION_GRANTS_FILE,
25 DELEGATION_AUDIT_FILE,
26 ];
27
28 /**
29 * @param {string} filename
30 * @returns {string}
31 */
32 export function delegationBlobKey(filename) {
33 return `delegation/${filename}`;
34 }
35
36 /**
37 * Load delegation store files from Blobs into DATA_DIR (hosted cold-start hydration).
38 *
39 * @param {BlobStore|null|undefined} blobStore
40 * @param {string} dataDir
41 */
42 export async function hydrateDelegationStoresFromBlob(blobStore, dataDir) {
43 if (!blobStore || typeof blobStore.get !== 'function') return;
44 fs.mkdirSync(dataDir, { recursive: true });
45 for (const filename of DELEGATION_BLOB_FILES) {
46 const fp = path.join(dataDir, filename);
47 try {
48 const raw = await blobStore.get(delegationBlobKey(filename), { type: 'text' });
49 if (typeof raw === 'string' && raw.trim()) {
50 fs.writeFileSync(fp, raw, 'utf8');
51 }
52 } catch {
53 /* keep existing file or empty */
54 }
55 }
56 }
57
58 /**
59 * Write delegation store files from DATA_DIR to Blobs after a mutation.
60 *
61 * @param {BlobStore|null|undefined} blobStore
62 * @param {string} dataDir
63 */
64 export async function persistDelegationStoresToBlob(blobStore, dataDir) {
65 if (!blobStore || typeof blobStore.set !== 'function') return;
66 for (const filename of DELEGATION_BLOB_FILES) {
67 const fp = path.join(dataDir, filename);
68 if (!fs.existsSync(fp)) continue;
69 try {
70 const raw = fs.readFileSync(fp, 'utf8');
71 if (raw.trim()) {
72 await blobStore.set(delegationBlobKey(filename), raw);
73 }
74 } catch {
75 /* non-fatal */
76 }
77 }
78 }
79
80 /**
81 * Run a delegation mutation with hosted Blob hydrate/persist when available.
82 *
83 * @template T
84 * @param {{
85 * blobStore: BlobStore|null|undefined,
86 * dataDir: string,
87 * run: () => T | Promise<T>,
88 * }} opts
89 * @returns {Promise<T>}
90 */
91 export async function withDelegationBlobSync(opts) {
92 await hydrateDelegationStoresFromBlob(opts.blobStore, opts.dataDir);
93 const result = await opts.run();
94 await persistDelegationStoresToBlob(opts.blobStore, opts.dataDir);
95 return result;
96 }
File History 1 commit
sha256:c541f54879f8110bb25bb9786cdb8ff7a4aec19f9f2a2789a55bc74e6f7fe095 fix(7C-L1c): persist hosted delegation indexes in Netlify Blobs Human minor 32 days ago