init2222
This commit is contained in:
@@ -4,6 +4,11 @@
|
||||
-- ║ Безопасно прогонять повторно на existing БД. ║
|
||||
-- ╚══════════════════════════════════════════════════════════════════╝
|
||||
|
||||
-- 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'ает их если они существуют от прошлой версии.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id VARCHAR(26) NOT NULL PRIMARY KEY,
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
@@ -43,9 +48,6 @@ BEGIN
|
||||
END $$;
|
||||
|
||||
-- Sanity check на blob size. Floor 100 (worst-case 12-word всё-3char mnemonic):
|
||||
-- plaintext 47 bytes + IV(12) + tag(16) = 75 raw → 100 base64
|
||||
-- typical 12-word: 113 raw → 152 base64; 24-word: 240 raw → 320 base64
|
||||
-- (Раньше floor 140 отвергал ~4% валидных 12-word mnemonics — fixed.)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_encrypted_mnemonic_size') THEN
|
||||
@@ -89,8 +91,6 @@ END $$;
|
||||
|
||||
-- ── WALLETS ─────────────────────────────────────────────────────────
|
||||
-- ON DELETE RESTRICT: hard-delete user → запрос отвергнут пока есть wallets.
|
||||
-- Это защита от unrecoverable fund loss при GDPR-wipe или admin удалении.
|
||||
-- Use is_deleted=true для soft-delete.
|
||||
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,
|
||||
@@ -118,38 +118,8 @@ BEGIN
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- ── AUDIT_LOG (durable sink для критических custodial операций) ─────
|
||||
-- Pre-mutation INSERT 'pending', post-mutation UPDATE 'completed' с txid.
|
||||
-- Если INSERT fails — операция НЕ происходит (fail-secure).
|
||||
CREATE TABLE IF NOT EXISTS audit_log (
|
||||
id VARCHAR(26) NOT NULL PRIMARY KEY,
|
||||
user_id VARCHAR(26) NOT NULL,
|
||||
event VARCHAR(64) NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'pending'
|
||||
CHECK (status IN ('pending', 'success', 'failure')),
|
||||
error_code VARCHAR(64),
|
||||
ip VARCHAR(64),
|
||||
trace_id VARCHAR(64),
|
||||
meta JSONB,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_log_user_id ON audit_log(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_log_event_created ON audit_log(event, created_at DESC);
|
||||
|
||||
-- ── IDEMPOTENCY_KEYS (защита от double-spend на retry) ──────────────
|
||||
-- Client шлёт Idempotency-Key header. Pre-mutation INSERT row, post-mutation UPDATE с response.
|
||||
-- На retry — возвращаем cached response без второго broadcast.
|
||||
CREATE TABLE IF NOT EXISTS idempotency_keys (
|
||||
user_id VARCHAR(26) NOT NULL,
|
||||
key VARCHAR(128) NOT NULL,
|
||||
request_hash VARCHAR(64) NOT NULL,
|
||||
response_status SMALLINT,
|
||||
response_body TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (user_id, key)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_idempotency_keys_created ON idempotency_keys(created_at);
|
||||
-- Retention cleanup (run via cron): DELETE FROM idempotency_keys WHERE created_at < NOW() - INTERVAL '24 hours';
|
||||
-- ── 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;
|
||||
|
||||
Reference in New Issue
Block a user