Files
hermes-skills/skills/software-development/portal-ui-mockups/references/ops-portal-asset-architecture.md
T

5.2 KiB

Ops Portal — Shared Asset Architecture

Created: 2026-07-08 Updated: 2026-07-08 (added config.html, cron.html, static-data-fallback pattern) Domain: ops.itpropartner.com Root: /var/www/ops/

Files produced this session

File Purpose Size
/var/www/ops/nav.html Shared navigation partial (responsive, 8 links, hamburger mobile) 5.8 KB
/var/www/ops/template.html Base page template (CSS ref + nav injection + utils + footer) 1.9 KB
/var/www/ops/css/ops.css Full shared stylesheet extracting all inline styles from dashboard 10 KB (355 lines)
/var/www/ops/js/utils.js Shared utility functions (fetch status, format, badges, colors) 6.2 KB (196 lines)
/var/www/ops/services.html Demo page showing the pattern (Services + API health) 4.7 KB
/var/www/ops/network.html Network infrastructure page: health overview, MikroTik placeholder, Ubiquiti placeholder, DNS/domains table with Cloudflare fallback, API/port health check grid 30 KB (920 lines)
/var/www/ops/logs.html Logs & Events page: API health timeline, service events grid, cron error card, multi-source failure summary with filter bar 30 KB (1005 lines)
/var/www/ops/config.html Config page: versions, config files table, environment grid, infra diagram link. Uses static embedded data + live API fallback pattern (see below) 14.8 KB (480 lines)
/var/www/ops/cron.html Cron jobs detail page added in same batch
/var/www/ops/backups.html Backup status page added in same batch
/var/www/ops/tracker-mockup.html FleetTracker360 live GPS dashboard concept (Cartagena trip scenario, pure CSS, no Tailwind) 37.9 KB (1088 lines)

Caddy config (from /etc/caddy/Caddyfile)

ops.itpropartner.com {
    root * /var/www/ops
    encode gzip
    file_server
    header /data/* {
        -Access-Control-Allow-Origin
        Access-Control-Allow-Origin "*"
    }
    log {
        output file /var/log/caddy/ops.log
    }
}

Data source

/data/ops-status.json — produced by the ops-data-collector cron job. Structure:

  • timestamp — ISO 8601 generation time
  • cron_jobs[] — array with name, schedule, lastRun, status, script
  • services — object mapping service names to status strings (active/inactive)
  • disk_memorydisk_used_pct, memory_used_pct
  • s3_backups — object mapping bucket names to {status, last_upload, age_hours}
  • api_checks — object mapping API names to status strings
  • versionshermes, caddy, python, os
  • netcup_server — object with id, template, ip
  • hetzner_servers[] — array with name, status, type, ip, id

Key patterns

  • Nav injection: <div id="nav"></div> + fetch('/nav.html'). Inline JS in nav.html handles active-page detection.
  • Data loading: fetchStatus() from utils.js, then render each section. 30-second auto-refresh via setInterval.
  • File mode fix: Always chmod 644 after write_file — Caddy 403s on mode 600 files.

Static data + live API fallback pattern (used by config.html)

Some ops portal pages combine embedded static data with live API enrichment. This is useful when:

  1. The page needs data the collector doesn't provide (e.g. file sizes, script counts, hardware specs)
  2. The data changes infrequently enough that a page-rebuild is acceptable
  3. The page should work even when the API is down

Pattern (from config.html):

// 1. Define embedded data as top-level JS objects in the page
var CONFIG_FILES = [
  { path: '/etc/caddy/Caddyfile', size: '2.8 KB', size_bytes: 2870, ... },
  // ...
];

var ENV_DATA = {
  hostname: 'core',
  os: 'Debian GNU/Linux 13 (trixie)',
  // ...
};

// 2. Render embedded data immediately (no API dependency)
renderConfigFiles();
renderEnvironment();

// 3. Try API fetch for enrichment — versions overlay onto embedded cards
fetchStatus()
  .then(function(data) {
    renderVersions(data);   // overrides version cards with live data
    // API failure is non-fatal — embedded data is already shown
  })
  .catch(function(err) {
    // API failed — embedded data is already displayed, so not critical
    console.warn('API fetch failed:', err);
  });

When to use this pattern vs pure API-driven:

  • Pure API-driven (index.html, services.html): All content comes from the JSON. Page is empty until fetch completes. Ideal when the collector already supplies everything needed.
  • Static + API fallback (config.html): Some sections rendered immediately from embedded data. API enriches versions/freshness if available. Use when the collector doesn't/can't know some data (file timestamps, hardware specs, script inventory).

All existing pages

  • /var/www/ops/index.html — main dashboard
  • /var/www/ops/servers.html — server inventory
  • /var/www/ops/backups.html — backup status
  • /var/www/ops/services.html — services demo
  • /var/www/ops/network.html — network infrastructure
  • /var/www/ops/logs.html — logs & events
  • /var/www/ops/cron.html — cron jobs
  • /var/www/ops/config.html — system configuration
  • /var/www/ops/nav.html — the nav partial itself
  • /var/www/ops/tracker-mockup.html — FleetTracker360 live GPS dashboard concept (see references/fleet-tracker-dashboard.md)