# Gigafibre — Infrastructure & Services

> Document de référence pour l'infrastructure complète. Sert de guide de transfert pour toute personne reprenant l'environnement.

## Vue d'ensemble

```
Internet
  │
  ▼
96.125.196.67 (Proxmox VM, Ubuntu 24.04)
  │
  ├─ Traefik v2.11 (ports 80/443)
  │   ├─ Let's Encrypt TLS auto-renew
  │   ├─ ForwardAuth middleware → Authentik
  │   └─ Routes → Docker containers
  │
  ├─ Authentik SSO (auth.targo.ca)
  │   └─ ForwardAuth pour toutes les apps protégées
  │
  ├─ ERPNext v16.10.1 (erp.gigafibre.ca)
  │   ├─ 9 containers (frontend, backend, worker, scheduler, redis, postgres)
  │   └─ Custom doctypes: Dispatch + FSM
  │
  ├─ Dispatch PWA (dispatch.gigafibre.ca)
  │   ├─ nginx servant les fichiers statiques Vue/Quasar
  │   └─ Proxy /api/ → ERPNext, /traccar/ → Traccar
  │
  ├─ n8n (n8n.gigafibre.ca)
  │   ├─ Auto-login proxy (lit X-authentik-email de forwardAuth)
  │   └─ Workflows: SMS routing, sharing, automation
  │
  ├─ Gitea (git.targo.ca)
  │
  ├─ www.gigafibre.ca
  │   ├─ React/Vite site vitrine
  │   └─ API Node.js (recherche adresses, contact, SMS)
  │
  ├─ Oktopus CE (oss.gigafibre.ca) — TR-069 CPE management
  │
  └─ Hub (hub.gigafibre.ca) — Dashboard interne
```

## DNS (Cloudflare)

Domaine `gigafibre.ca` géré sur Cloudflare en mode **DNS-only** (pas de proxy CF, Traefik gère le TLS).

| Record | Type | Valeur | Notes |
|--------|------|--------|-------|
| `@` | A | 96.125.196.67 | |
| `*.gigafibre.ca` | A | 96.125.196.67 | Wildcard |
| `@` | MX | aspmx.l.google.com (10) | Google Workspace |
| `@` | MX | alt1-4.aspmx.l.google.com | |
| `@` | TXT | v=spf1 include:spf.mailjet.com include:_spf.google.com ~all | |
| `mailjet._748826c7` | TXT | 748826c702abf9015ac2e8a202336527 | DKIM Mailjet |

Domaine `targo.ca` encore sur OpenSRS (migration Cloudflare planifiée).

## Reverse Proxy (Traefik)

**Emplacement serveur:** `/opt/traefik/`

- Version: v2.11 (ne pas upgrader à v3 — incompatible Docker 29, API v1.24 vs v1.40)
- TLS: Let's Encrypt HTTP-01 challenge
- Réseau Docker: `proxy` (tous les containers exposés doivent y être)

### Middleware Authentik

```yaml
# /opt/traefik/dynamic/routes.yml
http:
  middlewares:
    authentik:
      forwardAuth:
        address: http://authentik-server:9000/outpost.goauthentik.io/auth/traefik
        trustForwardHeader: true
        authResponseHeaders:
          - X-authentik-username
          - X-authentik-email
          - X-authentik-name
```

**Gotcha:** Le redirect HTTP→HTTPS ne doit pas intercepter `/.well-known/acme-challenge/` sinon Let's Encrypt échoue.

## Authentik SSO

**URL:** https://auth.targo.ca

### Flux d'authentification

```
Utilisateur → app.gigafibre.ca
  → Traefik vérifie session via forwardAuth
  → Pas de session? → Redirect auth.targo.ca/flows/authorize/
  → Login (username/password ou SSO)
  → Callback → Cookie session set → App charge
```

### Applications configurées

