Documentación Técnica ~35 min lectura

Manual Técnico — LegalDoc VE

Arquitectura, esquema de datos, servicios, seguridad, IA y despliegue del sistema de gestión legal y compliance.

1. Stack Tecnológico

Frontend

React 19.2.0
Vite 7.2.4
TypeScript 5.9.3
Tailwind CSS CDN + tailwind-merge
Recharts 3.6.0
Framer Motion 12.26.2
Lucide React 0.562.0
jsPDF 4.2.1 + AutoTable 5.0.7
date-fns 4.1.0

Backend / IA

Supabase JS 2.93.3
PostgreSQL 15+ (Supabase Cloud)
pgvector 1536 dimensiones
pgcrypto SHA-256 forense
OpenAI GPT-4o Análisis + Chat legal
text-embedding-3-small Vectores RAG
Edge Functions Deno runtime
Deploy Netlify
Nota: Existe un directorio server/ con Express + Prisma + SQLite (legacy). Está deprecado — toda la producción usa Supabase.

2. Arquitectura del Sistema

┌─────────────────────────────────────────────────────────────────┐
│                    NETLIFY CDN (SPA)                            │
│   React 19 + Vite 7 + TypeScript + Tailwind + Framer Motion   │
│                                                                 │
│   ┌───────────┐  ┌──────────┐  ┌──────────┐  ┌─────────────┐  │
│   │ Documents │  │Contracts │  │Compliance│  │ Expedientes │  │
│   │  Module   │  │ E-Sign   │  │  LOTTT   │  │  Judicial   │  │
│   └─────┬─────┘  └─────┬────┘  └─────┬────┘  └──────┬──────┘  │
│         │              │              │              │          │
│   ┌─────┴──────────────┴──────────────┴──────────────┴──────┐  │
│   │              SERVICE LAYER (TypeScript)                  │  │
│   │   auth · document · contract · signature · compliance   │  │
│   │   expediente · honorarios · calendar · flow · audit     │  │
│   │   ai · parameters · notification · report · pdf         │  │
│   └─────────────────────┬───────────────────────────────────┘  │
│                         │                                       │
│              ┌──────────┴──────────┐                           │
│              │  supabase-js 2.93   │                           │
│              │  + Web Crypto API   │                           │
│              └──────────┬──────────┘                           │
└─────────────────────────┼───────────────────────────────────────┘
                          │ HTTPS
┌─────────────────────────┼───────────────────────────────────────┐
│                  SUPABASE CLOUD                                 │
│                         │                                       │
│   ┌──────────┐  ┌───────┴──────┐  ┌────────────────────┐      │
│   │  Auth    │  │  PostgREST   │  │   Edge Functions   │      │
│   │ (JWT)    │  │  (REST API)  │  │  legal-ai-processor│      │
│   └──────────┘  └──────────────┘  │  exchange-rates    │      │
│                                    │  daily-alert       │      │
│   ┌────────────────────────────┐  └────────────────────┘      │
│   │        PostgreSQL 15+                                │      │
│   │  ┌─────────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐ │      │
│   │  │ pgvector│ │ pgcrypto │ │   RLS    │ │Triggers │ │      │
│   │  │ (1536d) │ │ (SHA256) │ │org_isol. │ │ audit   │ │      │
│   │  └─────────┘ └──────────┘ └──────────┘ └─────────┘ │      │
│   │  30+ tablas multi-tenant · Storage (archivos)       │      │
│   └──────────────────────────────────────────────────────┘      │
└─────────────────────────────────────────────────────────────────┘
Multi-tenant: Todas las tablas de negocio tienen organization_id. RLS garantiza aislamiento a nivel de kernel PostgreSQL.
Capa IA: GPT-4o para análisis documental, chat legal, predicción judicial. RAG con pgvector para base de conocimiento legal venezolana.
Forense: Cadena SHA-256 inmutable en audit_logs vía trigger PostgreSQL + pgcrypto. Verificación de integridad por hash encadenado.

3. Estructura del Proyecto

