-- CryptoWallet API — DB schema (idempotent, custodial v5.0) CREATE TABLE IF NOT EXISTS users ( id VARCHAR(26) PRIMARY KEY, email VARCHAR(255) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, last_name VARCHAR(255), first_name VARCHAR(255), middle_name VARCHAR(255), birth_date DATE, crypto_wallet VARCHAR(255), phone VARCHAR(64), bik VARCHAR(64), account_number VARCHAR(64), card_number VARCHAR(64), inn VARCHAR(64), kyc_verified BOOLEAN NOT NULL DEFAULT FALSE, kyc_verified_at TIMESTAMPTZ, is_deleted BOOLEAN NOT NULL DEFAULT FALSE, encrypted_vault TEXT, -- legacy vault_salt VARCHAR(128), -- legacy encrypted_mnemonic TEXT, -- AES-256-GCM blob (custodial) created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_name = 'users' AND column_name = 'encrypted_mnemonic' ) THEN ALTER TABLE users ADD COLUMN encrypted_mnemonic TEXT; END IF; END $$; -- AES-GCM blob: 12 IV + plaintext + 16 tag. -- 12-word mnemonic ~ 116 байт = ~156 base64 chars; 24-word ~ 212 байт = ~284 chars. DO $$ BEGIN 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 140 AND 512)); END IF; END $$; CREATE TABLE IF NOT EXISTS wallets ( id VARCHAR(26) PRIMARY KEY, user_id VARCHAR(26) NOT NULL REFERENCES users(id) ON DELETE CASCADE, chain VARCHAR(16) NOT NULL, address VARCHAR(256) NOT NULL, derivation_path VARCHAR(64) NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE (user_id, chain) ); CREATE INDEX IF NOT EXISTS idx_wallets_user_id ON wallets(user_id); -- sessions table removed — JWT-stateless, не используется в коде. -- Если существует от старой версии — оператор может drop вручную: -- DROP TABLE IF EXISTS sessions CASCADE;