# Documentación: Olvido de Contraseña y Envío de Emails

## Resumen

El sistema implementa un flujo **personalizado** de recuperación de contraseña (no usa el broker nativo de Laravel). El envío de emails se realiza a través de la **API de Brevo** (ex-Sendinblue), con una arquitectura de dos capas: un servicio Core externo que gestiona la API Key, y este servicio que construye y despacha los correos.

---

## 1. Flujo de Recuperación de Contraseña

### Diagrama

```
Usuario solicita reset
        │
        ▼
POST /api/v1/recovery-password
        │
        ├─ Valida email
        ├─ Genera token aleatorio (64 chars)
        ├─ Guarda token en users_passwords (expira en 1 hora)
        ├─ Construye link de reset con token + iduser encriptado
        └─ Envía email HTML via Core → send-mail-recoverypassword
                │
                ▼
        Usuario recibe email → hace clic en el link
                │
                ▼
        POST /api/v1/change-password-recovery
                │
                ├─ Desencripta iduser
                ├─ Verifica token en users_passwords
                ├─ Verifica que el token no haya expirado
                ├─ Actualiza contraseña (bcrypt)
                └─ Invalida el token (recoverytoken = 0)
```

---

## 2. Endpoints de la API

| Método | Ruta | Controlador | Autenticación | Descripción |
|--------|------|-------------|--------------|-------------|
| `POST` | `/api/v1/recovery-password` | `LoginController@recoveryPassword` | Pública | Solicita el reset y envía el email |
| `POST` | `/api/v1/change-password-recovery` | `LoginController@changePasswordRecovery` | Pública | Valida el token y actualiza la contraseña |
| `POST` | `/api/v1/change-password-first` | `LoginController@changePasswordFirts` | Pública | Permite al usuario cambiar su contraseña en el primer login |
| `POST` | `/api/v1/change-password` | `LoginController@changePassword` | JWT requerido | Cambia la contraseña de un usuario autenticado |

---

## 3. `recoveryPassword()` — Detalle del método

**Archivo:** `app/Http/Controllers/LoginController.php` (líneas 190–337)

### Parámetros de entrada (request)

| Campo | Tipo | Validación | Descripción |
|-------|------|-----------|-------------|
| `email` | string | `required\|email` | Email del usuario que solicita el reset |

### Lógica paso a paso

1. **Validación:** verifica que el campo `email` exista y tenga formato válido.
2. **Búsqueda de usuario:** busca en la tabla `users` por el email. Si no existe, retorna código `100`.
3. **Generación del token:** `Str::random(64)` — token alfanumérico de 64 caracteres.
4. **Persistencia del token:** `updateOrInsert` en `users_passwords`:
   - `recoverytoken` = token generado
   - `expires_at` = `now() + 1 hora`
5. **Construcción del link de reset:**
   ```
   {servercustomer}/change-password-recovery?token={token}&iduser={Crypt::encryptString($user->iduser)}
   ```
   El `iduser` se encripta con el facade `Crypt` de Laravel (AES-256-CBC).
6. **Envío del email:** se envía un `POST` al servicio Core externo:
   - URL: `{servercore}/send-mail-recoverypassword`
   - Body: `{ "to": "email@usuario.com", "message": "<html>..." }`
   - El HTML del email se construye inline (ver sección 6).

### Respuesta exitosa

```json
{
  "code": 200,
  "message": "Email enviado com sucesso",
  "data": {}
}
```

---

## 4. `changePasswordRecovery()` — Detalle del método

**Archivo:** `app/Http/Controllers/LoginController.php` (líneas 364–404)

### Parámetros de entrada (request)

| Campo | Tipo | Validación | Descripción |
|-------|------|-----------|-------------|
| `iduser` | string | `required` | ID del usuario encriptado con `Crypt` |
| `token` | string | `required` | Token de recuperación de 64 caracteres |
| `password` | string | `required` | Nueva contraseña |

### Lógica paso a paso

1. Desencripta `iduser` con `Crypt::decryptString()`.
2. Busca al usuario en la tabla `users`.
3. Consulta `users_passwords` donde `iduser` y `recoverytoken` coincidan.
4. Verifica que `expires_at` no haya pasado (`now()->greaterThan($expires_at)`).
5. Actualiza la contraseña con `bcrypt($request->password)`.
6. **Invalida el token** estableciendo `recoverytoken = 0`.

### Códigos de error

| Código | Mensaje | Causa |
|--------|---------|-------|
| `100` | `"iduser not found"` | Usuario no existe |
| `100` | `"token/iduser not found"` | Combinación token+iduser inválida |
| `100` | `"Token expired"` | El token superó la hora de validez |

---

## 5. Jobs de Envío de Email (Brevo API)

Los Jobs manejan el envío de emails para **creación** y **actualización** de administradores. Ambos usan `dispatchSync()` (ejecución síncrona, sin cola real).

### Flujo interno de cada Job

```
dispatchSync() llamado desde AdministratorsController
        │
        ▼
handle() del Job
        │
        ├─ 1. POST a {servercore}/get-mail-key   → obtiene la API Key de Brevo
        ├─ 2. Renderiza vista Blade como HTML
        └─ 3. POST a https://api.brevo.com/v3/smtp/email
                    Headers: api-key: {key}
                    Body: { sender, to, subject, htmlContent }
```

---

### 5.1 `SendWelcomeEmail` — Email de bienvenida

