From f460c2b8aaf56cec2280dfe3d21a0c546a4f9d0b Mon Sep 17 00:00:00 2001 From: acazares Date: Sun, 19 Oct 2025 21:05:07 -0500 Subject: [PATCH] feat(auth): add user registration and response DTOs, implement registration endpoint in AuthService feat(auth): create registration route and integrate with AuthService feat(auth): implement user registration logic in AuthService with Keycloak integration fix(config): add Keycloak admin credentials to settings fix(middleware): update tenant middleware to include new auth routes chore(docs): remove outdated testing guide and add project architecture documentation feat(frontend): implement user registration page and integrate with API feat(frontend): create login page with tenant selection and error handling refactor(frontend): update layout to use custom auth store and improve loading states --- PROJECT_COMPLETE.txt | 329 ---------------- SUMMARY.md | 301 --------------- backend/api/v1/modules/a76/auth/dto.py | 40 ++ backend/api/v1/modules/a76/auth/routes.py | 29 +- backend/api/v1/modules/a76/auth/service.py | 112 +++++- backend/core/config.py | 2 + backend/core/middleware.py | 6 +- docs/TESTING_GUIDE.md | 417 --------------------- a76.json => docs/a76.json | 0 frontend/project.inlang/.gitignore | 1 + frontend/src/routes/+layout.svelte | 8 +- frontend/src/routes/+page.svelte | 21 +- frontend/src/routes/callback/+page.svelte | 24 -- frontend/src/routes/login/+page.svelte | 120 ++++++ frontend/src/routes/register/+page.svelte | 237 ++++++++++++ start.sh | 2 +- 16 files changed, 559 insertions(+), 1090 deletions(-) delete mode 100644 PROJECT_COMPLETE.txt delete mode 100644 SUMMARY.md delete mode 100644 docs/TESTING_GUIDE.md rename a76.json => docs/a76.json (100%) create mode 100644 frontend/project.inlang/.gitignore delete mode 100644 frontend/src/routes/callback/+page.svelte create mode 100644 frontend/src/routes/login/+page.svelte create mode 100644 frontend/src/routes/register/+page.svelte diff --git a/PROJECT_COMPLETE.txt b/PROJECT_COMPLETE.txt deleted file mode 100644 index 418442d3..00000000 --- a/PROJECT_COMPLETE.txt +++ /dev/null @@ -1,329 +0,0 @@ -╔══════════════════════════════════════════════════════════════════════════════╗ -║ ║ -║ 🎉 PROYECTO ANEXO76 GENERADO 🎉 ║ -║ ║ -║ Aplicación SaaS Multi-tenant para Comercio Exterior ║ -║ ║ -╚══════════════════════════════════════════════════════════════════════════════╝ - - -📊 RESUMEN EJECUTIVO -═══════════════════════════════════════════════════════════════════════════════ - -✅ BACKEND (FastAPI + Keycloak + SQLAlchemy) - • 22 archivos Python generados - • 24 dependencias configuradas - • 3 módulos completos implementados - • ~3,200 líneas de código - - Módulos implementados: - ├── auth/ → Autenticación con Keycloak (OpenID Connect) - ├── tenants/ → Gestión de clientes multi-tenant - └── licenses/ → Control de licencias y planes - -✅ FRONTEND (SvelteKit + Keycloak-js + TailwindCSS) - • 8 archivos TypeScript - • 5 componentes Svelte - • Dashboard completo con autenticación - • ~2,000 líneas de código - - Funcionalidades: - ├── Login/Logout completo - ├── Dashboard con información de usuario - ├── Visualización de licencias - └── Cliente API integrado - -✅ DOCUMENTACIÓN - • README.md - Guía principal - • ARCHITECTURE.md - Arquitectura técnica completa - • KEYCLOAK_SETUP.md - Configuración paso a paso - • TESTING_GUIDE.md - Casos de prueba - • ~2,500 líneas de documentación - -✅ DEVOPS - • Docker Compose con 4 servicios - • Scripts de inicio automático - • Health checks configurados - • Hot reload en desarrollo - - -🏗️ ARQUITECTURA IMPLEMENTADA -═══════════════════════════════════════════════════════════════════════════════ - -PATRÓN: Modular Layered (estilo NestJS) - -┌─────────────────────────────────────────────────────────────┐ -│ FRONTEND (SvelteKit) │ -│ • Autenticación Keycloak │ -│ • Dashboard reactivo │ -│ • TailwindCSS para estilos │ -└────────────────┬────────────────────────────────────────────┘ - │ HTTP/REST + JWT -┌────────────────▼────────────────────────────────────────────┐ -│ API GATEWAY (FastAPI) │ -│ Middlewares: │ -│ ├── TenantMiddleware → Identifica tenant │ -│ ├── LicenseValidation → Valida licencia activa │ -│ └── RequestLogging → Logging de requests │ -└────────────────┬────────────────────────────────────────────┘ - │ - ┌────────┴────────┐ - │ │ -┌───────▼──────┐ ┌──────▼────────┐ -│ MÓDULOS │ │ CORE LAYER │ -│ │ │ │ -│ Cada módulo │ │ • Config │ -│ tiene: │ │ • Database │ -│ • models │ │ • Security │ -│ • dto │ │ • Middleware │ -│ • service │ │ │ -│ • routes │ │ │ -└───────┬──────┘ └───────────────┘ - │ -┌───────▼──────────────────────────────────┐ -│ MULTI-TENANT DATABASE │ -│ ┌──────────────┐ ┌─────────────────┐ │ -│ │ Core DB │ │ Tenant 1 DB │ │ -│ │ (Shared) │ │ (Dedicated) │ │ -│ └──────────────┘ └─────────────────┘ │ -└──────────────────────────────────────────┘ - - -🎯 CARACTERÍSTICAS CLAVE -═══════════════════════════════════════════════════════════════════════════════ - -✓ MULTI-TENANT HÍBRIDO - • BD compartida para clientes pequeños/medianos - • BD dedicada para clientes enterprise - • Migración automática entre modos - • Row-level security en BD compartida - -✓ AUTENTICACIÓN Y SEGURIDAD - • Keycloak como Identity Provider - • OpenID Connect (OIDC) - • JWT con firma RS256 - • RBAC (Role-Based Access Control) - • Roles: admin, user, auditor, system - -✓ CONTROL DE LICENCIAS - • 4 planes: Free, Basic, Professional, Enterprise - • Validación automática en cada request - • Tracking de uso (usuarios, storage, operaciones) - • Límites configurables por plan - -✓ ESTRUCTURA MODULAR - • Patrón estilo NestJS - • Separación clara de responsabilidades - • DTOs con Pydantic para validación - • Services para lógica de negocio - • Routes para exposición HTTP - -✓ DEVELOPER EXPERIENCE - • Hot reload en desarrollo - • Documentación automática (Swagger) - • Type safety (TypeScript + Pydantic) - • Scripts de inicio rápido - • Logs estructurados - - -📦 SERVICIOS DOCKER COMPOSE -═══════════════════════════════════════════════════════════════════════════════ - -┌────────────────┬─────────────────┬──────────────────────────────┐ -│ Servicio │ Puerto │ Descripción │ -├────────────────┼─────────────────┼──────────────────────────────┤ -│ frontend │ 5173 │ SvelteKit (desarrollo) │ -│ backend │ 8000 │ FastAPI + Uvicorn │ -│ keycloak │ 8080 │ Identity Provider │ -│ postgres │ 5432 │ Base de datos PostgreSQL 15 │ -└────────────────┴─────────────────┴──────────────────────────────┘ - - -📈 MÉTRICAS DEL PROYECTO -═══════════════════════════════════════════════════════════════════════════════ - -Líneas de código: ~5,700 líneas -Archivos generados: 60+ archivos -Módulos backend: 3 (auth, tenants, licenses) -Endpoints API: 18 endpoints -Documentación: 4 documentos completos -Tiempo estimado: 40+ horas de desarrollo manual - - -🚀 CÓMO INICIAR -═══════════════════════════════════════════════════════════════════════════════ - -1. Ejecutar script de inicio: - $ ./start.sh - -2. Configurar Keycloak: - Seguir: docs/KEYCLOAK_SETUP.md - -3. Acceder a la aplicación: - • Frontend: http://localhost:5173 - • Backend: http://localhost:8000/docs - • Keycloak: http://localhost:8080 - -4. Login de prueba: - Usuario: demo - Password: demo123 - - -📚 DOCUMENTACIÓN DISPONIBLE -═══════════════════════════════════════════════════════════════════════════════ - -📄 README.md - → Visión general del proyecto - → Quick start guide - → Estructura del proyecto - → Tabla de planes de licencia - -📄 docs/ARCHITECTURE.md - → Arquitectura técnica detallada - → Patrones implementados - → Flujos de autenticación - → Modelo de datos - → API reference completo - -📄 docs/KEYCLOAK_SETUP.md - → Configuración paso a paso de Keycloak - → Creación de clientes (backend y frontend) - → Configuración de usuarios - → Mappers de atributos - → Troubleshooting - -📄 docs/TESTING_GUIDE.md - → Verificación de servicios - → Pruebas de endpoints - → Casos de prueba con curl - → Pruebas de frontend - → Debugging tips - - -🎓 PRÓXIMOS MÓDULOS A IMPLEMENTAR -═══════════════════════════════════════════════════════════════════════════════ - -Siguiendo el mismo patrón modular, puedes agregar: - -📦 inventories/ - → Gestión de inventarios para IMMEX - → Control de entradas/salidas - → Trazabilidad de productos - -📦 pedimentos/ - → Pedimentos aduanales - → Anexo 24 compliance - → Validaciones SAT - -📦 invoices/ - → Facturas de importación/exportación - → Complementos de comercio exterior - → Integración con CFDI - -📦 reports/ - → Reportes avanzados - → Dashboards personalizados - → Exports (Excel, PDF) - -📦 webhooks/ - → Integraciones con sistemas externos - → Notificaciones automáticas - → Event-driven architecture - - -🔧 TECNOLOGÍAS Y VERSIONES -═══════════════════════════════════════════════════════════════════════════════ - -Backend: -├── Python 3.11+ -├── FastAPI 0.110.0 -├── SQLAlchemy 2.0.27 -├── Pydantic 2.6.1 -├── Keycloak-Python 3.9.1 -└── PostgreSQL 15 - -Frontend: -├── Node.js 20+ -├── SvelteKit 2.43.2 -├── Svelte 5.39.5 -├── TypeScript 5.9.2 -├── Keycloak-js 26.2.1 -├── TailwindCSS 4.1.13 -└── Vite 7.1.7 - -DevOps: -├── Docker 24+ -├── Docker Compose 2.x -└── (Futuro) Kubernetes - - -✅ CHECKLIST DE ENTREGA -═══════════════════════════════════════════════════════════════════════════════ - -[✓] Backend FastAPI con 3 módulos completos -[✓] Frontend SvelteKit con autenticación -[✓] Base de datos multi-tenant (modelo híbrido) -[✓] Sistema de autenticación con Keycloak -[✓] Control de licencias con middleware -[✓] DTOs con Pydantic (estilo NestJS) -[✓] Docker Compose con 4 servicios -[✓] Scripts de inicio automático (start.sh) -[✓] Script de verificación (verify.sh) -[✓] Documentación completa (4 docs) -[✓] README con instrucciones claras -[✓] .gitignore configurado -[✓] Datos de prueba iniciales -[✓] Health checks en todos los servicios -[✓] Hot reload en desarrollo -[✓] CORS configurado correctamente - - -🎊 ESTADO DEL PROYECTO -═══════════════════════════════════════════════════════════════════════════════ - - ✅ GENERACIÓN COMPLETADA AL 100% - -El proyecto Anexo76 ha sido generado completamente siguiendo el blueprint -técnico proporcionado. Todos los archivos, módulos, configuraciones y -documentación están listos para su uso. - - -🚦 PRÓXIMOS PASOS -═══════════════════════════════════════════════════════════════════════════════ - -1. ✓ Verificar estructura: ./verify.sh -2. → Iniciar servicios: ./start.sh -3. → Configurar Keycloak: Ver docs/KEYCLOAK_SETUP.md -4. → Probar aplicación: Ver docs/TESTING_GUIDE.md -5. → Desarrollar nuevos módulos: Ver docs/ARCHITECTURE.md - - -💡 TIPS ADICIONALES -═══════════════════════════════════════════════════════════════════════════════ - -• Para ver logs: docker-compose logs -f [servicio] -• Para detener: docker-compose down -• Para reiniciar: docker-compose restart [servicio] -• Para limpiar todo: docker-compose down -v -• Docs interactivas: http://localhost:8000/docs -• Monitorear BD: pgAdmin o herramienta similar - - -📞 SOPORTE -═══════════════════════════════════════════════════════════════════════════════ - -• Arquitectura: Ver docs/ARCHITECTURE.md -• Setup Keycloak: Ver docs/KEYCLOAK_SETUP.md -• Testing: Ver docs/TESTING_GUIDE.md -• Issues: Revisar logs con docker-compose - - -═══════════════════════════════════════════════════════════════════════════════ - - Proyecto generado el 17 de Octubre, 2025 - Stack: FastAPI + SvelteKit + Keycloak + PostgreSQL - Arquitectura: Multi-tenant Modular - - ¡Listo para producción! 🚀 - -═══════════════════════════════════════════════════════════════════════════════ diff --git a/SUMMARY.md b/SUMMARY.md deleted file mode 100644 index 867648c7..00000000 --- a/SUMMARY.md +++ /dev/null @@ -1,301 +0,0 @@ -# 🎉 Proyecto Anexo76 - Generado Exitosamente - -## ✅ Resumen de lo Generado - -### 📁 Estructura Completa Creada - -#### Backend (FastAPI + Keycloak + SQLAlchemy) -``` -backend/ -├── main.py ✅ Aplicación FastAPI con middlewares -├── init_db.py ✅ Script de inicialización de BD -├── requirements.txt ✅ Dependencias actualizadas con comentarios -├── Dockerfile ✅ Containerización -├── .env.example ✅ Variables de entorno -│ -├── core/ ✅ Capa core compartida -│ ├── config.py ✅ Configuración con Pydantic Settings -│ ├── database.py ✅ Multi-tenant DB (hybrid model) -│ ├── security.py ✅ Auth Keycloak + JWT -│ ├── middleware.py ✅ Tenant, License, Logging middlewares -│ └── __init__.py ✅ -│ -└── api/v1/ - ├── router.py ✅ Router principal v1 - └── modules/ ✅ Módulos estilo NestJS - ├── auth/ ✅ Autenticación completa - │ ├── dto.py - │ ├── service.py - │ ├── routes.py - │ └── __init__.py - ├── tenants/ ✅ Gestión de tenants - │ ├── models.py - │ ├── dto.py - │ ├── service.py - │ ├── routes.py - │ └── __init__.py - └── licenses/ ✅ Control de licencias - ├── models.py - ├── dto.py - ├── service.py - ├── routes.py - └── __init__.py -``` - -#### Frontend (SvelteKit + Keycloak-js) -``` -frontend/ -├── src/ -│ ├── routes/ -│ │ ├── +layout.svelte ✅ Layout con init Keycloak -│ │ ├── +page.svelte ✅ Dashboard completo -│ │ └── callback/ ✅ OAuth callback -│ │ └── +page.svelte -│ │ -│ ├── lib/ -│ │ ├── auth.ts ✅ Servicio autenticación -│ │ └── api.ts ✅ Cliente API -│ │ -│ └── app.html ✅ HTML base -│ -├── static/ -│ └── silent-check-sso.html ✅ SSO silencioso -│ -├── Dockerfile ✅ Containerización -├── .env ✅ Variables configuradas -└── package.json ✅ Con keycloak-js instalado -``` - -#### Documentación -``` -docs/ -├── ARCHITECTURE.md ✅ Arquitectura técnica completa -├── KEYCLOAK_SETUP.md ✅ Guía configuración Keycloak -└── TESTING_GUIDE.md ✅ Guía de pruebas exhaustiva -``` - -#### DevOps -``` -├── docker-compose.yml ✅ 4 servicios configurados -├── start.sh ✅ Script de inicio rápido -├── README.md ✅ Documentación principal -└── .gitignore ✅ Archivos a ignorar -``` - -## 🎯 Características Implementadas - -### Backend -- ✅ **FastAPI** con documentación automática (Swagger) -- ✅ **Autenticación Keycloak** (OpenID Connect) -- ✅ **Multi-tenant híbrido** (BD compartida + dedicada) -- ✅ **Middleware de licencias** con validación automática -- ✅ **Estructura modular** estilo NestJS con DTOs -- ✅ **Separación de capas**: models, dto, service, routes -- ✅ **3 módulos completos**: auth, tenants, licenses -- ✅ **CORS configurado** -- ✅ **Logging estructurado** -- ✅ **SQLAlchemy 2.0** con soporte async -- ✅ **Pydantic v2** para validaciones - -### Frontend -- ✅ **SvelteKit** con TypeScript -- ✅ **Integración Keycloak-js** completa -- ✅ **Login/Logout** funcional -- ✅ **Dashboard** con información de usuario y licencia -- ✅ **TailwindCSS 4** para estilos -- ✅ **Stores reactivos** para estado de auth -- ✅ **Cliente API** con manejo de tokens -- ✅ **SSO silencioso** configurado -- ✅ **Rutas protegidas** - -### Infraestructura -- ✅ **Docker Compose** con 4 servicios -- ✅ **PostgreSQL 15** para BD core -- ✅ **Keycloak 23** para autenticación -- ✅ **Hot reload** en desarrollo -- ✅ **Volúmenes persistentes** -- ✅ **Health checks** - -### Seguridad -- ✅ **JWT con RS256** -- ✅ **RBAC** (Role-Based Access Control) -- ✅ **Validación de tenant** en cada request -- ✅ **Control de licencias** automático -- ✅ **Isolation por tenant_id** - -## 📊 Endpoints API Disponibles - -### Autenticación (`/v1/auth`) -- `POST /auth/login` - Login con Keycloak -- `POST /auth/refresh` - Renovar token -- `GET /auth/me` - Info usuario actual -- `POST /auth/logout` - Cerrar sesión -- `GET /auth/health` - Health check - -### Tenants (`/v1/tenants`) -- `POST /tenants` - Crear tenant -- `GET /tenants` - Listar tenants -- `GET /tenants/{id}` - Obtener tenant -- `PUT /tenants/{id}` - Actualizar tenant -- `DELETE /tenants/{id}` - Eliminar tenant -- `GET /tenants/slug/{slug}` - Buscar por slug - -### Licencias (`/v1/licenses`) -- `POST /licenses` - Crear licencia -- `GET /licenses/tenant/{id}` - Obtener licencia -- `PUT /licenses/tenant/{id}` - Actualizar licencia -- `GET /licenses/validate/{id}` - Validar licencia -- `GET /licenses/usage/{id}` - Uso de licencia -- `GET /licenses/my-license` - Mi licencia - -## 🚀 Cómo Iniciar - -### Opción 1: Script Automático (Recomendado) -```bash -cd /home/alexeer/dev/anexo76 -./start.sh -``` - -### Opción 2: Manual -```bash -# 1. Copiar .env -cp backend/.env.example backend/.env -cp frontend/.env.example frontend/.env - -# 2. Iniciar servicios -docker-compose up -d - -# 3. Esperar PostgreSQL -sleep 10 - -# 4. Inicializar BD -cd backend -python3 init_db.py -cd .. -``` - -### Siguiente Paso: Configurar Keycloak -Seguir la guía: `docs/KEYCLOAK_SETUP.md` - -## 🔗 URLs de Acceso - -| Servicio | URL | Credenciales | -|----------|-----|--------------| -| Frontend | http://localhost:5173 | Usuario Keycloak | -| Backend API | http://localhost:8000 | Token JWT | -| API Docs | http://localhost:8000/docs | - | -| Keycloak | http://localhost:8080 | admin / admin | -| PostgreSQL | localhost:5432 | postgres / postgres | - -## 👤 Usuario de Prueba - -Después de configurar Keycloak: -- **Usuario**: `demo` -- **Password**: `demo123` -- **Tenant**: Empresa Demo (ID: 1) -- **Licencia**: Professional (50 usuarios, 100GB) - -## 📚 Documentación Generada - -1. **README.md** - Visión general y quick start -2. **docs/ARCHITECTURE.md** - Arquitectura técnica detallada -3. **docs/KEYCLOAK_SETUP.md** - Guía paso a paso Keycloak -4. **docs/TESTING_GUIDE.md** - Casos de prueba exhaustivos - -## 🎨 Características de Diseño - -### Arquitectura -- **Modular**: Estilo NestJS con separación clara -- **Escalable**: Multi-tenant híbrido -- **Mantenible**: DTOs + Services + Routes -- **Documentado**: Código auto-documentado + docs - -### Patrones Implementados -- **Repository Pattern** (implícito en services) -- **DTO Pattern** (Pydantic models) -- **Middleware Pattern** (tenant, license, logging) -- **Dependency Injection** (FastAPI Depends) -- **Store Pattern** (Svelte stores para auth) - -## 🔮 Próximos Módulos a Implementar - -Siguiendo la misma estructura, puedes agregar: - -``` -backend/v1/modules/ -├── inventories/ # Gestión de inventarios -│ ├── models.py -│ ├── dto.py -│ ├── service.py -│ └── routes.py -│ -├── pedimentos/ # Pedimentos aduanales -├── invoices/ # Facturas -├── reports/ # Reportes -└── webhooks/ # Integraciones -``` - -Cada módulo sigue el mismo patrón de 4 archivos. - -## 💡 Consejos para Desarrollo - -### Agregar Nuevo Módulo -1. Crear carpeta en `backend/v1/modules/{nombre}` -2. Crear 4 archivos: models.py, dto.py, service.py, routes.py -3. Registrar router en `backend/v1/router.py` -4. Crear migración de BD si hay modelos nuevos - -### Agregar Nueva Ruta Frontend -1. Crear carpeta en `frontend/src/routes/{ruta}` -2. Crear `+page.svelte` para la página -3. Usar `$isAuthenticated` para proteger ruta -4. Usar `api.{modulo}.metodo()` para llamar backend - -### Debugging -- Backend: `docker-compose logs -f backend` -- Frontend: Abrir DevTools (F12) en navegador -- BD: `docker-compose exec postgres psql -U postgres -d anexo76_core` - -## ✅ Checklist Post-Generación - -- [x] Backend generado con 3 módulos completos -- [x] Frontend con autenticación Keycloak -- [x] Docker Compose configurado -- [x] Base de datos con modelos multi-tenant -- [x] Middlewares de seguridad y licencias -- [x] DTOs con Pydantic para todos los módulos -- [x] Documentación completa (4 archivos) -- [x] Script de inicio automático -- [x] .gitignore configurado -- [x] README.md con instrucciones - -## 🎓 Recursos de Aprendizaje - -- **FastAPI**: https://fastapi.tiangolo.com -- **Keycloak**: https://www.keycloak.org/docs -- **SvelteKit**: https://svelte.dev/docs/kit -- **SQLAlchemy**: https://docs.sqlalchemy.org -- **Pydantic**: https://docs.pydantic.dev - -## 🤝 Contribuir - -El proyecto está listo para: -- ✅ Agregar nuevos módulos -- ✅ Implementar tests -- ✅ Configurar CI/CD -- ✅ Deploy a producción -- ✅ Agregar monitoreo - ---- - -## 🎊 ¡Proyecto Anexo76 Generado Exitosamente! - -**Todo está listo para empezar a desarrollar.** - -**Siguiente paso**: Ejecutar `./start.sh` y seguir `docs/KEYCLOAK_SETUP.md` - ---- - -**Generado**: Octubre 2025 -**Stack**: FastAPI + SvelteKit + Keycloak + PostgreSQL -**Arquitectura**: Multi-tenant Modular (estilo NestJS) diff --git a/backend/api/v1/modules/a76/auth/dto.py b/backend/api/v1/modules/a76/auth/dto.py index 611cb389..31e6d4ee 100644 --- a/backend/api/v1/modules/a76/auth/dto.py +++ b/backend/api/v1/modules/a76/auth/dto.py @@ -69,3 +69,43 @@ class UserInfoResponseDTO(BaseModel): class LogoutRequestDTO(BaseModel): """DTO para solicitud de logout""" refresh_token: str = Field(..., description="Refresh token para invalidar") + + +class RegisterRequestDTO(BaseModel): + """DTO para solicitud de registro""" + username: str = Field(..., min_length=3, max_length=50, description="Nombre de usuario") + email: EmailStr = Field(..., description="Email del usuario") + password: str = Field(..., min_length=8, description="Contraseña") + first_name: str = Field(..., min_length=2, max_length=50, description="Nombre") + last_name: str = Field(..., min_length=2, max_length=50, description="Apellido") + tenant_slug: str = Field(..., description="Slug del tenant") + + class Config: + json_schema_extra = { + "example": { + "username": "jperez", + "email": "jperez@ejemplo.com", + "password": "MiPassword123!", + "first_name": "Juan", + "last_name": "Pérez", + "tenant_slug": "empresa-abc" + } + } + + +class RegisterResponseDTO(BaseModel): + """DTO para respuesta de registro""" + user_id: str + username: str + email: str + message: str + + class Config: + json_schema_extra = { + "example": { + "user_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "username": "jperez", + "email": "jperez@ejemplo.com", + "message": "User registered successfully" + } + } diff --git a/backend/api/v1/modules/a76/auth/routes.py b/backend/api/v1/modules/a76/auth/routes.py index 8bc35f7f..34824a7b 100644 --- a/backend/api/v1/modules/a76/auth/routes.py +++ b/backend/api/v1/modules/a76/auth/routes.py @@ -12,7 +12,9 @@ from .dto import ( TokenResponseDTO, RefreshTokenRequestDTO, UserInfoResponseDTO, - LogoutRequestDTO + LogoutRequestDTO, + RegisterRequestDTO, + RegisterResponseDTO ) from .service import AuthService @@ -20,6 +22,31 @@ router = APIRouter(prefix="/auth", tags=["Authentication"]) security = HTTPBearer() +@router.post("/register", response_model=RegisterResponseDTO, status_code=201) +async def register( + register_data: RegisterRequestDTO, + db: Session = Depends(get_core_db) +): + """ + Registra un nuevo usuario en Keycloak + + El usuario debe proporcionar: + - username: Nombre de usuario único + - email: Email único + - password: Contraseña (mínimo 8 caracteres) + - first_name: Nombre + - last_name: Apellido + - tenant_slug: Slug del tenant al que pertenece + + El usuario se crea automáticamente en Keycloak con: + - Cuenta habilitada + - Rol 'user' asignado por defecto + - Atributos de tenant + """ + service = AuthService(db) + return service.register(register_data) + + @router.post("/login", response_model=TokenResponseDTO) async def login( login_data: LoginRequestDTO, diff --git a/backend/api/v1/modules/a76/auth/service.py b/backend/api/v1/modules/a76/auth/service.py index 46460f37..95285957 100644 --- a/backend/api/v1/modules/a76/auth/service.py +++ b/backend/api/v1/modules/a76/auth/service.py @@ -13,7 +13,9 @@ from .dto import ( TokenResponseDTO, RefreshTokenRequestDTO, UserInfoResponseDTO, - LogoutRequestDTO + LogoutRequestDTO, + RegisterRequestDTO, + RegisterResponseDTO ) logger = logging.getLogger(__name__) @@ -56,13 +58,19 @@ class AuthService: if not tenant.is_active: raise HTTPException(status_code=403, detail="Tenant is not active") - # Cambiar realm al del tenant - self.keycloak_openid.realm_name = tenant.keycloak_realm + # Crear nueva instancia de KeycloakOpenID con el realm del tenant + keycloak_client = KeycloakOpenID( + server_url=settings.KEYCLOAK_SERVER_URL, + client_id=settings.KEYCLOAK_CLIENT_ID, + realm_name=tenant.keycloak_realm, + client_secret_key=settings.KEYCLOAK_CLIENT_SECRET + ) # Obtener token de Keycloak - token_response = self.keycloak_openid.token( + token_response = keycloak_client.token( username=login_data.username, - password=login_data.password + password=login_data.password, + grant_type=["password"] ) logger.info(f"User logged in: {login_data.username} (tenant: {tenant.slug})") @@ -173,3 +181,97 @@ class AuthService: except Exception as e: logger.error(f"Logout error: {str(e)}") raise HTTPException(status_code=500, detail="Logout error") + + def register(self, register_data: RegisterRequestDTO) -> RegisterResponseDTO: + """ + Registra un nuevo usuario en Keycloak + + Args: + register_data: Datos del usuario a registrar + + Returns: + RegisterResponseDTO con información del usuario creado + + Raises: + HTTPException: Si el registro falla + """ + try: + # Verificar que el tenant existe + from api.v1.modules.a76.tenants.service import TenantService + tenant_service = TenantService(self.db) + tenant = tenant_service.get_tenant_by_slug(register_data.tenant_slug) + + if not tenant: + raise HTTPException(status_code=404, detail="Tenant not found") + + if not tenant.is_active: + raise HTTPException(status_code=403, detail="Tenant is not active") + + # Crear instancia de KeycloakAdmin para gestión de usuarios + keycloak_admin = KeycloakAdmin( + server_url=settings.KEYCLOAK_SERVER_URL, + username=settings.KEYCLOAK_ADMIN_USERNAME, + password=settings.KEYCLOAK_ADMIN_PASSWORD, + realm_name=tenant.keycloak_realm, + user_realm_name="master", # El admin suele estar en master realm + verify=True + ) + + # Preparar datos del usuario para Keycloak + user_data = { + "username": register_data.username, + "email": register_data.email, + "firstName": register_data.first_name, + "lastName": register_data.last_name, + "enabled": True, + "emailVerified": False, + "credentials": [{ + "type": "password", + "value": register_data.password, + "temporary": False + }], + "attributes": { + "tenant_id": str(tenant.id), + "tenant_slug": tenant.slug + } + } + + # Crear usuario en Keycloak + user_id = keycloak_admin.create_user(user_data) + + # Asignar rol por defecto (user) - opcional, solo si existe + try: + user_role = keycloak_admin.get_realm_role("user") + if user_role: + keycloak_admin.assign_realm_roles(user_id, [user_role]) + logger.info(f"Assigned 'user' role to {register_data.username}") + except KeycloakError as e: + # El rol 'user' no existe, no es un error crítico + logger.warning(f"Could not assign 'user' role: {str(e)}") + + logger.info(f"User registered: {register_data.username} (tenant: {tenant.slug}, user_id: {user_id})") + + return RegisterResponseDTO( + user_id=user_id, + username=register_data.username, + email=register_data.email, + message="User registered successfully" + ) + + except KeycloakError as e: + error_message = str(e) + logger.warning(f"Keycloak registration failed: {error_message}") + + # Mensajes de error más específicos + if "User exists" in error_message or "409" in error_message: + raise HTTPException(status_code=409, detail="Username or email already exists") + elif "Invalid" in error_message: + raise HTTPException(status_code=400, detail="Invalid user data") + else: + raise HTTPException(status_code=500, detail="Registration error") + + except HTTPException: + raise + except Exception as e: + logger.error(f"Registration error: {str(e)}") + raise HTTPException(status_code=500, detail="Registration error") diff --git a/backend/core/config.py b/backend/core/config.py index 2995df07..cfc509b6 100644 --- a/backend/core/config.py +++ b/backend/core/config.py @@ -26,6 +26,8 @@ class Settings(BaseSettings): KEYCLOAK_REALM: str = "master" KEYCLOAK_CLIENT_ID: str = "anexo76-backend" KEYCLOAK_CLIENT_SECRET: str = "" + KEYCLOAK_ADMIN_USERNAME: str = "admin" + KEYCLOAK_ADMIN_PASSWORD: str = "admin" # Security SECRET_KEY: str = "change-this-secret-key-in-production" diff --git a/backend/core/middleware.py b/backend/core/middleware.py index 9d0a29e3..4ed7155f 100644 --- a/backend/core/middleware.py +++ b/backend/core/middleware.py @@ -29,7 +29,9 @@ class TenantMiddleware(BaseHTTPMiddleware): "/docs", "/redoc", "/openapi.json", + "/api/v1/auth", "/v1/auth", + "/api/v1/status", "/v1/status", "/health", "/" @@ -86,7 +88,9 @@ class LicenseValidationMiddleware(BaseHTTPMiddleware): "/docs", "/redoc", "/openapi.json", + "/api/v1/auth", "/v1/auth", + "/api/v1/status", "/v1/status", "/health", "/" @@ -112,7 +116,7 @@ class LicenseValidationMiddleware(BaseHTTPMiddleware): db = CoreSessionLocal() try: # Importar aquí para evitar imports circulares - from api.v1.modules.licenses.service import LicenseService + from api.v1.modules.a76.licenses.service import LicenseService license_service = LicenseService(db) license_info = license_service.validate_license(tenant_id) diff --git a/docs/TESTING_GUIDE.md b/docs/TESTING_GUIDE.md deleted file mode 100644 index 7559f57b..00000000 --- a/docs/TESTING_GUIDE.md +++ /dev/null @@ -1,417 +0,0 @@ -# Guía de Prueba Rápida - Anexo76 - -Esta guía te ayudará a probar todas las funcionalidades básicas de Anexo76 después de la instalación. - -## Prerrequisitos - -✅ Haber ejecutado `./start.sh` exitosamente -✅ Haber configurado Keycloak siguiendo `docs/KEYCLOAK_SETUP.md` -✅ Tener los servicios corriendo - -## Verificar Estado de Servicios - -```bash -docker-compose ps -``` - -Deberías ver 4 servicios en estado "Up": -- postgres -- keycloak -- backend -- frontend - -## 1. Probar Backend API - -### Health Check -```bash -curl http://localhost:8000/health -``` - -Respuesta esperada: -```json -{ - "status": "healthy", - "environment": "development" -} -``` - -### Status de API -```bash -curl http://localhost:8000/v1/status -``` - -Respuesta esperada: -```json -{ - "status": "ok", - "version": "1.0.0", - "api": "v1" -} -``` - -### Documentación Interactiva -Abrir en navegador: http://localhost:8000/docs - -Deberías ver la interfaz Swagger UI con todos los endpoints documentados. - -## 2. Probar Autenticación con Keycloak - -### Obtener Token (vía API directa) - -```bash -# Reemplaza YOUR_CLIENT_SECRET con el secret de Keycloak -curl -X POST http://localhost:8080/realms/master/protocol/openid-connect/token \ - -H "Content-Type: application/x-www-form-urlencoded" \ - -d "client_id=anexo76-backend" \ - -d "client_secret=YOUR_CLIENT_SECRET" \ - -d "username=demo" \ - -d "password=demo123" \ - -d "grant_type=password" -``` - -Respuesta esperada (fragmento): -```json -{ - "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", - "expires_in": 300, - "refresh_expires_in": 1800, - "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", - "token_type": "Bearer" -} -``` - -### Usar Token para Llamar API - -```bash -# Guarda el access_token en una variable -TOKEN="tu-access-token-aqui" - -# Llamar endpoint protegido -curl -X GET http://localhost:8000/v1/auth/me \ - -H "Authorization: Bearer $TOKEN" -``` - -Respuesta esperada: -```json -{ - "sub": "a1b2c3d4-...", - "email": "demo@empresa-demo.com", - "name": "Usuario Demo", - "preferred_username": "demo", - "tenant_id": 1, - "roles": ["user", "admin"] -} -``` - -## 3. Probar Módulo de Tenants - -### Listar Tenants (requiere rol admin) -```bash -curl -X GET http://localhost:8000/v1/tenants \ - -H "Authorization: Bearer $TOKEN" -``` - -Respuesta esperada: -```json -{ - "tenants": [ - { - "id": 1, - "name": "Empresa Demo S.A. de C.V.", - "slug": "empresa-demo", - "type": "shared", - "keycloak_realm": "master", - "is_active": true, - ... - } - ], - "total": 1, - "page": 1, - "page_size": 50 -} -``` - -### Obtener Tenant por ID -```bash -curl -X GET http://localhost:8000/v1/tenants/1 \ - -H "Authorization: Bearer $TOKEN" -``` - -### Obtener Tenant por Slug -```bash -curl -X GET http://localhost:8000/v1/tenants/slug/empresa-demo \ - -H "Authorization: Bearer $TOKEN" -``` - -## 4. Probar Módulo de Licencias - -### Obtener Mi Licencia -```bash -curl -X GET http://localhost:8000/v1/licenses/my-license \ - -H "Authorization: Bearer $TOKEN" -``` - -Respuesta esperada: -```json -{ - "id": 1, - "tenant_id": 1, - "plan": "professional", - "status": "active", - "max_users": 50, - "max_storage_gb": 100, - "max_monthly_operations": 25000, - "feature_api_access": true, - "feature_advanced_reports": true, - "feature_integrations": true, - "feature_dedicated_support": false, - "expires_at": "2026-10-17T...", - ... -} -``` - -### Validar Licencia -```bash -curl -X GET http://localhost:8000/v1/licenses/validate/1 \ - -H "Authorization: Bearer $TOKEN" -``` - -Respuesta esperada: -```json -{ - "is_valid": true, - "status": "active", - "plan": "professional", - "expires_at": "2026-10-17T...", - "reason": null -} -``` - -### Obtener Uso de Licencia -```bash -curl -X GET http://localhost:8000/v1/licenses/usage/1 \ - -H "Authorization: Bearer $TOKEN" -``` - -## 5. Probar Frontend - -### Abrir Aplicación -Abrir en navegador: http://localhost:5173 - -### Probar Login -1. Click en botón "Iniciar Sesión" -2. Serás redirigido a Keycloak -3. Ingresar credenciales: - - Usuario: `demo` - - Password: `demo123` -4. Deberías ser redirigido de vuelta al dashboard - -### Verificar Dashboard -Después del login, deberías ver: -- ✅ Nombre y email del usuario -- ✅ Información de licencia (plan, estado, límites) -- ✅ Información de usuario (ID, roles, tenant ID) -- ✅ Botón "Cerrar Sesión" - -### Probar Logout -1. Click en "Cerrar Sesión" -2. Deberías volver a la pantalla de bienvenida - -## 6. Pruebas de Middleware - -### Probar sin Token (debe fallar) -```bash -curl -X GET http://localhost:8000/v1/tenants/1 -``` - -Respuesta esperada (error 401): -```json -{ - "detail": "Missing or invalid authorization header" -} -``` - -### Probar con Token Inválido (debe fallar) -```bash -curl -X GET http://localhost:8000/v1/tenants/1 \ - -H "Authorization: Bearer token-invalido" -``` - -Respuesta esperada (error 401): -```json -{ - "detail": "Could not validate credentials" -} -``` - -### Probar sin Tenant ID en Token (debe fallar) -Si el token no tiene `tenant_id`, debería recibir error 400: -```json -{ - "detail": "Tenant ID not found in token" -} -``` - -## 7. Verificar Logs - -### Ver logs de todos los servicios -```bash -docker-compose logs -f -``` - -### Ver logs solo del backend -```bash -docker-compose logs -f backend -``` - -### Ver logs solo del frontend -```bash -docker-compose logs -f frontend -``` - -### Ver logs de PostgreSQL -```bash -docker-compose logs -f postgres -``` - -## 8. Probar Base de Datos - -### Conectarse a PostgreSQL -```bash -docker-compose exec postgres psql -U postgres -d anexo76_core -``` - -### Consultas útiles -```sql --- Ver tenants -SELECT * FROM tenants; - --- Ver licencias -SELECT * FROM licenses; - --- Ver información de licencia con tenant -SELECT t.name, t.slug, l.plan, l.status, l.expires_at -FROM tenants t -JOIN licenses l ON l.tenant_id = t.id; - --- Salir -\q -``` - -## 9. Casos de Prueba Adicionales - -### Crear Nuevo Tenant (requiere rol admin) -```bash -curl -X POST http://localhost:8000/v1/tenants \ - -H "Authorization: Bearer $TOKEN" \ - -H "Content-Type: application/json" \ - -d '{ - "name": "Nueva Empresa S.A.", - "slug": "nueva-empresa", - "keycloak_realm": "master", - "type": "shared", - "contact_name": "Juan Pérez", - "contact_email": "juan@nueva-empresa.com" - }' -``` - -### Actualizar Tenant -```bash -curl -X PUT http://localhost:8000/v1/tenants/1 \ - -H "Authorization: Bearer $TOKEN" \ - -H "Content-Type: application/json" \ - -d '{ - "contact_phone": "+52 55 9999 8888" - }' -``` - -### Crear Licencia para Tenant -```bash -curl -X POST http://localhost:8000/v1/licenses \ - -H "Authorization: Bearer $TOKEN" \ - -H "Content-Type: application/json" \ - -d '{ - "tenant_id": 2, - "plan": "basic", - "max_users": 20, - "max_storage_gb": 50, - "max_monthly_operations": 10000, - "starts_at": "2025-10-17T00:00:00Z", - "expires_at": "2026-10-17T23:59:59Z" - }' -``` - -## Troubleshooting - -### Error: "Connection refused" al llamar API -- Verificar que el backend esté corriendo: `docker-compose ps` -- Ver logs: `docker-compose logs backend` -- Reiniciar: `docker-compose restart backend` - -### Error: "Tenant ID not found in token" -- Verificar que el usuario en Keycloak tenga el atributo `tenant_id` configurado -- Verificar que el mapper de Keycloak esté configurado correctamente - -### Frontend muestra "Cargando" indefinidamente -- Abrir consola del navegador (F12) y revisar errores -- Verificar que Keycloak esté accesible: http://localhost:8080 -- Verificar configuración en `frontend/.env` - -### Base de datos vacía -- Ejecutar script de inicialización: - ```bash - docker-compose exec backend python init_db.py - ``` - -### Keycloak no responde -- Esperar unos minutos (puede tardar en iniciar) -- Ver logs: `docker-compose logs keycloak` -- Reiniciar: `docker-compose restart keycloak` - -## Limpiar y Reiniciar - -### Detener todo -```bash -docker-compose down -``` - -### Detener y eliminar volúmenes (borra BD) -```bash -docker-compose down -v -``` - -### Reiniciar desde cero -```bash -docker-compose down -v -./start.sh -``` - -## Checklist de Verificación - -- [ ] Backend responde en http://localhost:8000 -- [ ] Frontend carga en http://localhost:5173 -- [ ] Keycloak accesible en http://localhost:8080 -- [ ] Documentación API visible en http://localhost:8000/docs -- [ ] Login funciona correctamente -- [ ] Dashboard muestra información de usuario -- [ ] Dashboard muestra información de licencia -- [ ] Logout funciona correctamente -- [ ] API responde a peticiones con token válido -- [ ] API rechaza peticiones sin token -- [ ] Base de datos tiene tenant y licencia de prueba - -## Próximos Pasos - -Una vez que todas las pruebas pasen: - -1. ✅ Revisar la documentación en `docs/ARCHITECTURE.md` -2. ✅ Explorar el código fuente de los módulos -3. ✅ Personalizar configuración según necesidades -4. ✅ Comenzar a desarrollar módulos adicionales -5. ✅ Configurar ambiente de producción - ---- - -**¿Problemas?** Revisa los logs con `docker-compose logs -f` o abre un issue. - -**¡Todo funciona!** 🎉 Estás listo para desarrollar sobre Anexo76. diff --git a/a76.json b/docs/a76.json similarity index 100% rename from a76.json rename to docs/a76.json diff --git a/frontend/project.inlang/.gitignore b/frontend/project.inlang/.gitignore new file mode 100644 index 00000000..5e465967 --- /dev/null +++ b/frontend/project.inlang/.gitignore @@ -0,0 +1 @@ +cache \ No newline at end of file diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte index 8daa24a6..489c6762 100644 --- a/frontend/src/routes/+layout.svelte +++ b/frontend/src/routes/+layout.svelte @@ -1,6 +1,6 @@ @@ -18,7 +18,7 @@ -{#if initialized && !$authStore.isLoading} +{#if initialized} {@render children?.()} {:else}
diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index 4ddc66d0..2e82cdf7 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -1,16 +1,17 @@ - -
-
-
-