| App | Type | Status |
|-----|------|--------|
| dispatch.gigafibre.ca | ForwardAuth (Traefik) | Actif |
| n8n.gigafibre.ca | ForwardAuth + auto-login proxy | Actif |
| hub.gigafibre.ca | ForwardAuth (Traefik) | Actif |
| erp.gigafibre.ca | OAuth2/OIDC (Provider) | En place |

### n8n auto-login (workaround)

n8n Community Edition ne supporte pas OIDC. Solution :
- Proxy Node.js (`/opt/n8n-proxy/server.js`) devant n8n
- Lit le header `X-authentik-email` injecté par forwardAuth
- Crée une session n8n via `POST /api/v1/login` avec owner credentials
- Forward le cookie de session au navigateur

## ERPNext

**URL:** https://erp.gigafibre.ca
**Compose:** `/opt/erpnext/`
**DB:** PostgreSQL, base `_eb65bdc0c4b1b2d6`

### Token API (service token)

```
$ERP_SERVICE_TOKEN   # stocké dans /opt/dispatch-app/.env sur le serveur
```

Utilisé par le Dispatch PWA pour les appels API côté serveur (pas de session utilisateur — Authentik gère l'auth frontend, le token fixe gère l'auth API).

**Gotcha:** Ne jamais appeler `generate_keys` sur l'utilisateur Administrator — ça invalide le token existant.

### Doctypes personnalisés (module: Dispatch)

