gigafibre-fsm/docs/ARCHITECTURE.md
louispaulb 320655b0a0 refactor: major cleanup — remove dead dispatch app, commit all backend code, extract client composables
- Remove apps/dispatch/ (100% replaced by ops dispatch module, unmaintained)
- Commit services/targo-hub/lib/ (24 modules, 6290 lines — was never tracked)
- Commit services/docuseal + services/legacy-db docker-compose configs
- Extract client app composables: useOTP, useAddressSearch, catalog data, format utils
- Refactor CartPage.vue 630→175 lines, CatalogPage.vue 375→95 lines
- Clean hardcoded credentials from config.js fallback values
- Add client portal: catalog, cart, checkout, OTP verification, address search
- Add ops: NetworkPage, AgentFlowsPage, ConversationPanel, UnifiedCreateModal
- Add ops composables: useBestTech, useConversations, usePermissions, useScanner
- Add field app: scanner composable, docker/nginx configs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 17:38:38 -04:00

14 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

Module Structure

services/targo-hub/
├── server.js        # 100 LOC — HTTP router, CORS, SSE setup, server.listen
├── lib/
│   ├── config.js    #  42 LOC — All env var config (ERP, Twilio, 3CX, GenieACS, SIP)
│   ├── helpers.js   # 129 LOC — log, json, parseBody, httpRequest, erpFetch, nbiRequest
│   ├── sse.js       #  57 LOC — SSE client registry, broadcast, broadcastAll
│   ├── twilio.js    # 191 LOC — SMS in/out/status, voice token/TwiML/status, SIP config
│   ├── pbx.js       # 159 LOC — 3CX webhook handler + call log poller
│   ├── telephony.js # 118 LOC — Fonoster/Routr PostgreSQL CRUD (/telephony/*)
│   ├── devices.js   # 417 LOC — GenieACS proxy, summarizeDevice, hosts, ACS config export
│   └── provision.js # 163 LOC — OLT pre-auth, on-scan, equipment swap
├── package.json
└── docker-compose.yml

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)