Initial import of verae-middleware from zapier monorepo
This commit is contained in:
commit
90713dcc78
70 changed files with 6773 additions and 0 deletions
119
src/store/db.js
Normal file
119
src/store/db.js
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
/**
|
||||
* @fileoverview JSON file-backed in-memory store (MVP persistence).
|
||||
* @module store/db
|
||||
*/
|
||||
|
||||
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
|
||||
import { dirname } from 'node:path';
|
||||
import { config } from '../config.js';
|
||||
import { createDebugger } from '../debug/logger.js';
|
||||
|
||||
const log = createDebugger('app');
|
||||
|
||||
/**
|
||||
* @typedef {Object} StoreShape
|
||||
* @property {Record<string, object>} tenants
|
||||
* @property {Record<string, string>} apiKeys - apiKey → tenantId
|
||||
* @property {Record<string, object>} usage - tenantId → counters
|
||||
* @property {object[]} webhooks
|
||||
* @property {object[]} jobWatchers
|
||||
*/
|
||||
|
||||
/** @type {StoreShape|null} */
|
||||
let store = null;
|
||||
|
||||
/**
|
||||
* Create an empty store document.
|
||||
* @returns {StoreShape}
|
||||
*/
|
||||
export function emptyStore() {
|
||||
return {
|
||||
tenants: {},
|
||||
apiKeys: {},
|
||||
usage: {},
|
||||
webhooks: [],
|
||||
jobWatchers: [],
|
||||
tokenRefs: {},
|
||||
blobs: {},
|
||||
shares: {},
|
||||
trees: {},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Load store from disk into memory (or create empty if missing).
|
||||
* @param {string} [path=config.storePath]
|
||||
* @returns {StoreShape}
|
||||
*/
|
||||
export function loadStore(path = config.storePath) {
|
||||
if (store) return store;
|
||||
|
||||
if (existsSync(path)) {
|
||||
try {
|
||||
const raw = readFileSync(path, 'utf8');
|
||||
const parsed = JSON.parse(raw);
|
||||
store = {
|
||||
...emptyStore(),
|
||||
...parsed,
|
||||
tenants: parsed.tenants ?? {},
|
||||
apiKeys: parsed.apiKeys ?? {},
|
||||
usage: parsed.usage ?? {},
|
||||
webhooks: Array.isArray(parsed.webhooks) ? parsed.webhooks : [],
|
||||
jobWatchers: Array.isArray(parsed.jobWatchers) ? parsed.jobWatchers : [],
|
||||
tokenRefs: parsed.tokenRefs ?? {},
|
||||
};
|
||||
log.debug('store loaded', { path, tenants: Object.keys(store.tenants).length });
|
||||
} catch (err) {
|
||||
log.error('store load failed, using empty', { path, error: err.message });
|
||||
store = emptyStore();
|
||||
}
|
||||
} else {
|
||||
store = emptyStore();
|
||||
log.debug('store initialized empty', { path });
|
||||
}
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the in-memory store (loads if needed).
|
||||
* @returns {StoreShape}
|
||||
*/
|
||||
export function getStore() {
|
||||
if (!store) return loadStore();
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the in-memory store (tests only).
|
||||
* @param {StoreShape|null} next
|
||||
* @returns {void}
|
||||
*/
|
||||
export function setStoreForTests(next) {
|
||||
store = next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist the in-memory store to disk.
|
||||
* @param {string} [path=config.storePath]
|
||||
* @returns {void}
|
||||
*/
|
||||
export function persist(path = config.storePath) {
|
||||
const data = getStore();
|
||||
const dir = dirname(path);
|
||||
if (!existsSync(dir)) {
|
||||
mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
writeFileSync(path, JSON.stringify(data, null, 2), 'utf8');
|
||||
log.debug('store persisted', { path });
|
||||
}
|
||||
|
||||
/**
|
||||
* Force reload from disk (drops memory).
|
||||
* @param {string} [path=config.storePath]
|
||||
* @returns {StoreShape}
|
||||
*/
|
||||
export function reloadStore(path = config.storePath) {
|
||||
store = null;
|
||||
return loadStore(path);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue