Initial commit — Zabbix Dashboard app

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-20 17:10:57 +02:00
commit 8b38d6a9c9
13 changed files with 2524 additions and 0 deletions

98
CLAUDE.md Normal file
View File

@@ -0,0 +1,98 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commands
```bash
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:
```bash
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 legacy `auth` body field)
- All hosts are scoped by tag `customer=QWE` (operator `1` = equals)
- Host groups use `selectHostGroups` / `host.hostgroups` — NOT the deprecated `selectGroups` / `host.groups`
- Countries come from a `country` host tag (e.g. `AT`, `SK`)
- Restaurants group by `location` host tag; device types group by host group, keyed as `groupid__country`
- Severity 0 (Not classified) and 1 (Information) are treated as OK throughout
- Acknowledged problems are excluded via `withLastEventUnacknowledged: true`
- `expandDescription: true` on `trigger.get` resolves `{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
1. Boot: fetch `/api/config`, then fetch grid data + `/api/stats` in parallel
2. Items are grouped by `country` field and rendered as equal-width `.country-col` columns
3. Each column renders: flag + country header → hex grid → problem list (locations with active problems)
4. Single click → detail modal (`/api/detail`); double click → Zabbix Problems page in new tab (240ms timer separates the two)
5. 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:
```css
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) |