87 lines
2.9 KiB
JavaScript
87 lines
2.9 KiB
JavaScript
// Compile-time constants for the dev-deploy plugin.
|
|
|
|
const PLUGIN_NAME = "dev-deploy";
|
|
const PLUGIN_VERSION = "0.0.7";
|
|
|
|
// Namespace UUID for deterministic IDs derived from (kind, name).
|
|
// Generated once via crypto.randomUUID() and frozen here forever.
|
|
// Two environments that bootstrap from the same metadata population
|
|
// will assign identical UUIDs to entities with the same (kind, name).
|
|
const ID_NAMESPACE = "8b3a1e0d-4f6c-4d2a-9c5b-7e8f9a0b1c2d";
|
|
|
|
const OP_SCHEMA_VERSION = 1;
|
|
|
|
// The ONLY global _sc_config keys dev-deploy syncs. One source of truth for both
|
|
// the journalling side (wrap.js only records set_config ops for these keys) and
|
|
// the apply side (applySetConfig refuses any other key), so a compromised peer
|
|
// cannot write security-relevant config it was never meant to touch (e.g.
|
|
// disable_csrf_routes, custom HTML headers, SMTP creds).
|
|
const SYNCABLE_CONFIG_KEYS = [
|
|
"menu_items",
|
|
"site_name",
|
|
"site_logo_id",
|
|
"base_url"
|
|
];
|
|
|
|
const DATA_MODES = {
|
|
MANAGED: "managed",
|
|
STARTER: "starter",
|
|
USER: "user"
|
|
};
|
|
|
|
// User tables that must NEVER receive peer-synced rows, and must never be flipped
|
|
// into managed/starter mode, no matter what a peer sends. `users` is the crown
|
|
// jewel: a synced row there is an injected account (role_id=1 => admin takeover).
|
|
// The admin UI already refuses to manage `users` (routes.js); the apply AND revert
|
|
// sides must refuse it too, since a malicious authenticated peer never goes through
|
|
// the UI. One source of truth for every enforcement point.
|
|
const ROW_SYNC_LOCKED_TABLES = ["users"];
|
|
|
|
const DESTRUCTIVE_POLICY = {
|
|
AUTO: "auto",
|
|
CONFIRM: "confirm",
|
|
REFUSE: "refuse"
|
|
};
|
|
|
|
// Entity kinds the plugin tracks. Children point at parents via parent_uuid.
|
|
const ENTITY_KINDS = {
|
|
TABLE: "table",
|
|
FIELD: "field",
|
|
VIEW: "view",
|
|
PAGE: "page",
|
|
TRIGGER: "trigger",
|
|
ROLE: "role",
|
|
LIBRARY: "library",
|
|
TAG: "tag",
|
|
CONSTRAINT: "constraint",
|
|
FILE: "file",
|
|
PAGE_GROUP: "page_group",
|
|
PAGE_GROUP_MEMBER: "page_group_member",
|
|
WORKFLOW_STEP: "workflow_step"
|
|
};
|
|
|
|
|
|
// Files don't have a meaningful integer id in current Saltcorn (File.create
|
|
// doesn't insert into _sc_files anymore — files are identified by their disk
|
|
// location and metadata via xattrs). We derive a stable 31-bit int from the
|
|
// location string so files fit the existing UNIQUE(kind, current_id) shape of
|
|
// _dd_entity_ids without a schema change.
|
|
const fileLocationToId = (location) => {
|
|
const crypto = require("crypto");
|
|
const hash = crypto.createHash("sha256").update(String(location)).digest();
|
|
return hash.readUInt32BE(0) & 0x7FFFFFFF;
|
|
};
|
|
|
|
|
|
module.exports = {
|
|
PLUGIN_NAME,
|
|
PLUGIN_VERSION,
|
|
ID_NAMESPACE,
|
|
OP_SCHEMA_VERSION,
|
|
SYNCABLE_CONFIG_KEYS,
|
|
DATA_MODES,
|
|
ROW_SYNC_LOCKED_TABLES,
|
|
DESTRUCTIVE_POLICY,
|
|
ENTITY_KINDS,
|
|
fileLocationToId
|
|
};
|