Procesando autenticación...

-
-
diff --git a/frontend/src/routes/login/+page.svelte b/frontend/src/routes/login/+page.svelte new file mode 100644 index 00000000..8f5600f7 --- /dev/null +++ b/frontend/src/routes/login/+page.svelte @@ -0,0 +1,120 @@ + + +
+
+
+

+ Anexo76 +

+

+ Inicia sesión en tu cuenta +

+
+ +
+ {#if error} +
+
+
+

+ {error} +

+
+
+
+ {/if} + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ +
+ +
+

+ Credenciales de prueba:
+ Usuario: demo | Contraseña: demo123 | Tenant: aduanasoft +

+
+
+
+
diff --git a/frontend/src/routes/register/+page.svelte b/frontend/src/routes/register/+page.svelte new file mode 100644 index 00000000..54e4254e --- /dev/null +++ b/frontend/src/routes/register/+page.svelte @@ -0,0 +1,237 @@ + + +
+
+ +
+

Crear cuenta

+

+ ¿Ya tienes una cuenta? + + Inicia sesión + +

+
+ + +
+
+
+ +
+ + +
+ + +
+ + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +
+ + +
+ + + {#if passwordError} +

{passwordError}

+ {/if} +
+ + +
+ + +
+ + + {#if error} +
+

{error}

+
+ {/if} + + +
+ + +
+
+ + +
+

+ Al registrarte, aceptas nuestros términos de servicio y política de privacidad. +

+
+
+
+
+
diff --git a/start.sh b/start.sh index 4f83cbad..b70ca6b9 100755 --- a/start.sh +++ b/start.sh @@ -92,7 +92,7 @@ echo "" # 3. Limpiar contenedores previos si existen echo -e "${BLUE}[3/7] Limpiando contenedores previos...${NC}" -docker-compose down -v 2>/dev/null || true +docker-compose down 2>/dev/null || true echo -e "${GREEN}✓ Contenedores previos limpiados${NC}" echo ""