backend/.golangci.yml declared version: "2" on line 1 but used the
golangci-lint v1 schema below it: a top-level linters-settings key and
an issues.exclude-use-default key that does not exist in v2. Under v2
that config does not validate, so every threshold in it was inert --
lll fell back to its 120-column default rather than the intended 88,
and funlen, cyclop and dupl were not applied at all. The `0 issues.`
result the repo has been relying on was therefore meaningless.
Replace it with the org-standard file verbatim (sha256
021cc83f4e6fc7c31b95b34b846723dfcf20b66b7baeea1dc40406e643346bcb) and
repoint the golangci-lint pin in Dockerfile.backend from v2.7.2 to the
org-standard v2.12.2.
Guard against the config drifting from the standard again by asserting
its sha256 as the first step of the backend lint target. The check is a
local hash comparison against a constant in the Makefile: it needs no
network, fetches nothing, and adds no unpinned external reference to
the build path. It also catches a strictly larger class of breakage
than schema validation would, since a schema-valid but non-canonical
config is exactly how this file got into its broken state.
With the config actually loading, lll reports server.go:65 at 93
columns. Fix it, plus the two other over-long lines called out on the
issue (server.go:97 at 81 and reportbuf.go:166 at 88) which are inside
the 88-column lint limit but over the 77-column hard wrap in the Go
styleguide. All three were long //nolint justifications on the code
line; move the justification into a preceding comment block and leave
a short directive behind. No suppression is added or widened, and
.golangci.yml is not touched after the copy.
Drop the //nolint:wsl in server.go entirely rather than relocating it.
The standard config disables wsl, so the directive suppressed nothing;
removing it still yields `0 issues.`
Verified: `cd backend && make check` reports `0 issues.` and passes
with the network unavailable, root `make check` passes, and
`docker build -f Dockerfile.backend .` builds green against the pinned
v2.12.2.
Resolves#11. The frontend/root `Dockerfile` ran only `RUN yarn build`, so `script/lint` and `script/fmt-check` (prettier) never gated CI — only a broken build failed it. (`script/cibuild`'s comment even claimed "the Dockerfile runs make check", which was false.) The backend `Dockerfile.backend` already runs `make check`; nothing covered the frontend's lint/fmt-check.
Change (single file, `Dockerfile`):
- `apk add ... git` -> `apk add ... git make` (build stage needs `make`).
- `RUN yarn build` -> `RUN make check` — which runs `script/test` (`yarn build`, producing `dist/`) then `script/lint` + `script/fmt-check`. `dist/` is still produced in one build (no redundant rebuild); the final nginx runtime image is unchanged.
Verified via a fresh clone (a worktree's `.git` pointer breaks `vite`'s `git rev-parse`, so builds must come from a real checkout — as CI's `actions/checkout` provides): positive `docker build` succeeds with in-image `make check` green; a negative test (a prettier-violating but build-valid file) makes the build fail at `make check`, confirming CI now goes red on a check regression, not just a broken build.
Left open for review (not merged).
Co-authored-by: sneak <sneak@sneak.berlin>
Reviewed-on: #12
Co-authored-by: clawbot <clawbot@noreply.example.org>
Co-committed-by: clawbot <clawbot@noreply.example.org>
Redesigns host rows for portrait/mobile viewports (<=768px):
- Host info panel stacks on top, full width
- Sparkline renders full width below
- Each host row becomes taller to accommodate vertical layout
- Summary line wraps gracefully
- Header controls stack below title
Desktop layout is unchanged — all changes are inside a `@media (max-width: 768px)` query and CSS class hooks added to the HTML.
Closes#2
Co-authored-by: user <user@Mac.lan guest wan>
Reviewed-on: #5
Co-authored-by: clawbot <clawbot@noreply.example.org>
Co-committed-by: clawbot <clawbot@noreply.example.org>
Detect mobile devices via user agent and viewport width (<=768px).
On mobile, skip all checker initialization and render only the
header, description, and a styled 'Not yet available on mobile' box.
Desktop behavior is completely unchanged — the mobile check returns
early before any existing code runs.
Place the backend Dockerfile at repo root as Dockerfile.backend so
the build context includes .git, giving git describe access for
version stamping. Fix .gitignore pattern to anchor /netwatch-server
so it does not exclude cmd/netwatch-server/. Remove .git from
.dockerignore. Update CI workflow and backend Makefile docker target.
Update base image from golang:1.24-alpine to golang:1.25-alpine
to match go.mod requirement. Install golangci-lint by pinned commit
hash so make check passes inside the container. Update runtime
image to alpine:3.23.
Add .gitea/workflows/check.yml that builds both the root and
backend Docker images on push. Add LICENSE and README.md to the
backend subproject to match repo standards.
Introduce the Go backend (netwatch-server) with an HTTP API that
accepts telemetry reports and persists them as zstd-compressed JSONL
files. Reports are buffered in memory and flushed to disk when the
buffer reaches 10 MiB or every 60 seconds.
All updateHostRow() and greyOutUI() className assignments were
dropping col-span-2, causing the stats line to only span column 1
instead of both grid columns.
- Name cell gets min-w-[200px] so titles don't over-truncate
- Grid is flex-shrink-0 at 420px so it never squeezes
- Removed overflow-hidden that was clipping the stats line
Use minmax(0,1fr) for the name column so it can shrink below its
min-content width, and add overflow-hidden on the grid container
to clip any overflow at the boundary.
Replace absolute positioning with a 2-column CSS grid so the stats
line (col-span-2, text-right) is contained within the 480px block
and cannot extend past the left edge of the row.
- Nginx: extract config from Dockerfile heredoc to nginx.conf, hardcode
port 8080, remove envsubst templating
- Host row: add bottom padding so stats line stays within the row well
- Host row: two-layer layout with name/URL on the left (normal flow)
and latency/stats on the right (absolute positioned), preventing
overlap and keeping sparklines aligned
- Docker: install git in build stage and include .git in context so
vite can resolve commit hash for footer
- Add S3 ap-southeast-1 (Singapore) endpoint for AWS peering comparison
- Debug log: togglable panel with timestamped, level-tagged, color-coded
entries (error/warning/notice/info/debug) from throughout the app
- Median latency: added to per-host stats and summary (min/med/avg/max)
- Recovery probe: rapid 500ms polling of 4 random hosts when hard offline,
triggers normal tick as soon as connectivity returns
- Health status: multi-level (healthy/slow/degraded/offline) with
hard-offline detection for recovery probe activation
- First tick discarded to avoid DNS/TLS cold-start latency skew
- Added Google, S3 ap-southeast-1 (Singapore) to monitored hosts
- UI: reduced row padding, larger sparkline canvas, bigger axis labels,
pin icon hidden (but space preserved) for local network hosts
- Commit hash shown in footer via vite define plugin
Summary now shows current min/avg/max and history-window min/max.
Each host row has a pin icon that pins it to the top. Pinned hosts
sort alphabetically, unpinned sort by latency. datavi.be is pinned
by default.
Hetzner speed test servers drop the connection on HEAD requests,
causing fetch to throw a network error. GET works universally and
with no-cors mode the response is opaque anyway.
GCS locational endpoints were too slow (>1500ms). Replace with 6
Hetzner speed test servers (Nuremberg DE, Falkenstein DE, Helsinki
FI, Ashburn VA-US, Hillsboro OR-US, Singapore SG) which are genuine
per-DC HTTPS endpoints. Bump update interval from 2s to 3s.
The max-w-7xl (1280px) constraint left too much dead space between
the host wells and the window edges. Remove it so the layout uses
all available width.
The avg latency text below each host's big number is now color-coded
using the same thresholds as the main figure. The sparkline Y-axis
stays 0-1000ms — values between 1000-1500ms pin to the top of the
chart but still show their real value in the latency display.
Local CPE (192.168.100.1) is always monitored. On startup, probe
192.168.1.1, 192.168.0.1, 192.168.8.1, and 10.0.0.1 in parallel
and add whichever responds first as "Local Gateway".
- Add prettier (4-space indents) and reformat all files
- Add Makefile with test/lint/fmt/fmt-check/check/docker targets
- Add MIT LICENSE file
- Add REPO_POLICIES.md
- Fix Dockerfile: listen on 8080 with PORT env var via envsubst
- Restructure README.md with all required sections
- Set up pre-commit hook (make check)
- Update .prettierignore, .gitignore, .dockerignore
Architecture:
- Extract AppState and HostState classes (no global mutable state)
- Extract SparklineRenderer class with static methods
- Extract CONFIG object for all constants
- Break monolithic functions into focused helpers
Features:
- Clickable service URLs (open in new tab, existing styling)
- Health status box above summary (red DEGRADED if >half unreachable)
- Local Gateway separated into bottom group
- Local Gateway excluded from WAN min/max/avg summary stats
- Pause stops probes but history keeps scrolling (blank gaps, no false outage)
- WAN_HOSTS / LOCAL_HOSTS separation with indexed rendering