Full system documentation: Docker containers, request flows, targo-hub endpoints, GenieACS integration, ops app structure, external service dependencies, and device diagnostics data flow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
13 KiB
13 KiB
Gigafibre FSM — Architecture
Service Map
┌──────────────────┐
│ Authentik SSO │
│ id.gigafibre.ca │
└────────┬─────────┘
│ OIDC / Proxy Auth
▼
┌──────────────────┐
│ Traefik │
│ Reverse Proxy │
│ + Let's Encrypt │
└──┬───┬───┬───┬───┘
│ │ │ │
┌───────────────────┘ │ │ └──────────────────┐
▼ ▼ ▼ ▼
┌─────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Ops App │ │ ERPNext v16 │ │ Targo-Hub │
│ erp.../ops/ │ │ erp.gigafibre.ca│ │ msg.gigafibre.ca │
│ (nginx+Quasar) │ │ (Frappe/Python) │ │ (Node.js) │
└───────┬─────────┘ └───────┬──────────┘ └──┬──────┬───────┘
│ │ │ │
│ /api/* proxy │ │ │
│ (token injected) │ │ │
└─────────────────────┘ │ │
│ │
┌──────────────────────────────────────┘ │
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ GenieACS NBI │ │ Twilio API │
│ 10.5.2.115:7557 │ │ SMS + Voice │
│ (TR-069 ACS) │ └──────────────────┘
└───────┬──────────┘
│ CWMP (TR-069)
▼
┌──────────────────┐ ┌──────────────────┐
│ CPE / ONT │ │ Oktopus (USP) │
│ TP-Link XX230v │ │ oss.gigafibre.ca│
│ Raisecom HT803G │ │ TR-369 (future) │
└──────────────────┘ └──────────────────┘
Host: 96.125.196.67 (hubdocker)
All services run on a single Docker host. DNS records erp.gigafibre.ca,
oss.gigafibre.ca, msg.gigafibre.ca all resolve to this IP.
Docker Containers
| Container | Image | Port | Network | Purpose |
|---|---|---|---|---|
| ops-frontend | nginx:alpine | 80 | proxy | Ops SPA + ERPNext API proxy |
| targo-hub | node:20-alpine | 3300 | proxy, erpnext | SSE relay, SMS, GenieACS proxy |
| erpnext-frontend | frappe/erpnext | 8080 | erpnext | ERPNext web + API |
| erpnext-backend | frappe/erpnext | 8000 | erpnext | Frappe worker |
| erpnext-db-1 | postgres:16 | 5432 | erpnext | ERPNext database |
| oktopus-acs-1 | oktopusp/acs | 9292 | oktopus | USP/TR-369 controller |
| oktopus-mongo-1 | mongo:7 | 27017 | oktopus | Oktopus datastore |
| fn-routr | fonoster/routr-one | — | fonoster | VoIP SIP routing |
| fn-asterisk | fonoster/asterisk | — | fonoster | PBX media server |
| fn-postgres | postgres:16 | — | fonoster | Fonoster DB |
| apps-targo-db-1 | postgres | — | apps | Targo-hub database |
| authentik-* | goauthentik | — | authentik | SSO provider |
Ops App (Quasar v2 + Vite)
Served from: /opt/ops-app/ via ops-frontend (nginx)
URL: https://erp.gigafibre.ca/ops/
Request Flow
Browser Traefik ops-frontend (nginx) ERPNext
│ │ │ │
│── GET /ops/... ──────▶ strip /ops ────────────▶ try_files ──▶ SPA │
│ │ │ │
│── GET /ops/api/... ──▶ strip /ops ────────────▶ /api/* proxy ─────▶
│ │ │ + Auth token │
│ │ │ injected │
Directory Structure
apps/ops/src/
├── api/ # API clients
│ ├── auth.js # Token auth + session check
│ ├── erp.js # ERPNext CRUD (listDocs, getDoc, updateDoc...)
│ ├── dispatch.js # Dispatch jobs/techs/tags CRUD
│ ├── sms.js # SMS via n8n webhook
│ ├── traccar.js # GPS tracking
│ ├── ocr.js # Ollama Vision OCR
│ └── service-request.js # Service booking (future)
├── components/
│ ├── shared/
│ │ ├── DetailModal.vue # Right-panel detail viewer
│ │ ├── InlineField.vue # Odoo-style inline editing
│ │ ├── TagEditor.vue # Tag selector with levels
│ │ └── detail-sections/ # Per-doctype detail views
│ │ ├── EquipmentDetail # ONT diagnostics + mesh topology
│ │ ├── IssueDetail # Ticket view
│ │ ├── InvoiceDetail # Invoice + payment status
│ │ ├── PaymentDetail # Payment entry
│ │ └── SubscriptionDetail# Subscription management
│ └── customer/
│ ├── CustomerHeader.vue # Name, status, actions
│ ├── ContactCard.vue # Phone/email
│ ├── CustomerInfoCard.vue # Flags, notes, tax category
│ ├── ChatterPanel.vue # SMS/call thread
│ ├── ComposeBar.vue # Message input
│ ├── SmsThread.vue # SMS history
│ └── PhoneModal.vue # Twilio voice/SIP
├── composables/ # 24 Vue composables
│ ├── useDeviceStatus.js # GenieACS device lookup + cache
│ ├── useSSE.js # Server-sent events (targo-hub)
│ ├── useInlineEdit.js # Inline field save logic
│ ├── usePhone.js # 3CX/Twilio voice config
│ └── ... # Dispatch, map, formatting, etc.
├── modules/
│ └── dispatch/components/ # Dispatch-specific UI (timeline, calendar, map)
├── pages/ # 10 routed pages
│ ├── DashboardPage # KPI overview
│ ├── ClientsPage # Search-first customer list
│ ├── ClientDetailPage # Customer detail (subs, invoices, tickets)
│ ├── TicketsPage # Issue/ticket list
│ ├── DispatchPage # Scheduling + timeline (~1600 LOC)
│ ├── EquipePage # Equipment fleet
│ ├── OcrPage # Invoice OCR
│ ├── TelephonyPage # VoIP management
│ ├── RapportsPage # Reports (stub)
│ └── SettingsPage # Config: API, SMS, 3CX
├── stores/ # Pinia: dispatch, auth
├── config/ # erpnext.js, nav.js, ticket-config.js
└── router/index.js
Targo-Hub (Node.js)
Container: targo-hub | URL: msg.gigafibre.ca | Port: 3300
Endpoints
| Method | Path | Purpose |
|---|---|---|
| GET | /sse?topics=customer:X |
Server-Sent Events stream |
| POST | /broadcast |
Push event to SSE clients |
| POST | /send/sms |
Send SMS via Twilio |
| POST | /webhook/twilio/sms-incoming |
Receive inbound SMS |
| POST | /webhook/twilio/sms-status |
SMS delivery status |
| GET | /voice/token |
Twilio voice JWT |
| GET | /devices/lookup?serial=X |
Find CPE in GenieACS |
| GET | /devices/summary |
Fleet statistics |
| GET | /devices/:id/hosts |
Connected clients + mesh mapping |
| POST | /devices/:id/tasks |
Send task (reboot, refresh) |
| GET | /health |
Health check |
GenieACS Device Lookup (3 fallbacks)
serial = "TPLGC4160688"
1. Exact: DeviceID.SerialNumber._value == serial → match?
2. GPON: Device.Optical...GponSn._value =~ /C4160688$/ → match?
3. ID: _id =~ /TPLGC4160688/ → match?
Hosts Endpoint Flow
GET /devices/:id/hosts?refresh
│
├── Task 1: getParameterValues → Device.Hosts.Host.{1-20}.*
│ (connection_request, timeout=15s)
│
├── Task 2: getParameterValues → Device.WiFi.MultiAP.APDevice.{1-3}
│ .Radio.{1-2}.AP.{1-4}.AssociatedDevice.{1-8}.*
│ (timeout=10s)
│
├── Read cached data from GenieACS MongoDB
│
├── Build clientNodeMap: MAC → {nodeName, band, signal, speed}
│
└── Return { total, hosts[{name, ip, mac, band, signal, attachedNode, lease}] }
GenieACS Provision (XX230v)
Provision: xx230v_inform / XX230v_inform_TpLink_fix
TP-Link requires clear() + commit() before re-reading to avoid error 9805/9806:
clear("Device.Hosts.Host", Date.now());
clear("Device.WiFi.MultiAP.APDevice.*.Radio.*.AP.*.AssociatedDevice", Date.now());
commit();
// Re-read in same provision cycle
declare("Device.Hosts.Host.*.HostName", {value: now});
declare("Device.WiFi.MultiAP.APDevice.*.Radio.*.AP.*.AssociatedDevice.*.MACAddress", {value: now});
Configures: TR-069 credentials, remote access (HTTPS on 443), VoIP digit map, NTP, superadmin password. Reads ~100 diagnostic parameters per inform.
Data Model (ERPNext Doctypes)
Customer (ERPNext native)
└─ Service Location (LOC-#####)
├─ Address + GPS coordinates
├─ Connection type (FTTH/FTTB/Cable/DSL)
├─ OLT port, VLAN, network config
│
├─ Service Equipment (EQP-#####)
│ ├─ Type: ONT / Router / Switch / AP / Decodeur
│ ├─ Serial number + MAC address
│ ├─ Status: Active / Inactive / En stock / Defectueux / Retourne
│ ├─ Network config (IP, firmware, credentials)
│ └─ Move history (Equipment Move Log)
│
└─ Service Subscription (SUB-#####)
├─ Plan: Internet / IPTV / VoIP / Bundle
├─ Billing: price, frequency, Stripe integration
└─ Status: pending → active → suspended → cancelled
Dispatch Job
├─ Customer + Service Location
├─ Assigned tech + assistants + tags
├─ Schedule: date, time, duration
├─ Equipment Items / Materials Used
└─ GPS position (Traccar)
External Services
| Service | URL | Used By | Purpose |
|---|---|---|---|
| ERPNext | erp.gigafibre.ca | Ops App, targo-hub | Business data |
| GenieACS | 10.5.2.115:7557 | targo-hub | CPE management (TR-069) |
| Twilio | api.twilio.com | targo-hub | SMS + Voice |
| Traccar | tracker.targointernet.com:8082 | Ops App | GPS fleet tracking |
| n8n | n8n.gigafibre.ca | Ops App | SMS workflow |
| Authentik | id.gigafibre.ca | Traefik | SSO (staff) |
| Authentik Client | auth.targo.ca | Portal | SSO (customers) |
| Mapbox | api.mapbox.com | Ops App | Maps + routing |
| 3CX PBX | targopbx.3cx.ca | targo-hub | Call logging |
Data Flow: Customer → Device Diagnostics
User clicks equipment chip in ClientDetailPage
│
▼
EquipmentDetail.vue
│
├── fetchStatus([{serial_number: "TPLGC4160688"}])
│ → GET msg.gigafibre.ca/devices/lookup?serial=TPLGC4160688
│ → targo-hub → GenieACS NBI → summarizeDevice()
│ → {interfaces[], mesh[], wifi{}, opticalStatus, ethernet{}}
│
├── fetchHosts(serial, refresh=true)
│ → GET msg.gigafibre.ca/devices/:id/hosts?refresh
│ → 2 inline tasks to CPE → read cache → clientNodeMap
│ → {total, hosts[{name, ip, mac, band, signal, attachedNode}]}
│
▼
UI renders:
┌─ Fibre [Up] Rx dBm (when available)
├─ IP Addresses (Internet, Gestion [clickable → /superadmin/], Service, LAN)
├─ WiFi (per-radio + total clients: direct + mesh)
├─ Ethernet ports
├─ General (firmware, SSID, uptime)
└─ Connected Clients (collapsible, grouped by mesh node)
├─ basement (5) — signal %, name, IP, MAC, band, lease
├─ hallway (2)
├─ living_room (4)
└─ Filaire / Autre (wired clients)