The ops tech module at /ops/#/j/* had drifted from the field app in two ways:
1. Scanner — a prior "restoration" re-added html5-qrcode, but the
design has always been native <input capture="environment"> → Gemini
2.5 Flash via targo-hub /vision/barcodes (up to 3 codes) and
/vision/equipment (structured labels, up to 5). Revert useScanner.js
+ ScanPage.vue + TechScanPage.vue to commit e50ea88 and drop
html5-qrcode from both package.json + lockfiles. No JS barcode
library, no camera stream, no polyfills.
2. Equipment UX — TechJobDetailPage.vue was a 186-line stub missing the
Ajouter bottom-sheet (Scanner / Rechercher / Créer), the debounced
SN-then-MAC search, the 5-field create dialog, Type + Priority
selects on the info card, and the location-detail contact expansion.
Port the full UX from apps/field/src/pages/JobDetailPage.vue (526
lines) into the ops module (458 lines after consolidation).
Rebuilt and deployed both apps. Remote smoke test confirms 0 bundles
reference html5-qrcode and the new TechJobDetailPage.1075b3b8.js chunk
(16.7 KB vs ~5 KB stub) ships the equipment bottom-sheet strings.
Docs:
- docs/features/tech-mobile.md — new. Documents all three delivery
surfaces (legacy SSR /t/{jwt}, transitional apps/field/, unified
/ops/#/j/*), Gemini-native scanner pipeline, equipment UX, magic-link
JWT, cutover plan. Replaces an earlier stub that incorrectly
referenced html5-qrcode.
- docs/features/dispatch.md — new. Dispatch board, scheduling, tags,
travel-time optimization, magic-link SMS, SSE updates.
- docs/features/customer-portal.md — new. Plan A passwordless magic-link
at portal.gigafibre.ca, Stripe self-service, file inventory.
- docs/architecture/module-interactions.md — new. One-page call graph
with sequence diagrams for the hot paths.
- docs/README.md — expanded module index (§2) now lists every deployed
surface with URL + primary doc + primary code locations (was missing
dispatch, tickets, équipe, rapports, telephony, network, agent-flows,
OCR, every customer-portal page). New cross-module edge map in §4.
- docs/features/README.md + docs/architecture/README.md — cross-link
all new docs.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
330 lines
22 KiB
Markdown
330 lines
22 KiB
Markdown
# Module Interactions
|
|
|
|
> The cross-cutting "who talks to whom" reference for the Gigafibre FSM
|
|
> codebase. Every feature doc links here for dependency questions instead of
|
|
> duplicating a matrix of its own.
|
|
>
|
|
> Sibling docs: [overview.md](overview.md) (containers, networks, Traefik) ·
|
|
> [data-model.md](data-model.md) (ERPNext doctypes).
|
|
|
|
---
|
|
|
|
## 1. Purpose
|
|
|
|
The codebase is split into a handful of frontends (`apps/`), a Node.js API
|
|
gateway (`services/targo-hub/`), a couple of side-car services, and a long tail
|
|
of external integrations (ERPNext, Stripe, Twilio, GenieACS, etc.). Any given
|
|
feature crosses three or four of these layers, which means the interesting
|
|
question is rarely "what does module X do" but "when module X does its job,
|
|
who does it call, and who reacts to the side-effect?".
|
|
|
|
This document is the single authoritative answer to that question. Feature
|
|
docs in [../features/](../features/README.md) describe a capability end-to-end
|
|
and link back here for the cross-module picture. If you are adding a new
|
|
module or moving an existing call, update this file first.
|
|
|
|
---
|
|
|
|
## 2. Module catalog
|
|
|
|
### 2.1 Apps (frontends)
|
|
|
|
| Module | Host / Route | Owns | Feature doc |
|
|
|---|---|---|---|
|
|
| `apps/ops` | `erp.gigafibre.ca/ops/#/*` | Staff PWA — Dispatch, Clients, Tickets, Équipe, Rapports, OCR, Téléphonie, Agent Flows, Réseau, Settings. Also serves the tech mobile tree `/j/*` (same bundle, different layout). Routes registered in [`apps/ops/src/router/index.js`](../../apps/ops/src/router/index.js). | [billing-payments.md](../features/billing-payments.md) · [cpe-management.md](../features/cpe-management.md) · [vision-ocr.md](../features/vision-ocr.md) · [flow-editor.md](../features/flow-editor.md) |
|
|
| `apps/client` | `portal.gigafibre.ca/#/*` | Customer portal PWA — Dashboard, Invoices, Tickets, Messages, Account, Catalog/Cart, payment return landings. Hash router, routes in [`apps/client/src/router/index.js`](../../apps/client/src/router/index.js). Plan A: passwordless magic-link JWT, no Authentik. | [billing-payments.md §4](../features/billing-payments.md) |
|
|
| `apps/field` | `/field/` (legacy) | Original Quasar tech app. Being retired in favour of `apps/ops` `/j/*` + SMS magic-link. See §8. | — |
|
|
| `apps/website` | `www.gigafibre.ca` | React + Vite marketing site. Lead-capture form posts to targo-hub `/api/checkout` / `/api/order`. Built from [`apps/website/`](../../apps/website/). | — |
|
|
| `apps/portal` | (Traefik only) | Not a runtime. Holds [`traefik-client-portal.yml`](../../apps/portal/traefik-client-portal.yml) which permanently redirects the legacy host `client.gigafibre.ca` → `portal.gigafibre.ca`, so stale SMS and bookmarks keep working. | — |
|
|
|
|
### 2.2 Services (backends we own)
|
|
|
|
| Module | Host / Port | Owns | Notes |
|
|
|---|---|---|---|
|
|
| `services/targo-hub` | `msg.gigafibre.ca:3300` | API gateway / monolith. Routes registered in [`services/targo-hub/server.js`](../../services/targo-hub/server.js). Every external integration (Twilio, Stripe, Mailjet, Gemini, GenieACS, Oktopus, Traccar) is reached *through* the hub — frontends never hit those directly. | Lib catalog below. |
|
|
| `services/modem-bridge` | `:3301` (internal) | Playwright + headless Chromium driving TP-Link XX230v web UI to read encrypted TR-181 parameters the vendor exposes only via client-side JS. Token auth, internal network only. | [`services/modem-bridge/server.js`](../../services/modem-bridge/server.js) |
|
|
| `services/docuseal` | `sign.gigafibre.ca` | DocuSeal container for commercial-contract e-signature. Residential contracts use a JWT-based acceptance flow (see `lib/acceptance.js` + `lib/contracts.js`) — DocuSeal is the commercial track. | — |
|
|
| `services/legacy-db` | `10.100.80.100:3307` | Read-only MariaDB bridge into the old PHP `gestionclient` database. Used by migration scripts and a handful of lookup queries in `lib/auth.js`. | [`services/legacy-db/docker-compose.yml`](../../services/legacy-db/docker-compose.yml) |
|
|
|
|
#### targo-hub library map (`services/targo-hub/lib/`)
|
|
|
|
One file per capability; the router in `server.js` dispatches by path prefix.
|
|
|
|
| File | Mount point | Responsibility |
|
|
|---|---|---|
|
|
| `acceptance.js` | `/accept*` | Residential contract acceptance tokens — JWT sign + signed-blob storage on Quotation. |
|
|
| `address-search.js` | (lib) | Calls the Supabase `rddrjzptzhypltuzmere` RQA address database for autocomplete. |
|
|
| `agent.js` | `/agent/*` | LLM agent runtime + tool-use dispatch (reads `agent-tools.json`). |
|
|
| `ai.js` | `/ai/*` | Internal AI decision-making powered by Gemini Flash (classification, summaries, suggestions). |
|
|
| `auth.js` | `/auth/*` | Staff auth bridge — reads Authentik headers, optionally checks legacy `gestionclient` MySQL for migration. |
|
|
| `checkout.js` | `/api/catalog`, `/api/checkout`, `/api/accept-for-client`, `/api/order`, `/api/address`, `/api/otp` | Onboarding wizard: catalog → cart → OTP → order. Creates ERPNext Lead/Quotation/Customer. |
|
|
| `config.js` | (lib) | Environment variable accessor with typed defaults. |
|
|
| `contracts.js` | `/contract*` | Signed-contract lifecycle — residential JWT and commercial DocuSeal. |
|
|
| `conversation.js` | `/conversations*` | Threaded inbox: SMS, email, web chat → ERPNext Communication / HD Ticket. |
|
|
| `device-extractors.js` | (lib) | Pure functions to extract WAN/LAN/WiFi/optical facts from GenieACS device JSON. |
|
|
| `device-hosts.js` | (lib) | `Device.Hosts.Host.{n}.*` TR-181 host list parser. |
|
|
| `devices.js` | `/devices`, `/acs/*` | GenieACS NBI proxy — device lookup, presets, tasks, diagnostic summary. |
|
|
| `dispatch.js` | `/dispatch*` | Dispatch Job assignment scoring, tech-to-job matching. |
|
|
| `email-templates.js` | (lib) | HTML templates for OTP, magic-link, invoice email. |
|
|
| `email.js` | (lib) | SMTP transport (Mailjet `in-v3.mailjet.com` per `config.js`). |
|
|
| `flow-api.js` | `/flow/start`, `/flow/advance`, `/flow/complete`, `/flow/event`, `/flow/runs/*` | HTTP endpoints for the flow runtime. |
|
|
| `flow-runtime.js` | (lib) | Execution engine for Flow Templates — `dispatchEvent()` is how modules fire triggers. |
|
|
| `flow-templates.js` | `/flow/templates*` | CRUD for Flow Template doctype. |
|
|
| `helpers.js` | (lib) | HTTP client, ERPNext REST wrapper (`erpFetch`, `erpRequest`), JSON response helper. |
|
|
| `ical.js` | `/dispatch/ical-token/:tech`, `/dispatch/calendar/:tech.ics` | Signed-token iCal feed — lets techs subscribe their jobs in Apple/Google Calendar. |
|
|
| `magic-link.js` | `/magic-link*` | JWT sign/verify + one-time magic-link issuance. |
|
|
| `modem-bridge.js` | `/modem*` | Thin client for the modem-bridge sidecar. |
|
|
| `network-intel.js` | `/network/*` | InfluxDB + Grafana log analysis → Gemini summarization. |
|
|
| `oktopus-mqtt.js` | (worker) | MQTT subscriber to the Oktopus broker — compensates for their broken events-controller hook. |
|
|
| `oktopus.js` | `/oktopus/*` | Oktopus CE REST API — TR-369/USP device preauth and provisioning. |
|
|
| `olt-snmp.js` | `/olt*` | SNMP poller for ONU status / optical power on the OLTs. |
|
|
| `otp.js` | (lib) | One-time-password issuance + email/SMS delivery (used by checkout + portal). |
|
|
| `outage-monitor.js` | `/webhook/kuma` | Uptime-Kuma webhook ingest; cross-references OLT poller to synthesize mass-outage alerts. |
|
|
| `payments.js` | `/payments*`, `/webhook/stripe` | Stripe Checkout session creation, customer portal links, webhook verification, Payment Entry reconciliation, PPA cron. Fires `on_payment_received` flow trigger. |
|
|
| `pbx.js` | `/webhook/3cx/call-event` | 3CX call-event webhook → ERPNext Communication. |
|
|
| `portal-auth.js` | `/portal/*` | Passwordless magic-link endpoint `POST /portal/request-link` — rate-limited, anti-enumeration. |
|
|
| `project-templates.js` | (lib) | Hard-coded job-template catalogue (fibre_install, etc.) for dispatch. |
|
|
| `provision.js` | `/provision/*` | Device provisioning orchestrator — GenieACS preset push + Oktopus preauth. |
|
|
| `referral.js` | `/api/referral/*` | Referral credit validation + application, backing the wizard. |
|
|
| `reports.js` | `/reports*` | GL / revenue / tax / A/R reports, Frappe PostgreSQL queries. |
|
|
| `sse.js` | `/sse`, `/broadcast` | Server-Sent Events fan-out. Topics: `customer:*`, `conversations`, `dispatch`, etc. |
|
|
| `tech-absence-sms.js` | (worker) | Auto-notify customers when their tech is absent. |
|
|
| `tech-mobile.js` | `/t/*` | Lightweight mobile tech page (predecessor of `/j/*`). |
|
|
| `telephony.js` | `/telephony/*` | Routr / identity SIP pool management. |
|
|
| `traccar.js` | `/traccar*` | Traccar REST API proxy (GPS fleet tracker). |
|
|
| `twilio.js` | `/send/sms`, `/webhook/twilio/sms-*`, `/voice/*` | Twilio SMS send + inbound webhook + Voice tokens + TwiML. |
|
|
| `vision.js` | `/vision/barcodes`, `/vision/equipment`, `/vision/invoice` | Gemini 2.5 Flash vision OCR endpoints. |
|
|
| `voice-agent.js` | `/voice/inbound`, `/voice/gather`, `/voice/connect-agent`, `/voice/ws` | Twilio IVR + agent runtime with Media Streams WebSocket. |
|
|
|
|
### 2.3 External integrations
|
|
|
|
| Provider | Used from | Channel | Purpose |
|
|
|---|---|---|---|
|
|
| ERPNext v16 / Frappe | hub (`helpers.erpFetch`), ops (nginx proxy), client (via hub) | REST `/api/resource`, `/api/method` | Source of truth for Customer, Subscription, Invoice, Ticket, Dispatch Job, etc. |
|
|
| Authentik SSO | Traefik ForwardAuth middleware | Header injection | Guards staff surfaces (`/ops/`, `n8n.`, `hub.`). |
|
|
| Twilio | `lib/twilio.js`, `lib/voice-agent.js` | SMS / Voice (REST + webhook + Media Streams WS) | SMS magic-links, OTP, IVR, Voice tokens. |
|
|
| Stripe | `lib/payments.js` | REST + webhook (`Stripe-Signature`) | Checkout sessions, Billing Portal, card-on-file, Klarna BNPL. |
|
|
| Mailjet | `lib/email.js` (SMTP `in-v3.mailjet.com`) | SMTP | Transactional email (OTP, invoices, magic-links). |
|
|
| GenieACS | `lib/devices.js`, `helpers.nbiRequest` | NBI REST / TR-069 | Modem provisioning + diagnostics (legacy CPE fleet). |
|
|
| Oktopus CE | `lib/oktopus.js`, `lib/oktopus-mqtt.js` | REST + MQTT (TR-369/USP) | New CPE fleet. |
|
|
| Traccar | `lib/traccar.js` | REST + WebSocket | GPS breadcrumbs for field techs. |
|
|
| Gemini 2.5 Flash | `lib/vision.js`, `lib/ai.js` | REST (`generativelanguage.googleapis.com`) | Vision OCR + internal AI. |
|
|
| n8n | (via Authentik header proxy) | REST | Workflow automation — long-running batch jobs. |
|
|
| DocuSeal | `sign.gigafibre.ca` | REST + webhook | Commercial contract e-signature. |
|
|
|
|
---
|
|
|
|
## 3. Interaction matrix
|
|
|
|
Row = caller, column = callee. Cell = primary channel (`REST`, `SSE`, `WS`,
|
|
`MQTT`, `SMS`, `SMTP`, `TR-069`, `TR-369`, `Webhook`, `JWT`, `ForwardAuth`,
|
|
iCal). Empty = no direct interaction (use the hub as a relay).
|
|
|
|
| caller ↓ / callee → | ops | client | field | hub | ERPNext | GenieACS | Oktopus | Traccar | Twilio | Stripe | Gemini | Authentik | DocuSeal |
|
|
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
| **ops** | — | — | — | REST + SSE | REST (nginx proxy w/ token) | — (via hub) | — (via hub) | WS (via hub proxy) | — (via hub) | — (via hub) | — (via hub `/vision`) | ForwardAuth | — |
|
|
| **client** | — | — | — | REST + SSE | — (via hub) | — | — | — | — (via hub) | — (via hub `checkout-link`) | — (via hub) | JWT (magic-link, not Authentik) | — |
|
|
| **field** | — | — | — | REST | REST (legacy, token) | — | — | — | — | — | — | — | — |
|
|
| **website** | — | — | — | REST (`/api/checkout`, `/api/order`) | — (via hub) | — | — | — | — | — | — | — | — |
|
|
| **hub** | SSE | SSE | — | — | REST (`erpFetch`, `Authorization: token ...`) | REST (NBI `nbiRequest`) | REST + MQTT | REST + WS | SMS REST + Webhook | REST + Webhook (`Stripe-Signature`) | REST | — | REST + Webhook |
|
|
| **modem-bridge** | — | — | — | — (hub calls it, `:3301`) | — | — | — | — | — | — | — | — | — |
|
|
| **ERPNext** | — | — | — | — (fire-and-forget HTTP to hub `/flow/*` from server scripts) | — | — | — | — | — | — | — | — | — |
|
|
| **Traefik** | Forwards | Forwards | Forwards | Forwards | Forwards | — | — | — | — | — | — | ForwardAuth | — |
|
|
| **Twilio** | — | — | — | Webhook (`/webhook/twilio/*`) | — | — | — | — | — | — | — | — | — |
|
|
| **Stripe** | — | — | — | Webhook (`/webhook/stripe`) | — | — | — | — | — | — | — | — | — |
|
|
| **DocuSeal**| — | — | — | Webhook (`/contract/*`) | — | — | — | — | — | — | — | — | — |
|
|
|
|
---
|
|
|
|
## 4. Canonical flows
|
|
|
|
Six flows cover 90 % of the interactions. Each is grounded in the hub lib
|
|
file that drives it.
|
|
|
|
### 4.1 New customer onboarding
|
|
|
|
```text
|
|
apps/website (React lead form)
|
|
│ POST /api/order (lib/checkout.js)
|
|
▼
|
|
targo-hub ──► address-search.js ──► Supabase RQA
|
|
──► otp.js ──► Twilio SMS + Mailjet SMTP
|
|
──► erpFetch ──► ERPNext (Lead → Quotation → Customer)
|
|
──► payments.js ──► Stripe Checkout session
|
|
│
|
|
│ user pays
|
|
▼
|
|
Stripe ──webhook──► hub /webhook/stripe (lib/payments.js, Stripe-Signature verify)
|
|
├─► ERPNext Payment Entry
|
|
├─► flow-runtime.dispatchEvent('on_payment_received')
|
|
│ ├─► contracts.js (DocuSeal or JWT accept)
|
|
│ └─► dispatch.js (create Dispatch Job)
|
|
└─► sse.broadcast('customer:<name>', ...)
|
|
```
|
|
|
|
Feature doc: [billing-payments.md](../features/billing-payments.md).
|
|
|
|
### 4.2 Tech dispatches a job
|
|
|
|
```text
|
|
apps/ops /dispatch (DispatchPage.vue)
|
|
│ drag-drop assignment, POST /dispatch/assign (lib/dispatch.js)
|
|
▼
|
|
hub ──► ERPNext Dispatch Job (scoring weights in dispatch.js)
|
|
──► twilio.sendSms → customer cell: "/j/<JWT>"
|
|
│
|
|
▼
|
|
Tech opens /j/<JWT> on phone (apps/ops/src/modules/tech/*)
|
|
│ JWT verified by magic-link.js
|
|
│ GPS check-in → traccar.js → Traccar REST → WS breadcrumbs back to ops
|
|
│ Scan device barcode → useScanner → /vision/barcodes → Gemini
|
|
│ Mark complete → flow-runtime.dispatchEvent('on_job_complete')
|
|
▼
|
|
ops SSE topic `dispatch` refreshes the Kanban
|
|
```
|
|
|
|
Feature docs: [cpe-management.md](../features/cpe-management.md) (device scan) ·
|
|
[vision-ocr.md](../features/vision-ocr.md) (OCR pipeline) ·
|
|
[flow-editor.md](../features/flow-editor.md) (post-job trigger).
|
|
|
|
### 4.3 Customer pays an invoice from the portal
|
|
|
|
```text
|
|
apps/client /#/invoices/INV-0000123 (InvoiceDetailPage.vue)
|
|
│ click "Payer maintenant"
|
|
│ POST /billing/checkout-link (lib/payments.js)
|
|
▼
|
|
hub ──► Stripe Checkout session (mode=payment, client_reference_id=INV-…)
|
|
│ browser redirects to Stripe, user pays
|
|
▼
|
|
Stripe ──webhook──► hub /webhook/stripe
|
|
├─► ERPNext Payment Entry
|
|
├─► flow-runtime.dispatchEvent('on_payment_received')
|
|
└─► sse.broadcast('customer:<name>', 'invoice.paid', …)
|
|
▼
|
|
Both apps/ops (cust detail) and apps/client (dashboard) update live over SSE.
|
|
```
|
|
|
|
### 4.4 Customer requests support
|
|
|
|
```text
|
|
apps/client /#/messages (MessagesPage.vue)
|
|
│ POST /conversations (lib/conversation.js)
|
|
▼
|
|
hub ──► ERPNext HD Ticket (erpFetch)
|
|
──► sse.broadcast('conversations', …)
|
|
▼
|
|
apps/ops /tickets (TicketsPage.vue) — live refresh
|
|
│ staff replies → conversation.handle → ERPNext Communication
|
|
▼
|
|
hub ──► twilio.sendSms (if customer channel = SMS) or email.js (if email)
|
|
```
|
|
|
|
### 4.5 Modem diagnostic from ops
|
|
|
|
```text
|
|
apps/ops /network (NetworkPage.vue)
|
|
│ GET /devices/lookup?serial=… (lib/devices.js)
|
|
▼
|
|
hub ──► GenieACS NBI (helpers.nbiRequest)
|
|
│ if TP-Link XX230v → ask modem-bridge :3301 for encrypted params
|
|
▼
|
|
modem-bridge ──► Playwright headless Chromium ──► modem web UI (172.17.x.x:443)
|
|
──► decrypted TR-181 JSON back to hub
|
|
▼
|
|
hub ──► device-extractors.summarizeDevice → consolidated JSON
|
|
▼
|
|
ops UI renders WAN / LAN / WiFi / Optical; SSE topic `customer:<name>`
|
|
updates any other ops tab watching the same client.
|
|
```
|
|
|
|
Feature doc: [cpe-management.md](../features/cpe-management.md).
|
|
|
|
### 4.6 Passwordless login (customer)
|
|
|
|
```text
|
|
apps/client /#/login (LoginPage.vue)
|
|
│ POST /portal/request-link (lib/portal-auth.js)
|
|
│ rate-limited, anti-enumeration (constant-time response)
|
|
▼
|
|
hub ──► otp.js / magic-link.js (JWT HS256, 24h exp)
|
|
──► twilio.sendSms ("Votre lien: https://portal.gigafibre.ca/#/?token=…")
|
|
──► email.js (SMTP Mailjet)
|
|
▼
|
|
customer clicks link → apps/client router guard hydrates store from token
|
|
→ store.customerId set → dashboard loads
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Authentication matrix
|
|
|
|
| Surface | Mechanism | Source of trust | Notes |
|
|
|---|---|---|---|
|
|
| Staff apps (ops, n8n, hub UI) | Authentik ForwardAuth via Traefik `authentik-client@file` middleware | `id.gigafibre.ca` session cookie | Headers `X-Authentik-Email`, `X-Authentik-Groups` injected into upstream. |
|
|
| Customer portal (`apps/client`) | 24 h HS256 JWT (magic-link) | `lib/magic-link.js` secret | No Authentik; no password form reachable (Traefik redirects legacy `/login`). |
|
|
| Tech mobile (`/j/*` inside `apps/ops`) | 24 h HS256 JWT sent by SMS | Same secret | Same JWT primitive as portal. |
|
|
| hub → ERPNext | `Authorization: token <key>:<secret>` (Frappe API token) | ERPNext Administrator token — never regenerate ("Generate Keys" in ERPNext UI breaks the hub) | Used by `helpers.erpFetch`. |
|
|
| ops → ERPNext (frontend) | nginx same-origin reverse proxy injects the same token server-side | Token stays server-side; browser never sees it | See `apps/ops/infra/`. |
|
|
| Webhooks | Per-provider signature verification | `Stripe-Signature`, `X-Twilio-Signature`, DocuSeal shared secret | Rejected synchronously before any side-effect. |
|
|
| `modem-bridge` | Shared bearer token | env var on both hub + bridge containers | Internal network only, not exposed via Traefik. |
|
|
| Traccar | Bearer token preferred (fall-back Basic) | env var on hub | See `lib/traccar.js` header note. |
|
|
|
|
---
|
|
|
|
## 6. Data ownership
|
|
|
|
Source of truth per entity. "Hub cache" means the hub keeps a read-through
|
|
copy; the authoritative write goes to the listed owner.
|
|
|
|
| Entity | Source of truth | Hub role | Frontend read path |
|
|
|---|---|---|---|
|
|
| Customer | ERPNext `Customer` | thin pass-through (`erpFetch`) | ops: direct via nginx proxy · client: via hub |
|
|
| Address (Service Location) | ERPNext `Service Location` | pass-through; `address-search.js` also calls Supabase RQA for autocomplete | — |
|
|
| Subscription | ERPNext `Subscription` | pass-through | — |
|
|
| Invoice | ERPNext `Sales Invoice` | pass-through + `/vision/invoice` OCR | — |
|
|
| Payment | ERPNext `Payment Entry` | written from Stripe webhook in `lib/payments.js` | — |
|
|
| Ticket | ERPNext `HD Ticket` | written from `lib/conversation.js` | — |
|
|
| Dispatch Job | ERPNext `Dispatch Job` (custom doctype) | written from `lib/dispatch.js` | ops SSE topic `dispatch` |
|
|
| Technician | ERPNext `Technician` (custom) | pass-through | — |
|
|
| Service Equipment | ERPNext `Service Equipment` (custom) | pass-through | — |
|
|
| Tag | ERPNext `Tag` | pass-through | — |
|
|
| Flow Template | ERPNext `Flow Template` (custom) | CRUD in `lib/flow-templates.js` | ops `/agent-flows` |
|
|
| Flow Run | ERPNext `Flow Run` (custom) | written from `lib/flow-runtime.js` | ops `/agent-flows` |
|
|
| **Exceptions** | | | |
|
|
| Live modem state (WAN IP, SNR, host list) | GenieACS / Oktopus | hub never persists — always fetched live | — |
|
|
| GPS breadcrumbs | Traccar | hub proxies WS + REST | ops `/dispatch` map |
|
|
| JWT sessions | hub process memory + browser `localStorage` | — | no server-side session store |
|
|
|
|
---
|
|
|
|
## 7. Real-time channels
|
|
|
|
| Channel | Transport | Producer | Consumer | Topic / payload |
|
|
|---|---|---|---|---|
|
|
| Ops / portal live updates | SSE | `lib/sse.js` | `apps/ops`, `apps/client` | `customer:<name>`, `conversations`, `dispatch`, ad-hoc |
|
|
| TR-369 device events | MQTT | Oktopus broker | `lib/oktopus-mqtt.js` worker | device-online, heartbeat |
|
|
| GPS breadcrumbs | WebSocket | Traccar | `lib/traccar.js` proxy → ops dispatch | `deviceId`, `latitude`, `longitude`, `speed` |
|
|
| Twilio Media Streams | WebSocket upgrade on hub `/voice/ws` | Twilio | `lib/voice-agent.js` | raw audio frames for IVR agent |
|
|
| SSE keep-alive | server-pushed `: ping` | `lib/sse.js` 25 s interval | browsers | heartbeat |
|
|
|
|
---
|
|
|
|
## 8. Retired / in-retirement
|
|
|
|
Do not build new features on these. Linked target is where the replacement
|
|
lives.
|
|
|
|
| Retiring | Replacement | Status | Notes |
|
|
|---|---|---|---|
|
|
| `auth.targo.ca` | `id.gigafibre.ca` (Authentik) | active migration | ForwardAuth middleware already on the new host. |
|
|
| `apps/field` | `apps/ops` `/j/*` tree | in retirement | Same bundle, different layout — removes the separate deploy surface. |
|
|
| `dispatch-app` (legacy standalone) | `apps/ops` `/dispatch` | retired | Feature parity reached; decommission pending DNS cleanup. |
|
|
| `client.gigafibre.ca` | `portal.gigafibre.ca` | retired | Traefik permanent-redirect via [`apps/portal/traefik-client-portal.yml`](../../apps/portal/traefik-client-portal.yml). |
|
|
| Frappe `/login` form for customers | `/portal/request-link` magic-link | retired | Legacy MD5 hashes not migrated — customers forced through the new flow. |
|
|
| Ollama local `llama3.2-vision` | `lib/vision.js` → Gemini 2.5 Flash | retired | Ops VM has no GPU. |
|
|
|
|
---
|
|
|
|
Back to [docs/README.md](../README.md).
|