229 lines
15 KiB
SQL
229 lines
15 KiB
SQL
CREATE TABLE IF NOT EXISTS platform_metadata (
|
|
singleton boolean PRIMARY KEY DEFAULT true CHECK(singleton), mode text NOT NULL CHECK(mode IN ('demo','production'))
|
|
);
|
|
CREATE TABLE IF NOT EXISTS administrators (
|
|
id text PRIMARY KEY, username text NOT NULL UNIQUE, display_name text NOT NULL,
|
|
role text NOT NULL CHECK (role IN ('super_admin','operator','viewer')), password_hash text NOT NULL
|
|
);
|
|
ALTER TABLE administrators ADD COLUMN IF NOT EXISTS enabled boolean NOT NULL DEFAULT true;
|
|
CREATE TABLE IF NOT EXISTS admin_sessions (
|
|
id text PRIMARY KEY, admin_id text NOT NULL REFERENCES administrators(id), expires_at timestamptz NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS admin_session_details (
|
|
session_id text PRIMARY KEY REFERENCES admin_sessions(id) ON DELETE CASCADE,
|
|
created_at timestamptz NOT NULL DEFAULT now(), client_ip text NOT NULL DEFAULT '', user_agent text NOT NULL DEFAULT ''
|
|
);
|
|
CREATE TABLE IF NOT EXISTS admin_mfa (
|
|
admin_id text PRIMARY KEY REFERENCES administrators(id) ON DELETE CASCADE,
|
|
encrypted_secret text NOT NULL DEFAULT '', last_counter bigint NOT NULL DEFAULT -1,
|
|
pending_secret text NOT NULL DEFAULT '', pending_session text NOT NULL DEFAULT '',
|
|
pending_expires timestamptz, enabled_at timestamptz
|
|
);
|
|
CREATE TABLE IF NOT EXISTS admin_mfa_recovery (
|
|
admin_id text NOT NULL REFERENCES admin_mfa(admin_id) ON DELETE CASCADE,
|
|
code_hash text NOT NULL, PRIMARY KEY(admin_id,code_hash)
|
|
);
|
|
CREATE TABLE IF NOT EXISTS desktops (
|
|
id text PRIMARY KEY, name text NOT NULL, os text NOT NULL, node text NOT NULL, vmid integer NOT NULL,
|
|
pool text NOT NULL DEFAULT '', power_state text NOT NULL, service_state text NOT NULL DEFAULT 'unknown',
|
|
provider text NOT NULL CHECK (provider IN ('demo','proxmox')), endpoint text NOT NULL DEFAULT '', app text NOT NULL DEFAULT 'Desktop',
|
|
width integer NOT NULL DEFAULT 2560, height integer NOT NULL DEFAULT 1440, fps integer NOT NULL DEFAULT 30,
|
|
updated_at timestamptz NOT NULL DEFAULT now(), UNIQUE(provider,node,vmid)
|
|
);
|
|
CREATE TABLE IF NOT EXISTS terminals (
|
|
id text PRIMARY KEY, sn text NOT NULL UNIQUE, name text NOT NULL, group_name text NOT NULL DEFAULT '',
|
|
status text NOT NULL DEFAULT 'unregistered', agent_version text NOT NULL DEFAULT '', last_seen timestamptz,
|
|
session_desktop_id text REFERENCES desktops(id), config_version integer NOT NULL DEFAULT 1,
|
|
bootstrap_hash text NOT NULL, token_hash text UNIQUE, fps double precision, rtt double precision
|
|
);
|
|
ALTER TABLE terminals ADD COLUMN IF NOT EXISTS enabled boolean NOT NULL DEFAULT true;
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id text PRIMARY KEY, username text NOT NULL UNIQUE, display_name text NOT NULL,
|
|
enabled boolean NOT NULL DEFAULT true, terminal_id text UNIQUE REFERENCES terminals(id)
|
|
);
|
|
CREATE TABLE IF NOT EXISTS bindings (
|
|
user_id text NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
desktop_id text NOT NULL REFERENCES desktops(id), PRIMARY KEY(user_id,desktop_id)
|
|
);
|
|
CREATE TABLE IF NOT EXISTS resources (
|
|
id text PRIMARY KEY, name text NOT NULL, status text NOT NULL, cpu double precision NOT NULL,
|
|
memory double precision NOT NULL, storage double precision NOT NULL, provider text NOT NULL,
|
|
updated_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
CREATE TABLE IF NOT EXISTS tasks (
|
|
id text PRIMARY KEY, kind text NOT NULL, action text NOT NULL, target_id text NOT NULL, target_name text NOT NULL,
|
|
actor text NOT NULL, status text NOT NULL, message text NOT NULL DEFAULT '', provider_ref text NOT NULL DEFAULT '',
|
|
idempotency_key text NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(),
|
|
UNIQUE(actor,idempotency_key)
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS tasks_active_target ON tasks(kind,target_id) WHERE status IN ('queued','running');
|
|
CREATE TABLE IF NOT EXISTS audit_logs (
|
|
id text PRIMARY KEY, actor text NOT NULL, action text NOT NULL, target text NOT NULL, result text NOT NULL,
|
|
request_id text NOT NULL, created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
CREATE TABLE IF NOT EXISTS connection_tickets (
|
|
token_hash text PRIMARY KEY, terminal_id text NOT NULL REFERENCES terminals(id), desktop_id text NOT NULL REFERENCES desktops(id),
|
|
expires_at timestamptz NOT NULL, consumed_at timestamptz
|
|
);
|
|
CREATE INDEX IF NOT EXISTS audit_created ON audit_logs(created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS tasks_created ON tasks(created_at DESC);
|
|
ALTER TABLE terminals ADD COLUMN IF NOT EXISTS session_id text NOT NULL DEFAULT '';
|
|
ALTER TABLE terminals ADD COLUMN IF NOT EXISTS session_state text NOT NULL DEFAULT 'idle';
|
|
CREATE TABLE IF NOT EXISTS task_diagnostics (
|
|
task_id text PRIMARY KEY REFERENCES tasks(id) ON DELETE CASCADE,
|
|
content jsonb NOT NULL, created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
CREATE TABLE IF NOT EXISTS terminal_log_archives (
|
|
id text PRIMARY KEY, task_id text NOT NULL UNIQUE REFERENCES tasks(id) ON DELETE CASCADE,
|
|
terminal_id text NOT NULL REFERENCES terminals(id) ON DELETE CASCADE,
|
|
captured_at timestamptz NOT NULL DEFAULT now(), boot_id text NOT NULL DEFAULT '',
|
|
agent_version text NOT NULL DEFAULT '', kiosk_state text NOT NULL DEFAULT '', log_text text NOT NULL DEFAULT '',
|
|
metadata jsonb NOT NULL DEFAULT '{}'::jsonb
|
|
);
|
|
CREATE INDEX IF NOT EXISTS terminal_log_archives_terminal_time ON terminal_log_archives(terminal_id,captured_at DESC,id);
|
|
CREATE INDEX IF NOT EXISTS terminal_log_archives_time ON terminal_log_archives(captured_at DESC);
|
|
CREATE TABLE IF NOT EXISTS image_templates (
|
|
id text PRIMARY KEY, name text NOT NULL, version text NOT NULL, os text NOT NULL CHECK(os IN ('Windows','Ubuntu','麒麟')),
|
|
node text NOT NULL, vmid integer NOT NULL CHECK(vmid>=100), digest text NOT NULL,
|
|
enabled boolean NOT NULL DEFAULT true, validated_at timestamptz NOT NULL DEFAULT now(),
|
|
UNIQUE(node,vmid), UNIQUE(name,version)
|
|
);
|
|
CREATE TABLE IF NOT EXISTS desktop_pools (
|
|
id text PRIMARY KEY, name text NOT NULL UNIQUE, type text NOT NULL CHECK(type IN ('dedicated','shared')),
|
|
max_count integer NOT NULL CHECK(max_count BETWEEN 1 AND 10000),
|
|
template_id text REFERENCES image_templates(id), enabled boolean NOT NULL DEFAULT true,
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
ALTER TABLE desktops ADD COLUMN IF NOT EXISTS pool_id text REFERENCES desktop_pools(id);
|
|
ALTER TABLE desktops ADD COLUMN IF NOT EXISTS setup_required boolean NOT NULL DEFAULT false;
|
|
CREATE TABLE IF NOT EXISTS provisioning_jobs (
|
|
task_id text PRIMARY KEY REFERENCES tasks(id), template_id text NOT NULL REFERENCES image_templates(id),
|
|
pool_id text REFERENCES desktop_pools(id), desktop_id text NOT NULL UNIQUE, vmid integer NOT NULL UNIQUE,
|
|
request_json text NOT NULL, request_hash text NOT NULL, state text NOT NULL DEFAULT 'reserved'
|
|
CHECK(state IN ('reserved','created','failed','uncertain')), created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS desktops_pool ON desktops(pool_id);
|
|
CREATE TABLE IF NOT EXISTS import_batches (
|
|
id text PRIMARY KEY, actor_id text NOT NULL REFERENCES administrators(id), kind text NOT NULL,
|
|
row_count integer NOT NULL, idempotency_key text NOT NULL, request_hash text NOT NULL, result jsonb NOT NULL,
|
|
created_at timestamptz NOT NULL DEFAULT now(), UNIQUE(actor_id,idempotency_key)
|
|
);
|
|
CREATE TABLE IF NOT EXISTS session_history (
|
|
id text PRIMARY KEY, session_id text NOT NULL, terminal_id text NOT NULL REFERENCES terminals(id),
|
|
terminal_name text NOT NULL, desktop_id text NOT NULL REFERENCES desktops(id), desktop_name text NOT NULL,
|
|
username text NOT NULL DEFAULT '', display_name text NOT NULL DEFAULT '',
|
|
state text NOT NULL CHECK(state IN ('connecting','connected','ended','interrupted')),
|
|
source text NOT NULL CHECK(source IN ('ticket','observed')),
|
|
reserved_at timestamptz NOT NULL DEFAULT now(), connected_at timestamptz,
|
|
last_reported_at timestamptz NOT NULL DEFAULT now(), ended_at timestamptz, end_reason text NOT NULL DEFAULT ''
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS session_history_active ON session_history(terminal_id) WHERE ended_at IS NULL;
|
|
CREATE INDEX IF NOT EXISTS session_history_time ON session_history(reserved_at DESC,id);
|
|
CREATE TABLE IF NOT EXISTS resource_samples (
|
|
resource_id text NOT NULL, sampled_at timestamptz NOT NULL DEFAULT now(), cpu double precision,
|
|
memory double precision, storage double precision, PRIMARY KEY(resource_id,sampled_at)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS resource_samples_time ON resource_samples(sampled_at);
|
|
CREATE TABLE IF NOT EXISTS alert_rules (
|
|
id text PRIMARY KEY, name text NOT NULL, metric text NOT NULL CHECK(metric IN ('node_cpu','node_memory','node_storage','node_unavailable','terminal_offline')),
|
|
threshold double precision NOT NULL, hold_seconds integer NOT NULL DEFAULT 0, enabled boolean NOT NULL DEFAULT true
|
|
);
|
|
ALTER TABLE alert_rules DROP CONSTRAINT IF EXISTS alert_rules_metric_check;
|
|
ALTER TABLE alert_rules ADD CONSTRAINT alert_rules_metric_check CHECK(metric IN
|
|
('node_cpu','node_memory','node_storage','node_unavailable','terminal_offline','terminal_cpu','terminal_memory','terminal_temperature'));
|
|
ALTER TABLE alert_rules ADD COLUMN IF NOT EXISTS updated_at timestamptz NOT NULL DEFAULT now();
|
|
INSERT INTO alert_rules(id,name,metric,threshold,hold_seconds) VALUES
|
|
('rule_cpu','节点 CPU 高水位','node_cpu',90,120),('rule_memory','节点内存高水位','node_memory',90,120),
|
|
('rule_storage','节点存储高水位','node_storage',90,120),('rule_node','节点状态或采集异常','node_unavailable',120,0),
|
|
('rule_terminal','终端离线','terminal_offline',180,0) ON CONFLICT(id) DO NOTHING;
|
|
CREATE TABLE IF NOT EXISTS alerts (
|
|
id text PRIMARY KEY, rule_id text NOT NULL REFERENCES alert_rules(id), rule_name text NOT NULL,
|
|
metric text NOT NULL, target_id text NOT NULL, target_name text NOT NULL, value double precision NOT NULL,
|
|
threshold double precision NOT NULL, status text NOT NULL DEFAULT 'firing', started_at timestamptz NOT NULL,
|
|
opened_at timestamptz NOT NULL DEFAULT now(), resolved_at timestamptz, resolve_reason text NOT NULL DEFAULT '',
|
|
acknowledged_at timestamptz, acknowledged_by text NOT NULL DEFAULT ''
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS alerts_open ON alerts(rule_id,target_id) WHERE status='firing';
|
|
CREATE INDEX IF NOT EXISTS alerts_time ON alerts(opened_at DESC,id);
|
|
CREATE TABLE IF NOT EXISTS alert_states (
|
|
rule_id text NOT NULL REFERENCES alert_rules(id), target_id text NOT NULL, first_seen timestamptz NOT NULL DEFAULT now(),
|
|
PRIMARY KEY(rule_id,target_id)
|
|
);
|
|
ALTER TABLE alert_states ADD COLUMN IF NOT EXISTS last_sample_at timestamptz;
|
|
CREATE TABLE IF NOT EXISTS terminal_policy_templates (
|
|
id text PRIMARY KEY, name text NOT NULL, version text NOT NULL,
|
|
heartbeat_interval integer NOT NULL CHECK(heartbeat_interval BETWEEN 15 AND 60),
|
|
command_poll_interval integer NOT NULL CHECK(command_poll_interval BETWEEN 3 AND 30),
|
|
desktop_refresh_interval integer NOT NULL CHECK(desktop_refresh_interval BETWEEN 5 AND 120),
|
|
created_at timestamptz NOT NULL DEFAULT now(), UNIQUE(name,version)
|
|
);
|
|
CREATE TABLE IF NOT EXISTS terminal_policy_capabilities (
|
|
terminal_id text PRIMARY KEY REFERENCES terminals(id) ON DELETE CASCADE,
|
|
protocol integer NOT NULL DEFAULT 0 CHECK(protocol IN (0,1))
|
|
);
|
|
CREATE TABLE IF NOT EXISTS terminal_policy_deployments (
|
|
id text PRIMARY KEY, template_id text NOT NULL REFERENCES terminal_policy_templates(id), actor text NOT NULL,
|
|
idempotency_key text NOT NULL, request_hash text NOT NULL, created_at timestamptz NOT NULL DEFAULT now(),
|
|
UNIQUE(actor,idempotency_key)
|
|
);
|
|
CREATE TABLE IF NOT EXISTS terminal_policy_deliveries (
|
|
id text PRIMARY KEY, deployment_id text NOT NULL REFERENCES terminal_policy_deployments(id),
|
|
terminal_id text NOT NULL REFERENCES terminals(id), config_version integer NOT NULL,
|
|
status text NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','applied','failed','superseded')),
|
|
error_code text NOT NULL DEFAULT '', acknowledged_at timestamptz,
|
|
UNIQUE(terminal_id,config_version), UNIQUE(deployment_id,terminal_id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS pve_connection (
|
|
singleton boolean PRIMARY KEY DEFAULT true CHECK(singleton), revision text NOT NULL,
|
|
encrypted_config text NOT NULL, updated_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
CREATE TABLE IF NOT EXISTS login_limits (
|
|
key text PRIMARY KEY, window_started timestamptz NOT NULL DEFAULT now(), attempts integer NOT NULL
|
|
);
|
|
CREATE INDEX IF NOT EXISTS login_limits_time ON login_limits(window_started);
|
|
CREATE TABLE IF NOT EXISTS hardware_jobs (
|
|
task_id text PRIMARY KEY REFERENCES tasks(id), request_json text NOT NULL, request_hash text NOT NULL,
|
|
stage text NOT NULL DEFAULT 'prepared' CHECK(stage IN ('prepared','submitting','completed','failed','uncertain'))
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS tasks_compute_target ON tasks(target_id) WHERE kind IN ('desktop','hardware') AND status IN ('queued','running');
|
|
|
|
CREATE TABLE IF NOT EXISTS snapshot_jobs (
|
|
task_id text PRIMARY KEY REFERENCES tasks(id), request_json text NOT NULL, request_hash text NOT NULL,
|
|
target_hash text NOT NULL,
|
|
stage text NOT NULL DEFAULT 'prepared' CHECK(stage IN ('prepared','submitting','completed','failed','uncertain'))
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS tasks_vm_maintenance_target ON tasks(target_id) WHERE kind IN ('desktop','hardware','snapshot') AND status IN ('queued','running');
|
|
|
|
CREATE TABLE IF NOT EXISTS power_batches (
|
|
id text PRIMARY KEY, actor text NOT NULL, action text NOT NULL CHECK(action IN ('start','shutdown','reboot')),
|
|
idempotency_key text NOT NULL, request_hash text NOT NULL, created_at timestamptz NOT NULL DEFAULT now(),
|
|
UNIQUE(actor,idempotency_key)
|
|
);
|
|
CREATE TABLE IF NOT EXISTS power_batch_items (
|
|
batch_id text NOT NULL REFERENCES power_batches(id), desktop_id text NOT NULL, desktop_name text NOT NULL,
|
|
vmid integer NOT NULL, task_id text UNIQUE REFERENCES tasks(id), code text NOT NULL, message text NOT NULL,
|
|
fingerprint text NOT NULL, stage text NOT NULL CHECK(stage IN ('skipped','prepared','submitting','completed','failed','uncertain')),
|
|
PRIMARY KEY(batch_id,desktop_id)
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS tasks_all_vm_actions_target ON tasks(target_id) WHERE kind IN ('desktop','hardware','snapshot','batch-desktop') AND status IN ('queued','running');
|
|
|
|
CREATE TABLE IF NOT EXISTS terminal_metrics_current (
|
|
terminal_id text PRIMARY KEY REFERENCES terminals(id) ON DELETE CASCADE,
|
|
sampled_at timestamptz NOT NULL, supported boolean NOT NULL,
|
|
cpu_percent double precision CHECK(cpu_percent BETWEEN 0 AND 100),
|
|
memory_percent double precision CHECK(memory_percent BETWEEN 0 AND 100),
|
|
soc_temperature_c double precision CHECK(soc_temperature_c BETWEEN -40 AND 150),
|
|
uptime_seconds double precision CHECK(uptime_seconds BETWEEN 0 AND 3155760000)
|
|
);
|
|
CREATE TABLE IF NOT EXISTS terminal_metric_samples (
|
|
terminal_id text NOT NULL REFERENCES terminals(id) ON DELETE CASCADE,
|
|
sampled_at timestamptz NOT NULL,
|
|
cpu_percent double precision CHECK(cpu_percent BETWEEN 0 AND 100),
|
|
memory_percent double precision CHECK(memory_percent BETWEEN 0 AND 100),
|
|
soc_temperature_c double precision CHECK(soc_temperature_c BETWEEN -40 AND 150),
|
|
uptime_seconds double precision CHECK(uptime_seconds BETWEEN 0 AND 3155760000),
|
|
PRIMARY KEY(terminal_id,sampled_at)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS terminal_metric_samples_expiry ON terminal_metric_samples(sampled_at);
|