end-to-end CI to match containerization on rest of adipu_server
All checks were successful
Deploy to Server / deploy (push) Successful in 18s

This commit is contained in:
2026-05-05 00:10:39 +00:00
parent 3da410770a
commit 7da8bda5eb
16 changed files with 1814 additions and 3061 deletions

98
db/schema.sql Normal file
View File

@@ -0,0 +1,98 @@
-- BlindMaster schema. Idempotent: CREATE … IF NOT EXISTS so it works on a
-- fresh DB and replays cleanly against an existing one.
-- ── Core auth ───────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name TEXT,
email TEXT NOT NULL UNIQUE,
password_hash_string TEXT NOT NULL,
verification_token TEXT,
is_verified BOOLEAN NOT NULL DEFAULT FALSE,
timezone TEXT DEFAULT 'America/Chicago',
apns_token TEXT,
fcm_token TEXT, -- legacy (FCM era), kept harmless
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- One active session token per user. Code does delete-then-insert on login,
-- so PK on user_id enforces it.
CREATE TABLE IF NOT EXISTS user_tokens (
user_id INTEGER PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
token TEXT NOT NULL UNIQUE,
connected BOOLEAN NOT NULL DEFAULT FALSE,
socket TEXT
);
CREATE TABLE IF NOT EXISTS password_reset_tokens (
email TEXT PRIMARY KEY,
token TEXT NOT NULL,
expires_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS user_pending_emails (
user_id INTEGER PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
pending_email TEXT NOT NULL,
token TEXT NOT NULL,
expires_at TIMESTAMPTZ NOT NULL
);
-- ── Hardware: hubs (devices) and their controlled blinds (peripherals) ──────
CREATE TABLE IF NOT EXISTS devices (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
device_name TEXT NOT NULL,
max_ports INTEGER NOT NULL DEFAULT 4,
battery_soc SMALLINT,
timezone TEXT,
UNIQUE (user_id, device_name)
);
-- token UNIQUE so socket-auth can `select device_id … where token=$1` quickly.
CREATE TABLE IF NOT EXISTS device_tokens (
device_id INTEGER NOT NULL REFERENCES devices(id) ON DELETE CASCADE,
token TEXT NOT NULL UNIQUE,
connected BOOLEAN NOT NULL DEFAULT FALSE,
socket TEXT
);
CREATE INDEX IF NOT EXISTS device_tokens_device_id_idx ON device_tokens(device_id);
CREATE TABLE IF NOT EXISTS peripherals (
id SERIAL PRIMARY KEY,
device_id INTEGER NOT NULL REFERENCES devices(id) ON DELETE CASCADE,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
peripheral_number INTEGER NOT NULL,
peripheral_name TEXT NOT NULL,
last_pos INTEGER,
last_set TIMESTAMPTZ,
calibrated BOOLEAN DEFAULT FALSE,
await_calib BOOLEAN DEFAULT FALSE,
UNIQUE (device_id, peripheral_number)
);
CREATE INDEX IF NOT EXISTS peripherals_user_id_idx ON peripherals(user_id);
-- ── Multi-blind groups (e.g. "all kitchen blinds") ──────────────────────────
CREATE TABLE IF NOT EXISTS groups (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
timezone TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE (user_id, name)
);
CREATE TABLE IF NOT EXISTS group_peripherals (
group_id INTEGER NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
peripheral_id INTEGER NOT NULL REFERENCES peripherals(id) ON DELETE CASCADE,
PRIMARY KEY (group_id, peripheral_id)
);
-- ── Idempotent column additions for older DBs ───────────────────────────────
-- These mirror the runtime-bootstrap ALTERs the index.js used to do on every
-- startup. Safe on fresh DBs because the columns are already in CREATE TABLE.
ALTER TABLE users ADD COLUMN IF NOT EXISTS apns_token TEXT;
ALTER TABLE users ADD COLUMN IF NOT EXISTS timezone TEXT DEFAULT 'America/Chicago';
ALTER TABLE devices ADD COLUMN IF NOT EXISTS battery_soc SMALLINT;
ALTER TABLE devices ADD COLUMN IF NOT EXISTS timezone TEXT;
ALTER TABLE groups ADD COLUMN IF NOT EXISTS timezone TEXT;