# Base Admin Web
Proyecto base de Spring Boot con Metronic, autenticación Cognito (o local), panel administrativo y API de escaneo para la app móvil Flutter.
Sirve como plantilla reutilizable para nuevos paneles internos: estructura modular, seguridad dual (web + API), módulo de códigos pase con QR y soporte H2/PostgreSQL.
---
## Tabla de contenidos
- [Vista general](#vista-general)
- [Stack](#stack)
- [Arquitectura del sistema](#arquitectura-del-sistema)
- [Módulos Maven](#módulos-maven)
- [Seguridad y autenticación](#seguridad-y-autenticación)
- [Cognito: local (Floci) vs AWS](#cognito-local-floci-vs-aws)
- [Módulo de códigos pase](#módulo-de-códigos-pase)
- [API REST](#api-rest)
- [App Flutter (escáner)](#app-flutter-escáner)
- [Arranque local](#arranque-local)
- [Arranque manual e IntelliJ IDEA](#arranque-manual-e-intellij-idea)
- [Perfiles Spring](#perfiles-spring)
- [Variables de entorno](#variables-de-entorno)
- [Objetivo de esta base](#objetivo-de-esta-base)
---
## Vista general
El sistema tiene **dos canales de acceso** con roles distintos:
| Canal | Usuario | Rol | Autenticación |
|-------|---------|-----|---------------|
| Panel web (Thymeleaf) | `admin` | `ROLE_ADMIN` | Sesión HTTP (form login) |
| API móvil / externa | `scanner` | `ROLE_SCANNER` | JWT Bearer (stateless) |
Por defecto ambos canales usan **Amazon Cognito** (emulado en local con Floci). Con `APP_COGNITO_ENABLED=false` se usa autenticación local contra la base de datos y JWT firmado con secreto propio.
```mermaid
flowchart TB
subgraph clients["Clientes"]
Browser["Navegador
Panel admin"]
Flutter["App Flutter
QR Scanner"]
end
subgraph app["Spring Boot — base-admin-web-jar"]
WebChain["Filter chain web
sesión + CSRF"]
ApiChain["Filter chain API
stateless + JWT"]
PassCodes["PassCodeService"]
DB[("H2 / PostgreSQL")]
end
subgraph auth["Autenticación"]
Cognito["Cognito
Floci local o AWS"]
LocalAuth["BD local + JWT HS256
APP_COGNITO_ENABLED=false"]
end
Browser -->|"/login, /admin/**"| WebChain
Flutter -->|"/api/auth/login
/api/pass-codes/scan"| ApiChain
WebChain --> PassCodes
ApiChain --> PassCodes
PassCodes --> DB
WebChain --> Cognito
ApiChain --> Cognito
WebChain -.-> LocalAuth
ApiChain -.-> LocalAuth
```
**URLs locales:**
| Recurso | URL |
|---------|-----|
| Login | http://localhost:8080/login |
| Dashboard | http://localhost:8080/dashboard |
| Códigos pase | http://localhost:8080/admin/pass-codes |
| H2 Console | http://localhost:8080/h2-console |
| Floci (Cognito local) | http://localhost:4566 |
---
## Stack
| Capa | Tecnología |
|------|------------|
| Backend | Spring Boot 4.0.6, Java 25 |
| Seguridad | Spring Security, OAuth2 Resource Server, AWS SDK Cognito |
| Vistas | Thymeleaf + Metronic (`base-web`) |
| Persistencia | JPA / Hibernate, H2 (dev), PostgreSQL (perfil `postgres`) |
| Auth | Cognito (default) o BD local + JWT HS256 |
| Móvil | Flutter (`flutter-app/qrscanner`) |
| Local AWS | [Floci](https://floci.io/floci/getting-started/quick-start/) (Cognito en puerto 4566) |
---
## Arquitectura del sistema
### Capas y dependencias
```mermaid
flowchart BT
jar["base-admin-web-jar
punto de entrada + application.yml"]
web["base-admin-web-web
controladores + Thymeleaf"]
config["base-admin-web-configuration
seguridad, JWT, Cognito"]
app["base-admin-web-application
servicios + carga inicial"]
domain["base-admin-web-domain
entidades + repositorios"]
jar --> web
jar --> config
jar --> app
jar --> domain
web --> config
web --> app
config --> app
app --> domain
```
| Módulo | Responsabilidad |
|--------|-----------------|
| `base-admin-web-domain` | Entidades JPA (`AppUser`, `PassCode`, `Role`), repositorios |
| `base-admin-web-application` | `PassCodeService`, `AppUserDetailsService`, `InitialDataLoader` |
| `base-admin-web-configuration` | `SecurityConfig`, `ApiSecurityConfig`, Cognito, `JwtConfig` |
| `base-admin-web-web` | Controladores MVC y API, DTOs, plantillas HTML |
| `base-admin-web-jar` | `BaseAdminWebApplication`, perfiles, dependencias runtime (H2, PostgreSQL) |
### Dos cadenas de seguridad
Spring Security define **dos filter chains** con distinto comportamiento:
```mermaid
flowchart LR
subgraph order1["@Order(1) — ApiSecurityConfig"]
A1["/api/**"]
A2["Stateless"]
A3["JWT Bearer"]
A4["ROLE_SCANNER en /scan"]
end
subgraph order2["@Order(2) — SecurityConfig"]
B1["Todo excepto /api/**"]
B2["Sesión HTTP"]
B3["Form login /login"]
B4["ROLE_ADMIN en /admin/**"]
end
A1 --> A2 --> A3 --> A4
B1 --> B2 --> B3 --> B4
```
| Ruta | Acceso | Mecanismo |
|------|--------|-----------|
| `/login`, `/assets/**`, `/error`, `/h2-console/**` | Público | — |
| `/dashboard`, `/admin/**` | `ROLE_ADMIN` | Sesión web |
| `/api/auth/login`, `/api/auth/refresh` | Público | Emite tokens (solo `scanner`) |
| `/api/pass-codes/scan` | `ROLE_SCANNER` | JWT Bearer |
---
## Seguridad y autenticación
### Usuarios iniciales
#### Administrador (panel web)
- Usuario: `admin` / `Admin123*`
- Grupo Cognito: `admins` → `ROLE_ADMIN`
- Acceso: login en `/login`, dashboard, módulo de códigos pase
- El usuario `scanner` **no** puede entrar al panel web
#### Escáner (API / Flutter)
- Usuario: `scanner` / `Scanner123*`
- Grupo Cognito: `scanners` → `ROLE_SCANNER`
- Acceso: `POST /api/auth/login` y `POST /api/pass-codes/scan`
- El usuario `admin` **no** puede autenticarse por API
### Flujo de login web (panel admin)
```mermaid
sequenceDiagram
actor Admin as Navegador (admin)
participant SC as SecurityConfig
participant CP as CognitoWebAuthenticationProvider
participant CA as CognitoUserAuthenticator
participant Cognito as Cognito (Floci/AWS)
Admin->>SC: POST /login (user, password)
SC->>CP: authenticate()
CP->>CA: admin client + ROLE_ADMIN
CA->>Cognito: InitiateAuth USER_PASSWORD_AUTH
Cognito-->>CA: tokens + cognito:groups
CA-->>CP: AuthenticatedWebUser (grupo admins)
CP-->>SC: sesión HTTP creada
SC-->>Admin: redirect /dashboard
```
**Clases clave:** `SecurityConfig`, `CognitoWebSecurityConfig`, `CognitoWebAuthenticationProvider`, `CognitoUserAuthenticator`, `AuthController`
Con `APP_COGNITO_ENABLED=false`, el flujo usa `DaoAuthenticationProvider` → `AppUserDetailsService` → tabla `users` (BCrypt).
### Flujo de login y escaneo API
```mermaid
sequenceDiagram
actor App as Flutter (scanner)
participant API as AuthApiController
participant AS as ApiAuthService
participant Cognito as Cognito
participant Scan as PassCodeApiController
participant Svc as PassCodeService
App->>API: POST /api/auth/login
API->>AS: login()
AS->>Cognito: scanner client + ROLE_SCANNER
Cognito-->>App: accessToken, refreshToken, idToken
App->>Scan: POST /api/pass-codes/scan
Authorization: Bearer ...
Scan->>Scan: validar JWT (JWK o HS256)
Scan->>Svc: scan(code)
Svc-->>App: código marcado UTILIZADO
```
**Clases clave:** `ApiSecurityConfig`, `AuthApiController`, `ApiAuthService`, `CognitoAuthService`, `PassCodeApiController`, `JwtConfig`
### Modo local sin Cognito
Con `APP_COGNITO_ENABLED=false` (perfil `test` lo usa automáticamente):
```mermaid
flowchart LR
subgraph web["Web"]
W1["Form login"] --> W2["AppUser en BD"]
end
subgraph api["API"]
A1["POST /api/auth/login"] --> A2["BD + JwtEncoder HS256"]
A3["POST /api/auth/refresh"] --> A4["No disponible"]
A5["Bearer token"] --> A6["localJwtDecoder HS256"]
end
```
| `APP_COGNITO_ENABLED` | Web | API login | Validación JWT | Refresh |
|----------------------|-----|-----------|----------------|---------|
| `true` (default) | Cognito client admin | Cognito client scanner | JWK Set URI | Sí |
| `false` | BD + BCrypt | BD + JWT local | Secreto HS256 | No |
Los usuarios se siembran con `InitialDataLoader` usando `APP_ADMIN_*` y `APP_SCANNER_*`.
---
## Cognito: local (Floci) vs AWS
El SDK de Cognito elige el destino según `AWS_ENDPOINT_URL`:
```mermaid
flowchart TD
Start["APP_COGNITO_ENABLED=true"]
Check{"¿AWS_ENDPOINT_URL
definido?"}
Floci["Floci localhost:4566
credenciales test/test"]
AWS["Cognito real en AWS
DefaultCredentialsProvider
IAM / perfil / env AWS"]
Start --> Check
Check -->|Sí| Floci
Check -->|No| AWS
```
### Local con Floci
`./run-local.sh` hace todo el setup:
1. Levanta Floci (y PostgreSQL si el perfil lo incluye)
2. Espera el bootstrap de Cognito (hasta 120 s)
3. Carga `docker/floci/generated/cognito.local.env` automáticamente
El script `docker/floci/init/ready.d/10-cognito-bootstrap.sh` crea:
| Recurso | Valor |
|---------|-------|
| User Pool ID | `us-east-1_BaseAdminWeb` |
| Usuario admin | `admin` / `Admin123*` → grupo `admins` |
| Usuario scanner | `scanner` / `Scanner123*` → grupo `scanners` |
| App client admin | `base-admin-web-admin` |
| App client scanner | `base-admin-web-scanner` |
Variables generadas en `docker/floci/generated/cognito.local.env` (gitignored).
**Levantar solo Floci:**
```bash
./scripts/floci-up.sh
```
**Levantar stack Docker completo:**
```bash
docker compose -f docker-compose.local.yml up -d
```
| Servicio | Puerto | Persistencia |
|----------|--------|--------------|
| Floci | 4566 | volumen `base_admin_web_floci_data` |
| PostgreSQL | 5432 | volumen `base_admin_web_postgres_data` |
### Producción con AWS Cognito
**No definir** `AWS_ENDPOINT_URL`. Configura el User Pool real:
```bash
export APP_COGNITO_ENABLED=true
export COGNITO_REGION=us-east-1
export COGNITO_USER_POOL_ID=us-east-1_XXXXX
export COGNITO_ISSUER_URI=https://cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXX
export COGNITO_JWK_SET_URI=https://cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXX/.well-known/jwks.json
export COGNITO_ADMIN_CLIENT_ID=...
export COGNITO_SCANNER_CLIENT_ID=...
# Credenciales AWS vía IAM role, perfil o AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY
```
En AWS replica la misma estructura que Floci: grupos `admins`/`scanners`, clients con `USER_PASSWORD_AUTH` habilitado.
### Verificar Cognito local
```bash
export AWS_ENDPOINT_URL=http://localhost:4566
export AWS_DEFAULT_REGION=us-east-1
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
aws cognito-idp list-user-pools --max-results 10 --endpoint-url $AWS_ENDPOINT_URL
aws cognito-idp list-users --user-pool-id us-east-1_BaseAdminWeb --endpoint-url $AWS_ENDPOINT_URL
```
---
## Módulo de códigos pase
Panel para dar de alta y consultar códigos pase con QR.
```mermaid
stateDiagram-v2
[*] --> ACTIVO: admin crea código (10 chars)
ACTIVO --> UTILIZADO: scanner escanea QR
```
| Campo | Descripción |
|-------|-------------|
| Nombre | Etiqueta descriptiva |
| Código | Aleatorio de 10 caracteres (único) |
| Estatus | `ACTIVO` / `UTILIZADO` |
| Escaneado | Indicador booleano |
| Fechas | Creación y escaneo |
**Panel web:** `GET /admin/pass-codes` — tabla Metronic, modal QR, refresco automático al cerrar el modal.
**Entidad:** `PassCode` → **Servicio:** `PassCodeService` → **Controladores:** `PassCodeController` (web), `PassCodeApiController` (API).
---
## API REST
### Login escáner
```http
POST /api/auth/login
Content-Type: application/json
{
"username": "scanner",
"password": "Scanner123*"
}
```
Respuesta (Cognito):
```json
{
"success": true,
"accessToken": "eyJhbG...",
"refreshToken": "dXMtZWFzdC0x...",
"idToken": "eyJhbG...",
"tokenType": "Bearer",
"expiresIn": 3600,
"username": "scanner",
"roles": ["ROLE_SCANNER"]
}
```
### Renovar token
```http
POST /api/auth/refresh
Content-Type: application/json
{
"refreshToken": "dXMtZWFzdC0x..."
}
```
Solo disponible con Cognito habilitado.
### Escanear código pase
```http
POST /api/pass-codes/scan
Authorization: Bearer eyJhbG...
Content-Type: application/json
{
"code": "K7P2M9XQ4R"
}
```
### Listado JSON (panel admin, requiere sesión)
```http
GET /admin/pass-codes/list
```
---
## App Flutter (escáner)
Ubicación: `flutter-app/qrscanner/`
```mermaid
flowchart LR
Login["LoginScreen"] --> Home["HomeScreen"]
Home --> Scanner["ScannerScreen"]
Scanner -->|QR detectado| API["POST /api/pass-codes/scan"]
Login -->|credenciales| Auth["POST /api/auth/login"]
```
| Archivo | Rol |
|---------|-----|
| `lib/screens/login_screen.dart` | Login y sesión |
| `lib/screens/scanner_screen.dart` | Lectura QR + escaneo |
| `lib/services/auth_service.dart` | Login, SharedPreferences |
| `lib/services/pass_code_service.dart` | Scan con Bearer token |
| `lib/config/api_config.dart` | URL base de la API |
**Ejecutar apuntando al backend local:**
```bash
cd flutter-app/qrscanner
flutter run --dart-define=API_BASE_URL=http://TU_IP:8080
```
La app valida que el usuario tenga `ROLE_SCANNER` (o grupo `scanners`) antes de permitir el escaneo.
---
## Arranque local
### Opción recomendada
```bash
./run-local.sh
```
El script:
- Detecta Java 25 (Temurin)
- Levanta Floci (y PostgreSQL si aplica)
- Espera y carga `cognito.local.env`
- Compila e inicia `base-admin-web-jar`
Si ya tienes `AWS_ENDPOINT_URL` exportado, no sobrescribe tus variables (útil para probar contra AWS).
### Maven manual (terminal)
```bash
# 1. Levantar Floci (Cognito local)
docker compose -f docker-compose.local.yml up -d floci
# 2. Esperar el bootstrap (genera cognito.local.env)
# Revisa que exista: docker/floci/generated/cognito.local.env
# 3. Cargar variables Cognito en la shell
set -a && source docker/floci/generated/cognito.local.env && set +a
# 4. Compilar y arrancar
mvn -pl base-admin-web-jar -am -DskipTests install
mvn -pl base-admin-web-jar spring-boot:run
```
Si Floci ya estaba levantado de antes, basta con el `source` y el `spring-boot:run`.
### PostgreSQL local
```bash
SPRING_PROFILES_ACTIVE=postgres ./run-local.sh
```
### PostgreSQL remoto
```bash
SPRING_PROFILES_ACTIVE=postgres \
SPRING_DATASOURCE_URL=jdbc:postgresql://TU_SERVIDOR:5432/base_admin_web \
SPRING_DATASOURCE_USERNAME=tu_usuario \
SPRING_DATASOURCE_PASSWORD=tu_password \
./run-local.sh
```
---
## Arranque manual e IntelliJ IDEA
Si abres el proyecto en IntelliJ (o cualquier IDE) sin usar `run-local.sh`, debes levantar Floci y configurar variables de entorno tú mismo. `run-local.sh` automatiza exactamente esos pasos.
```mermaid
flowchart TD
A["Abrir proyecto Maven en IntelliJ"] --> B["Java 25 como Project SDK"]
B --> C["docker compose up floci"]
C --> D["Esperar cognito.local.env"]
D --> E["Configurar Run Configuration"]
E --> F["Run BaseAdminWebApplication"]
F --> G["http://localhost:8080/login"]
```
### Requisitos previos
| Requisito | Versión / nota |
|-----------|----------------|
| JDK | **Java 25** (Temurin recomendado) |
| Maven | Incluido en el proyecto (wrapper no obligatorio; usa el Maven del IDE) |
| Docker | Para Cognito local (Floci) en puerto `4566` |
| IntelliJ | Ultimate o Community (con plugin Spring Boot en Community) |
### 1. Importar el proyecto
1. **File → Open** y selecciona la carpeta raíz del repo (donde está el `pom.xml` padre).
2. IntelliJ detecta el proyecto **Maven multi-módulo**. Espera a que termine la indexación e importación.
3. **File → Project Structure → Project**: SDK = **Java 25**, language level = **25**.
4. **File → Settings → Build → Build Tools → Maven → Runner**: JRE = **Project SDK (Java 25)**.
### 2. Levantar Floci (Cognito local)
En una terminal (puede ser la Terminal integrada de IntelliJ):
```bash
docker compose -f docker-compose.local.yml up -d floci
```
La primera vez Floci ejecuta el bootstrap y escribe:
```
docker/floci/generated/cognito.local.env
```
Comprueba que el archivo existe (puede tardar ~30–120 s):
```bash
cat docker/floci/generated/cognito.local.env
```
Si no aparece, revisa los logs:
```bash
docker logs base-admin-web-floci
```
**PostgreSQL local:** si usas el perfil `postgres`, levanta también el servicio:
```bash
docker compose -f docker-compose.local.yml up -d postgres floci
```
### 3. Run Configuration en IntelliJ
Crea una configuración **Spring Boot** (o **Application**):
| Campo | Valor |
|-------|-------|
| **Name** | `BaseAdminWebApplication` |
| **Main class** | `mx.gob.slp.baseadminweb.jar.BaseAdminWebApplication` |
| **Module** | `base-admin-web-jar` |
| **Working directory** | `$PROJECT_DIR$/base-admin-web-jar` |
| **Active profiles** | `dev` (o `postgres` si aplica) |
> **Working directory:** H2 guarda datos en `./data/h2/` relativo al directorio de trabajo. Debe ser `base-admin-web-jar` para que coincida con `mvn spring-boot:run` y no mezclar rutas de BD.
#### Variables de entorno (Cognito local)
En **Environment variables**, pega el contenido de `docker/floci/generated/cognito.local.env`:
```
COGNITO_USER_POOL_ID=us-east-1_BaseAdminWeb
COGNITO_REGION=us-east-1
COGNITO_ISSUER_URI=http://localhost:4566/us-east-1_BaseAdminWeb
COGNITO_JWK_SET_URI=http://localhost:4566/us-east-1_BaseAdminWeb/.well-known/jwks.json
COGNITO_ADMIN_CLIENT_ID=
COGNITO_SCANNER_CLIENT_ID=
COGNITO_ADMIN_GROUP=admins
COGNITO_SCANNER_GROUP=scanners
AWS_ENDPOINT_URL=http://localhost:4566
AWS_DEFAULT_REGION=us-east-1
AWS_ACCESS_KEY_ID=test
AWS_SECRET_ACCESS_KEY=test
```
Los `CLIENT_ID` cambian en cada bootstrap; **copia siempre los del archivo generado**, no uses valores fijos de ejemplos antiguos.
**Alternativa:** plugin [EnvFile](https://plugins.jetbrains.com/plugin/7861-envfile) → añade `docker/floci/generated/cognito.local.env` como archivo de entorno en la Run Configuration.
#### Perfil `postgres` en IntelliJ
Además de **Active profiles** = `postgres`, añade en Environment variables:
```
SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/base_admin_web
SPRING_DATASOURCE_USERNAME=postgres
SPRING_DATASOURCE_PASSWORD=postgres
```
(Y las variables Cognito del paso anterior.)
### 4. Ejecutar
1. **Build → Build Project** (o `Ctrl+F9`).
2. Run ▶ en `BaseAdminWebApplication`.
3. Abre http://localhost:8080/login — usuario `admin` / `Admin123*`.
### 5. Desarrollo sin Docker (modo local puro)
Si no quieres Floci, desactiva Cognito y usa usuarios de la BD:
| Variable | Valor |
|----------|-------|
| `APP_COGNITO_ENABLED` | `false` |
No hace falta `AWS_ENDPOINT_URL` ni levantar Docker. Los usuarios `admin` y `scanner` se crean al arrancar vía `InitialDataLoader`.
El login API devuelve JWT local (HS256); **refresh no está disponible** en este modo.
### 6. Checklist de problemas frecuentes
| Síntoma | Causa probable | Solución |
|---------|----------------|----------|
| Login web falla con Cognito habilitado | Floci no está arriba o faltan env vars | `docker compose up -d floci` + cargar `cognito.local.env` |
| Login va a AWS real en local | Falta `AWS_ENDPOINT_URL` | Definir `AWS_ENDPOINT_URL=http://localhost:4566` |
| `Invalid client id` | Client IDs desactualizados | Regenerar con bootstrap y copiar de `cognito.local.env` |
| H2 vacío o ruta rara | Working directory incorrecto | `$PROJECT_DIR$/base-admin-web-jar` |
| Error de versión Java | SDK distinto a 25 | Project Structure → SDK Java 25 |
| Tests en IDE fallan por Cognito | Perfil `test` desactiva Cognito | Ejecutar tests con perfil `test` (ya configurado en `application-test.yml`) |
### 7. Comandos útiles desde IntelliJ
| Acción | Comando Maven (ventana Maven o terminal) |
|--------|------------------------------------------|
| Compilar todo | `mvn -pl base-admin-web-jar -am -DskipTests install` |
| Tests | `mvn test` |
| Solo módulo jar | clic derecho en `base-admin-web-jar` → Run Maven → `spring-boot:run` (tras exportar env vars en la shell) |
---
## Perfiles Spring
| Perfil | Base de datos | Cognito | Uso |
|--------|---------------|---------|-----|
| `dev` (default) | H2 en archivo `./data/h2/` | Floci (manual o `run-local.sh`) | Desarrollo diario |
| `postgres` | PostgreSQL | Floci | Desarrollo con BD real |
| `prod` | Configurable | AWS | Producción |
| `test` | H2 en memoria `create-drop` | Deshabilitado | `mvn test` |
---
## Variables de entorno
### Aplicación
| Variable | Default | Descripción |
|----------|---------|-------------|
| `SERVER_PORT` | `8080` | Puerto HTTP |
| `SPRING_PROFILES_ACTIVE` | `dev` | Perfil Spring |
| `APP_TITLE` | `Base Admin Web` | Título en vistas |
### Cognito
| Variable | Local (Floci) | Producción (AWS) |
|----------|---------------|------------------|
| `APP_COGNITO_ENABLED` | `true` | `true` |
| `COGNITO_REGION` | `us-east-1` | tu región |
| `COGNITO_USER_POOL_ID` | `us-east-1_BaseAdminWeb` | `us-east-1_XXXXX` |
| `COGNITO_ISSUER_URI` | `http://localhost:4566/...` | `https://cognito-idp.{region}.amazonaws.com/{poolId}` |
| `COGNITO_JWK_SET_URI` | `http://localhost:4566/.../jwks.json` | `https://cognito-idp.{region}.amazonaws.com/{poolId}/.well-known/jwks.json` |
| `COGNITO_ADMIN_CLIENT_ID` | generado por Floci | client del pool |
| `COGNITO_SCANNER_CLIENT_ID` | generado por Floci | client del pool |
| `COGNITO_ADMIN_GROUP` | `admins` | `admins` |
| `COGNITO_SCANNER_GROUP` | `scanners` | `scanners` |
| `AWS_ENDPOINT_URL` | `http://localhost:4566` | **no definir** |
### Base de datos
| Variable | Descripción |
|----------|-------------|
| `SPRING_DATASOURCE_URL` | JDBC URL |
| `SPRING_DATASOURCE_USERNAME` | Usuario BD |
| `SPRING_DATASOURCE_PASSWORD` | Contraseña BD |
| `SPRING_JPA_HIBERNATE_DDL_AUTO` | `update` por defecto |
### JWT local (solo `APP_COGNITO_ENABLED=false`)
| Variable | Descripción |
|----------|-------------|
| `APP_JWT_SECRET` | Secreto HS256 (mín. 32 caracteres) |
| `APP_JWT_EXPIRATION_HOURS` | Expiración del token |
### Usuarios semilla (modo local sin Cognito)
| Variable | Default |
|----------|---------|
| `APP_ADMIN_USERNAME` / `APP_ADMIN_PASSWORD` | `admin` / `Admin123*` |
| `APP_SCANNER_USERNAME` / `APP_SCANNER_PASSWORD` | `scanner` / `Scanner123*` |
---
## Objetivo de esta base
Esta base deja resuelto lo mínimo para arrancar nuevos proyectos internos:
- Autenticación Cognito (local y AWS) con fallback a BD local
- Panel web con Metronic y sesión HTTP para administradores
- API stateless con JWT para escaneo móvil o externo
- App Flutter de referencia para escaneo QR
- Módulo de códigos pase con alta, listado, QR y API de escaneo
- Estructura modular Maven limpia
- Soporte rápido para H2 y PostgreSQL
- Floci para desarrollo sin depender de AWS
El dashboard se mantiene deliberadamente sencillo como punto de partida para futuras integraciones.