src/
├── App.tsx                          # Router principal (state-based switch)
├── main.tsx                         # Entry point
├── core/
│   ├── supabase.ts                  # Cliente Supabase
│   ├── auth.service.ts              # Login, logout, sync, permisos
│   ├── user.types.ts                # UserRole, User, Permission, ROLE_PERMISSIONS
│   ├── api.ts                       # Axios (legacy — deprecado)
│   └── rgpd.service.ts              # Consentimiento RGPD
├── ui/layouts/
│   └── MainLayout.tsx               # Shell con sidebar de navegación
├── modules/
│   ├── dashboard/                   # DashboardView.tsx
│   ├── documents/                   # CRUD + análisis IA + workflow aprobación
│   │   ├── types.ts                 # DocumentType, Status, RiskLevel
│   │   ├── documents.service.ts     # CRUD + file upload + signed URLs
│   │   ├── ai.service.ts            # Interfaz IAIService
│   │   ├── openai.service.ts        # Chat vía Edge Function
│   │   └── workflow.service.ts      # Flujo de aprobación por pasos
│   ├── contracts/                   # Contratos + firma electrónica
│   │   ├── types.ts                 # ContractType, Status
│   │   ├── contract.service.ts      # CRUD
│   │   ├── signature.service.ts     # SHA-256, firma, verificación, revocación
│   │   ├── SignaturePanel.tsx        # UI de firma con biometría
│   │   └── ExternalSignView.tsx     # Portal público para firma externa
│   ├── compliance/                  # Cumplimiento regulatorio
│   │   ├── compliance.service.ts    # CRUD + alertas + RGPD audit
│   │   └── RiskMatrixView.tsx       # Matriz de riesgos NIST/ISO
│   ├── expedientes/                 # Expedientes judiciales
│   │   ├── expediente.service.ts    # CRUD + actuaciones + audiencias
│   │   └── ActuacionesTimeline.tsx  # Línea de tiempo procesal
│   ├── honorarios/                  # Facturación legal
│   │   ├── honorarios.service.ts    # 6 sub-servicios (Client, Matter, etc.)
│   │   └── types.ts                 # FeeType, Currency, InvoiceType
│   ├── calendar/                    # Calendario judicial
│   │   ├── calendar.service.ts      # Feriados, días hábiles, lapsos
│   │   └── LapsosWidget.tsx         # Widget de vencimientos
│   ├── flows/                       # Flujos BPM
│   │   └── flow.service.ts          # Templates, stages, instances, tasks
│   ├── legal-team/                  # Equipo de abogados
│   ├── iam/                         # Gestión de usuarios
│   ├── parameters/                  # Parámetros del sistema (42 seeds)
│   └── shared/                      # Servicios transversales
│       ├── audit.service.ts         # Auditoría forense SHA-256
│       ├── bcv-rate.service.ts      # Tasa BCV USD/VES
│       ├── legal-knowledge.service.ts # Seed corpus legal venezolano
│       ├── notification.service.ts  # Notificaciones multi-canal
│       ├── predictive-ai.service.ts # Predicción judicial GPT-4o
│       ├── report.service.ts        # Reportes HTML con pie forense
│       ├── pdf-report.service.ts    # PDFs certificados (jsPDF)
│       ├── terms.service.ts         # Cálculo de plazos judiciales
│       └── i18n.service.ts          # Monedas, conversiones, formatos

6. Autenticación

Flujo de Login

1. Usuario envía email + password
2. authService.login() → supabase.auth.signInWithPassword()
3. Éxito → query profiles WHERE id = auth.user.id
4. Validar: profile existe, is_active = true, organization_id asignado
5. Mapear a User: { id, email, name, role, organizationId }
6. Guardar en localStorage key 'legal_user'
7. App.tsx: setUser() → renderizar MainLayout
8. En reload: syncSession() → supabase.auth.getSession() → re-fetch profile
9. Check RGPD: rgpdService.hasConsent(userId) → mostrar banner si falta
10. SecurityReminderOverlay mostrado en cada login

Registro

supabase.auth.signUp() con metadata {name, role}, luego upsert en tabla profiles.

Cambio de Clave

Intenta auth.admin.updateUserById() (service_role), fallback a auth.updateUser(), luego resetPasswordForEmail().

RGPD

rgpdService: consentimiento, exportación de datos personales, derecho al olvido. Tabla rgpd_consents.

8. Sistema de Tipos TypeScript

Core — Roles y Permisos

type UserRole = 'consultor_general' | 'abogado_senior' | 'abogado_junior' 
             | 'consultor_principal' | 'aprendiz'

interface User {
  id: string; email: string; name: string; role: UserRole;
  avatar?: string; isActive: boolean; organizationId?: string;
}

type Permission = 'view_dashboard' | 'manage_users' | 'approve_contracts' 
               | 'manage_documents' | 'manage_compliance' | ... // 11 total

const ROLE_PERMISSIONS: Record<UserRole, Permission[]>

Documentos

type DocumentType = 'contract' | 'policy' | 'regulatory' | 'evidence' 
                  | 'legal_opinion' | 'permit_license' | 'circular_memo' 
                  | 'corporate_governance' | 'tax_fiscal' | 'labor' 
                  | 'insurance' | 'other'  // 12 tipos

