Auditoría Completa — ProsApp Migration
Fecha: 2026-06-02
Proyecto: Migración de Firebase a NestJS + PostgreSQL + Coolify
Índice
- Resumen Ejecutivo
- Arquitectura General
- Backend NestJS
- Admin Panel Next.js
- prosappco (App Móvil Flutter)
- prosapp_web_app (Web Flutter)
- dashpro (Admin Laravel)
- prosapp (Landing Page)
- Base de Datos PostgreSQL
- Issues Críticos
- 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)
2.2 Después (NestJS API)
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)
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:
- Reescribir los 8 repositorios Firebase → HTTP API
- Reemplazar
FirebaseAuth → JWT (register, login, OTP)
- Reemplazar
FirebaseStorage → multipart upload a NestJS
- Reemplazar Firestore snapshots → polling o WebSocket
- Reemplazar FCM directo → endpoint NestJS de notificaciones
- 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:
- Refactorizar los 18 providers que llaman Firebase directo
- Crear capa de repositorio (hoy no existe)
- Reemplazar
FirebaseAuth → JWT
- Reemplazar
FirebaseStorage → upload a NestJS
- Reemplazar Firestore snapshots → polling o WebSocket
- 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
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)
Fase 2: Asegurar Admin Panel
Fase 3: Migrar prosapp_web_app
Fase 4: Migrar prosappco
Fase 5: Retirar dashpro
Fin del documento de auditoría