sc-idp/lib/configStore.js
2026-06-17 17:37:45 -05:00

51 lines
1.9 KiB
JavaScript

// Durable per-tenant key/value storage in Saltcorn's _sc_config, used for
// saltcorn-idp state that is GENERATED during onLoad (env identity, signing
// keys, SAML cert).
//
// Why _sc_config and not a registered table or the plugin config blob:
// - It survives backup: backup_config dumps every non-fixed _sc_config key,
// and restore_config restores them BEFORE install_pack runs the plugin's
// onLoad -- so the restored value is already present when onLoad reads it
// (no onLoad-generates-then-restore-imports duplicate, which a registered
// table suffers because restore_tables runs AFTER onLoad). This matters
// because onLoad creates the env row, the active signing key, and the SAML
// cert if missing; a duplicate active key would break JWKS.
// - It does NOT write the plugin's own _sc_plugins row (Plugin.upsert during
// onLoad cascades plugin re-loading + duplicates rows). setConfig writes
// _sc_config only.
//
// Reads go DIRECT to _sc_config: getState().getConfig only surfaces keys in
// Saltcorn's known configTypes schema, so a custom plugin key is invisible
// through it even though it is stored and backed up. Writes go through setConfig
// (correct jsonb/text encoding on both backends; value wrapped as {v: ...}).
const db = require("@saltcorn/data/db");
const readKey = async (key) => {
const row = await db.selectMaybeOne("_sc_config", { key: key });
if (!row || row.value === null || row.value === undefined) {
return undefined;
}
let v = row.value;
if (typeof v === "string") {
try {
v = JSON.parse(v);
} catch (e) {
return undefined;
}
}
return v && typeof v === "object" && "v" in v ? v.v : v;
};
const writeKey = async (key, value) => {
const { getState } = require("@saltcorn/data/db/state");
await getState().setConfig(key, value);
};
module.exports = {
readKey,
writeKey
};