**Core:**
- `Dispatch Job` — Ordres de travail (SUP-#####)
- `Dispatch Technician` — Profils techniciens + GPS
- `Dispatch Tag` — Catégorisation

**FSM (extension):**
- `Service Location` (LOC-#####) — Locaux client
- `Service Equipment` (EQP-#####) — ONT, routeur, etc.
- `Service Subscription` (SUB-#####) — Forfaits actifs
- `Checklist Template` — Templates réutilisables

**Tables enfants:** Equipment Move Log, Job Equipment Item, Job Material Used, Job Checklist Item, Job Photo, Checklist Template Item, Dispatch Job Assistant, Dispatch Tag Link

### Patch appliqué

`number_card.py` — ERPNext v16 PostgreSQL: `ORDER BY creation` sur queries agrégées cause erreur. Corrigé avec `order_by=""`.

## Dispatch PWA

**URL:** https://dispatch.gigafibre.ca
**Repo:** `git.targo.ca/louis/OSS-BSS-Field-Dispatch`
**Stack:** Vue 3 / Quasar / Pinia / Mapbox GL JS

### Architecture du code

```
src/
  App.vue                     — Router view + auth check au mount
  api/
    auth.js                   — Service token, détection expiry Authentik
    dispatch.js               — CRUD ERPNext (authFetch)
    traccar.js                — GPS REST + WebSocket
  stores/
    dispatch.js               — Store principal: jobs, techs, GPS
    auth.js                   — Session check (simplifié, Authentik gère)
  pages/
    DispatchV2Page.vue        — Page principale (~1500 lignes)
  composables/
    useMap.js                 — Carte Mapbox, marqueurs SVG progress ring
    useScheduler.js           — Algorithme timeline
    useDragDrop.js            — Drag & drop avec batch
    useSelection.js           — Lasso, multi-select
    useAutoDispatch.js        — Auto-distribute + route optimize
    useBottomPanel.js         — Panel jobs non-assignés
    useUndo.js                — Undo stack groupé
  config/
    erpnext.js                — BASE_URL (vide en prod = same-origin)
```

### GPS Tracking

- **Traccar** sur `tracker.targointernet.com:8082`
- Proxy nginx: `dispatch.gigafibre.ca/traccar/api/*` → Traccar
- **Hybride REST + WebSocket:** REST initial → WebSocket real-time → fallback polling 30s
- Guards module-level (`__gpsStarted`, `__gpsPolling`) pour survivre au remount Vue

### Build & Deploy

```bash
cd dispatch-app
DEPLOY_BASE=/ npx quasar build -m pwa
tar czf /tmp/dispatch-pwa.tar.gz -C dist/pwa .
cat /tmp/dispatch-pwa.tar.gz | ssh -i ~/.ssh/proxmox_vm root@96.125.196.67 \
  'rm -rf /opt/dispatch-app/*.js /opt/dispatch-app/*.html /opt/dispatch-app/*.json /opt/dispatch-app/assets /opt/dispatch-app/icons; cat > /tmp/d.tar.gz && cd /opt/dispatch-app && tar xzf /tmp/d.tar.gz && rm /tmp/d.tar.gz'
```

### nginx config

`/opt/dispatch-app/nginx.conf`:
- SPA fallback: `try_files $uri /index.html`
- Proxy `/api/` → `erpnext-frontend-1:8080` (Docker DNS)
- Proxy `/traccar/` → `tracker.targointernet.com:8082`
- No-cache sur `index.html` et `sw.js` (éviter caching stale)
- Réseau partagé: container dans `erpnext_erpnext` network + `resolver 127.0.0.11`

## Site web (www.gigafibre.ca)

**Repo:** `git.targo.ca/louis/site-web-targo`
**Stack:** React / Vite / Tailwind / shadcn-ui
**Origine:** lovable.dev (modifié)

### API backend

`/opt/www-api/server.js` — Node.js Express:

| Endpoint | Fonction |
|----------|----------|
| `POST /rpc/search_addresses` | Recherche fuzzy 5.2M adresses québécoises |
| `POST /rpc/contact` | Formulaire contact → email Mailjet |
| `POST /rpc/lead` | Qualification adresse → email Mailjet |
| `POST /rpc/sms` | Envoi SMS Twilio |
| `POST /rpc/sms-incoming` | Webhook Twilio entrant → email Mailjet |

### Base de données adresses (RQA)

- Table `rqa_addresses`: 5.2M lignes (Répertoire Québécois des Adresses)
- Table `fiber_availability`: 21,623 entrées (matching numero + code_postal)
- Recherche 3 phases: exact → fuzzy pg_trgm → J0S/J0L priority
- PostgreSQL dans le compose ERPNext (même instance DB)

## Email (Mailjet)

**Expéditeur:** `noreply@targo.ca` (Name: Gigafibre)

### Flux

```
Contact form (www) → /rpc/contact → Mailjet API → support@targo.ca
Lead capture (www) → /rpc/lead → Mailjet API → louis@targo.ca
SMS entrant (Twilio) → webhook → Mailjet API → louis@targo.ca
```

### DNS requis (targo.ca)

- SPF: `v=spf1 include:spf.mailjet.com include:_spf.google.com ~all`
- DKIM: `mailjet._748826c7.targo.ca` → valeur Mailjet
- **Status:** SPF/DKIM pour targo.ca en erreur côté Mailjet (à corriger)

## SMS (Twilio)

**Numéro:** +1 (438) 231-3838
**SID:** `$TWILIO_ACCOUNT_SID`  # voir 1Password / secrets manager
**Status:** Compte trial (SMS limité aux numéros vérifiés)

### Flux

```
Envoi: App → /rpc/sms → Twilio API → Destinataire
Réception: Twilio → webhook POST → n8n.gigafibre.ca/webhook/sms-incoming
  → Forward vers www.gigafibre.ca/rpc/sms-incoming → Email Mailjet
```

**À faire:**
- Upgrader vers compte Twilio production
- Configurer 10DLC ou acheter numéro Toll-Free
- Changer webhook vers n8n directement une fois workflow activé

## n8n Workflows

**URL:** https://n8n.gigafibre.ca
**Compose:** Dans `/opt/erpnext/` (override n8n)

### Workflows créés

| Nom | ID | Fonction | Status |
|-----|-----|----------|--------|
| [System] Share all workflows | MC5CxJ6QD2s8OCrr | Partage workflows/credentials entre projets (SQLite) | À activer via UI |
| SMS Incoming → Email + Router | j8lC0f6aHrV9L5Ud | Webhook Twilio → forward API | À activer via UI |

**Gotcha:** L'activation via API (`POST /activate`) ne registre pas les webhooks dans n8n 2.x — toujours activer via le toggle UI.

**Gotcha:** Le node `executeCommand` n'existe pas dans n8n 2.x — utiliser `n8n-nodes-base.code` avec `require('child_process')`.

## Repos Git (git.targo.ca)

| Repo | Contenu |
|------|---------|
| `louis/OSS-BSS-Field-Dispatch` | Dispatch PWA (Vue/Quasar) |
| `louis/site-web-targo` | www.gigafibre.ca (React/Vite) |
| `louis/gigafibre-fsm` | Docs architecture, scripts setup FSM |
| `louis/gigafibre-infra` | Configs serveur (traefik, docker-compose) |

> **Note:** `git.gigafibre.ca` a été supprimé — utiliser uniquement `git.targo.ca`.

## Docker — Réseaux et Compose

### Fichiers compose sur le serveur

| Emplacement | Services |
|-------------|----------|
| `/opt/traefik/docker-compose.yml` | Traefik |
| `/opt/erpnext/compose.yaml` | ERPNext (9 containers) |
| `/opt/erpnext/overrides/compose.n8n.yaml` | n8n + auto-login proxy |
| `/opt/apps/docker-compose.yml` | Gitea + dispatch-frontend + targo-db |
| `/opt/oktopus/docker-compose.yml` | Oktopus CE (8 containers) |
| `/opt/www-api/docker-compose.yml` | API www.gigafibre.ca |

### Réseaux Docker

| Réseau | Usage |
|--------|-------|
| `proxy` | Traefik ↔ tous les containers exposés |
| `erpnext_erpnext` | ERPNext interne + dispatch nginx (pour proxy /api/) |

**Gotcha:** Containers multi-réseaux → label `traefik.docker.network=proxy` obligatoire.

## Gotchas & Pièges

1. **Traefik v3 incompatible** avec Docker 29 (API v1.24 vs v1.40) — rester sur v2.11
2. **HTTP→HTTPS redirect** ne doit pas intercepter ACME challenge
3. **MongoDB 5+** nécessite AVX — CPU Proxmox doit être type "host"
4. **netplan** override systemd-networkd — supprimer `netplan.io`
5. **DEPLOY_BASE=/** requis pour `quasar build` en déploiement root domain
6. **nginx SPA** fallback: `try_files $uri /index.html`
7. **ERPNext generate_keys** invalide le token existant — ne jamais appeler
8. **Mixed content HTTPS→HTTP** — toujours utiliser proxy pour Traccar
9. **Service Worker cache** — headers no-cache sur index.html et sw.js
10. **Traccar API** ne supporte qu'un seul `deviceId` par requête — parallel calls

## Accès serveur

```bash
# SSH (voir 1Password pour la clé)
ssh -i ~/.ssh/proxmox_vm root@96.125.196.67

# Logs Traefik
docker logs -f traefik

# Logs ERPNext
cd /opt/erpnext && docker compose logs -f backend

# Logs n8n
cd /opt/erpnext && docker compose -f compose.yaml -f overrides/compose.n8n.yaml logs -f n8n

# Restart dispatch nginx
docker restart dispatch-frontend

# Rebuild dispatch
# (voir section Build & Deploy ci-haut)
```

## Tâches en cours

- [ ] Activer les 2 workflows n8n via UI toggle
- [ ] Twilio: upgrade production + 10DLC
- [ ] Corriger SPF/DKIM targo.ca dans Mailjet
- [ ] Google Workspace: ajouter gigafibre.ca comme domaine alias
- [ ] FSM Phase 2: customer/location picker, equipment scan, checklist UI
- [ ] Portail client self-service sur www.gigafibre.ca
- [ ] Pousser frappe_docker mods sur gitea
