This commit is contained in:
ZOMBIIIIIII
2026-05-13 12:35:05 +03:00
parent 762a46871b
commit 9fe5311bbf
2 changed files with 60 additions and 34 deletions

View File

@@ -1,14 +1,23 @@
-- ╔══════════════════════════════════════════════════════════════════╗
-- ║ CryptoWallet API — Production DB schema (idempotent, custodial)
-- ║ Применять: psql -h <host> -U postgres_user -d postgres -f ...
-- ║ Безопасно прогонять повторно на existing БД.
-- ║ CryptoWallet API — Production DB schema
-- ║
-- ║ APPEND-ONLY / NON-DESTRUCTIVE:
-- ║ Безопасно прогонять повторно. Ничего не DROP'ает, не overwrite. ║
-- ║ Если оператор добавил кастомные таблицы / индексы / constraints ║
-- ║ вручную — они НЕ будут затронуты. ║
-- ║ ║
-- ║ Применять: psql -h <host> -U <user> -d <db> -f cryptowallet-schema.sql ║
-- ╚══════════════════════════════════════════════════════════════════╝
-- NOTE: idempotency_keys + audit_log таблицы УДАЛЕНЫ из БД.
-- - idempotency_keys → KeyDB (Redis cache), см. apps/api/src/config/redis.ts
-- - audit_log → stdout-only (Docker logs / log-aggregator подбирает JSON lines)
-- Migration ниже drop'ает их если они существуют от прошлой версии.
-- NOTE: idempotency_keys и audit_log таблицы НЕ используются.
-- - idempotency_keys → KeyDB (Redis cache) apps/api/src/config/redis.ts
-- - audit_log → stdout JSON-lines — apps/api/src/lib/audit-log.ts
-- Скрипт их НЕ дропает (чтобы re-run был non-destructive).
-- Если оператор хочет cleanup — manual one-time:
-- DROP TABLE IF EXISTS audit_log CASCADE;
-- DROP TABLE IF EXISTS idempotency_keys CASCADE;
-- ── USERS ───────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS users (
id VARCHAR(26) NOT NULL PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
@@ -33,7 +42,7 @@ CREATE TABLE IF NOT EXISTS users (
encrypted_mnemonic TEXT
);
-- Idempotent ALTERs для existing БД без extension-columns
-- Idempotent ALTERs для existing БД у которой нет extension-columns (только ADD если нет колонки)
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'users' AND column_name = 'encrypted_mnemonic') THEN
@@ -47,15 +56,16 @@ BEGIN
END IF;
END $$;
-- Sanity check на blob size. Floor 100 (worst-case 12-word всё-3char mnemonic):
-- Constraint: blob size check (only ADDs if missing, никогда не DROP).
-- Floor 100 (worst-case 12-word 3-char mnemonic = 100 base64 chars).
-- Если оператор изменил этот constraint вручную — наш script его НЕ перезатрёт.
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_encrypted_mnemonic_size') THEN
ALTER TABLE users DROP CONSTRAINT users_encrypted_mnemonic_size;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_encrypted_mnemonic_size') THEN
ALTER TABLE users
ADD CONSTRAINT users_encrypted_mnemonic_size
CHECK (encrypted_mnemonic IS NULL OR (char_length(encrypted_mnemonic) BETWEEN 100 AND 512));
END IF;
ALTER TABLE users
ADD CONSTRAINT users_encrypted_mnemonic_size
CHECK (encrypted_mnemonic IS NULL OR (char_length(encrypted_mnemonic) BETWEEN 100 AND 512));
END $$;
-- Case-insensitive email uniqueness (Alice@x.com ≠ alice@x.com → ACCOUNT HIJACKING fix)
@@ -91,6 +101,7 @@ END $$;
-- ── WALLETS ─────────────────────────────────────────────────────────
-- ON DELETE RESTRICT: hard-delete user → запрос отвергнут пока есть wallets.
-- Это защита от unrecoverable fund loss при GDPR-wipe или admin удалении.
CREATE TABLE IF NOT EXISTS wallets (
id VARCHAR(26) NOT NULL PRIMARY KEY,
user_id VARCHAR(26) NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
@@ -105,21 +116,11 @@ CREATE TABLE IF NOT EXISTS wallets (
CREATE INDEX IF NOT EXISTS idx_wallets_user_id ON wallets(user_id);
CREATE INDEX IF NOT EXISTS idx_wallets_address ON wallets(address);
-- Idempotent FK migration: если raised на старой DB с CASCADE — поменять
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.referential_constraints
WHERE constraint_name LIKE 'wallets_user_id_fkey%' AND delete_rule = 'CASCADE'
) THEN
ALTER TABLE wallets DROP CONSTRAINT IF EXISTS wallets_user_id_fkey;
ALTER TABLE wallets ADD CONSTRAINT wallets_user_id_fkey
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE RESTRICT;
END IF;
END $$;
-- ── DROP legacy tables (если existing БД от прошлой версии) ────────
-- idempotency_keys → KeyDB cache (apps/api/src/lib/idempotency.ts → Redis)
-- audit_log → stdout-only (apps/api/src/lib/audit-log.ts)
DROP TABLE IF EXISTS audit_log CASCADE;
DROP TABLE IF EXISTS idempotency_keys CASCADE;
-- NOTE: если БД старая и wallets.user_id_fkey ON DELETE CASCADE (а нужен RESTRICT
-- для защиты от fund loss при delete user), оператор делает manual ОДИН раз:
--
-- ALTER TABLE wallets DROP CONSTRAINT wallets_user_id_fkey;
-- ALTER TABLE wallets ADD CONSTRAINT wallets_user_id_fkey
-- FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE RESTRICT;
--
-- Этот script ничего не дропает — re-run полностью non-destructive.