77 lines
4.4 KiB
Markdown
77 lines
4.4 KiB
Markdown
---
|
|
name: ops-portal-pipeline
|
|
version: 2.0.0
|
|
---
|
|
|
|
# Ops Portal — Architecture & Common Failure Modes
|
|
|
|
Created 2026-07-08 for ops.itpropartner.com. Updated 2026-07-12 with JS architecture fixes.
|
|
|
|
## Data Pipeline
|
|
|
|
Data Collector (every 5 min) → `/root/.hermes/scripts/ops-data-collector.py` → writes `/var/www/ops/data/ops-status.json` → dashboard pages fetch `/data/ops-status.json`
|
|
|
|
## JS Architecture
|
|
|
|
The portal uses TWO JavaScript files loaded on every page:
|
|
|
|
| File | Location | What it exports |
|
|
|------|----------|----------------|
|
|
| `utils.js` | `/opt/ops-portal/static/js/utils.js` | `window.initNav`, `window.fetchStatus`, `window.escapeHtml`, `window.statusBadge`, `window.fmtTime` |
|
|
| `app.js` | `/opt/ops-portal/static/js/app.js` | `window.Ops` (login, logout, isAuthenticated, getStatus, etc.), `window.initNav`, `window.escapeHtml`, `window.fmtTime`, `window.fetchStatus` |
|
|
|
|
**Both files MUST exist in the backend's static directory** (`/opt/ops-portal/static/js/`). A second copy lives at `/var/www/ops/js/` for Caddy's static file handler (controlled by `/etc/caddy/Caddyfile`). If either location goes out of sync, pages will 404 on one of the files.
|
|
|
|
**Critical failure (Jul 12):** `utils.js` existed at `/var/www/ops/js/utils.js` but NOT at `/opt/ops-portal/static/js/utils.js` — all pages referencing `/js/utils.js` got a 404. Fix: copy `utils.js` to the backend's static directory.
|
|
|
|
## Auth Flow
|
|
|
|
```
|
|
Page loads → app.js checks localStorage.getItem('ops_token')
|
|
→ token exists? → hide login overlay, load page data
|
|
→ no token? → show login overlay, wait for credentials
|
|
→ user submits → POST /api/auth/login → store token in localStorage → load page
|
|
```
|
|
|
|
**Token storage:** `localStorage` (NOT `sessionStorage`). `localStorage` persists across tabs and browser restarts.
|
|
|
|
**Credentials:** `germaine` / `itpp2026!` (stored in `/root/.hermes/.env` as `ADMIN_PASSWORD`).
|
|
|
|
## Nav Loading Pattern
|
|
|
|
Pages either have inline nav (index.html) or fetch nav dynamically:
|
|
|
|
**Fetched nav pattern (most pages):**
|
|
```html
|
|
<div id="nav"></div>
|
|
...
|
|
fetch('/nav.html').then(function(r) { return r.text(); }).then(function(h) {
|
|
document.getElementById('nav').innerHTML = h;
|
|
if (typeof Ops.initNav === 'function') Ops.initNav();
|
|
}).catch(function(e) { console.warn('Nav failed:', e); });
|
|
```
|
|
|
|
**The hamburger toggle** has an inline `onclick` handler that toggles `classList.toggle('open')` on `#navLinks`. The `Ops.initNav()` function used to ALSO add an `addEventListener` that did the same toggle — causing both handlers to fire on every click, cancelling each other out. **Fixed Jul 12:** removed the duplicate toggle from `initNav()`. initNav now only handles:
|
|
1. Active-link highlighting (`.nav-link.active`)
|
|
2. Closing the menu when tapping outside (`document click` listener)
|
|
|
|
## Common Failure Modes & Fixes
|
|
|
|
| Symptom | Root Cause | Fix |
|
|
|---------|------------|-----|
|
|
| Hamburger does nothing on mobile | Duplicate toggle handler (inline onclick + initNav addEventListener) | Remove addEventListener from initNav; inline onclick is sufficient |
|
|
| Hamburger does nothing (2) | `window.initNav` is undefined | Export `window.initNav = initNav` in app.js |
|
|
| Login overlay shows on every page load | `audit.html` uses `sessionStorage` not `localStorage` | Change to `localStorage` |
|
|
| Services/servers/backups crash on load | `getElementById('logoutBtn')` called before nav fetch completes | Wrap in interval or move inside fetch callback |
|
|
| cost.html blank on load | `window.fetchStatus` is undefined | Export `window.fetchStatus = fetchStatus` in app.js |
|
|
| config.html renders nothing | `window.escapeHtml` is undefined | Export `window.escapeHtml = escapeHtml` in app.js |
|
|
| JS/CSS files return 403 | File permissions are 600 | `chmod 644` on all static files |
|
|
| JS returns wrong content-type | Backend FileResponse without explicit media_type | Set `media_type` explicitly or serve through Caddy `file_server` |
|
|
| Pages load stale broken JS | Browser cached old version | Add `Cache-Control: no-cache` headers and `?v=N` on script tags |
|
|
| API shows 6/7 with one failing | `cloudflare_zones` returns a list, not "ok" string | Filter it out of `api_checks` in collector |
|
|
| Nav loaded but menu doesn't toggle | nav.html fetched but inline onclick not firing | Verify `onclick` attribute exists on toggle in nav.html |
|
|
|
|
## All Pages
|
|
|
|
/, /services.html, /servers.html, /backups.html, /network.html, /cron.html, /config.html, /logs.html, /audit.html, /cost.html, /fleettracker360.html, /nav.html
|