Files

450 lines
18 KiB
Markdown

# 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*