**Archivo:** `app/Jobs/SendWelcomeEmail.php`

Disparado en: `AdministratorsController@createAdm` (línea 357) cuando se crea un nuevo administrador.

| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| `$userName` | string | Nombre del nuevo usuario |
| `$userEmail` | string | Email destino |
| `$passwordToSave` | string | Contraseña (temporal o personalizada) |
| `$usedCustomPassword` | bool | `true` si fue una contraseña personalizada, `false` si fue la temporal `'Daniadami2025!'` |

**Vista Blade:** `resources/views/emails/welcome.blade.php`

**Asunto del email:** `"Bem-vindo(a) à plataforma Dani Adami - Seus Acessos"`

**Remitente:** `no-reply@goappy.net` (nombre: `Dani Adami App`)

---

### 5.2 `SendUpdateEmail` — Email de actualización de perfil

**Archivo:** `app/Jobs/SendUpdateEmail.php`

Disparado en: `AdministratorsController@updateAdm` (línea 590) cuando se actualiza un administrador y hay cambios detectados.

| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| `$userName` | string | Nombre del usuario actualizado |
| `$userEmail` | string | Email destino |
| `$changes` | array | Array de cambios con formato `[campo => [old, new]]` |

El Job valida que `$changes` no esté vacío antes de enviar.

**Vista Blade:** `resources/views/emails/update.blade.php`

**Asunto del email:** `"Atualização de Perfil - Dani Adami Segurança"`

**Remitente:** `no-reply@goappy.net` (nombre: `Dani Adami App`)

---

## 6. Template HTML del Email de Recuperación

El HTML se construye **inline** dentro de `recoveryPassword()` (no usa una vista Blade).

### Contenido del email

| Sección | Descripción |
|---------|-------------|
| **Header** | Logo de Dani Adami + gradiente marrón `#6b5235 → #a37f56` |
| **Saludo** | `"Prezado(a) {userName},"` |
| **Botón CTA** | `REDEFINIR MINHA SENHA` — enlace al link de reset |
| **Aviso de expiración** | El link es válido solo por **1 hora** |
| **Link alternativo** | El URL completo en texto plano por si el botón no funciona |
| **Aviso de seguridad** | Si no solicitó el cambio, ignorar el email |
| **Footer** | `© {año} Dani Adami - Psicóloga` |

### Variables del template

| Variable | Valor | Fuente |
|----------|-------|--------|
| `$logoUrl` | `{servercustomer}/img/logo.png` | `SharedmethodsController` |
| `$userName` | Nombre del usuario (`htmlspecialchars`) | Tabla `users` |
| `$resetLink` | URL con `token` + `iduser` encriptado | Generado en el método |
| `$year` | Año actual | `date('Y')` |

---

## 7. Estructura de Base de Datos

### Tabla `users_passwords`

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `iduser` | int (FK) | Referencia al usuario en tabla `users` |
| `password` | string | Contraseña hasheada con bcrypt |
| `recoverytoken` | string (64) | Token de recuperación activo (0 = invalidado) |
| `expires_at` | datetime | Expiración del token (1 hora desde la solicitud) |
| `state` | tinyint | `0` = contraseña temporal, `1` = contraseña ya cambiada |
| `lastaccess` | timestamp | Último acceso del usuario |

---

## 8. Seguridad

| Medida | Detalle |
|--------|---------|
| **Expiración del token** | 1 hora exacta desde la solicitud |
| **Encriptación del iduser** | `Crypt::encryptString()` / `Crypt::decryptString()` (AES-256-CBC) |
| **Hashing de contraseña** | `bcrypt()` — nunca se almacena en texto plano |
| **Invalidación del token** | Tras el cambio exitoso, `recoverytoken` se fija en `0` |
| **Validación de entrada** | Regex `^[^;`<>]+$` para prevenir inyección en token e iduser |
| **Bloqueo por intentos fallidos** | 3 intentos fallidos → bloqueo de cuenta por 20 minutos |

---

## 9. Servicio Externo: Core

El servicio **Core** es una API externa centralizada que provee:

| Endpoint | Uso |
|----------|-----|
| `{servercore}/send-mail-recoverypassword` | Recibe el HTML y el email destino, envía el correo de recuperación |
| `{servercore}/get-mail-key` | Devuelve la API Key de Brevo para los Jobs |

**URL del Core (producción):** `https://goappy.net/dev/core/services/public/api/v1/`

La comunicación se realiza mediante `cURL` (método `getServicesInfoCurl2` en `SharedmethodsController`) o el facade `Http` de Laravel.

---

## 10. Archivos Relevantes

| Archivo | Responsabilidad |
|---------|----------------|
| `app/Http/Controllers/LoginController.php` | Lógica de solicitud y confirmación de reset |
| `app/Http/Controllers/AdministratorsController.php` | Dispara los Jobs de email al crear/editar admins |
| `app/Http/Controllers/SharedmethodsController.php` | URLs del Core, método `getServicesInfoCurl2` |
| `app/Jobs/SendWelcomeEmail.php` | Job de bienvenida vía Brevo |
| `app/Jobs/SendUpdateEmail.php` | Job de actualización vía Brevo |
| `resources/views/emails/welcome.blade.php` | Template Blade del email de bienvenida |
| `resources/views/emails/update.blade.php` | Template Blade del email de actualización |
| `routes/api.php` | Definición de rutas de la API |
| `.env` | Variables de entorno (MAIL_* para Mailpit en local) |
