# Auditoría Completa — ProsApp Migration > Fecha: 2026-06-02 > Proyecto: Migración de Firebase a NestJS + PostgreSQL + Coolify --- ## Índice 1. [Resumen Ejecutivo](#1-resumen-ejecutivo) 2. [Arquitectura General](#2-arquitectura-general) 3. [Backend NestJS](#3-backend-nestjs) 4. [Admin Panel Next.js](#4-admin-panel-nextjs) 5. [prosappco (App Móvil Flutter)](#5-prosappco-app-móvil-flutter) 6. [prosapp_web_app (Web Flutter)](#6-prosapp_web_app-web-flutter) 7. [dashpro (Admin Laravel)](#7-dashpro-admin-laravel) 8. [prosapp (Landing Page)](#8-prosapp-landing-page) 9. [Base de Datos PostgreSQL](#9-base-de-datos-postgresql) 10. [Issues Críticos](#10-issues-críticos) 11. [Plan de Migración por Fases](#11-plan-de-migración-por-fases) --- ## 1. Resumen Ejecutivo El ecosistema ProsApp consta de **5 proyectos** que operaban sobre una arquitectura 100% Firebase (Auth + Firestore + Storage + Messaging + Functions). Actualmente se migra a un backend NestJS con PostgreSQL y un nuevo admin panel Next.js, manteniendo las apps Flutter como clientes. | Proyecto | Tipo | Stack Actual | Estado Migración | |----------|------|-------------|------------------| | `backend/` | API REST | NestJS + Prisma + PostgreSQL | ✅ Completado | | `admin/` | Panel Admin | Next.js 16 + shadcn/ui | ✅ Construido | | `prosappco/` | App Móvil | Flutter + Bloc + Firebase | ⏳ Pendiente | | `prosapp_web_app/` | Web App | Flutter + Provider + Firebase | ⏳ Pendiente | | `dashpro/` | Admin Legacy | Laravel 8 + Firebase + MongoDB | 🔄 Reemplazado | | `prosapp/` | Landing Page | HTML + Tailwind CDN | ✅ Sin cambios | **Total código fuente**: ~69 archivos backend, ~31 admin, ~167 prosappco, ~95 web_app, ~40 dashpro --- ## 2. Arquitectura General ### 2.1 Antes (Firebase-centric) ``` prosappco ──→ Firebase Auth + Firestore (SDK cliente) prosapp_web ──→ Firebase Auth + Firestore (SDK cliente) dashpro ────→ Firebase Admin SDK + Firestore (Laravel servidor) Functions ──→ Firebase Cloud Functions (notificaciones) ``` ### 2.2 Después (NestJS API) ``` prosappco ──→ NestJS API (HTTP/JSON) ──→ PostgreSQL prosapp_web ──→ NestJS API (HTTP/JSON) ──→ PostgreSQL admin ──────→ NestJS API (HTTP/JSON) ──→ PostgreSQL prosapp ────→ (sin cambios - HTML estático) ``` ### 2.3 Servicios compartidos | Servicio | Antes | Después | |----------|-------|---------| | Auth | Firebase Auth (email, phone, Google) | JWT (passport-jwt, 7 días exp) | | DB Usuarios | Firestore `users` + `professional_info` | PostgreSQL + Prisma ORM | | DB Admin | MongoDB Atlas | PostgreSQL (misma) | | Archivos | Firebase Storage | Pendiente (MinIO/S3) | | Notificaciones | FCM directo desde cliente | Pendiente (endpoint NestJS) | | WebSockets | Firestore snapshots | Socket.IO con JWT | | API Docs | No existía | Swagger en `/docs` | --- ## 3. Backend NestJS ### 3.1 Ficha técnica | Atributo | Valor | |----------|-------| | Framework | NestJS 11.1.24 | | ORM | Prisma 7.8.0 (`@prisma/client`) | | Base de datos | PostgreSQL 16 en Coolify | | Host | `46.202.93.92:5432` | | DB | `prosapp` / User: `prosapp_user` | | Puerto | 3000 | | Prefijo API | `/api/v1` | | Auth | Passport + JWT (7 días expiración) | | Validación | class-validator + ValidationPipe global | | WebSocket | Socket.IO (`@nestjs/platform-socket.io`) | | Documentación | Swagger en `/docs` | ### 3.2 Módulos y Endpoints | Módulo | Endpoints | Auth | Archivos | |--------|-----------|------|----------| | **Auth** | `POST /register`, `/login`, `/phone`, `/verify-phone`, `/link-email`; `GET /me` | 3 públicos, 3 JWT | 6 archivos | | **Users** | `GET /me`, `PATCH /me`, `PATCH /me/fcm-token`, `GET /:id` | 3 JWT, 1 público | 3 archivos | | **Professionals** | `GET /`, `/pending`, `/me`, `/:id`; `POST /request`, `/:id/approve`, `/:id/deny`; `PATCH /me`, `/me/schedules` | 7 JWT, 2 públicos | 7 archivos | | **Services** | `POST /`; `GET /me`, `/professional`, `/professional/requests`, `/professional/history`, `/me/history`, `/professional/calendar`, `/public-calendar/:id`, `/:id`; `PATCH /:id/status` | 9 JWT, 1 público | 10 archivos | | **Comments** | `POST /`; `GET /user/:id`, `/professional/:id`, `/reputation/:id` | 1 JWT, 3 públicos | 4 archivos | | **Chat** | `POST /start/:id`, `/:id/message`; `GET /my`, `/:id/messages` | Todos JWT | 4 archivos REST + Gateway WS | | **Locations** | `GET /countries`, `/countries/:id/regions`, `/regions/:id/cities` | Todos públicos | 3 archivos | | **Professions** | `GET /` | Público | 1 archivo | | **Settings** | `GET /` | Público | 1 archivo | | **Storage** | `POST /upload` | JWT | 1 archivo | **Total**: ~37 endpoints + WebSocket Gateway ### 3.3 Modelos Prisma (15 tablas) ``` users ──→ professionals (1:1) users ──→ reputations (1:1) users ──→ services (1:N) users ──→ messages (1:N) users ──→ chats (1:N como user_id y professional_id) users ──→ comments (1:N como author_id y destination_id) professionals ──→ schedules (1:N) professionals ──→ specializations (1:N) professionals ──→ payment_methods (1:1) professionals ──→ services (1:N) services ──→ comments (1:N) chats ──→ messages (1:N, cascade) countries ──→ regions ──→ cities ``` ### 3.4 WebSocket Gateway | Evento | Dirección | Payload | Descripción | |--------|-----------|---------|-------------| | `connection` | Cliente→Server | `auth.token` o `query.token` | Auth JWT, join a `user:{id}` | | `disconnect` | Cliente→Server | - | Limpieza de socket | | `sendMessage` | Cliente→Server | `{ chatId, content }` | Guarda y emite a ambos participantes | | `joinChat` | Cliente→Server | `chatId` | Join a sala `chat:{id}` | | `newMessage` | Server→Client | `message` | Notifica a ambos users | ### 3.5 Issues del Backend | # | Severidad | Archivo | Problema | |---|-----------|---------|----------| | 1 | 🔴 Crítico | `chat/chat.service.ts` | `getOrCreateChat()` usa `prof.id` (PK de professionals) en vez de `professionalUserId` (FK users). Causa violación FK en runtime | | 2 | 🔴 Crítico | `.env` | Credenciales de BD hardcodeadas y commiteadas | | 3 | 🟠 Alto | `storage/` | Upload no persiste archivos — es un no-op | | 4 | 🟠 Alto | `prisma/` | No existe `prisma/migrations` — los cambios de esquema no tienen tracking | | 5 | 🟠 Alto | Todos los `findMany` | Sin paginación (`skip`/`take`) — rompe con datos reales | | 6 | 🟡 Medio | `auth.service.ts` | `verifyOtpAndLinkPhone` no hace verificación OTP real | | 7 | 🟡 Medio | `common/roles.guard.ts` | `RolesGuard` y `OwnershipGuard` definidos pero no usados | | 8 | 🟡 Medio | `users.service.ts` | `findAll()` existe sin ruta HTTP — código muerto | | 9 | 🟢 Bajo | `auth/dto/auth.dto.ts` | `PhoneDto.phone` sin validación de formato | | 10 | 🟢 Bajo | Chat Gateway | Usa `jsonwebtoken` raw duplicando lógica JWT | | 11 | 🟢 Bajo | `professionals.controller.ts` | Orden de rutas frágil (`/me` antes de `/:id` | --- ## 4. Admin Panel Next.js ### 4.1 Ficha técnica | Atributo | Valor | |----------|-------| | Framework | Next.js 16.2.7 (App Router) | | UI | shadcn/ui (base-nova) + Tailwind v4 | | Iconos | Lucide React | | Notificaciones | Sonner | | Auth | JWT con localStorage | | Build | ✅ Compila sin errores | ### 4.2 Rutas y Funcionalidad | Ruta | Función | Estado | |------|---------|--------| | `/login` | Login email + password → JWT | ✅ | | `/` | Dashboard con stats (usuarios, profesionales, servicios) | ✅ | | `/users` | Tabla con búsqueda por nombre/email/teléfono | ✅ | | `/professionals` | Tabs: Activos / Pendientes (aprobar/rechazar) | ✅ | | `/services` | Lista con filtro por estado + badges coloridos | ✅ | | `/professions` | CRUD completo (agregar, eliminar profesiones) | ✅ | | `/cities` | Vista jerárquica país → región → ciudad | ✅ | | `/settings` | JSON de configuración global (solo lectura) | ✅ | ### 4.3 Issues del Admin | # | Severidad | Archivo | Problema | |---|-----------|---------|----------| | 1 | 🔴 Crítico | `layout.tsx` | `AuthGuard` está definido pero **no se usa**. Todas las rutas son públicas si se navega directo | | 2 | 🟠 Alto | `environ` | No hay `.env.example` — la URL base del API está hardcodeada a localhost | | 3 | 🟠 Alto | Varios | Sin estados de carga ni error para fallos de API (todo se traga con `.catch(() => [])`) | | 4 | 🟡 Medio | Todos | Sin paginación en tablas | | 5 | 🟡 Medio | `settings/` | Solo lectura — no hay formulario para editar configuración | | 6 | 🟡 Medio | `profession/` | Solo add/delete — no se puede renombrar | | 7 | 🟢 Bajo | General | Sin modo oscuro (next-themes instalado pero sin toggle) | --- ## 5. prosappco (App Móvil Flutter) ### 5.1 Ficha técnica | Atributo | Valor | |----------|-------| | Tipo | App móvil multiplataforma | | Versión | 1.0.14+14 | | SDK Dart | `>=2.19.3 <3.0.0` | | State | Bloc (flutter_bloc 8.1.4) | | DI | Injector | | Routing | BlocBuilder (implícito) | | Firebase | Auth + Firestore + Storage + Messaging + Functions | | Paquetes locales | 8 repositorios (user, chat, professional, service, score, city, profession, setting) | | Archivos Dart | ~167 | | LOC estimado | ~18,923 | ### 5.2 Firebase Collections usadas | Colección | Uso | |-----------|-----| | `users` | Perfiles de usuario (cliente + profesional) | | `professional_info` | Datos profesionales (cédula, certificados, etc.) | | `services` | Solicitudes de servicio | | `countries v2` / `Colombia` | Ciudades y departamentos | | `settings` / `global` | Configuración de la app | | `professions` / `professions` | Lista de profesiones | | `reputations` | Reputación acumulada | | `comments` | Calificaciones | | `chats` | Conversaciones | ### 5.3 Issues | # | Severidad | Problema | |---|-----------|----------| | 1 | 🔴 Crítico | **FCM Server Key hardcodeada** en `local_notifications.dart` — clave expuesta en cliente | | 2 | 🟠 Alto | `diacritic`, `flutter_animate`, `table_calendar` estaban en `null` (ya corregido) | | 3 | 🟠 Alto | `firebase_storage` nativo no declarado en pubspec principal (solo `_web`) | | 4 | 🟡 Medio | SDK constraint `>=2.19.3` incompatible con Firebase Messaging Web moderno | | 5 | 🟡 Medio | `google_sign_in` declarado pero nunca usado | | 6 | 🟢 Bajo | `app.dart` no usado (código muerto) | | 7 | 🟢 Bajo | Typo en nombre de directorio `sing_in_bloc/` | ### 5.4 Migración a NestJS **Esfuerzo estimado**: 4-5 semanas Cambios necesarios: 1. Reescribir los 8 repositorios Firebase → HTTP API 2. Reemplazar `FirebaseAuth` → JWT (register, login, OTP) 3. Reemplazar `FirebaseStorage` → multipart upload a NestJS 4. Reemplazar Firestore snapshots → polling o WebSocket 5. Reemplazar FCM directo → endpoint NestJS de notificaciones 6. Los BLoCs y UI pueden quedar igual (solo cambia capa de datos) --- ## 6. prosapp_web_app (Web Flutter) ### 6.1 Ficha técnica | Atributo | Valor | |----------|-------| | Tipo | Web app Flutter | | Versión | 1.0.0+1 | | SDK Dart | `>=3.4.1 <4.0.0` | | State | Provider (ChangeNotifier) | | DI | get_it (declarado pero no usado) | | Routing | Fluro (28 rutas) | | Firebase | Auth + Firestore + Storage | | Archivos Dart | ~95 | | LOC estimado | ~11,164 | ### 6.2 Issues | # | Severidad | Problema | |---|-----------|----------| | 1 | 🔴 Crítico | **FCM Server Key hardcodeada** en `local_notifications.dart` | | 2 | 🟠 Alto | `http` no estaba en pubspec.yaml (ya corregido) | | 3 | 🟡 Medio | `get_it` declarado pero sin registros — código muerto | | 4 | 🟢 Bajo | Sin `firebase_messaging` package (usa HTTP directo a FCM) | ### 6.3 Migración a NestJS **Esfuerzo estimado**: 3-4 semanas Cambios necesarios: 1. Refactorizar los 18 providers que llaman Firebase directo 2. Crear capa de repositorio (hoy no existe) 3. Reemplazar `FirebaseAuth` → JWT 4. Reemplazar `FirebaseStorage` → upload a NestJS 5. Reemplazar Firestore snapshots → polling o WebSocket 6. UI y rutas Fluro pueden quedar igual --- ## 7. dashpro (Admin Laravel) ### 7.1 Ficha técnica | Atributo | Valor | |----------|-------| | Framework | Laravel 8.75 | | PHP | ^7.3 / ^8.0 | | DB Admins | MongoDB Atlas (vía `jenssegers/mongodb`) | | DB App | Firebase Firestore | | Frontend | Livewire 2.12 + Alpine.js + Tailwind CDN | | Auth Admin | Laravel Breeze (session-based, MongoDB) | | Firebase SDK | `google/cloud-firestore`, `kreait/firebase-php` | | Storage | Firebase Cloud Storage | | Componentes | 13 Livewire | ### 7.2 Issues | # | Severidad | Problema | |---|-----------|----------| | 1 | 🔴 Crítico | `dd()` en `ShowSettings.php:223` — mata ejecución, notificaciones no se envían | | 2 | 🔴 Crítico | `dd()` en `MenuController.php:106` — ruta `/noti` rota | | 3 | 🔴 Crítico | `Route /confirmar/{id}` sin auth — cualquiera puede aprobar servicios | | 4 | 🔴 Crítico | MongoDB Atlas credenciales hardcodeadas en `config/database.php` | | 5 | 🔴 Crítico | Firebase credentials filename mal en 2 Livewire components (ya corregido) | | 6 | 🟠 Alto | Contraseña débil `'123456'` hardcodeada en 4 componentes | | 7 | 🟠 Alto | Google Maps API key hardcodeada en `MenuController.php` | | 8 | 🟠 Alto | `config/firebase.php` no existe — paquete `kreait/laravel-firebase` sin config | | 9 | 🟡 Medio | `FirebaseDataExport` usa campos incorrectos (`phoneNumber` vs `phone`) | | 10 | 🟡 Medio | Notificación `solicitud.php` referencia `$name` que nunca se pasa | ### 7.3 Reemplazo dashpro **no se migrará** — será reemplazado por el nuevo admin panel Next.js que ya consume directamente la API NestJS. --- ## 8. prosapp (Landing Page) ### 8.1 Ficha técnica | Atributo | Valor | |----------|-------| | Tipo | HTML estático | | CSS | Tailwind CDN | | JS | Alpine.js 3.x CDN | | Archivos | 1 HTML (398 lines) + 7 imágenes | ### 8.2 Issues Ninguno. No requiere migración. --- ## 9. Base de Datos PostgreSQL ### 9.1 Conexión ``` Host: 46.202.93.92 Puerto: 5432 Base de datos: prosapp Usuario: prosapp_user Password: DateTGNpxQfoPkE6OA2fOl9M10 ``` ### 9.2 Modelos (15 tablas) | Tabla | Columnas | PK | FKs | |-------|----------|----|-----| | `users` | 17 | `id (uuid)` | - | | `professionals` | 18 | `id (uuid)` | `user_id → users` | | `services` | 19 | `id (uuid)` | `professional_id → professionals`, `user_id → users` | | `comments` | 8 | `id (uuid)` | `author_id → users`, `destination_id → users`, `service_id → services` | | `chats` | 4 | `id (uuid)` | `user_id → users`, `professional_id → users` | | `messages` | 5 | `id (uuid)` | `chat_id → chats (cascade)`, `sender_id → users` | | `professions` | 2 | `id (uuid)` | - | | `specializations` | 4 | `id (uuid)` | `professional_id → professionals (cascade)` | | `schedules` | 9 | `id (uuid)` | `professional_id → professionals (cascade)` | | `payment_methods` | 5 | `id (uuid)` | `professional_id → professionals (cascade)` | | `reputations` | 6 | `user_id (uuid)` | `user_id → users (cascade)` | | `countries` | 2 | `id (uuid)` | - | | `regions` | 3 | `id (uuid)` | `country_id → countries (cascade)` | | `cities` | 5 | `id (uuid)` | `region_id → regions (cascade)` | | `settings` | 3 | `key (varchar)` | - | ### 9.3 Enums - `service_status`: `pending, accepted, denied, active, cancelled, completed, self_booked` - `service_location`: `office, delivery` ### 9.4 Triggers - `update_reputation()` — actualiza reputación automáticamente al insertar/actualizar comments --- ## 10. Issues Críticos ### 🔴 Deben resolverse antes del deploy a producción | ID | Proyecto | Issue | Solución | |----|----------|-------|----------| | C-1 | Backend | `chat.service.ts` usa `prof.id` en vez de `professionalUserId` | Cambiar a `professionalUserId` | | C-2 | Backend | `.env` con credenciales commiteadas | Agregar `.env` a `.gitignore`, usar variables de entorno en Coolify | | C-3 | Backend | Storage upload es no-op | Implementar subida a disco/S3/MinIO | | C-4 | Backend | Sin migraciones Prisma | Ejecutar `prisma migrate dev` para crear historial | | C-5 | Backend | Sin paginación en listados | Agregar `skip`/`take` con defaults | | C-6 | Admin | Sin auth guard en rutas | Conectar `AuthGuard` en layout o middleware | | C-7 | prosappco | FCM key hardcodeada | Mover a servidor, usar endpoint NestJS | | C-8 | prosapp_web | FCM key hardcodeada | Mover a servidor, usar endpoint NestJS | | C-9 | dashpro | 2x `dd()` matan funcionalidad | Reemplazar con logs, eliminar dd() | | C-10 | dashpro | `/confirmar` sin auth | Agregar middleware auth | | C-11 | dashpro | MongoDB creds hardcodeadas | Usar env vars | --- ## 11. Plan de Migración por Fases ### Fase 1: Completar Backend (en progreso) - [ ] Corregir issue C-1 (chat FK) - [ ] Corregir issue C-4 (migraciones Prisma) - [ ] Corregir issue C-5 (paginación) - [ ] Implementar storage real (C-3) - [ ] Agregar health check endpoint - [ ] Agregar rate limiting - [ ] Desplegar en Coolify ### Fase 2: Asegurar Admin Panel - [ ] Conectar AuthGuard (C-6) - [ ] Agregar `.env.example` - [ ] Agregar estados de carga/error - [ ] Implementar páginas de detalle (profesionales, servicios, usuarios) - [ ] Deploy en Coolify ### Fase 3: Migrar prosapp_web_app - [ ] Crear API service layer - [ ] Migrar auth (JWT reemplaza FirebaseAuth) - [ ] Migrar 18 providers uno por uno - [ ] Reemplazar Storage - [ ] WebSockets para chat ### Fase 4: Migrar prosappco - [ ] Reescribir 8 repositorios locales - [ ] Migrar auth - [ ] Migrar storage - [ ] Migrar notificaciones push - [ ) Reemplazar Firestore snapshots ### Fase 5: Retirar dashpro - [ ] Verificar paridad funcional con admin nuevo - [ ] Crear módulos faltantes en admin (CRUD ciudades, editor settings) - [ ] Dar de baja dashpro - [ ] Eliminar Firebase project --- *Fin del documento de auditoría*