4.2 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Commands
npm install # install dependencies
npm start # run production server (port 3000)
npm run dev # run with --watch (auto-restart on file changes)
There are no tests or linters configured.
Docker:
docker-compose up -d --build
Architecture
Single-process Node.js app. server.js acts as both an API proxy (keeping the Zabbix token server-side) and a static file host for the vanilla JS frontend in public/.
server.js Express backend — Zabbix JSON-RPC proxy + REST API
public/
index.html Shell: header, view toggle, countries layout container, modal
style.css Light theme, CSS variables for hex sizing, hex grid layout
app.js All frontend logic — fetch, render, interactions
.env Runtime config (gitignored)
docker-compose.yml Reads ZABBIX_TOKEN from env or .env
Config (env vars)
| Variable | Default | Purpose |
|---|---|---|
ZABBIX_URL |
https://monitor.stranto.com |
Zabbix instance |
ZABBIX_TOKEN |
— | API bearer token (required) |
CUSTOMER_TAG_VALUE |
QWE |
Zabbix host tag customer=<value> that identifies KFC hosts |
COUNTRY_TAG |
country |
Zabbix host tag name used for country grouping |
PORT |
3000 |
HTTP port |
Zabbix API conventions
- Auth:
Authorization: Bearer <token>header (Zabbix 6.4+ style — NOT the legacyauthbody field) - All hosts are scoped by tag
customer=QWE(operator1= equals) - Host groups use
selectHostGroups/host.hostgroups— NOT the deprecatedselectGroups/host.groups - Countries come from a
countryhost tag (e.g.AT,SK) - Restaurants group by
locationhost tag; device types group by host group, keyed asgroupid__country - Severity 0 (Not classified) and 1 (Information) are treated as OK throughout
- Acknowledged problems are excluded via
withLastEventUnacknowledged: true expandDescription: trueontrigger.getresolves{HOST.NAME}and other macros in descriptions
Backend API endpoints
| Endpoint | Returns |
|---|---|
GET /api/restaurants |
Array of { location, country, hostCount, problemCount, severity, problems[] } |
GET /api/devices |
Same shape but grouped by host group; name instead of location |
GET /api/stats |
{ [countryCode]: { hostCount, itemCount, triggerCount } } — fetched in parallel with grid data |
GET /api/detail?type=&id= |
Per-host problem detail for modal |
GET /api/config |
{ zabbixUrl, customerTagValue } |
GET /api/health |
Zabbix connectivity check |
Each problems[] entry: { description, priority, lastchange (unix seconds), hostName }, sorted by severity desc then time desc.
Frontend data flow
- Boot: fetch
/api/config, then fetch grid data +/api/statsin parallel - Items are grouped by
countryfield and rendered as equal-width.country-colcolumns - Each column renders: flag + country header → hex grid → problem list (locations with active problems)
- Single click → detail modal (
/api/detail); double click → Zabbix Problems page in new tab (240ms timer separates the two) - Auto-refresh every 30 seconds
Hex grid geometry
For equal gaps on all 6 sides, the hex dimensions must satisfy --hex-w = --hex-h × sin(60°) ≈ --hex-h × 0.866. The row vertical overlap is:
margin-top: calc(var(--hex-h) * -0.25 + var(--hex-gap) * 0.866);
Changing --hex-gap without updating margin-top will make diagonal gaps unequal. The even-row offset (margin-left on .hex-row.offset) is always (hex-w + hex-gap) / 2.
Zabbix Problems URL format (7.x)
Parameters use no filter_ prefix. Key params: show=1 (recent), evaltype=0, tags[N][tag/value/operator], groupids[], severities[]=2..5. The legacy filter_show, filter_tags, filter_set format does not work in Zabbix 7.x.
Severity mapping
| Zabbix priority | Label | Colour |
|---|---|---|
| -1 (no problems / ack / info) | OK | green #2da44e |
| 2 | Warning | amber #c69026 |
| 3 | Average | orange #e16f24 |
| 4 | High | red #d1242f |
| 5 | Disaster | purple #8250df (pulses) |