# Deploy — canale MQTT scanner ⇆ cloud (aggiornamento c4rgocloud online)

Runbook operativo per attivare in produzione il broker MQTT centrale e il worker
`app:scanner:listener`. Per il **perché** dell'architettura vedi [README.md](README.md);
qui c'è solo la sequenza da eseguire.

Branch di riferimento: `feat/scanner-mqtt-bridge`.

## 0. Prerequisiti sul server cloud

- Docker + docker compose, stack c4rgocloud già in esecuzione.
- DNS pubblico `my.c4rgo.cloud` → IP del server; porta **8883/tcp** aperta in ingresso.
- La **chiave privata della C4rgo-CA** (`ca.key`) + `ca.crt`, la stessa CA che firma i
  cert dei device (`/opt/.c4rgo/etc/certs/ca.crt`). Serve per firmare il cert del broker.
  Se non l'hai, `setup-broker.sh` documenta l'alternativa "CA nuova" (ridistribuendo il
  nuovo `ca.crt` ai device come `bridge_cafile`).
- Accesso al DB MySQL/MariaDB dell'app.

## 1. Codice

**Via git (consigliato):**
```bash
cd /path/to/c4rgocloud
git fetch origin
git checkout feat/scanner-mqtt-bridge
git pull --ff-only            # branch già riconciliato, fast-forward pulito
```
**Via pacchetto offline** (se il server non ha accesso al remoto git): scompatta
`c4rgocloud-scanner-mqtt-<data>.tar.gz` nella root del progetto. Contiene i 9 file nuovi
autonomi; per i 3 file da integrare (`docker-compose.yml`, `.env`, `config/services.yaml`)
applica gli hunk in `INTEGRATION.patch` invece di sovrascrivere (il server potrebbe averli
modificati).

## 2. Schema DB — colonna `mqtt_device_id`

Il progetto non usa le Doctrine migrations; la colonna va garantita a mano (idempotente):
```bash
docker compose exec -T php \
  mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" < docker/mqtt/ensure-mqtt-schema.sql
```
Poi associa ogni scanner alla sua macchina:
```sql
UPDATE macchine SET mqtt_device_id = 'kiosk_36b94b28' WHERE id = 131;
```
Senza questa riga il listener logga `mqttDeviceId sconosciuto` e scarta i messaggi.

## 3. Broker: certificati + utenti

```bash
cd docker/mqtt
cp /percorso/della/ca/ca.crt certs/ca.crt      # crea la dir se serve
cp /percorso/della/ca/ca.key certs/ca.key      # chiave CA — NON committare (gitignored)
./setup-broker.sh
```
Lo script genera `certs/my.c4rgo.cloud.{crt,key}` (SAN = `my.c4rgo.cloud`) e il file
`passwd` con l'utente **`r_server`**. **Annota la password stampata**: va in `.env.local`
al passo 4 (non è salvata altrove). Poi crea un utente per ogni device:
```bash
mosquitto_passwd -b passwd kiosk_36b94b28 '<password-robusta-per-questo-device>'
```
> Mai dare `r_server` a un device. Un utente per device (username == device_id) così una
> fuga di credenziali non è sistemica. L'ACL (`pattern readwrite c4rgo/%u/#`) isola i namespace.

## 4. Segreti in `.env.local`

Il `.env` è solo un template con placeholder. I valori reali vanno in `.env.local`
(gitignored):
```dotenv
SCANNER_MQTT_PASSWORD=<password di r_server dal passo 3>
```
Gli altri `SCANNER_MQTT_*` di default vanno bene per la comunicazione worker→broker
**dentro** la rete docker (host `mqtt`, porta `1883`, no TLS). Non toccare il blocco
legacy `MQTT_*` (SIM7600, deprecato).

## 5. Avvio broker + worker

Il worker riusa l'immagine `php`: se hai aggiornato il codice, ricostruiscila.
```bash
docker compose build php scanner-listener
docker compose up -d mqtt scanner-listener
```
Verifiche immediate:
```bash
docker compose logs -f mqtt              # deve fare "Opening ipv4 listen socket on port 8883" e 1883
docker compose logs -f scanner-listener  # deve loggare "Connesso" e la subscribe ai topic
```
Il listener 1883 è **solo** interno alla rete docker (compose pubblica solo 8883). È il
canale che il worker usa: bind su `0.0.0.0` nel container, non esposto sull'host.

## 6. Lato scanner — bridge per-device

Su ogni Raspberry, una volta creato l'utente broker del passo 3:
```bash
# parti da docker/mqtt/device-bridge.conf.example, sostituisci device_id/remote_password
sudo cp device-bridge.conf /etc/mosquitto/conf.d/c4rgo-cloud-bridge.conf
sudo systemctl restart mosquitto
```
Il bridge esporta solo `c4rgo/<device_id>/#` verso `my.c4rgo.cloud:8883` in TLS
(`bridge_insecure false`, `bridge_cafile` = la CA). Nessun daemon Rust va modificato.

## 7. Verifica end-to-end (dal server, senza web)

```bash
# dal cloud, come r_server verso il broker (8883 TLS). Il device si passa con
# --device-id; `ping` interroga tutti i service, `status <service>` uno solo.
python3 python/scripts/c4rgo_scanner_probe.py --host my.c4rgo.cloud --port 8883 \
  --username r_server --password '<pwd>' --ca-cert docker/mqtt/certs/ca.crt \
  --device-id kiosk_36b94b28 ping
python3 python/scripts/c4rgo_scanner_probe.py --host my.c4rgo.cloud --port 8883 \
  --username r_server --password '<pwd>' --ca-cert docker/mqtt/certs/ca.crt \
  --device-id kiosk_36b94b28 status modem
```
Atteso: LWT `online`, telemetria (`/modem/info` con `data.state=Connected` se il modem è su).
Nel worker: nessun `mqttDeviceId sconosciuto`; le entità macchina si aggiornano.

## Rollback

```bash
docker compose stop scanner-listener mqtt
git checkout <commit-precedente>     # oppure il branch di produzione
docker compose up -d
```
La colonna `mqtt_device_id` è nullable e inerte per il resto dell'app: lasciarla non
richiede rollback DB. Il blocco legacy `MQTT_*`/SIM7600 resta invariato.

## Manifest del pacchetto

Nuovi (autonomi):
- `docker/mqtt/mosquitto.conf`, `acl`, `setup-broker.sh`, `device-bridge.conf.example`,
  `.gitignore`, `README.md`, `DEPLOY.md`, `ensure-mqtt-schema.sql`
- `src/Command/ScannerMqttListenerCommand.php`
- `src/Command/Sim7600MqttListenerCommand.php` (legacy, `@deprecated`)
- `src/Services/SIM7600Service.php` (legacy, `@deprecated`)

Da integrare in file esistenti:
- `docker-compose.yml` (servizi `mqtt`, `scanner-listener`, volume `mqtt_data`)
- `.env` (blocco `SCANNER_MQTT_*`)
- `config/services.yaml` (parametri `scanner_mqtt_*` + wiring del command)
