Documentación Técnica
Arquitectura, modelo de datos, seguridad y operación del sistema.
1. Stack
- Frontend: React 19 + TanStack Start (SSR Worker) + Vite 7 + Tailwind v4 + shadcn/ui.
- Backend: Supabase (Postgres + Auth + Storage). Lógica server-side vía
createServerFn. - Runtime SSR: Cloudflare Workers (nodejs_compat).
- Routing: File-based en
src/routes; layout autenticado en_authenticated/conssr: false.
2. Modelo de datos (resumen)
Tablas centrales (más de 60 en total):
companies,company_members,company_modules,company_member_sites.plans,plan_modules,licenses(serial único, plan, vigencia, límites).sites,floors,locations.device_catalog,assets,asset_ports,asset_port_vlans,vlan_catalog.stock_items,stock_movements,asset_movements,transfer_requests.racks,rack_unit_change_log,port_connections.projects,project_*(deliverables, documents, matrix, engineering, costing).crm_*(accounts, contacts, opportunities, pipelines, activities).audit_log,label_print_log,email_*,alerts,alert_thresholds.computer_equipment(PCs, laptops, servidores; enumscomputer_kind/computer_status; trigger reutilizadoensure_device_catalog_entrypara auto-alta de catálogo).software_licenses(claves, tipo, asientos totales/usados con constraint, vencimientos, vínculo opcional acomputer_equipment).
3. Generación automática de licencia
assign_plan_to_company(company, plan_code, enabled_by, override)fue actualizada para crear la licencia en la misma transacción.- Valida que el plan exista y esté activo.
- Si
override=true, desactiva loscompany_modulesprevios. - Habilita los
plan_modulesdel plan elegido. - Genera serial:
UPPER(plan_code) || '-' || YYYYMMDD || '-' || HEX10. - Inserta en
licensescon vigencianow() + 1 yeary copiamax_users, max_projects, max_devices, storage_quota_mbdel plan. - Marca licencias activas anteriores como
expired. - Setea
companies.current_license_ida la nueva licencia.
4. RLS y roles
- Roles centralizados en
user_roles+ enumapp_role(super_admin, admin, employee). - Función
has_role(_user_id, _role)SECURITY DEFINER para evitar recursión. is_company_member(company_id)yis_super_admin()son los predicados base de las policies.- Toda tabla de datos de empresa tiene RLS habilitado y políticas scope por
company_id.
5. Server functions
Toda lógica server-side se implementa con createServerFn desde @tanstack/react-start. Las funciones autenticadas usan el middleware requireSupabaseAuth que inyecta supabase (cliente con bearer del usuario), userId y claims en el contexto.
Operaciones privilegiadas cargan supabaseAdmin con await import('@/integrations/supabase/client.server') dentro del handler, previa verificación con has_role.
6. Storage
- Buckets:
company-logos,catalog-images,project-documents,prompt-images. - Acceso vía signed URLs para documentos privados.
- Logos y catálogo son públicos con políticas de tamaño y tipo MIME.
7. Observabilidad
audit_log: cambios sensibles con actor, tabla, fila y diff.email_send_log+email_dlq_retry_log: trazabilidad de cada correo.alerts+alert_thresholds: motor de reglas configurable por empresa.rack_sync_log: sincronizaciones con sistemas externos.
8. Despliegue
- SSR en Cloudflare Workers, assets estáticos en CDN.
- Migraciones SQL versionadas y revisadas antes de ejecutarse en producción.
- Linter de Supabase ejecutado tras cada migración.
9. Desactivación de módulos por licencia (trigger + cron)
Cuando licenses.status cambia a expired, revoked osuspended, un trigger AFTER UPDATE ejecuta UPDATE company_modules SET enabled = false WHERE company_id = OLD.company_id. Un job programado (pg_cron) corre a diario buscando licencias con expires_at < now() y las pasa a expired, disparando el mismo trigger. Al asignar un nuevo plan con assign_plan_to_company(..., override := true)los módulos se rehabilitan desde plan_modules.
10. PWA de escaneo (QR / código de barras)
public/manifest.webmanifest+ iconos maskables 192/512; meta tagsapple-mobile-web-app-capableyviewport-fit=coverensrc/routes/__root.tsx. Sin service worker en esta iteración.- Escáner con
@zxing/browser+@zxing/library(BrowserMultiFormatReader, hints: QR_CODE, CODE_128, EAN_13). Componente reutilizablesrc/components/camera-scanner.tsxcon torch y feedback háptico. - Resolución de códigos en
src/lib/scan.ts: URL de activo → id; UUID → lookup directo; fallback porbarcode→asset_tag→serial. - Rutas:
/scan(consulta),/scan/transfer(traslados en lote víainventory_register_transfer),/scan/audit(auditoría física con export CSV). Todas bajo_authenticated. - Acceso desde
AppLayout: FAB flotante en móvil y entrada de sidebar.