// 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 };