Appearance
Arquitectura Backend - Intranet Despertares (Híbrida)
Resumen del Modelo Híbrido
| Ubicación | Componentes | Responsabilidad |
|---|---|---|
| AWS (nube) | S3, SES, Cognito | Almacenamiento, correos, autenticación |
| Servidor (VPS/on‑premise) | API + Base de datos | Lógica de negocio, persistencia |
Solo tres servicios en AWS: S3, SES y Cognito. El resto (API y base de datos) corre en un servidor propio. Backend en Java (Spring Boot).
Stack Tecnológico
| Capa | Tecnología | Dónde corre | Propósito |
|---|---|---|---|
| API | Spring Boot 3 + Spring Web (MVC) | Servidor | REST, controladores, seguridad |
| Lenguaje | Java 17+ | - | - |
| Validación | Bean Validation (Jakarta) | Servidor | Validación de DTOs |
| Base de datos | PostgreSQL | Servidor | Datos relacionales |
| ORM | Spring Data JPA / Hibernate | Servidor | Entidades y repositorios |
| Migraciones | Flyway | Servidor | Cambios de schema versionados (desde el inicio) |
| Autenticación | AWS Cognito | AWS | JWT; Spring Security valida el token |
| Storage | AWS S3 | AWS | AWS SDK for Java 2.x, signed URLs |
| AWS SES | AWS | AWS SDK for Java 2.x | |
| Compresión imágenes | Thumbnailator / ImageIO | Servidor | Comprimir antes de subir a S3 |
Requisitos del servidor que alojará el backend
El servidor corre API Java (Spring Boot) y PostgreSQL (mismo equipo o separados). Dimensionado para una intranet escolar (centenares de usuarios, no millones).
Carga esperada (referencia)
| Métrica | Estimación |
|---|---|
| Usuarios concurrentes pico | 50 – 150 |
| Requests/minuto pico | 200 – 500 |
| Uso más pesado | Compresión de imágenes (Thumbnailator), reportes, envío de correos |
| Base de datos | Tablas estándar (usuarios, notas, noticias, pagos); crecimiento moderado |
Especificaciones recomendadas
| Recurso | Mínimo (apretado) | Recomendado | Cómodo |
|---|---|---|---|
| vCPUs | 1 | 2 | 4 |
| RAM | 2 GB | 4 GB | 8 GB |
| Disco | 40 GB SSD | 60–80 GB SSD | 100+ GB SSD |
| Red | 100 Mbps | 100 Mbps | 1 Gbps |
| OS | Ubuntu 22.04 LTS | Ubuntu 22.04 LTS | Ubuntu 22.04 LTS |
Cómo se usa cada recurso
| Componente | Uso aproximado |
|---|---|
| API Java (Spring Boot) | ~512 MB – 1.2 GB RAM (JVM); 1–2 vCPUs. |
| PostgreSQL | ~512 MB – 1.5 GB RAM según datos y conexiones. 1 vCPU suficiente; disco para datos + WAL + logs. |
| Sistema + logs | ~500 MB RAM; 5–10 GB disco para OS, 10–20 GB para logs y backups locales. |
Servidor único (API + PostgreSQL en la misma máquina)
- Recomendado: 2 vCPU, 4 GB RAM, 60–80 GB SSD.
- Mínimo viable: 1 vCPU, 2 GB RAM, 40 GB SSD (suficiente para empezar; monitorear y escalar si hace falta).
API y base de datos en servidores separados
| Rol | vCPU | RAM | Disco |
|---|---|---|---|
| Servidor API | 1–2 | 2 GB | 20–40 GB SSD |
| Servidor PostgreSQL | 1–2 | 2 GB | 40–60 GB SSD |
Requisitos adicionales (software / red)
| Requisito | Detalle |
|---|---|
| SO | Linux (Ubuntu 22.04 LTS recomendado). |
| Runtime API | Java 17+ (OpenJDK). |
| PostgreSQL | 15 o 16. |
| Puertos | 22 (SSH), 80/443 (API o detrás de Nginx/Caddy), 5432 solo local o VPN si DB en mismo host. |
| Salida a internet | HTTPS para AWS (Cognito, S3, SES) y SUNAT. |
| Conexión estable | No hace falta ancho de banda alto; estabilidad y baja latencia sí. |
Ejemplos de ofertas (referencia de precio)
| Proveedor | Plan típico | Especificaciones | Precio aprox./mes |
|---|---|---|---|
| DigitalOcean | Basic Droplet | 2 vCPU, 4 GB RAM, 80 GB SSD | ~24 USD |
| Hetzner | CX22 | 2 vCPU, 4 GB RAM, 40 GB SSD | ~6 EUR |
| Linode | Shared 4GB | 2 vCPU, 4 GB RAM, 80 GB SSD | ~24 USD |
| AWS EC2 | t3.medium | 2 vCPU, 4 GB RAM | ~30 USD + almacenamiento |
| Vultr | Cloud Compute | 2 vCPU, 4 GB RAM, 80 GB SSD | ~24 USD |
Para este proyecto, 2 vCPU, 4 GB RAM, 60–80 GB SSD en un VPS es un buen equilibrio coste/rendimiento.
Entornos: dev, staging, prod
Tres entornos bien definidos desde el inicio. Cada uno tiene su perfil Spring, su base de datos (o schema) y su URL pública si aplica.
| Entorno | Propósito | Perfil Spring | Base de datos | URL típica | Rama / Origen |
|---|---|---|---|---|---|
| dev | Desarrollo local e integración | dev | despertares_dev (local o compartida) | http://localhost:8080 | develop o local |
| staging | Pruebas preproducción, UAT | staging | despertares_staging (servidor staging) | https://staging.despertares.edu.pe | staging o tag |
| prod | Usuarios reales | prod | despertares (servidor producción) | https://despertares.edu.pe | main + tag o release |
Reglas por entorno
| Aspecto | dev | staging | prod |
|---|---|---|---|
| Datos | Datos de prueba o seed; se puede borrar/restaurar | Copia sanitizada o subset de prod; no datos reales sensibles | Datos reales; solo cambios controlados |
| Logs | Nivel DEBUG permitido; stack traces completos | INFO; sin volcar datos sensibles | WARN/ERROR; sin PII en logs |
| Secrets | .env local o defaults; nunca commitear | Variables de entorno o vault | Solo variables de entorno o vault; rotación documentada |
| Migraciones | Flyway aplica al arrancar; se pueden resetear DB | Flyway aplica en deploy; no ejecutar DDL manual | Flyway aplica en deploy; cambios solo vía migraciones versionadas |
| Backups | Opcional (ej. antes de refactors) | Diarios; retención 7–14 días | Diarios; retención ≥30 días; pruebas de restore |
| Deploy | Local o CI en cada push a develop | Automático desde staging o manual con confirmación | Solo con aprobación; desde main o tag |
| Cognito / S3 / SES | Mismo User Pool de dev o pool separado; buckets/dominios de dev | Pool y buckets de staging (no mezclar con prod) | Pool y buckets exclusivos de producción |
Variables y configuración
- dev:
SPRING_PROFILES_ACTIVE=dev;DATABASE_URL,DB_USER,DB_PASSWORD(o defaults enapplication-dev.yml); credenciales AWS de dev si aplica. - staging / prod: Todas las config sensibles vía variables de entorno (o secret manager). No dejar URLs de prod en código; usar
${COGNITO_USER_POOL_ID},${AWS_S3_BUCKET}, etc., enapplication-staging.ymlyapplication-prod.yml.
Buenas prácticas (nivel senior)
Criterios que el equipo debe seguir para mantener código mantenible, seguro y desplegable.
Arquitectura y diseño
| Práctica | Descripción |
|---|---|
| Capas claras | Controller → Service → Repository. Controllers delgados (validación, delegación); lógica de negocio en servicios; acceso a datos en repositorios. |
| DTOs para API | No exponer entidades JPA directamente. Request/Response DTOs con Bean Validation; mapeo con MapStruct o manual en servicio. |
| Transacciones explícitas | @Transactional en capa de servicio, con ámbito bien definido (readOnly cuando solo lectura). |
| Manejo de errores centralizado | @ControllerAdvice + excepciones de negocio; respuestas HTTP y mensajes consistentes; no filtrar stack traces en prod en respuestas al cliente. |
| Seguridad | Spring Security con JWT de Cognito; roles en el token; @PreAuthorize o filtros por rol; CORS configurado por entorno; sin credenciales en código ni en logs. |
Base de datos
| Práctica | Descripción |
|---|---|
| Solo Flyway para schema | Sin ddl-auto: create/update en ningún entorno. Todo cambio de tablas/índices vía migraciones versionadas (V1__, V2__, etc.). |
| Migraciones reversibles | Donde sea posible, migración “down” o script de rollback documentado para cambios destructivos. |
| Índices y consultas | Revisar queries lentas; índices para filtros y FKs usados en búsquedas; evitar N+1 (fetch joins, DTOs con proyecciones). |
Código y pruebas
| Práctica | Descripción |
|---|---|
| Nomenclatura | Nombres claros; paquetes por contexto (auth, backoffice, admin, teacher, parent, student); convenciones de Java/Spring. |
| Tests | Unit tests en servicios (lógica crítica); tests de integración para controllers y repositorios; mocks para AWS y externos en tests. |
| Sin lógica en controllers | Controllers solo reciben request, validan (Bean Validation), llaman al servicio y devuelven DTO. |
| Logging | Logs estructurados si es posible; nivel por perfil (dev/staging/prod); no loguear contraseñas ni tokens. |
Operaciones y despliegue
| Práctica | Descripción |
|---|---|
| Health checks | Endpoint /actuator/health (y si aplica readiness/liveness) para orquestación y monitoreo. |
| Config por entorno | Un perfil por entorno; secretos fuera del repo; documentar variables necesarias en README o en docs. |
| Backups antes de cambios | En staging y prod, backups de BD antes de desplegar migraciones grandes o cambios críticos. |
| Documentación | README con cómo levantar dev; documentar migraciones y proceso de backup/restore. |
Patrones nivel senior: cache y event bus
| Patrón | Uso en este backend | Detalle |
|---|---|---|
| Cache (Caffeine) | Listado público de noticias aprobadas (GET /public/news) | Reduce lecturas a BD en la landing. TTL 5 min; máximo 100 entradas por página. Se invalida al aprobar o rechazar una noticia. Config: CacheConfig, PublicNewsService con @Cacheable. |
| Event bus (in-process) | Desacoplar efectos secundarios del flujo principal | Spring ApplicationEventPublisher + @EventListener. Eventos: GradePostedEvent (al registrar/actualizar nota), NewsApprovedEvent (al aprobar noticia). Listeners en DomainEventListeners: hoy solo log; se puede extender a envío de email al padre, auditoría, etc. Sin colas externas (RabbitMQ/Kafka) para mantener el alcance “solo 3 servicios AWS”. |
| Backup BD → S3 | No es responsabilidad del backend | El backend no ejecuta pg_dump ni sube a S3. Los scripts backup-db.sh / restore-db.sh generan o consumen archivos locales; la programación (cron) y la subida a S3 se configuran en infraestructura. Ver OPERACION_BACKUP_RESTORE.md. |
Migraciones y backups (desde el inicio)
Implementar migraciones versionadas y estrategia de backups desde el primer día; evita deuda técnica y pérdida de datos.
Migraciones con Flyway
- Herramienta: Flyway (integrado con Spring Boot).
- Ubicación:
src/main/resources/db/migration/. - Nomenclatura:
V{número}__descripción_corta.sql(ej.V1__create_users_and_roles.sql,V2__add_news_table.sql). - Ejecución: Al arrancar la aplicación, Flyway aplica las migraciones pendientes en orden. En todos los entornos (dev, staging, prod) el schema se gestiona solo con Flyway; no usar
ddl-auto: createniupdate. - Buenas prácticas:
- Migraciones idempotentes cuando sea posible (ej.
CREATE TABLE IF NOT EXISTSsolo si Flyway no tiene control de checksums y se requiere; en general Flyway controla por versión). - No modificar un script ya aplicado; para cambios posteriores, crear una nueva migración (V3, V4, …).
- En cambios destructivos (DROP COLUMN, DROP TABLE), documentar en el script o en un README el impacto y, si existe, el procedimiento de rollback manual.
- Migraciones idempotentes cuando sea posible (ej.
Estrategia de backups (desde el inicio)
| Entorno | Frecuencia | Retención | Destino | Notas |
|---|---|---|---|---|
| dev | Bajo demanda (antes de refactors) | Según necesidad | Local o carpeta compartida | Opcional. |
| staging | Diario (cron) | 7–14 días | Servidor o almacenamiento externo (S3, NFS) | Permite restaurar staging para reproducir fallos. |
| prod | Diario (cron) + pre-deploy si hay migraciones | Mínimo 30 días | Almacenamiento externo (S3, otro servidor) | Crítico; cifrado y permisos restringidos. |
Contenido del backup
- Base de datos PostgreSQL:
pg_dump(formato custom-Fcpara restores selectivos, o SQL plano para inspección). - No incluir en el backup de BD: archivos subidos a S3 (S3 tiene su propia versión y políticas); solo la BD.
Comandos de referencia (backup/restore)
Backup (ejemplo):
bash
# Backup completo, formato custom (recomendado para restore)
pg_dump -h localhost -U postgres -Fc -f despertares_$(date +%Y%m%d_%H%M%S).dump despertares
# Subir a S3 o copiar a servidor de backups
aws s3 cp despertares_*.dump s3://bucket-backups/despertares/Restore (solo cuando sea necesario, con precaución):
bash
# Detener la aplicación que usa la BD
pg_restore -h localhost -U postgres -d despertares_restore --clean --if-exists despertares_YYYYMMDD.dump
# Revisar integridad antes de apuntar la aplicación a la BD restauradaAutomatización y documentación
- Scripts: Incluir en el repo (ej.
infra/scripts/backup-db.sh,restore-db.sh) con instrucciones en el README o en esta doc. - Cron (staging/prod): Ejemplo diario a las 02:00:
0 2 * * * /ruta/backup-db.sh. - Pruebas de restore: Al menos una vez por trimestre en staging (o tras cambios importantes), restaurar un backup y validar que la aplicación arranca y los datos son coherentes.
- Migraciones + backups: Antes de desplegar una migración que borre columnas o tablas, hacer backup explícito de prod y documentar el punto de restauración.
Con esto, migraciones y backups quedan definidos desde el inicio y listos para aplicar en dev, staging y prod.
Arquitectura General
┌─────────────────────────────────────────────────────────────────────────┐
│ CLIENTES │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Landing │ │ Backoffice │ │ Intranet │ │ Intranet │ │
│ │ (Public) │ │ (Admin) │ │ (Padres) │ │ (Profesores)│ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
└─────────┼─────────────────┼─────────────────┼─────────────────┼─────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Servidor (VPS / On‑premise / EC2) │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ API Java (Spring Boot) │ │
│ │ • REST /api/* • Spring Security (JWT Cognito) • Lógica negocio │ │
│ │ • Compresión imágenes (Thumbnailator) antes de subir a S3 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ PostgreSQL │ │
│ │ • Usuarios, alumnos, profesores, notas, noticias, pagos, etc. │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
│ │ │
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ AWS Cognito │ │ AWS S3 │ │ AWS SES │
│ (Auth) │ │ (Archivos) │ │ (Correos) │
│ │ │ │ │ │
│ • User Pool │ │ • Bucket │ │ • Envío │
│ • Login alias │ │ privado │ │ transaccional │
│ • JWT │ │ • Signed URLs │ │ • @despertares │
│ • Triggers │ │ • Compresión │ │ .edu.pe │
│ │ │ en servidor │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘Qué va en AWS (solo 3 servicios)
1. Cognito
- User Pool con login por alias (preferred_username).
- Grupos: ADMIN, EDITOR, TEACHER, PARENT, STUDENT.
- Triggers opcionales: PreSignUp, PostConfirmation, PreTokenGeneration (para sincronizar con PostgreSQL si se desea).
- La API en el servidor solo verifica el JWT con el public key de Cognito; no aloja usuarios.
2. S3
- Un bucket privado para archivos e imágenes.
- Acceso desde la API vía SDK (IAM o access key del servidor).
- El frontend recibe signed URLs generadas por la API (no acceso directo al bucket).
- Compresión de imágenes hecha en el servidor (Thumbnailator/ImageIO) antes de
PutObject.
3. SES
- Dominio verificado
@despertares.edu.pe. - Envío de correos transaccionales (notificaciones, recuperación de contraseña, etc.).
- La API usa el SDK de SES desde el servidor con credenciales IAM o SMTP.
Qué va en el servidor
API (Java + Spring Boot)
- Una sola aplicación Spring Boot que sirve todas las rutas
/api/*. - Spring Security: validar JWT de Cognito (Resource Server o filtro con public key).
- Controladores organizados por contexto: auth, backoffice, admin, teacher, parent, student.
- Subida de archivos:
MultipartFile→ comprimir con Thumbnailator/ImageIO → subir a S3 → guardar referencia en PostgreSQL. - Configuración por perfiles (
application-dev.yml,application-staging.yml,application-prod.yml) para cada entorno.
Base de datos (PostgreSQL)
- Entidades de negocio: usuarios (vinculados a
cognito_id), estudiantes, profesores, padres, cursos, clases, notas, asistencia, noticias, pagos, archivos, notificaciones, etc. - Migraciones con Flyway desde el primer día (ver sección Migraciones y backups).
- Servidor: VPS, EC2 u on‑premise.
Estructura de Carpetas (Backend Java - Buenas prácticas)
Estructura tipo capas y paquetes por dominio/contexto, alineada con prácticas senior (separación de responsabilidades, testabilidad).
despertares-intranet/
│
├── backend/ # API Java (Spring Boot)
│ ├── pom.xml # o build.gradle.kts
│ ├── Dockerfile
│ ├── src/
│ │ ├── main/
│ │ │ ├── java/pe/edu/despertares/intranet/
│ │ │ │ ├── IntranetApplication.java
│ │ │ │ │
│ │ │ │ ├── config/ # Configuración (Security, S3, SES, CORS)
│ │ │ │ ├── security/ # JWT Cognito, filtros, roles
│ │ │ │ ├── common/ # Excepciones globales, DTOs base, constantes
│ │ │ │ │
│ │ │ │ ├── auth/ # Contexto: autenticación
│ │ │ │ │ ├── controller/
│ │ │ │ │ ├── service/
│ │ │ │ │ ├── repository/
│ │ │ │ │ └── domain/
│ │ │ │ ├── backoffice/
│ │ │ │ ├── admin/
│ │ │ │ ├── teacher/
│ │ │ │ ├── parent/
│ │ │ │ └── student/
│ │ │ │
│ │ │ └── resources/
│ │ │ ├── application.yml
│ │ │ ├── application-dev.yml
│ │ │ ├── application-staging.yml
│ │ │ ├── application-prod.yml
│ │ │ └── db/migration/ # Flyway (V1__...sql, V2__...sql)
│ │ └── test/
│ │ └── java/... # Tests unitarios e integración
│ │
│ ├── infra/ # IaC AWS (Terraform o SST)
│ │ ├── terraform/
│ │ │ ├── main.tf
│ │ │ ├── cognito.tf
│ │ │ ├── s3.tf
│ │ │ └── ses.tf
│ │ └── scripts/
│ │ ├── backup-db.sh
│ │ └── restore-db.sh
│ │
│ └── web/ # Frontend React (si monorepo)
│ └── ...
│
└── docs/
├── COBERTURA_INTRANET_LMS.md
├── ARQUITECTURA_BACKEND.md
└── GUIA_DESPLIEGUE.md- Perfiles Spring:
spring.profiles.active=dev|staging|prodsegún entorno. - Flyway: scripts en
src/main/resources/db/migration/; se ejecutan al arrancar la aplicación. - Tests: mismo paquete que el código (
*Test,*ITpara integración).
Flujos Principales
Autenticación (Cognito + API): cómo está el login ahora
Hay dos formas de que el usuario obtenga un JWT; la que tienes configurada (Managed login + dominio propio) es la opción 1.
Opción 1: Managed login (Hosted UI) — la que tienes ahora
El login ocurre en la pantalla de Cognito en https://auth.despertares.edu.pe. El frontend nunca ve la contraseña; solo redirige al usuario y luego recibe un código (o tokens) por redirect.
Flujo paso a paso:
- Usuario hace clic en “Iniciar sesión” en tu app (ej.
https://app.despertares.edu.peohttp://localhost:5173). - El frontend redirige al navegador a la URL de autorización de Cognito, por ejemplo:
https://auth.despertares.edu.pe/oauth2/authorize?response_type=code&client_id=<CLIENT_ID>&redirect_uri=https://app.despertares.edu.pe/login/callback&scope=openid+email+profile&state=<random>redirect_uridebe ser exactamente una de las Callback URL(s) del App client (por eso las configuramos en el Paso 1).
- El usuario ve la pantalla de Cognito (Managed login) en
auth.despertares.edu.pe: introduce alias y contraseña. Solo se permiten correos @despertares.edu.pe (Pre-sign-up Lambda). - Cognito valida y redirige de vuelta a tu app a la callback URL con un código en la query, por ejemplo:
https://app.despertares.edu.pe/login/callback?code=abc123...&state=... - El frontend (en la ruta
/login/callback) lee elcodey lo intercambia por tokens llamando al token endpoint de Cognito (desde el frontend o desde tu backend):Cognito devuelvePOST https://auth.despertares.edu.pe/oauth2/token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code&code=abc123...&client_id=<CLIENT_ID>&redirect_uri=https://app.despertares.edu.pe/login/callbackaccess_token,id_token,refresh_tokenyexpires_in. - El frontend guarda los tokens (memoria, sessionStorage o cookie segura) y ya considera al usuario “logueado”.
- En cada petición a tu API, el frontend envía:
Authorization: Bearer <access_token> - La API (Spring Boot) no hace login: solo valida el JWT con la clave pública de Cognito (JWK Set de
auth.despertares.edu.peo del User Pool), leesub,custom:userId,custom:role(si los tienes) y autoriza el request.
Resumen lógico:
- Login = redirigir a Cognito → usuario pone alias/contraseña en Cognito → redirect a tu callback con
code→ frontend cambiacodepor tokens. - API = recibe
Authorization: Bearer <token>, valida JWT y usa los claims para saber quién es el usuario y su rol.
Opción 2: Login “directo” (formulario en tu app)
El usuario escribe alias y contraseña en tu propia página; el frontend (o el backend) llama a Cognito con InitiateAuth (flujo USER_PASSWORD_AUTH) y recibe los tokens. La contraseña pasa por tu frontend (o por tu backend si el login lo hace la API). No hay redirect a auth.despertares.edu.pe.
- Si lo hace el frontend: usa el SDK de Cognito (ej.
amazon-cognito-identity-jso Amplify Auth) consignIn(alias, password). - Si lo hace el backend: el frontend envía alias + contraseña a
POST /api/auth/login; la API llama a CognitoInitiateAuthy devuelve los tokens al frontend.
En ambos casos, la API sigue igual: solo valida el JWT en las peticiones protegidas; no hace el “login” por el usuario.
Qué tienes configurado (resumen)
| Elemento | Estado |
|---|---|
| Dominio de login | https://auth.despertares.edu.pe (Managed login) |
| Quién pide alias/contraseña | Cognito (pantalla de Managed login) |
| Callback tras login | Redirect a la URL que pusiste en “Callback URL(s)” (ej. .../login/callback) con ?code=... |
| Restricción de correo | Solo @despertares.edu.pe (Pre-sign-up) |
| Rol de la API | Solo validar JWT; no aloja la pantalla de login ni hace InitiateAuth (salvo que añadas opción 2) |
Subida de archivos (S3)
- El cliente envía el archivo a la API (multipart).
- La API comprime con Thumbnailator/ImageIO y sube a S3 con AWS SDK.
- La API guarda en PostgreSQL la clave del objeto (ej.
uploads/2025/xxx.jpg). - Para descargar, el cliente pide a la API una signed URL; la API la genera con S3 Presigner y la devuelve.
Envío de correos (SES)
- La API usa AWS SES (SDK) desde el servidor con credenciales configuradas (env o IAM).
- Dominio verificado
@despertares.edu.pe. - Uso: notificaciones, recuperación de contraseña (si se hace vía API), citaciones, etc.
Seguridad y datos sensibles (cumplimiento de estándares)
La intranet maneja datos sensibles (notas, asistencia, datos de menores, pagos). A continuación se resume cómo el diseño actual cumple con prácticas de seguridad habituales y qué se recomienda reforzar en producción.
Lo que ya cumple el diseño actual
| Área | Práctica / estándar | Cómo se cumple en este proyecto |
|---|---|---|
| Transporte | Cifrado en tránsito (TLS) | Login en HTTPS (auth.despertares.edu.pe con certificado ACM). API y frontend en HTTPS en staging y producción. Comunicación con AWS (Cognito, S3, SES) por HTTPS. |
| Autenticación | Contraseñas no expuestas en la app | Con Managed login, la contraseña solo se introduce en la página de Cognito; el frontend nunca la recibe. La API no procesa contraseñas; solo valida JWT. |
| Contraseñas | Política robusta | Cognito: mínimo 8 caracteres, mayúscula, minúscula y número. Recuperación por email verificado. |
| Acceso restringido | Solo usuarios del dominio | Pre-sign-up Lambda: solo se permite registro con correo @despertares.edu.pe. |
| Autorización | Roles y permisos | Grupos Cognito (ADMIN, EDITOR, TEACHER, PARENT, STUDENT). API valida JWT y aplica control por rol (@PreAuthorize o filtros). |
| Secrets | No en código ni en repo | Contraseñas de BD, credenciales AWS y tokens se inyectan por variables de entorno (o vault). No commitear .env ni archivos con secretos. Terraform: no credenciales en .tf ni en .tfvars commitados. |
| Almacenamiento (S3) | Cifrado y acceso privado | Bucket privado, Block Public Access activado, encriptación SSE-S3 en reposo. Acceso solo vía API (IAM/signed URLs). |
| Logs | No registrar datos sensibles | En prod: nivel WARN/ERROR; no loguear contraseñas, tokens ni PII. En staging: INFO sin volcar datos sensibles. |
| CORS | Origen controlado | CORS configurado por entorno; solo orígenes permitidos (app.despertares.edu.pe, sin wildcard abierto en prod). |
| Callbacks OAuth | URLs explícitas | Callback y sign-out URLs registradas en el App client; en producción usar solo URLs HTTPS de tu dominio (no localhost). |
Recomendaciones adicionales para producción (datos sensibles)
| Recomendación | Acción |
|---|---|
| PKCE en el flujo OAuth | Usar PKCE (code_verifier / code_challenge) en el flujo authorization code desde el frontend (SPA). Cognito lo soporta; reduce el riesgo si el código de autorización fuera interceptado. |
| Almacenamiento de tokens en el frontend | Preferir cookies HttpOnly, Secure, SameSite (o un proxy en el backend que guarde el token en cookie) para access/refresh en lugar de localStorage, para mitigar XSS. Si se usan tokens en memoria/sessionStorage, limitar superficie de XSS (CSP, sanitización). |
| MFA (opcional) | Para roles con acceso muy sensible (ej. ADMIN), valorar MFA en Cognito (TOTP o SMS) como capa adicional. |
| Callbacks en producción | En prod, en el App client de Cognito no incluir http://localhost en Callback URL(s). Solo URLs HTTPS de tu dominio (ej. https://app.despertares.edu.pe/login/callback). |
| Rate limiting | En la API, aplicar límite de peticiones por IP o por usuario (ej. filtro o gateway) para mitigar abuso y fuerza bruta en endpoints sensibles. |
| Base de datos | Cifrado en reposo del volumen donde corre PostgreSQL (muchos proveedores VPS/cloud lo ofrecen). Backups cifrados y acceso restringido. |
| Headers de seguridad | En la API y en el frontend: HSTS, X-Content-Type-Options, Content-Security-Policy (CSP) según lo que permita tu stack. |
Resumen de confirmación
- Sí, el diseño actual está alineado con estándares de seguridad habituales para una intranet con datos sensibles: transporte cifrado, autenticación delegada en Cognito sin manejo de contraseñas en la API, autorización por roles, secretos fuera del código, almacenamiento S3 cifrado y privado, y logs sin datos sensibles.
- Para endurecer en producción, aplicar las recomendaciones de la tabla anterior (PKCE, almacenamiento seguro de tokens, callbacks solo HTTPS en prod, rate limiting, cifrado de BD y backups, headers de seguridad).
Orden de Desarrollo (Backend Java)
Fase 1: Fundamentos (Semana 1–2)
| # | Tarea | Dónde | Tiempo |
|---|---|---|---|
| 1.1 | Proyecto Spring Boot 3 + perfiles (dev/staging/prod) | backend | 2h |
| 1.2 | PostgreSQL + Flyway: primera migración (V1__init.sql) | backend | 3h |
| 1.3 | Cognito en AWS (User Pool, alias, grupos) | infra | 4h |
| 1.4 | Spring Security + validación JWT Cognito | backend | 4h |
| 1.5 | Controladores auth (login/refresh vía Cognito, logout) | backend | 4h |
| 1.6 | Entidad User + sincronización con Cognito (opcional) | backend + migración | 3h |
Fase 2: Storage y Email (Semana 2–3)
| # | Tarea | Dónde | Tiempo |
|---|---|---|---|
| 2.1 | S3 bucket privado en AWS | infra | 1h |
| 2.2 | Cliente S3 en API + signed URLs | backend | 3h |
| 2.3 | Subida de archivos + compresión (Thumbnailator) | backend | 4h |
| 2.4 | SES dominio + envío desde API | infra + backend | 3h |
Fase 3: Backoffice y Admin (Semana 3–4)
| # | Tarea | Dónde | Tiempo |
|---|---|---|---|
| 3.1 | CRUD noticias, aprobaciones (migraciones Flyway) | backend | 6h |
| 3.2 | Notificaciones y gestión de imágenes | backend | 4h |
| 3.3 | Panel admin: usuarios, pagos, horarios | backend | 8h |
| 3.4 | Integración SUNAT (si aplica) | backend | 8h |
Fase 4: Portales (Semana 4–6)
| # | Tarea | Dónde | Tiempo |
|---|---|---|---|
| 4.1 | Rutas profesor (clases, notas, asistencia, archivos) | backend | 12h |
| 4.2 | Rutas padre (hijos, notas, calendario, agenda) | backend | 8h |
| 4.3 | Rutas estudiante (notas, tareas, archivos, Corefo iframe) | backend | 6h |
Infraestructura AWS (solo S3, SES, Cognito)
Repositorio: La IaC puede vivir en un repositorio separado (ej. despertares-intranet-infra) para aislar state, permisos y ciclos de vida. Ver docs/GUIA_DESPLIEGUE.md para despliegue en AWS y servidor.
Opción A: Terraform
- Ventaja: Un solo lenguaje de IaC si ya lo usan para el servidor o redes.
- Alcance: Tres módulos:
cognito.tf,s3.tf,ses.tf. Sin Lambda, sin API Gateway, sin DynamoDB.
Opción B: SST (solo recursos AWS)
- Ventaja: Rápido para solo 3 servicios (Cognito, S3, SES).
- Alcance: Un único
sst.config.tsque declare User Pool, bucket S3 y configuración SES (verificación de dominio suele hacerse manual o con un recurso Route53 si el DNS está en AWS).
Configuración por entorno (Spring profiles)
Cada entorno usa su perfil y su propio archivo de propiedades. No commitear secretos; usar variables de entorno o un vault en prod.
application.yml (común):
yaml
spring:
application:
name: despertares-intranet-api
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev}
flyway:
enabled: true
jpa:
hibernate:
ddl-auto: validate # Nunca create/update; solo Flywayapplication-dev.yml (desarrollo local):
yaml
server:
port: 8080
spring:
datasource:
url: jdbc:postgresql://localhost:5432/despertares_dev
username: ${DB_USER:postgres}
password: ${DB_PASSWORD:postgres}
logging:
level:
pe.edu.despertares: DEBUG
org.hibernate.SQL: DEBUGapplication-staging.yml (staging):
yaml
spring:
datasource:
url: ${DATABASE_URL}
username: ${DB_USER}
password: ${DB_PASSWORD}
logging:
level:
pe.edu.despertares: INFOapplication-prod.yml (producción):
yaml
spring:
datasource:
url: ${DATABASE_URL}
username: ${DB_USER}
password: ${DB_PASSWORD}
logging:
level:
pe.edu.despertares: WARN
root: WARNVariables por entorno (Cognito, S3, SES, DB) se inyectan vía env (ej. COGNITO_USER_POOL_ID, AWS_S3_BUCKET, AWS_REGION) y se referencian en los application-*.yml con ${VAR}.
Costos Estimados (Híbrido)
AWS (solo 3 servicios)
| Servicio | Uso | Costo/mes |
|---|---|---|
| Cognito | < 50K MAU | $0 |
| S3 | ~20 GB + requests | ~$1–2 |
| SES | ~10K correos | ~$1 |
| Total AWS | ~$2–3 |
Servidor (VPS / EC2)
- VPS 2 vCPU, 4 GB RAM: ~$20–40/mes (según proveedor).
- PostgreSQL: incluido en el mismo servidor o RDS si más adelante lo pasan a AWS.
Total estimado: ~$25–45/mes (AWS + servidor), dependiendo del proveedor del servidor.
Resumen del Plan Backend
| Tema | Decisión |
|---|---|
| En la nube (AWS) | Solo S3, SES y Cognito. |
| En servidor | API Java (Spring Boot) y PostgreSQL. |
| Auth | Cognito (login por alias); API verifica JWT con Spring Security. |
| Archivos | S3 privado; compresión en servidor (Thumbnailator); acceso por signed URLs. |
| Correos | SES desde la API. |
| Base de datos | PostgreSQL; Flyway para migraciones desde el inicio. |
| Entornos | dev, staging, prod con perfiles Spring y config específica. |
| Backups | Estrategia definida desde el inicio (ver sección Migraciones y backups). |
| IaC AWS | Terraform o SST solo para Cognito, S3 y SES. |