# 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<br/>Panel admin"]
        Flutter["App Flutter<br/>QR Scanner"]
    end

    subgraph app["Spring Boot — base-admin-web-jar"]
        WebChain["Filter chain web<br/>sesión + CSRF"]
        ApiChain["Filter chain API<br/>stateless + JWT"]
        PassCodes["PassCodeService"]
        DB[("H2 / PostgreSQL")]
    end

    subgraph auth["Autenticación"]
        Cognito["Cognito<br/>Floci local o AWS"]
        LocalAuth["BD local + JWT HS256<br/>APP_COGNITO_ENABLED=false"]
    end

    Browser -->|"/login, /admin/**"| WebChain
    Flutter -->|"/api/auth/login<br/>/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<br/>punto de entrada + application.yml"]
    web["base-admin-web-web<br/>controladores + Thymeleaf"]
    config["base-admin-web-configuration<br/>seguridad, JWT, Cognito"]
    app["base-admin-web-application<br/>servicios + carga inicial"]
    domain["base-admin-web-domain<br/>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<br/>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<br/>definido?"}
    Floci["Floci localhost:4566<br/>credenciales test/test"]
    AWS["Cognito real en AWS<br/>DefaultCredentialsProvider<br/>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=<valor del archivo generado>
COGNITO_SCANNER_CLIENT_ID=<valor del archivo generado>
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.