type DocumentStatus = 'draft' | 'in_review' | 'approved' | 'published' 
                    | 'archived' | 'expired'

type RiskLevel = 'low' | 'medium' | 'high' | 'critical'

Contratos

type ContractType = 'SERVICE' | 'EMPLOYMENT' | 'NDA' | 'LEASE' 
                  | 'PARTNERSHIP' | 'SUPPLY' | 'CONSULTING' 
                  | 'FRANCHISE' | 'LOAN' | 'OTHER'  // 10 tipos

type ContractStatus = 'DRAFT' | 'REVIEW' | 'ACTIVE' | 'EXPIRED' 
                    | 'TERMINATED' | 'CANCELLED'

Expedientes Judiciales

type TipoProceso = 'CIVIL' | 'LABORAL' | 'MERCANTIL' | 'PENAL' 
                | 'ADMINISTRATIVO' | 'CONSTITUCIONAL' | 'ARBITRAJE'

type ExpedienteStatus = 'ACTIVO' | 'SUSPENDIDO' | 'CERRADO' 
                      | 'GANADO' | 'PERDIDO' | 'CONCILIADO'

type TipoActuacion = 'ESCRITO' | 'AUDIENCIA' | 'SENTENCIA' | ...  // 9 tipos
type TipoAudiencia = 'PRELIMINAR' | 'JUICIO' | 'CONCILIACION' | ... // 7 tipos

Honorarios

type FeeType = 'HOURLY' | 'FIXED' | 'CONTINGENCY' | 'RETAINER'
type Currency = 'USD' | 'EUR' | 'VES'
type InvoiceStatus = 'DRAFT' | 'SENT' | 'PAID' | 'OVERDUE' | 'CANCELLED'
type PaymentMethod = 'TRANSFER' | 'CASH' | 'CHECK' | 'ZELLE' | 'CRYPTO'
type ExpenseCategory = 'COURT_FEE' | 'NOTARY' | 'EXPERT' | 'TRAVEL' | ...
type TimeCategory = 'CONSULTATION' | 'DRAFTING' | 'REVIEW' | 'COURT' | ...

Parámetros del Sistema

type ParamCategory = 'LAPSOS' | 'ARANCELES' | 'DIVISAS' | 'NOTIFICACIONES' 
                   | 'IA_CUOTAS' | 'COMPLIANCE' | 'HONORARIOS' 
                   | 'SISTEMA' | 'CALENDARIO'  // 9 categorías

type Jurisdiction = 'ALL' | 'VE' | 'US' | 'EU' | 'LATAM' 
                  | 'MERCOSUR' | 'CAN' | 'CARICOM'  // 7 opciones

10. Cadena de Auditoría Forense

Trigger PostgreSQL (pgcrypto)

-- Función trigger: audit_trigger_func
-- Extensión: pgcrypto (SHA-256)

CREATE OR REPLACE FUNCTION audit_trigger_func()
RETURNS TRIGGER AS $$
DECLARE
    v_previous_hash TEXT;
    v_checksum TEXT;
    v_data JSONB;
BEGIN
    -- Obtener hash del registro anterior (cadena)
    SELECT checksum INTO v_previous_hash
    FROM audit_logs
    ORDER BY created_at DESC LIMIT 1;
    
    -- Construir datos para hash
    v_data := jsonb_build_object(
        'action', TG_OP,
        'table', TG_TABLE_NAME,
        'old', CASE WHEN TG_OP IN ('UPDATE','DELETE') 
               THEN row_to_json(OLD)::jsonb ELSE NULL END,
        'new', CASE WHEN TG_OP IN ('INSERT','UPDATE') 
               THEN row_to_json(NEW)::jsonb ELSE NULL END,
        'previous_hash', COALESCE(v_previous_hash, 'GENESIS')
    );
    
    -- Calcular checksum SHA-256
    v_checksum := encode(
        digest(v_data::text, 'sha256'), 'hex'
    );
    
    INSERT INTO audit_logs (
        action, entity_type, entity_id,
        old_data, new_data,
        previous_hash, checksum
    ) VALUES (
        TG_OP, TG_TABLE_NAME, 
        COALESCE(NEW.id, OLD.id),
        CASE WHEN TG_OP IN ('UPDATE','DELETE') 
             THEN row_to_json(OLD)::jsonb END,
        CASE WHEN TG_OP IN ('INSERT','UPDATE') 
             THEN row_to_json(NEW)::jsonb END,
        COALESCE(v_previous_hash, 'GENESIS'),
        v_checksum
    );
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Verificación de la Cadena

