120 lines
5.1 KiB
JavaScript
120 lines
5.1 KiB
JavaScript
// Compile-time constants for the site-sync plugin.
|
|
//
|
|
// site-sync is a one-way DESIRED-STATE deployer: it snapshots a source site's
|
|
// current design state, diffs it against a target, and applies the difference so
|
|
// the target converges to the source. Unlike dev-deploy (an ops journal) there is
|
|
// no edit log -- identity is derived from the current state itself (entities keyed
|
|
// by NAME, the same key Saltcorn's *_pack builders make canonical; roles by id).
|
|
|
|
// SYNCABLE_CONFIG_KEYS, ROW_SYNC_LOCKED_TABLES, DATA_MODES (stored per table in
|
|
// _ss_table_modes) and the isInfraName / isLockedTable / isSyncedMode predicates
|
|
// are shared with dev-deploy: one source of truth in lib/common/syncConstants.js,
|
|
// re-exported here. capture.js intersects the produced config to
|
|
// SYNCABLE_CONFIG_KEYS and apply refuses any other key -- install_pack's
|
|
// setConfig clobbers unconditionally, so a compromised source must not reach
|
|
// security config. (install_pack also writes function_code_pages from
|
|
// pack.code_pages; site-sync handles code pages as a first-class previewed +
|
|
// prunable kind in diff.js, never as raw config.)
|
|
const syncConstants = require("./common/syncConstants");
|
|
const { ADMIN_ROLE_ID } = require("./common/adminGate");
|
|
|
|
const PLUGIN_NAME = "site-sync";
|
|
const PLUGIN_VERSION = require("../package.json").version;
|
|
|
|
// Saltcorn role ids used by the file layer: ADMIN (lib/common/adminGate.js) is the
|
|
// most privileged role, PUBLIC the least (min_role_read = PUBLIC means anyone may
|
|
// read). Synced files are owned by the admin user (file ownership is not portable
|
|
// between user tables).
|
|
const PUBLIC_ROLE_ID = 100;
|
|
const ADMIN_USER_ID = 1;
|
|
|
|
// How a sync treats entities present in the TARGET but absent from the SOURCE.
|
|
// mirror (default) -- prune them so the target exactly matches the source.
|
|
// additive -- never remove; the target stays a superset of the source.
|
|
const SYNC_MODES = {
|
|
MIRROR: "mirror",
|
|
ADDITIVE: "additive"
|
|
};
|
|
|
|
// Policy for DATA-BEARING drops (dropping a table that holds rows, or a field that
|
|
// holds column data). Metadata-only deletes (views, pages, triggers, empty fields)
|
|
// prune freely under mirror mode; a data-bearing drop is gated by this policy.
|
|
// confirm (default) -- allowed only when the table name was TYPED into the
|
|
// prunable allow-list (never pre-filled) AND the data-loss
|
|
// acknowledgement was given in the apply request.
|
|
// refuse -- never drop a data-bearing table/field (leave it, warn).
|
|
// auto -- drop without the acknowledgement (still requires the
|
|
// typed allow-list).
|
|
const DELETE_POLICY = {
|
|
AUTO: "auto",
|
|
CONFIRM: "confirm",
|
|
REFUSE: "refuse"
|
|
};
|
|
|
|
// Whether a paired peer may trigger an apply remotely.
|
|
// confirm (default) -- only an admin applies a previewed plan by plan_id.
|
|
// auto -- a paired peer may push-apply remotely
|
|
// (POST /site-sync/api/apply); the pull path always previews.
|
|
const APPLY_POLICY = {
|
|
AUTO: "auto",
|
|
CONFIRM: "confirm"
|
|
};
|
|
|
|
// Lifecycle of a persisted plan (_ss_plans.status). Transitions are claimed
|
|
// atomically (UPDATE ... WHERE status IN (...)) so a double-submit cannot run two
|
|
// applies or two rollbacks of the same plan concurrently.
|
|
const PLAN_STATUS = {
|
|
PENDING: "pending",
|
|
APPLYING: "applying",
|
|
APPLIED: "applied",
|
|
ROLLING_BACK: "rolling_back",
|
|
ROLLED_BACK: "rolled_back"
|
|
};
|
|
|
|
// Custom request Content-Type for signed peer bodies. Chosen so Saltcorn's
|
|
// express.json() middleware leaves the stream untouched and peerAuth can read the
|
|
// exact raw bytes for the HMAC. Must match on both the sign and verify sides.
|
|
const PEER_CONTENT_TYPE = "application/vnd.site-sync+json";
|
|
|
|
// Signed-request header names. The HMAC canonical contains NO header names, so
|
|
// these are free to rename -- but the sign side (transport.js) and verify side
|
|
// (peerAuth.js) must agree byte-for-byte.
|
|
const HEADERS = {
|
|
ENV_ID: "X-SS-Env-Id",
|
|
TIMESTAMP: "X-SS-Timestamp",
|
|
NONCE: "X-SS-Nonce",
|
|
SIGNATURE: "X-SS-Signature",
|
|
RESPONSE_SIGNATURE: "X-SS-Response-Signature"
|
|
};
|
|
|
|
// HKDF salt AND info of the KEK sealing peer secrets at rest (lib/peers.js).
|
|
// site-sync-specific, so site-sync and dev-deploy on one instance derive
|
|
// INDEPENDENT KEKs from the same session secret. Must stay byte-identical
|
|
// forever: changing it orphans every existing pairing.
|
|
const PEER_KEK_INFO = "site-sync:peer-secrets:aes-gcm-key:v1";
|
|
// Registered peers table and the raw nonce store of the inbound replay guard
|
|
// (both created in lib/schema.js).
|
|
const PEERS_TABLE = "_ss_peers";
|
|
const SEEN_NONCES_TABLE = "_ss_seen_nonces";
|
|
// The raw site-wide apply lock (lib/siteLock.js; created in lib/schema.js).
|
|
const APPLY_LOCK_TABLE = "_ss_apply_lock";
|
|
|
|
|
|
module.exports = {
|
|
...syncConstants,
|
|
PLUGIN_NAME,
|
|
PLUGIN_VERSION,
|
|
ADMIN_ROLE_ID,
|
|
PUBLIC_ROLE_ID,
|
|
ADMIN_USER_ID,
|
|
SYNC_MODES,
|
|
DELETE_POLICY,
|
|
APPLY_POLICY,
|
|
PLAN_STATUS,
|
|
PEER_CONTENT_TYPE,
|
|
HEADERS,
|
|
PEER_KEK_INFO,
|
|
PEERS_TABLE,
|
|
SEEN_NONCES_TABLE,
|
|
APPLY_LOCK_TABLE
|
|
};
|