## Documentation - Align MCH-029 to MCH-032 with template-saas modules (SAAS-008 to SAAS-015) - Create MCH-034 (Analytics) and MCH-035 (Reports) from SAAS-016/017 - Update PLAN-DESARROLLO.md with Phase 7 and 8 - Update _MAP.md indexes (35 total epics) ## Database (5 new schemas, 14 tables) - Add storage schema: buckets, files, signed_urls - Add webhooks schema: endpoints, deliveries - Add audit schema: logs, retention_policies - Add features schema: flags, tenant_flags (14 seeds) - Add analytics schema: metrics, events, reports, report_schedules - Add auth.oauth_connections for MCH-030 - Add timestamptz_to_date() IMMUTABLE function - Update EXPECTED_SCHEMAS in recreate-database.sh ## Analysis Reports - ANALISIS-INTEGRACION-TEMPLATE-SAAS-2026-01-13.md - VALIDACION-COHERENCIA-2026-01-13.md - GAP-ANALYSIS-BD-2026-01-13.md - REPORTE-EJECUCION-2026-01-13.md Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
131 lines
3.9 KiB
SQL
131 lines
3.9 KiB
SQL
-- =============================================================================
|
|
-- MICHANGARRITO - 04 AUTH
|
|
-- =============================================================================
|
|
-- Autenticación y usuarios
|
|
-- =============================================================================
|
|
|
|
-- Usuarios
|
|
CREATE TABLE IF NOT EXISTS auth.users (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Identificación
|
|
phone VARCHAR(20) NOT NULL,
|
|
email VARCHAR(100),
|
|
name VARCHAR(100) NOT NULL,
|
|
|
|
-- Autenticación
|
|
pin_hash VARCHAR(255),
|
|
biometric_enabled BOOLEAN DEFAULT false,
|
|
biometric_key TEXT,
|
|
|
|
-- Rol
|
|
role VARCHAR(20) NOT NULL DEFAULT 'owner',
|
|
permissions JSONB DEFAULT '{}',
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'active',
|
|
last_login_at TIMESTAMPTZ,
|
|
failed_attempts INTEGER DEFAULT 0,
|
|
locked_until TIMESTAMPTZ,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
UNIQUE(tenant_id, phone)
|
|
);
|
|
|
|
CREATE INDEX idx_users_tenant ON auth.users(tenant_id);
|
|
CREATE INDEX idx_users_phone ON auth.users(phone);
|
|
|
|
CREATE TRIGGER update_users_updated_at
|
|
BEFORE UPDATE ON auth.users
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
|
|
-- Sesiones
|
|
CREATE TABLE IF NOT EXISTS auth.sessions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
|
|
token_hash VARCHAR(255) NOT NULL,
|
|
refresh_token_hash VARCHAR(255),
|
|
|
|
device_type VARCHAR(20),
|
|
device_info JSONB,
|
|
ip_address VARCHAR(45),
|
|
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
refresh_expires_at TIMESTAMPTZ,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
last_activity_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_sessions_user ON auth.sessions(user_id);
|
|
CREATE INDEX idx_sessions_token ON auth.sessions(token_hash);
|
|
|
|
-- Códigos OTP
|
|
CREATE TABLE IF NOT EXISTS auth.otp_codes (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
phone VARCHAR(20) NOT NULL,
|
|
|
|
code VARCHAR(6) NOT NULL,
|
|
purpose VARCHAR(20) NOT NULL,
|
|
|
|
attempts INTEGER DEFAULT 0,
|
|
max_attempts INTEGER DEFAULT 3,
|
|
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
used_at TIMESTAMPTZ,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_otp_phone ON auth.otp_codes(phone, purpose);
|
|
|
|
-- =============================================================================
|
|
-- OAUTH CONNECTIONS (MCH-030: Auth Social)
|
|
-- =============================================================================
|
|
-- Conexiones OAuth para login con Google/Apple
|
|
|
|
CREATE TABLE IF NOT EXISTS auth.oauth_connections (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
|
|
-- Proveedor OAuth
|
|
provider VARCHAR(20) NOT NULL, -- 'google', 'apple'
|
|
provider_user_id VARCHAR(255) NOT NULL,
|
|
|
|
-- Tokens
|
|
access_token TEXT,
|
|
refresh_token TEXT,
|
|
token_expires_at TIMESTAMPTZ,
|
|
|
|
-- Datos del perfil
|
|
email VARCHAR(255),
|
|
name VARCHAR(255),
|
|
avatar_url TEXT,
|
|
|
|
-- Datos crudos del proveedor
|
|
raw_data JSONB DEFAULT '{}',
|
|
|
|
-- Timestamps
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
-- Constraints
|
|
UNIQUE(provider, provider_user_id),
|
|
UNIQUE(user_id, provider)
|
|
);
|
|
|
|
CREATE INDEX idx_oauth_connections_user ON auth.oauth_connections(user_id);
|
|
CREATE INDEX idx_oauth_connections_provider ON auth.oauth_connections(provider, provider_user_id);
|
|
|
|
CREATE TRIGGER update_oauth_connections_updated_at
|
|
BEFORE UPDATE ON auth.oauth_connections
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
|
|
COMMENT ON TABLE auth.oauth_connections IS 'Conexiones OAuth para login social (Google, Apple)';
|
|
COMMENT ON COLUMN auth.oauth_connections.provider IS 'Proveedor OAuth: google, apple';
|
|
COMMENT ON COLUMN auth.oauth_connections.raw_data IS 'Datos JSON completos retornados por el proveedor';
|