gigafibre-fsm/docs/INFRASTRUCTURE.md
louispaulb 2e55a7d031 security: remove exposed credentials, add .gitignore, harden infra
- Replace hardcoded ERPNext token and Twilio SID with $VAR placeholders
- Add .gitignore to exclude .env files, node_modules, build output
- Untrack apps/website/.env (contained Supabase key)
- Remove git.gigafibre.ca references (use git.targo.ca only)

Server-side (applied live):
- Traefik: disable dashboard, close port 8080
- Oktopus: add Authentik forwardAuth middleware
- Log level: DEBUG → WARN

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 09:17:33 -04:00

364 lines
12 KiB
Markdown

# 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