Eventos de Trigger (reales): INSERT/UPDATE/DELETE generados automáticamente por PostgreSQL. Tienen checksum y previous_hash válidos, formando una cadena criptográfica.
Eventos Virtuales: LOGIN, EXPORT generados desde el frontend con auditService.log(). Usan checksum: 'VIRTUAL_EVENT'. La verificación los omite — no rompen la cadena.
verifyChain(orgId?, limit = 100):
  1. Fetch últimos N logs ORDER BY created_at DESC
  2. Filtrar solo registros forenses (checksum ≠ 'VIRTUAL_EVENT')
  3. Para cada registro: verificar que previous_hash 
     coincida con el checksum de un registro padre
  4. Tolerar forks naturales (inserciones concurrentes 
     con el mismo padre)
  5. Retornar { healthy: boolean, details: string }

12. Pipeline RAG (Retrieval-Augmented Generation)

Infraestructura

Extensión: pgvector habilitado en PostgreSQL de Supabase
Tabla: document_vectors con columna embedding vector(1536)
Modelo: text-embedding-3-small (OpenAI) — 1536 dimensiones

Función SQL — match_legal_knowledge

-- Búsqueda por similitud coseno en pgvector
CREATE FUNCTION match_legal_knowledge(
    query_embedding vector(1536),
    match_threshold float DEFAULT 0.65,
    match_count int DEFAULT 5,
    p_organization_id uuid DEFAULT NULL
) RETURNS TABLE (
    id uuid, content text, metadata jsonb, similarity float
) AS $$
    SELECT id, content, metadata,
           1 - (embedding <=> query_embedding) AS similarity
    FROM document_vectors
    WHERE 1 - (embedding <=> query_embedding) > match_threshold
      AND (organization_id = p_organization_id 
           OR organization_id = '00000000-0000-0000-0000-000000000000')
    ORDER BY embedding <=> query_embedding
    LIMIT match_count;
$$ LANGUAGE sql;

Corpus Legal Venezolano (Seed)

legalKnowledgeService.seedBaseKnowledge() indexa automáticamente el siguiente corpus con org_id público (00000000-...0000):

  • LDFE — Ley de Firmas Electrónicas
  • LOPCYMAT — Ley de Salud Laboral
  • Código Civil — Título de Contratos
  • Jurisprudencia TSJ — Sentencias vinculantes

Flujo de Indexación

1. aiService.indexLegalKnowledge(entityId, type, content, orgId, metadata)
2. → aiService.generateEmbedding(content)
   → Edge Function 'legal-ai-processor' { action: 'embedding', body: { text } }
   → OpenAI text-embedding-3-small → vector float[1536]
3. → INSERT INTO document_vectors (entity_id, entity_type, content, 
      embedding, metadata, organization_id)
4. Disponible para búsqueda semántica vía match_legal_knowledge()

13. Funciones SQL y RPC

Función Parámetros Retorno Descripción
calculate_business_days p_start_date, p_working_days, p_jurisdiction DATE Calcula fecha de vencimiento sumando días hábiles, excluyendo fines de semana y judicial_holidays
count_business_days p_start_date, p_end_date, p_jurisdiction INTEGER Cuenta días hábiles entre dos fechas
generate_expediente_id TEXT Genera ID secuencial: EXP-001, EXP-002...
match_legal_knowledge query_embedding, match_threshold, match_count, p_org_id TABLE (id, content, metadata, similarity) Búsqueda semántica por similitud coseno en pgvector
audit_trigger_func — (trigger) TRIGGER Auto-inserta en audit_logs con cadena SHA-256 vía pgcrypto

Invocadas desde el frontend con supabase.rpc('function_name', { params }).

15. Variables de Entorno

Frontend (.env)

Variable Requerida Descripción
VITE_SUPABASE_URL URL del proyecto Supabase
VITE_SUPABASE_ANON_KEY Clave pública anon de Supabase
VITE_OPENAI_API_KEY Opcional API key de OpenAI (para llamadas client-side)

Edge Functions (Supabase Dashboard)

Variable Requerida Descripción
OPENAI_API_KEY Clave de OpenAI para GPT-4o y embeddings
SUPABASE_SERVICE_ROLE_KEY Service role key para operaciones admin
Seguridad: Nunca exponer SUPABASE_SERVICE_ROLE_KEY ni OPENAI_API_KEY del servidor en el frontend. Las variables VITE_* son públicas por diseño de Vite. Las Edge Functions usan sus propias keys configuradas en el dashboard de Supabase.