You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
122 lines
6.9 KiB
122 lines
6.9 KiB
# Self-service
|
|
|
|
**Self-service** permite al dueño de un hotspot **iniciar sesión** en el panel, **editar las opciones de su dispositivo** (listas estáticas de TG, reflector por defecto, temporizador, idioma, etc.) y que esas opciones se **envíen al ADN DMR Peer Server** sin editar YAML a mano en el servidor.
|
|
|
|
Intervienen **cuatro** piezas: **MySQL** (tabla `Clients`), **API del monitor** (FastAPI, sesión + REST), **React** (página `/self-service`) y el **proxy hotspot integrado** en **adn-server** (**RPTO** periódico al master). El **peer server** es quien aplica al final **RPTO** / **OPTIONS** al bridge en ejecución y al comportamiento del hotspot.
|
|
|
|
---
|
|
|
|
## Requisitos previos
|
|
|
|
1. Bloque **`SELF_SERVICE`** en **`adn-monitor.yaml`** con credenciales **MySQL** válidas y parámetros **PBKDF2** alineados con tu herramienta de contraseñas (misma sal/iteraciones que **`hotspot_proxy_self_service.py`** cuando se use).
|
|
2. **`DASHBOARD.SELF_SERVICE: true`** para que la UI muestre la entrada **Self-service** (y `monitor.py` exponga `/api/self-service/*` cuando la BD conecta).
|
|
3. Tabla **`Clients`** con filas: **`callsign`**, **`int_id`** (ID DMR), **`psswd`** (hex PBKDF2-SHA256), **`options`** (línea `KEY=value` separada por `;`), **`logged_in`**, **`host`**, **`modified`**, etc. (ver esquema / migraciones en el repo adn-monitor).
|
|
4. El tráfico del hotspot debe pasar por el **proxy** si dependes de **`modified`** y del envío **RPTO** (ver flujo abajo).
|
|
|
|
---
|
|
|
|
## Autenticación
|
|
|
|
| Endpoint | Uso |
|
|
|----------|-----|
|
|
| **`POST /api/auth/login`** | Cuerpo: `callsign`, `password`. Verifica hash **PBKDF2** contra **`Clients.psswd`** en filas con **`logged_in = 1`**. Éxito: cookie de sesión con **`user_id`**, **`int_ids`** (todos los DMR ID de ese indicativo). |
|
|
| **`GET /api/auth/login-by-ip`** | Opcional: una coincidencia de usuario por **`Clients.host`** = IP del cliente (misma forma de sesión). |
|
|
| **`POST /api/auth/logout`** | Cierra sesión. |
|
|
| **`GET /api/auth/me`** | Devuelve `{ callsign, int_ids, selected_int_id }` para React. |
|
|
|
|
La sesión se prolonga con actividad (**SelfServiceController** usa un timeout largo de inactividad).
|
|
|
|
---
|
|
|
|
## API de dispositivo (tras login)
|
|
|
|
| Método | Ruta | Uso |
|
|
|--------|------|-----|
|
|
| **GET** | `/api/self-service/device?int_id=` | Carga fila **`Clients`** para ese **`int_id`** (debe estar en **`int_ids`** de sesión). JSON: **`int_id`**, **`callsign`**, **`mode`**, **`options`** parseadas en listas TS1/TS2, DIAL, VOICE, LANG, SINGLE, TIMER. |
|
|
| **POST** | `/api/self-service/device/options` | Cuerpo: **`int_id`**, cadena **`options`** (línea Homebrew **OPTIONS**). Debe terminar en **`;`**. Longitud máx. **4096**. Actualiza BD: **`Clients.options`**, **`modified = 1`**. |
|
|
| **GET** | `/api/self-service/device/modified?int_id=` | Devuelve **`{ modified: 0|1 }`** desde **`Clients.modified`** (la UI puede hacer polling hasta que el proxy lo limpie). |
|
|
| **POST** | `/api/self-service/device/select` | Cuerpo: **`int_id`** — fija **`selected_int_id`** en sesión si el usuario tiene varios dispositivos. |
|
|
|
|
---
|
|
|
|
## Flujo extremo a extremo (cómo llegan las opciones al hotspot)
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant UI as React /self-service
|
|
participant API as monitor FastAPI
|
|
participant DB as MySQL Clients
|
|
participant SRV as adn-server PROXY
|
|
participant HS as Hotspot
|
|
|
|
UI->>API: POST /api/self-service/device/options
|
|
API->>DB: UPDATE options, modified = 1
|
|
Note over SRV: bucle send_opts ~cada 10 s
|
|
SRV->>DB: lee filas con modified = 1
|
|
SRV->>SRV: RPTO en pata MASTER
|
|
SRV->>HS: OPTIONS vía HBP
|
|
SRV->>DB: limpia modified
|
|
```
|
|
|
|
1. El usuario guarda opciones en la web → la **API del monitor** escribe **`Clients.options`** y **`modified = 1`**.
|
|
2. El **proxy hotspot** ejecuta **`send_opts`** en bucle (~cada **10 s**). Para filas con **`modified = 1`**, lee opciones de la BD, envía **RPTO** **al peer server** en **`(MASTER, puerto_destino_asignado)`**, luego limpia **`modified`** en la BD.
|
|
3. El **ADN DMR Peer Server** recibe **RPTO** en la pata MASTER y actualiza su estado **OPTIONS** / bridge (mismo camino que un refresco normal de registro del hotspot).
|
|
4. El servidor envía la señalización adecuada al **hotspot** para que TG estáticos / reflector / temporizador surtan efecto **sin** reinicio completo del hotspot (el comportamiento coincide con el flujo HBP **OPTIONS** del servidor).
|
|
|
|
Importante: el proxy envía **RPTO solo al master**, no al hotspot directamente. Si el proxy no está en el camino, necesitas otro mecanismo que aplique **OPTIONS** o ejecutas el servidor sin este camino proxy.
|
|
|
|
> **Prerrequisito:** el hotspot debe enviar `PASS=` en su línea OPTIONS para
|
|
> login por contraseña y sincronización bidireccional con la BD. Si el hotspot
|
|
> envía contenido explícito (TGs, SINGLE, etc.) **sin** `PASS=`, el servidor
|
|
> toma las opciones directamente de esa línea y la fila de la BD se ignora. Si
|
|
> el hotspot no envía RPTO (el timer expira) o envía OPTIONS vacío, el servidor
|
|
> hace fallback a la base de datos.
|
|
> Ver [Proxy hotspot — comportamiento de la línea OPTIONS](../server/user-guide/hotspot-proxy.md#comportamiento-de-la-linea-options).
|
|
|
|
---
|
|
|
|
## Reconciliación de `logged_in`
|
|
|
|
El flag **`logged_in`** controla tanto el login por **contraseña** como por
|
|
**IP**: sólo las filas con `logged_in = 1` pueden autenticarse en el dashboard.
|
|
El **peer server** mantiene este flag preciso reconciliándolo contra los peers
|
|
realmente conectados cada **120 s** (el bucle `lst_seen`):
|
|
|
|
- Los peers actualmente conectados al MASTER inject-only quedan `logged_in = 1`.
|
|
- Las demás filas quedan `logged_in = 0`.
|
|
- El bucle arranca con `now=True`, de modo que el **primer tick al arrancar
|
|
limpia todos los flags obsoletos inmediatamente** — tras un reinicio del
|
|
servidor, los hotspots que no se reconectaron no pueden autenticarse vía
|
|
login-by-IP.
|
|
|
|
Esto reemplazó el `clean_tbl` horario del legado (barrido de 24 h de inactividad),
|
|
que dejaba `logged_in = 1` en peers ya desconectados tras un reinicio.
|
|
|
|
---
|
|
|
|
## Hash de contraseñas
|
|
|
|
**`AuthenticateUser`** usa:
|
|
|
|
```text
|
|
hash_pbkdf2('sha256', password, PBKDF2_SALT, PBKDF2_ITERATIONS)
|
|
```
|
|
|
|
almacenado en hex en **`Clients.psswd`**. Los mismos parámetros deben usarse donde se **registran** contraseñas (p. ej. **hotspot_proxy_self_service.py** en el repo de tooling / adn-dmr-server).
|
|
|
|
---
|
|
|
|
## Interfaz
|
|
|
|
- Ruta **`/self-service`** en React (`SelfService.tsx`): carga **`/api/auth/me`**, luego detalle del dispositivo para **`selected_int_id`**, edita la cadena de opciones, guarda.
|
|
- Un enlace externo **SelfCare** (p. ej. `selfcare.adn.systems`) puede aparecer en el nav como producto aparte — no es el mismo que este **self-service local**.
|
|
|
|
---
|
|
|
|
## Ver también
|
|
|
|
- [Inicio de la documentación](../README.md)
|
|
- [Configuración](configuration.md) — `SELF_SERVICE`, `DASHBOARD`, `PROXY`
|
|
- [Arquitectura](architecture.md) — proxy + canal de informes
|
|
- Detalle del proxy: **`proxy/README.md`** en adn-monitor (tabla de temporización RPTO)
|