docs: README completo para Windows/Linux/macOS + fix conflicto de puertos
- README.md reescrito con instrucciones detalladas para principiantes y expertos * Tabla de contenidos con 14 secciones * Inicio rápido con Docker (Windows, Linux, macOS) * Configuración de variables de entorno con explicaciones * Sección de desarrollo local sin Docker * Comandos útiles (Docker, Alembic, calidad de código, testing) * Solución de problemas extensa (puertos, módulos, tenant, migraciones, Node.js) * Historial de versiones - Fix: conflicto de puertos cuando ambos frontends corren en local * frontend-internal/vite.config.js: usa PORT=3001 por defecto (3000 en Docker) * frontend-internal/package.json: dev script sin puerto hardcodeado * docker-compose.yml: frontend-internal recibe PORT=3000 como variable de env
This commit is contained in:
835
README.md
835
README.md
@@ -1,178 +1,755 @@
|
|||||||
# ServiceManagerWeb - Mesa de Ayuda B2B
|
# ServiceManagerWeb — Mesa de Ayuda B2B
|
||||||
|
|
||||||
Sistema multi-tenant de Mesa de Ayuda/Soporte Técnico empresarial para Aduanasoft.
|
> **Versión actual:** v1.15.1 — Módulo de reportes implementado
|
||||||
|
>
|
||||||
|
> Sistema multi-tenant de Mesa de Ayuda / Soporte Técnico empresarial desarrollado para Aduanasoft.
|
||||||
|
> Arquitectura Modular Monolith con Clean Architecture, preparado para escalar a microservicios.
|
||||||
|
|
||||||
## Arquitectura
|
---
|
||||||
|
|
||||||
- **Frontend**: SvelteKit + TypeScript (portal clientes + panel interno)
|
## Tabla de Contenidos
|
||||||
- **Backend**: Python FastAPI + Pydantic v2
|
|
||||||
- **Workers**: Celery + Redis (notificaciones, SLAs, jobs)
|
|
||||||
- **BD**: PostgreSQL + Alembic migrations
|
|
||||||
- **Auth**: JWT + Refresh tokens + 2FA opcional (TOTP)
|
|
||||||
- **Infra**: Docker Compose local, preparado para producción
|
|
||||||
|
|
||||||
## Estructura del Monorepo
|
1. [Requisitos previos](#requisitos-previos)
|
||||||
|
2. [Inicio rápido con Docker (recomendado)](#inicio-rápido-con-docker-recomendado)
|
||||||
|
3. [Configuración de variables de entorno](#configuración-de-variables-de-entorno)
|
||||||
|
4. [Cargar datos de prueba](#cargar-datos-de-prueba)
|
||||||
|
5. [URLs y puertos por defecto](#urls-y-puertos-por-defecto)
|
||||||
|
6. [Credenciales de prueba](#credenciales-de-prueba)
|
||||||
|
7. [Desarrollo local sin Docker](#desarrollo-local-sin-docker)
|
||||||
|
8. [Arquitectura del proyecto](#arquitectura-del-proyecto)
|
||||||
|
9. [Roles y permisos](#roles-y-permisos)
|
||||||
|
10. [Comandos útiles](#comandos-útiles)
|
||||||
|
11. [Pruebas (testing)](#pruebas-testing)
|
||||||
|
12. [Solución de problemas](#solución-de-problemas)
|
||||||
|
13. [Contribución](#contribución)
|
||||||
|
14. [Historial de versiones](#historial-de-versiones)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Requisitos previos
|
||||||
|
|
||||||
|
Antes de clonar el proyecto, asegúrate de tener instalado:
|
||||||
|
|
||||||
|
| Herramienta | Versión mínima | Descarga |
|
||||||
|
|-------------|---------------|---------|
|
||||||
|
| **Git** | 2.x | https://git-scm.com/downloads |
|
||||||
|
| **Docker Desktop** | 24.x | https://www.docker.com/products/docker-desktop |
|
||||||
|
| **Docker Compose** | v2.x (incluido en Docker Desktop) | — |
|
||||||
|
|
||||||
|
> **Nota para desarrolladores que quieran editar código localmente (sin Docker):**
|
||||||
|
> también necesitarás Python 3.11+ y Node.js 18+. Ver sección
|
||||||
|
> [Desarrollo local sin Docker](#desarrollo-local-sin-docker).
|
||||||
|
|
||||||
|
### Verificar que Docker esté corriendo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker --version # Debe mostrar Docker version 24.x o superior
|
||||||
|
docker compose version # Debe mostrar Docker Compose version v2.x
|
||||||
|
```
|
||||||
|
|
||||||
|
Si `docker compose version` falla, prueba `docker-compose --version` (versión standalone).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Inicio rápido con Docker (recomendado)
|
||||||
|
|
||||||
|
Este es el método más simple y funciona igual en **Windows, Linux y macOS**.
|
||||||
|
Solo necesitas Docker Desktop instalado y corriendo.
|
||||||
|
|
||||||
|
### Paso 1 — Clonar el repositorio
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://git.aduanasoft.com/ADUANASOFT/service_manager.git
|
||||||
|
cd service_manager
|
||||||
|
```
|
||||||
|
|
||||||
|
### Paso 2 — Crear el archivo de variables de entorno
|
||||||
|
|
||||||
|
**Linux / macOS:**
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
**Windows (PowerShell):**
|
||||||
|
```powershell
|
||||||
|
Copy-Item .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
**Windows (CMD):**
|
||||||
|
```cmd
|
||||||
|
copy .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Importante:** El archivo `.env` nunca se sube a git (está en `.gitignore`).
|
||||||
|
> Para desarrollo local los valores del `.env.example` funcionan sin cambios.
|
||||||
|
> En producción **debes** generar claves secretas únicas (ver sección de variables de entorno).
|
||||||
|
|
||||||
|
### Paso 3 — Levantar todos los servicios
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Este comando descarga las imágenes, construye los contenedores e inicia todo el stack.
|
||||||
|
La primera vez tarda entre 3 y 8 minutos dependiendo de la conexión a internet.
|
||||||
|
|
||||||
|
> **Alternativa con herramientas de desarrollo** (Adminer, MailHog, Redis Commander):
|
||||||
|
> ```bash
|
||||||
|
> docker compose --profile dev up -d
|
||||||
|
> ```
|
||||||
|
|
||||||
|
### Paso 4 — Verificar que todo esté funcionando
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose ps
|
||||||
|
```
|
||||||
|
|
||||||
|
Deberías ver todos los servicios con estado `Up` o `healthy`:
|
||||||
|
|
||||||
|
```
|
||||||
|
NAME STATUS
|
||||||
|
servicemanager-db Up (healthy)
|
||||||
|
servicemanager-redis Up (healthy)
|
||||||
|
servicemanager-backend Up (healthy)
|
||||||
|
servicemanager-worker Up
|
||||||
|
servicemanager-beat Up
|
||||||
|
servicemanager-client-frontend Up
|
||||||
|
servicemanager-internal-... Up
|
||||||
|
servicemanager-nginx Up
|
||||||
|
```
|
||||||
|
|
||||||
|
Si algún servicio muestra `Exit` o `Restarting`, revisa la sección
|
||||||
|
[Solución de problemas](#solución-de-problemas).
|
||||||
|
|
||||||
|
### Paso 5 — Cargar datos de ejemplo (opcional pero recomendado)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec servicemanager-backend python /scripts/seed_data.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Esto crea el tenant de demostración, categorías, usuarios y tickets de prueba.
|
||||||
|
|
||||||
|
### ¡Listo! Abre el navegador
|
||||||
|
|
||||||
|
| Aplicación | URL |
|
||||||
|
|------------|-----|
|
||||||
|
| Portal de clientes | http://localhost:3000 |
|
||||||
|
| Panel interno (staff) | http://localhost:3001 |
|
||||||
|
| API REST | http://localhost:8000 |
|
||||||
|
| Documentación API (Swagger) | http://localhost:8000/docs |
|
||||||
|
| Documentación API (ReDoc) | http://localhost:8000/redoc |
|
||||||
|
| Health check | http://localhost:8000/health |
|
||||||
|
|
||||||
|
> **Con perfil dev** activo también tendrás:
|
||||||
|
> - Adminer (gestor visual de PostgreSQL): http://localhost:8080
|
||||||
|
> - MailHog (pruebas de email): http://localhost:8025
|
||||||
|
> - Redis Commander (inspector de Redis): http://localhost:8081
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuración de variables de entorno
|
||||||
|
|
||||||
|
El archivo `.env` controla todo el comportamiento de la aplicación.
|
||||||
|
Copia `.env.example` como `.env` y revisa los valores siguientes:
|
||||||
|
|
||||||
|
### Variables críticas
|
||||||
|
|
||||||
|
| Variable | Descripción | Valor por defecto (dev) |
|
||||||
|
|----------|-------------|-------------------------|
|
||||||
|
| `SECRET_KEY` | Clave secreta general de Flask/FastAPI | _(cambiar en producción)_ |
|
||||||
|
| `JWT_SECRET_KEY` | Clave para firmar tokens JWT | _(cambiar en producción)_ |
|
||||||
|
| `DATABASE_URL` | Cadena de conexión a PostgreSQL | `postgresql+asyncpg://servicemanager:...@postgres:5432/servicemanager` |
|
||||||
|
| `REDIS_URL` | URL de conexión a Redis | `redis://redis:6379/0` |
|
||||||
|
| `ENVIRONMENT` | Entorno actual | `development` |
|
||||||
|
| `DEBUG` | Modo debug (muestra errores detallados) | `true` |
|
||||||
|
|
||||||
|
### Generar claves seguras para producción
|
||||||
|
|
||||||
|
**Linux / macOS:**
|
||||||
|
```bash
|
||||||
|
openssl rand -base64 32 # Genera SECRET_KEY
|
||||||
|
openssl rand -base64 32 # Genera JWT_SECRET_KEY
|
||||||
|
```
|
||||||
|
|
||||||
|
**Windows (PowerShell):**
|
||||||
|
```powershell
|
||||||
|
[Convert]::ToBase64String((1..32 | ForEach-Object { Get-Random -Maximum 256 }))
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Advertencia:** Nunca uses las claves del `.env.example` en producción.
|
||||||
|
> Cambiar las claves en producción invalida todas las sesiones activas.
|
||||||
|
|
||||||
|
### Desarrollo local vs Docker
|
||||||
|
|
||||||
|
En `.env.example` las URLs apuntan a nombres de servicio Docker (`postgres`, `redis`, `backend`).
|
||||||
|
Si ejecutas el backend directamente en tu máquina (sin Docker), cambia:
|
||||||
|
|
||||||
|
```dotenv
|
||||||
|
# Para desarrollo local sin Docker:
|
||||||
|
DATABASE_URL=postgresql+asyncpg://servicemanager:servicemanager123@localhost:5432/servicemanager
|
||||||
|
REDIS_URL=redis://localhost:6379/0
|
||||||
|
CELERY_BROKER_URL=redis://localhost:6379/0
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cargar datos de prueba
|
||||||
|
|
||||||
|
El script `seed_data.py` crea datos iniciales en la base de datos.
|
||||||
|
|
||||||
|
**Con Docker (recomendado):**
|
||||||
|
```bash
|
||||||
|
docker exec servicemanager-backend python /scripts/seed_data.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Sin Docker:**
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
python ../scripts/seed_data.py
|
||||||
|
```
|
||||||
|
|
||||||
|
El script crea:
|
||||||
|
- Tenant de demostración: `aduanasoft-demo`
|
||||||
|
- Categorías de tickets (Soporte Técnico, Facturación, Incidentes Críticos, etc.)
|
||||||
|
- Sistemas registrados
|
||||||
|
- Usuarios de prueba con distintos roles
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## URLs y puertos por defecto
|
||||||
|
|
||||||
|
| Servicio | Puerto | Descripción |
|
||||||
|
|----------|--------|-------------|
|
||||||
|
| Frontend Clientes | **3000** | Portal para usuarios clientes |
|
||||||
|
| Frontend Interno | **3001** | Panel para staff (agentes, admins) |
|
||||||
|
| Backend API | **8000** | FastAPI — endpoints REST |
|
||||||
|
| PostgreSQL | **5432** | Base de datos (no exponer en producción) |
|
||||||
|
| Redis | **6379** | Cache y broker Celery (no exponer en producción) |
|
||||||
|
| Nginx | **80** | Reverse proxy |
|
||||||
|
| Adminer *(perfil dev)* | **8080** | GUI para PostgreSQL |
|
||||||
|
| MailHog *(perfil dev)* | **8025** | Capturador de emails en desarrollo |
|
||||||
|
| Redis Commander *(perfil dev)* | **8081** | GUI para Redis |
|
||||||
|
|
||||||
|
### ¿Conflicto de puertos?
|
||||||
|
|
||||||
|
Si algún puerto ya está en uso en tu máquina, edita `docker-compose.yml` y cambia
|
||||||
|
el número **izquierdo** del mapeo `host:container`. Por ejemplo, para backend en el 8080:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
ports:
|
||||||
|
- "8080:8000" # ahora accesible en localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Credenciales de prueba
|
||||||
|
|
||||||
|
Después de ejecutar el seed, puedes iniciar sesión con:
|
||||||
|
|
||||||
|
| Campo | Valor |
|
||||||
|
|-------|-------|
|
||||||
|
| Email | `admin@aduanasoft.com` |
|
||||||
|
| Contraseña | `admin123` |
|
||||||
|
| Tenant | `aduanasoft-demo` |
|
||||||
|
| Rol | `ADMIN` |
|
||||||
|
|
||||||
|
> Otros usuarios creados por el seed tienen el mismo sufijo de contraseña (`123`).
|
||||||
|
> Revisa `scripts/seed_data.py` para ver la lista completa.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Desarrollo local sin Docker
|
||||||
|
|
||||||
|
Útil cuando necesitas depurar el código con breakpoints o acelerar el ciclo de desarrollo.
|
||||||
|
Requiere que **PostgreSQL y Redis sí corran en Docker** (o instalación nativa).
|
||||||
|
|
||||||
|
### Requisitos adicionales
|
||||||
|
|
||||||
|
| Herramienta | Versión | Descarga |
|
||||||
|
|------------|---------|---------|
|
||||||
|
| Python | 3.11 o 3.12 | https://www.python.org/downloads/ |
|
||||||
|
| Node.js (con npm) | 18 LTS | https://nodejs.org/ |
|
||||||
|
| pip | incluido con Python | — |
|
||||||
|
|
||||||
|
### Iniciar solo la base de datos y Redis
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d postgres redis
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backend (FastAPI)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
|
||||||
|
# Crear entorno virtual (solo la primera vez)
|
||||||
|
python -m venv ../.venv
|
||||||
|
|
||||||
|
# Activar entorno virtual
|
||||||
|
# Linux / macOS:
|
||||||
|
source ../.venv/bin/activate
|
||||||
|
# Windows (PowerShell):
|
||||||
|
..\.venv\Scripts\Activate.ps1
|
||||||
|
# Windows (CMD):
|
||||||
|
..\.venv\Scripts\activate.bat
|
||||||
|
|
||||||
|
# Instalar dependencias (solo la primera vez o cuando cambie requirements.txt)
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Ejecutar migraciones de base de datos
|
||||||
|
alembic upgrade head
|
||||||
|
|
||||||
|
# Iniciar servidor de desarrollo
|
||||||
|
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
||||||
|
```
|
||||||
|
|
||||||
|
> Si `uvicorn` no se encuentra, asegúrate de que el entorno virtual está activado
|
||||||
|
> (`(.venv)` debe aparecer en tu terminal).
|
||||||
|
|
||||||
|
### Frontend Clientes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend-client
|
||||||
|
|
||||||
|
# Instalar dependencias (solo la primera vez)
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Iniciar servidor de desarrollo en puerto 3000
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontend Interno (staff)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend-internal
|
||||||
|
|
||||||
|
# Instalar dependencias (solo la primera vez)
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Iniciar servidor de desarrollo en puerto 3001
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
> Los dos frontends tienen puertos distintos (3000 y 3001) para que no haya conflicto
|
||||||
|
> cuando corren al mismo tiempo.
|
||||||
|
|
||||||
|
### Workers Celery (opcional en desarrollo)
|
||||||
|
|
||||||
|
Necesario solo si desarrollas funcionalidades de notificaciones o SLAs automáticos.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd workers
|
||||||
|
|
||||||
|
# Activar el mismo entorno virtual del backend:
|
||||||
|
# Linux / macOS:
|
||||||
|
source ../.venv/bin/activate
|
||||||
|
# Windows:
|
||||||
|
..\.venv\Scripts\Activate.ps1
|
||||||
|
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Worker principal
|
||||||
|
celery -A app.celery worker --loglevel=info
|
||||||
|
|
||||||
|
# Scheduler de tareas periódicas (en otra terminal)
|
||||||
|
celery -A app.celery beat --loglevel=info --schedule=/tmp/celerybeat-schedule
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Arquitectura del proyecto
|
||||||
|
|
||||||
```
|
```
|
||||||
ServiceManagerWeb/
|
ServiceManagerWeb/
|
||||||
├── backend/ # FastAPI app
|
├── backend/ # Aplicación FastAPI (Python 3.11)
|
||||||
├── frontend-client/ # SvelteKit app para clientes
|
│ ├── app/
|
||||||
├── frontend-internal/ # SvelteKit app para staff interno
|
│ │ ├── main.py # Punto de entrada, lifespan, middlewares
|
||||||
├── workers/ # Celery tasks
|
│ │ ├── api/v1/
|
||||||
├── db/ # Migrations y esquemas
|
│ │ │ ├── router.py # Registro de todos los routers
|
||||||
├── docker/ # Dockerfiles específicos
|
│ │ │ └── endpoints/ # Endpoints REST por dominio
|
||||||
├── docs/ # Documentación adicional
|
│ │ ├── core/ # Config, seguridad, base de datos, caché
|
||||||
├── scripts/ # Scripts de desarrollo/despliegue
|
│ │ ├── models/ # Modelos SQLAlchemy (ORM)
|
||||||
|
│ │ ├── services/ # Lógica de negocio
|
||||||
|
│ │ └── middleware/ # Tenant context, Correlation ID
|
||||||
|
│ ├── migrations/ # Migraciones Alembic
|
||||||
|
│ ├── tests/ # Pruebas backend
|
||||||
|
│ └── requirements.txt # Dependencias Python
|
||||||
|
│
|
||||||
|
├── frontend-client/ # Portal de clientes (SvelteKit + TypeScript)
|
||||||
|
│ └── src/routes/ # Páginas: login, tickets, perfil
|
||||||
|
│
|
||||||
|
├── frontend-internal/ # Panel de staff (SvelteKit + TypeScript)
|
||||||
|
│ └── src/routes/ # Páginas: dashboard, tickets, reportes, auditoría
|
||||||
|
│
|
||||||
|
├── workers/ # Tareas asíncronas Celery
|
||||||
|
│ └── app/tasks/ # email_tasks.py, sla_tasks.py, etc.
|
||||||
|
│
|
||||||
|
├── docker/ # Dockerfiles y configuración Nginx
|
||||||
|
├── db/ # schema.sql inicial
|
||||||
|
├── docs/ # Documentación técnica adicional
|
||||||
|
├── scripts/ # seed_data.py, setup-dev.sh, etc.
|
||||||
├── docker-compose.yml # Orquestación completa
|
├── docker-compose.yml # Orquestación completa
|
||||||
└── .env.example # Variables de entorno
|
└── .env.example # Plantilla de variables de entorno
|
||||||
```
|
```
|
||||||
|
|
||||||
## Stack Tecnológico
|
### Stack tecnológico
|
||||||
|
|
||||||
### Backend (Python)
|
**Backend:** Python 3.11 · FastAPI · Pydantic v2 · SQLAlchemy 2.0 (async) · Alembic · Argon2 · PyJWT · Celery · Redis
|
||||||
- FastAPI (async)
|
|
||||||
- Pydantic v2
|
|
||||||
- SQLAlchemy 2.0 (async)
|
|
||||||
- Alembic (migrations)
|
|
||||||
- Argon2 (hashing passwords)
|
|
||||||
- PyJWT
|
|
||||||
- Celery + Redis
|
|
||||||
|
|
||||||
### Frontend (JavaScript/TypeScript)
|
**Frontend:** Node.js 18 · SvelteKit · TypeScript · TailwindCSS · Zod
|
||||||
- SvelteKit
|
|
||||||
- TypeScript
|
|
||||||
- TailwindCSS
|
|
||||||
- shadcn/ui o similar
|
|
||||||
- Zod (validación)
|
|
||||||
|
|
||||||
### Infraestructura
|
**Infraestructura:** PostgreSQL 15 · Redis 7 · Docker Compose · Nginx
|
||||||
- PostgreSQL 15+
|
|
||||||
- Redis 7+
|
|
||||||
- Docker & Docker Compose
|
|
||||||
- Nginx (reverse proxy)
|
|
||||||
|
|
||||||
## Dominios del Sistema
|
---
|
||||||
|
|
||||||
1. **Auth**: Usuarios, roles, permisos, 2FA
|
## Roles y permisos
|
||||||
2. **Tenants**: Multi-tenancy, organizaciones
|
|
||||||
3. **Tickets**: Gestión de tickets, estados, SLAs
|
|
||||||
4. **Notifications**: Email, plantillas, logs
|
|
||||||
5. **Audit**: Bitácora de acciones
|
|
||||||
|
|
||||||
## Roles de Usuario
|
### Personal interno (staff)
|
||||||
|
| Rol | Descripción |
|
||||||
### Internos (Staff)
|
|-----|-------------|
|
||||||
- `ADMIN`: Control total del sistema
|
| `ADMIN` | Control total del sistema |
|
||||||
- `SUPPORT_MANAGER`: Gestión de equipos y SLAs
|
| `SUPPORT_MANAGER` | Gestión de equipos y configuración de SLAs |
|
||||||
- `AGENT`: Atención de tickets
|
| `AGENT` | Atención y resolución de tickets |
|
||||||
- `AUDITOR`: Solo lectura para auditoría
|
| `AUDITOR` | Solo lectura para revisiones y cumplimiento |
|
||||||
|
|
||||||
### Clientes
|
### Clientes
|
||||||
- `CLIENT_ADMIN`: Gestión de organización cliente
|
| Rol | Descripción |
|
||||||
- `CLIENT_USER`: Creación y seguimiento de tickets
|
|-----|-------------|
|
||||||
|
| `CLIENT_ADMIN` | Gestión de su organización cliente |
|
||||||
|
| `CLIENT_USER` | Creación y seguimiento de sus propios tickets |
|
||||||
|
|
||||||
## Quick Start
|
---
|
||||||
|
|
||||||
|
## Comandos útiles
|
||||||
|
|
||||||
|
### Docker Compose
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clonar y configurar
|
# Levantar todos los servicios (segundo plano)
|
||||||
git clone <repo>
|
docker compose up -d
|
||||||
cd ServiceManagerWeb
|
|
||||||
cp .env.example .env
|
|
||||||
|
|
||||||
# Levantar servicios
|
# Levantar con herramientas de desarrollo
|
||||||
docker-compose up -d
|
docker compose --profile dev up -d
|
||||||
|
|
||||||
# Verificar estado
|
# Ver logs en tiempo real de todos los servicios
|
||||||
docker-compose ps
|
docker compose logs -f
|
||||||
|
|
||||||
|
# Ver logs de un servicio específico
|
||||||
|
docker compose logs -f backend
|
||||||
|
docker compose logs -f frontend-internal
|
||||||
|
|
||||||
|
# Detener todos los servicios (mantiene los datos)
|
||||||
|
docker compose down
|
||||||
|
|
||||||
|
# Detener Y borrar todos los volúmenes (¡borra la base de datos!)
|
||||||
|
docker compose down -v
|
||||||
|
|
||||||
|
# Reconstruir imagen de un servicio (después de cambiar Dockerfile o requirements)
|
||||||
|
docker compose build backend
|
||||||
|
docker compose up -d backend
|
||||||
|
|
||||||
|
# Reiniciar un servicio
|
||||||
|
docker compose restart backend
|
||||||
```
|
```
|
||||||
|
|
||||||
## URLs por Defecto
|
### Base de datos (Alembic)
|
||||||
|
|
||||||
- Frontend Clientes: http://localhost:3000
|
|
||||||
- Frontend Interno: http://localhost:3001
|
|
||||||
- API Backend: http://localhost:8000
|
|
||||||
- API Docs: http://localhost:8000/docs
|
|
||||||
- Adminer (DB): http://localhost:8080
|
|
||||||
|
|
||||||
## Scripts de Desarrollo
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Backend
|
# Aplicar todas las migraciones pendientes
|
||||||
cd backend
|
cd backend
|
||||||
python -m uvicorn app.main:app --reload --port 8000
|
alembic upgrade head
|
||||||
|
|
||||||
# Frontend Cliente
|
# Ver estado de migraciones
|
||||||
cd frontend-client
|
alembic current
|
||||||
npm run dev -- --port 3000
|
|
||||||
|
|
||||||
# Frontend Interno
|
# Revertir última migración
|
||||||
cd frontend-internal
|
alembic downgrade -1
|
||||||
npm run dev -- --port 3001
|
|
||||||
|
|
||||||
# Workers
|
# Crear nueva migración (después de modificar models/)
|
||||||
cd workers
|
alembic revision --autogenerate -m "nombre descriptivo del cambio"
|
||||||
celery -A app.worker worker --loglevel=info
|
|
||||||
celery -A app.worker beat --loglevel=info
|
# Con Docker:
|
||||||
|
docker exec servicemanager-backend alembic upgrade head
|
||||||
```
|
```
|
||||||
|
|
||||||
## Testing
|
### Calidad de código
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Backend tests
|
|
||||||
cd backend
|
cd backend
|
||||||
|
|
||||||
|
# Linter y auto-fix
|
||||||
|
ruff check . --fix
|
||||||
|
|
||||||
|
# Formateador
|
||||||
|
black .
|
||||||
|
|
||||||
|
# Verificación de tipos
|
||||||
|
mypy .
|
||||||
|
|
||||||
|
# Todo de una vez
|
||||||
|
ruff check . --fix && black . && mypy .
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pruebas (testing)
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
|
||||||
|
# Ejecutar todas las pruebas
|
||||||
pytest
|
pytest
|
||||||
|
|
||||||
# Frontend tests
|
# Con cobertura detallada
|
||||||
cd frontend-client
|
pytest --cov=app --cov-report=html
|
||||||
npm test
|
|
||||||
cd ../frontend-internal
|
# Abrir reporte de cobertura (Linux/macOS)
|
||||||
npm test
|
open htmlcov/index.html
|
||||||
|
# Windows
|
||||||
|
start htmlcov/index.html
|
||||||
|
|
||||||
|
# Prueba específica
|
||||||
|
pytest tests/test_auth.py -v
|
||||||
|
|
||||||
|
# Con Docker
|
||||||
|
docker exec servicemanager-backend pytest -v --cov=app
|
||||||
```
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
### Frontend
|
||||||
|
|
||||||
### Error 500 en Login / Proxy Error
|
|
||||||
|
|
||||||
**Síntoma**: Error 500 al intentar hacer login, o error de proxy de Vite "connect ECONNREFUSED".
|
|
||||||
|
|
||||||
**Causa**: Configuración incorrecta de la comunicación entre servicios de Docker.
|
|
||||||
|
|
||||||
**Solución**:
|
|
||||||
1. En desarrollo con Docker, los servicios usan nombres de servicio (no `localhost`)
|
|
||||||
2. Verificar `vite.config.js`: el proxy debe apuntar a `http://backend:8000`
|
|
||||||
3. Verificar `docker-compose.yml`: `PUBLIC_API_URL` debe ser `http://backend:8000`
|
|
||||||
4. Después de cambios, reiniciar contenedor: `docker-compose restart frontend-internal`
|
|
||||||
|
|
||||||
**Nota**: Para desarrollo local sin Docker, cambiar el proxy a `http://localhost:8000`.
|
|
||||||
|
|
||||||
### Tenant Slug Incorrecto
|
|
||||||
|
|
||||||
**Síntoma**: Error de autenticación incluso con credenciales correctas.
|
|
||||||
|
|
||||||
**Causa**: El `tenant_slug` en el login no coincide con los tenants en la BD.
|
|
||||||
|
|
||||||
**Solución**:
|
|
||||||
1. Verificar tenants existentes: `docker exec servicemanager-backend python check_tenants.py`
|
|
||||||
2. Actualizar el tenant_slug en el código de login
|
|
||||||
3. Tenants por defecto: `aduanasoft-demo`, `test-tenant`
|
|
||||||
|
|
||||||
### Credenciales de Prueba
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend-internal # o frontend-client
|
||||||
|
npm test # Ejecutar una vez
|
||||||
|
npm run test:watch # Modo observador
|
||||||
```
|
```
|
||||||
Email: admin@aduanasoft.com
|
|
||||||
Password: admin123
|
---
|
||||||
Tenant: aduanasoft-demo
|
|
||||||
Role: ADMIN
|
## Solución de problemas
|
||||||
|
|
||||||
|
### El backend no inicia — error en `DATABASE_URL`
|
||||||
|
|
||||||
|
**Síntoma:** El contenedor `servicemanager-backend` reinicia continuamente.
|
||||||
|
|
||||||
|
**Causa frecuente:** El archivo `.env` no existe o tiene `DATABASE_URL` apuntando a `localhost`
|
||||||
|
en lugar del nombre del servicio Docker `postgres`.
|
||||||
|
|
||||||
|
**Solución:**
|
||||||
|
```bash
|
||||||
|
# Verificar que .env existe
|
||||||
|
ls .env # Linux/macOS
|
||||||
|
dir .env # Windows
|
||||||
|
|
||||||
|
# Si no existe, crearlo
|
||||||
|
cp .env.example .env # Linux/macOS
|
||||||
|
Copy-Item .env.example .env # Windows PowerShell
|
||||||
|
|
||||||
|
# Verificar el valor correcto en .env:
|
||||||
|
# DATABASE_URL=postgresql+asyncpg://servicemanager:servicemanager123@postgres:5432/servicemanager
|
||||||
|
# ^^^^^^^
|
||||||
|
# Nombre de servicio Docker, NO localhost
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Error 500 en login / "connect ECONNREFUSED"
|
||||||
|
|
||||||
|
**Síntoma:** El frontend muestra error 500 al hacer login, o la consola del navegador
|
||||||
|
muestra `ECONNREFUSED 127.0.0.1:8000`.
|
||||||
|
|
||||||
|
**Causa:** El proxy de Vite no encuentra el backend.
|
||||||
|
|
||||||
|
**Solución en Docker:** El proxy ya está configurado para usar `PUBLIC_API_URL`.
|
||||||
|
Verifica en `docker-compose.yml` que `frontend-internal` y `frontend-client` tienen:
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
- PUBLIC_API_URL=http://backend:8000
|
||||||
|
```
|
||||||
|
Después reinicia:
|
||||||
|
```bash
|
||||||
|
docker compose restart frontend-internal frontend-client
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solución en desarrollo local:** Asegúrate de que el backend está corriendo:
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/health
|
||||||
|
# Debe responder: {"status": "ok", ...}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### El frontend-internal y frontend-client usan el mismo puerto localmente
|
||||||
|
|
||||||
|
**Síntoma:** Al correr ambos frontends sin Docker, uno de los dos falla
|
||||||
|
con `Port 3000 is already in use`.
|
||||||
|
|
||||||
|
**Solución:**
|
||||||
|
- `frontend-client` → usa el puerto **3000** (por defecto con `npm run dev`)
|
||||||
|
- `frontend-internal` → usa el puerto **3001** (configurado en `vite.config.js`)
|
||||||
|
|
||||||
|
Nunca hay conflicto si los iniciaste con `npm run dev` en cada carpeta por separado.
|
||||||
|
Si aún hay conflicto, mata el proceso en ese puerto:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linux / macOS
|
||||||
|
lsof -ti:3000 | xargs kill -9
|
||||||
|
|
||||||
|
# Windows (PowerShell)
|
||||||
|
Get-Process -Id (Get-NetTCPConnection -LocalPort 3000).OwningProcess | Stop-Process -Force
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### El tenant slug es incorrecto al hacer login
|
||||||
|
|
||||||
|
**Síntoma:** Login falla con "credenciales inválidas" aunque el email y contraseña son correctos.
|
||||||
|
|
||||||
|
**Causa:** El campo `tenant_slug` no corresponde a ningún tenant en la base de datos.
|
||||||
|
|
||||||
|
**Solución:**
|
||||||
|
```bash
|
||||||
|
# Ver los tenants disponibles
|
||||||
|
docker exec servicemanager-backend python -c "
|
||||||
|
import asyncio
|
||||||
|
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
||||||
|
from sqlalchemy import text
|
||||||
|
import os
|
||||||
|
async def main():
|
||||||
|
engine = create_async_engine(os.environ['DATABASE_URL'])
|
||||||
|
async with AsyncSession(engine) as s:
|
||||||
|
result = await s.execute(text('SELECT slug, name FROM tenants'))
|
||||||
|
for row in result:
|
||||||
|
print(row)
|
||||||
|
asyncio.run(main())
|
||||||
|
"
|
||||||
|
```
|
||||||
|
Tenant por defecto (después del seed): **`aduanasoft-demo`**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Puerto ocupado — cambiar puertos de los servicios
|
||||||
|
|
||||||
|
Edita `docker-compose.yml` y modifica **solo el número izquierdo** del mapeo de puertos:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Ejemplo: mover el backend al puerto 9000
|
||||||
|
backend:
|
||||||
|
ports:
|
||||||
|
- "9000:8000" # accesible en localhost:9000
|
||||||
|
|
||||||
|
# Ejemplo: mover el frontend al puerto 4000
|
||||||
|
frontend-client:
|
||||||
|
ports:
|
||||||
|
- "4000:3000" # accesible en localhost:4000
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Migraciones fallidas — `alembic upgrade head` da error
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Verificar el estado actual
|
||||||
|
docker exec servicemanager-backend alembic current
|
||||||
|
|
||||||
|
# Si hay conflicto, hacer downgrade hasta la base y volver a subir
|
||||||
|
docker exec servicemanager-backend alembic downgrade base
|
||||||
|
docker exec servicemanager-backend alembic upgrade head
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Módulo Python no encontrado (`ModuleNotFoundError`)
|
||||||
|
|
||||||
|
**Con Docker:** El módulo no está en `requirements.txt` o la imagen no fue reconstruida.
|
||||||
|
```bash
|
||||||
|
# Reconstruir la imagen del backend
|
||||||
|
docker compose build backend
|
||||||
|
docker compose up -d backend
|
||||||
|
```
|
||||||
|
|
||||||
|
**Local:** El entorno virtual no está activado.
|
||||||
|
```bash
|
||||||
|
# Verificar que el venv está activo (debe aparecer (.venv) en el prompt)
|
||||||
|
which python # Linux/macOS — debe apuntar a .venv/
|
||||||
|
# Windows:
|
||||||
|
where python # debe apuntar a .venv\Scripts\python.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `npm: command not found` o versión de Node incorrecta
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node --version # Debe ser v18.x o superior
|
||||||
|
npm --version # Debe ser 9.x o superior
|
||||||
|
```
|
||||||
|
|
||||||
|
Si Node no está instalado, descárgalo desde https://nodejs.org/ (elige "LTS").
|
||||||
|
|
||||||
|
En macOS con Homebrew:
|
||||||
|
```bash
|
||||||
|
brew install node@18
|
||||||
|
```
|
||||||
|
|
||||||
|
En Linux (Ubuntu/Debian):
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
|
||||||
|
sudo apt-get install -y nodejs
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `docker-compose` no se reconoce como comando
|
||||||
|
|
||||||
|
En versiones modernas de Docker Desktop, el comando es `docker compose` (con espacio, sin guion).
|
||||||
|
Si tienes instalación separada de Docker Compose v1, usa `docker-compose` (con guion).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Logs de los contenedores
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ver qué está fallando
|
||||||
|
docker compose logs backend --tail=50
|
||||||
|
docker compose logs frontend-internal --tail=50
|
||||||
|
docker compose logs postgres --tail=20
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Contribución
|
## Contribución
|
||||||
|
|
||||||
1. Fork del proyecto
|
1. Haz fork del proyecto
|
||||||
2. Crear feature branch (`git checkout -b feature/nueva-funcionalidad`)
|
2. Crea una rama de funcionalidad: `git checkout -b feature/nombre-funcionalidad`
|
||||||
3. Commit cambios (`git commit -am 'Agregar nueva funcionalidad'`)
|
3. Realiza tus cambios siguiendo las convenciones del proyecto
|
||||||
4. Push a branch (`git push origin feature/nueva-funcionalidad`)
|
4. Ejecuta las pruebas: `pytest` y el linter: `ruff check .`
|
||||||
5. Crear Pull Request
|
5. Haz commit con un mensaje descriptivo: `git commit -m "feat: agregar exportación a CSV"`
|
||||||
|
6. Sube tu rama: `git push origin feature/nombre-funcionalidad`
|
||||||
|
7. Abre un Pull Request hacia `main`
|
||||||
|
|
||||||
|
### Convenciones de nombres
|
||||||
|
|
||||||
|
- **Modelos**: `PascalCase` → `User`, `Ticket`, `TenantOrganization`
|
||||||
|
- **Endpoints (URL)**: `kebab-case` → `/api/v1/user-management/`
|
||||||
|
- **Componentes Svelte**: `PascalCase.svelte` → `TicketCard.svelte`
|
||||||
|
- **Stores**: `camelCase` → `ticketStore.ts`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Historial de versiones
|
||||||
|
|
||||||
|
| Versión | Descripción |
|
||||||
|
|---------|-------------|
|
||||||
|
| **v1.15.1** | Módulo de reportes implementado |
|
||||||
|
| v1.14.x | Mejoras al módulo de auditoría |
|
||||||
|
| v1.13.x | Sistema de SLAs automático |
|
||||||
|
| v1.12.x | Notificaciones por email |
|
||||||
|
| v1.0.0 | MVP inicial — tickets, tenants, autenticación |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Licencia
|
## Licencia
|
||||||
|
|
||||||
Propietario - Aduanasoft © 2026
|
Propietario — Aduanasoft © 2026. Todos los derechos reservados.
|
||||||
@@ -189,6 +189,7 @@ services:
|
|||||||
- NODE_ENV=${ENVIRONMENT:-development}
|
- NODE_ENV=${ENVIRONMENT:-development}
|
||||||
- PUBLIC_API_URL=http://backend:8000
|
- PUBLIC_API_URL=http://backend:8000
|
||||||
- PUBLIC_APP_NAME=ServiceManager Admin
|
- PUBLIC_APP_NAME=ServiceManager Admin
|
||||||
|
- PORT=3000 # El contendor corre en 3000; docker mapea 3001:3000 al host
|
||||||
volumes:
|
volumes:
|
||||||
- ./frontend-internal:/app
|
- ./frontend-internal:/app
|
||||||
- /app/node_modules
|
- /app/node_modules
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev --port 3000 --host 0.0.0.0",
|
"dev": "vite dev --host 0.0.0.0",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview --port 3000 --host 0.0.0.0",
|
"preview": "vite preview --port 3000 --host 0.0.0.0",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import { defineConfig } from 'vite';
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [sveltekit()],
|
plugins: [sveltekit()],
|
||||||
server: {
|
server: {
|
||||||
port: 3000,
|
// Puerto: 3001 por defecto en local; Docker lo sobreescribe con PORT=3000
|
||||||
|
port: parseInt(process.env.PORT || '3001'),
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
watch: {
|
watch: {
|
||||||
usePolling: true,
|
usePolling: true,
|
||||||
@@ -19,7 +20,7 @@ export default defineConfig({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
preview: {
|
preview: {
|
||||||
port: 3000,
|
port: parseInt(process.env.PORT || '3001'),
|
||||||
host: '0.0.0.0'
|
host: '0.0.0.0'
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
|
|||||||
Reference in New Issue
Block a user