Compare commits
2 Commits
2389e269ce
...
feature/mo
| Author | SHA1 | Date | |
|---|---|---|---|
| 485aa4194a | |||
|
|
a279cf8583 |
@@ -3,12 +3,9 @@ FROM node@sha256:e4bf2a82ad0a4037d28035ae71529873c069b13eb0455466ae0bc13363826e3
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY package.json yarn.lock ./
|
COPY package.json yarn.lock ./
|
||||||
RUN yarn install --frozen-lockfile
|
RUN yarn install --frozen-lockfile
|
||||||
RUN apk add --no-cache git make
|
RUN apk add --no-cache git
|
||||||
COPY . .
|
COPY . .
|
||||||
# make check runs script/check (test + lint + fmt-check); its test step
|
RUN yarn build
|
||||||
# is the production yarn build, so this both produces dist/ and gates the
|
|
||||||
# image on lint/fmt-check/test regressions, not merely a broken build.
|
|
||||||
RUN make check
|
|
||||||
|
|
||||||
# nginx:stable-alpine as of 2026-02-22
|
# nginx:stable-alpine as of 2026-02-22
|
||||||
FROM nginx@sha256:15e96e59aa3b0aada3a121296e3bce117721f42d88f5f64217ef4b18f458c6ab
|
FROM nginx@sha256:15e96e59aa3b0aada3a121296e3bce117721f42d88f5f64217ef4b18f458c6ab
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ FROM golang:1.25-alpine@sha256:f6751d823c26342f9506c03797d2527668d095b0a15f1862c
|
|||||||
|
|
||||||
RUN apk add --no-cache git make gcc musl-dev
|
RUN apk add --no-cache git make gcc musl-dev
|
||||||
|
|
||||||
# golangci-lint v2.12.2 (2026-08-09)
|
# golangci-lint v2.7.2 (2026-02-27)
|
||||||
RUN CGO_ENABLED=0 go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@c0d3ddc9cf3faa61a4e378e879ece580256d76e5
|
RUN CGO_ENABLED=0 go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@9f61b0f53f80672872fced07b6874397c3ed197b
|
||||||
|
|
||||||
WORKDIR /repo/backend
|
WORKDIR /repo/backend
|
||||||
COPY backend/go.mod backend/go.sum ./
|
COPY backend/go.mod backend/go.sum ./
|
||||||
|
|||||||
@@ -125,6 +125,9 @@ dist/
|
|||||||
false outage)
|
false outage)
|
||||||
- Clickable service URLs
|
- Clickable service URLs
|
||||||
- Canvas-based sparkline rendering with devicePixelRatio scaling
|
- Canvas-based sparkline rendering with devicePixelRatio scaling
|
||||||
|
- Mobile detection: viewports narrower than 768px show a friendly "not yet
|
||||||
|
available on mobile" message instead of the monitoring UI (no polling or
|
||||||
|
network requests on mobile)
|
||||||
- Zero runtime dependencies: all resources bundled into build artifacts
|
- Zero runtime dependencies: all resources bundled into build artifacts
|
||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
@@ -148,6 +151,8 @@ properties.
|
|||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
|
- **Mobile**: Viewports below 768px wide show a static "not yet available"
|
||||||
|
message. The full monitoring UI requires a desktop-width browser.
|
||||||
- **CORS**: Some hosts may block cross-origin HEAD requests. The app uses
|
- **CORS**: Some hosts may block cross-origin HEAD requests. The app uses
|
||||||
`no-cors` mode which allows the request but provides opaque responses. Latency
|
`no-cors` mode which allows the request but provides opaque responses. Latency
|
||||||
is still measurable based on request timing.
|
is still measurable based on request timing.
|
||||||
|
|||||||
26
TODO.md
26
TODO.md
@@ -10,29 +10,23 @@
|
|||||||
|
|
||||||
# Status
|
# Status
|
||||||
|
|
||||||
pre-1.0. No git tags. `feat/reportbuf-storage` is merged; the backend, the CI
|
pre-1.0. No git tags. Backend work in flight on feat/reportbuf-storage (dirty:
|
||||||
workflow, and the backend repo standard files are all on `main`. Frontend and
|
src/main.js). Frontend is functional; backend is new and unmerged.
|
||||||
backend are both functional. Working toward the 1.0.0 milestone by closing the
|
|
||||||
remaining repo-compliance issues on the tracker.
|
|
||||||
|
|
||||||
# Next Step
|
# Next Step
|
||||||
|
|
||||||
Compliance top-up as one small commit: add `.editorconfig` and add the `hooks`
|
Land feat/reportbuf-storage: finish the in-progress src/main.js change, get make
|
||||||
target to the root Makefile.
|
check green, and merge the branch to main. The branch adds the backend (buffered
|
||||||
|
zstd-compressed report storage), the CI workflow, and backend repo standard
|
||||||
|
files, so merging it also closes most compliance gaps.
|
||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
- 2026-08-09: adopted the org-standard `backend/.golangci.yml` verbatim and
|
|
||||||
bumped the pinned golangci-lint to v2.12.2; the previous config declared
|
|
||||||
`version: "2"` but used v1 schema keys, so every threshold in it was inert and
|
|
||||||
its green result was meaningless. `make lint` now runs
|
|
||||||
`golangci-lint config verify` first so an invalid config fails the build
|
|
||||||
instead of silently falling back to defaults
|
|
||||||
- 2026-07-07 Adopted scripts-to-rule-them-all: `script/` entrypoints, Makefile
|
- 2026-07-07 Adopted scripts-to-rule-them-all: `script/` entrypoints, Makefile
|
||||||
shims, README Entrypoints section
|
shims, README Entrypoints section
|
||||||
- 2026-02-27: backend with buffered zstd-compressed report storage; CI workflow
|
- 2026-02-27: backend with buffered zstd-compressed report storage; CI workflow
|
||||||
and backend repo standard files; backend Dockerfile fixed (Go 1.25,
|
and backend repo standard files; backend Dockerfile fixed (Go 1.25,
|
||||||
golangci-lint) and moved to repo root (feat/reportbuf-storage)
|
golangci-lint) and moved to repo root (feat/reportbuf-storage, unmerged)
|
||||||
- 2026-02-26: host row layout redesigned with CSS grid; overflow and spacing
|
- 2026-02-26: host row layout redesigned with CSS grid; overflow and spacing
|
||||||
fixes; nginx config extracted; port hardcoded to 8080
|
fixes; nginx config extracted; port hardcoded to 8080
|
||||||
- 2026-02-26: debug log panel, median stats, recovery probe, Docker build fix,
|
- 2026-02-26: debug log panel, median stats, recovery probe, Docker build fix,
|
||||||
@@ -45,7 +39,9 @@ target to the root Makefile.
|
|||||||
|
|
||||||
# Future Steps
|
# Future Steps
|
||||||
|
|
||||||
- Confirm `.gitea/workflows/check.yml` is on `main` and CI is green (main always
|
- Compliance top-up as one small commit: add .editorconfig and add the hooks
|
||||||
green policy)
|
target to the Makefile
|
||||||
|
- After merge, confirm .gitea/workflows/check.yml is on main and CI is green
|
||||||
|
(main always green policy)
|
||||||
- Decide what to do with untracked resume.sh: commit it, gitignore it, or delete
|
- Decide what to do with untracked resume.sh: commit it, gitignore it, or delete
|
||||||
it
|
it
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
version: "2"
|
version: "2"
|
||||||
|
|
||||||
# Config schema uses the golangci-lint v2 layout (settings live under
|
|
||||||
# linters.settings, not top-level linters-settings) so that the
|
|
||||||
# thresholds below are actually applied by golangci-lint >= v2.
|
|
||||||
|
|
||||||
run:
|
run:
|
||||||
timeout: 5m
|
timeout: 5m
|
||||||
modules-download-mode: readonly
|
modules-download-mode: readonly
|
||||||
@@ -18,17 +14,19 @@ linters:
|
|||||||
- wsl # Deprecated, replaced by wsl_v5
|
- wsl # Deprecated, replaced by wsl_v5
|
||||||
- wrapcheck # Too verbose for internal packages
|
- wrapcheck # Too verbose for internal packages
|
||||||
- varnamelen # Short names like db, id are idiomatic Go
|
- varnamelen # Short names like db, id are idiomatic Go
|
||||||
settings:
|
|
||||||
lll:
|
linters-settings:
|
||||||
line-length: 88
|
lll:
|
||||||
funlen:
|
line-length: 88
|
||||||
lines: 80
|
funlen:
|
||||||
statements: 50
|
lines: 80
|
||||||
cyclop:
|
statements: 50
|
||||||
max-complexity: 15
|
cyclop:
|
||||||
dupl:
|
max-complexity: 15
|
||||||
threshold: 100
|
dupl:
|
||||||
|
threshold: 100
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
|
exclude-use-default: false
|
||||||
max-issues-per-linter: 0
|
max-issues-per-linter: 0
|
||||||
max-same-issues: 0
|
max-same-issues: 0
|
||||||
|
|||||||
@@ -24,11 +24,7 @@ build: ./$(BINARY)
|
|||||||
test:
|
test:
|
||||||
timeout 30 go test ./...
|
timeout 30 go test ./...
|
||||||
|
|
||||||
# `config verify` is run first so that a .golangci.yml which does not
|
|
||||||
# validate against the golangci-lint schema fails the build instead of
|
|
||||||
# silently falling back to default thresholds.
|
|
||||||
lint:
|
lint:
|
||||||
golangci-lint config verify
|
|
||||||
golangci-lint run ./...
|
golangci-lint run ./...
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
|
|||||||
@@ -163,9 +163,7 @@ func (b *Buffer) writeFile(data []byte) {
|
|||||||
name := fmt.Sprintf("reports-%s.jsonl.zst", ts)
|
name := fmt.Sprintf("reports-%s.jsonl.zst", ts)
|
||||||
path := filepath.Join(b.dataDir, name)
|
path := filepath.Join(b.dataDir, name)
|
||||||
|
|
||||||
// path is built from the operator-supplied dataDir plus a
|
f, err := os.OpenFile( //nolint:gosec // path built from controlled dataDir + timestamp
|
||||||
// generated timestamp, so it carries no external input.
|
|
||||||
f, err := os.OpenFile( //nolint:gosec // see comment above
|
|
||||||
path,
|
path,
|
||||||
os.O_WRONLY|os.O_CREATE|os.O_EXCL,
|
os.O_WRONLY|os.O_CREATE|os.O_EXCL,
|
||||||
filePerms,
|
filePerms,
|
||||||
|
|||||||
@@ -62,10 +62,7 @@ func New(
|
|||||||
OnStart: func(_ context.Context) error {
|
OnStart: func(_ context.Context) error {
|
||||||
s.startupTime = time.Now().UTC()
|
s.startupTime = time.Now().UTC()
|
||||||
|
|
||||||
// The fx OnStart context is scoped to startup and is
|
go func() { //nolint:contextcheck // fx OnStart ctx is startup-only; run() creates its own
|
||||||
// cancelled once the hook returns; run() derives its
|
|
||||||
// own context instead of inheriting this one.
|
|
||||||
go func() { //nolint:contextcheck // see comment above
|
|
||||||
s.run()
|
s.run()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -97,9 +94,7 @@ func (s *Server) run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) serve() int {
|
func (s *Server) serve() int {
|
||||||
// Declared up front because the multi-assign below also writes
|
var ctx context.Context //nolint:wsl // ctx must be declared before multi-assign
|
||||||
// s.cancelFunc, so := cannot be used.
|
|
||||||
var ctx context.Context //nolint:wsl // see comment above
|
|
||||||
|
|
||||||
ctx, s.cancelFunc = context.WithCancel(
|
ctx, s.cancelFunc = context.WithCancel(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
|
|||||||
35
src/main.js
35
src/main.js
@@ -1126,11 +1126,46 @@ function handleResize(state) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Mobile Detection --------------------------------------------------------
|
||||||
|
|
||||||
|
const MOBILE_BREAKPOINT = 768;
|
||||||
|
|
||||||
|
function isMobileViewport() {
|
||||||
|
return window.innerWidth < MOBILE_BREAKPOINT;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildMobileUI() {
|
||||||
|
const app = document.getElementById("app");
|
||||||
|
app.innerHTML = `
|
||||||
|
<div class="mx-auto px-[5%] py-8">
|
||||||
|
<header class="mb-8">
|
||||||
|
<h1 class="text-3xl font-bold text-white"><a href="https://git.eeqj.de/sneak/netwatch" target="_blank" rel="noopener" class="underline decoration-dashed decoration-gray-500 underline-offset-4">NetWatch</a> by <a href="https://sneak.berlin" target="_blank" rel="noopener" class="text-blue-400 underline hover:text-blue-300">@sneak</a></h1>
|
||||||
|
<p class="text-gray-400 text-sm mt-2">Real-time network latency monitor</p>
|
||||||
|
</header>
|
||||||
|
<div class="flex items-center justify-center min-h-[60vh]">
|
||||||
|
<div class="bg-gray-800/70 border border-gray-700/50 rounded-lg p-8 max-w-md text-center">
|
||||||
|
<p class="text-4xl mb-4">📡</p>
|
||||||
|
<p class="text-xl font-semibold text-white mb-2">Not yet available on mobile</p>
|
||||||
|
<p class="text-gray-400 text-sm">NetWatch requires a wider viewport to display latency charts and monitoring data. Please visit on a desktop browser.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer class="mt-8 text-center text-gray-600 text-xs">
|
||||||
|
<p><a href="https://git.eeqj.de/sneak/netwatch/commit/${__COMMIT_FULL__}" target="_blank" rel="noopener" class="text-gray-600 hover:text-gray-400">${__COMMIT_HASH__}</a></p>
|
||||||
|
</footer>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
// --- Bootstrap ---------------------------------------------------------------
|
// --- Bootstrap ---------------------------------------------------------------
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
log.info("NetWatch starting");
|
log.info("NetWatch starting");
|
||||||
|
|
||||||
|
if (isMobileViewport()) {
|
||||||
|
log.info("Mobile viewport detected — skipping monitoring");
|
||||||
|
buildMobileUI();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Probe common gateway IPs to find the local router
|
// Probe common gateway IPs to find the local router
|
||||||
const gateway = await detectGateway();
|
const gateway = await detectGateway();
|
||||||
const localHosts = [LOCAL_CPE];
|
const localHosts = [LOCAL_CPE];
|
||||||
|
|||||||
Reference in New Issue
Block a user