Adds an optional web dashboard served by OpenGFW itself, enabled with a new
`web` section in the config file.
Backend (web package, decoupled from engine/io so it builds on any OS):
- hub.go collects statistics off the engine logger callbacks: atomic counters,
two ring-buffered time series (10s and 1min buckets), top N hosts/blocked
destinations/rules/analyzers, and a 512 entry event buffer fanned out to
connected clients over SSE. Slow clients drop frames instead of blocking
the engine.
- api.go exposes /api/v1 for info, meta, metrics, events, the SSE stream and
ruleset read/validate/replace.
- auth.go implements password login with in-memory session tokens and login
rate limiting. Mutating endpoints require the bearer token (the session
cookie is only accepted for GET), which makes them CSRF-safe.
- cmd/web.go implements the rule manager: rules are compiled before anything
is written, the file is replaced atomically and the engine is hot reloaded.
The SIGHUP handler now shares that same path.
- web/devserver serves the UI with synthetic traffic for frontend work on
machines where the engine itself cannot be built.
Frontend (web/frontend, Vue 3 + Vite + Tailwind CSS v4 + Reka UI):
dashboard, live event feed with analyzer property inspection, visual and YAML
rule editors, analyzer overview and settings. Responsive down to phone sizes
with a bottom tab bar and bottom-sheet dialogs, plus light/dark themes and
English/Chinese translations.
The built UI in web/dist is committed and embedded with go:embed so that
`go build` works without Node; CI builds the frontend and checks that the
committed output is up to date.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This tests fails when there are no specific binary files, but there is
no method to get the files during test.
Marking the tests as a skipped will gives us a false sense of security. So it's
better to remove it at all.
Fixed:
- "ineffective break statement. Did you mean to break out of the outer loop?" (SA4011)
- "channels used with os/signal.Notify should be buffered" (SA1017)
- "os.Kill cannot be trapped (did you mean syscall.SIGTERM?)" (SA1016)
- "func envOrDefaultBool is unused" (U1000)
- "should use time.Since instead of time.Now().Sub" (S1012)