Compare commits
2 Commits
bd2bc9f626
...
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
|
||||||
|
|||||||
@@ -1,50 +1,25 @@
|
|||||||
# Lint stage — fast feedback on formatting and lint issues. The
|
|
||||||
# golangci/golangci-lint image ships Go, gofmt, make and the linter, so
|
|
||||||
# nothing is installed here.
|
|
||||||
# golangci/golangci-lint:v2.7.2 (2026-08-09)
|
|
||||||
FROM golangci/golangci-lint@sha256:5d6d5c70a61f1356adfd9dd6316ce286799fefc9d743421356ff1b00842368ba AS lint
|
|
||||||
|
|
||||||
WORKDIR /src
|
|
||||||
COPY backend/go.mod backend/go.sum ./
|
|
||||||
RUN go mod download
|
|
||||||
COPY backend/ .
|
|
||||||
RUN make fmt-check
|
|
||||||
RUN make lint
|
|
||||||
|
|
||||||
# Build stage
|
|
||||||
# golang:1.25-alpine (2026-02-27)
|
# golang:1.25-alpine (2026-02-27)
|
||||||
FROM golang:1.25-alpine@sha256:f6751d823c26342f9506c03797d2527668d095b0a15f1862cddb4d927a7a4ced AS builder
|
FROM golang:1.25-alpine@sha256:f6751d823c26342f9506c03797d2527668d095b0a15f1862cddb4d927a7a4ced AS builder
|
||||||
|
|
||||||
RUN apk add --no-cache make
|
RUN apk add --no-cache git make gcc musl-dev
|
||||||
|
|
||||||
WORKDIR /src
|
# golangci-lint v2.7.2 (2026-02-27)
|
||||||
|
RUN CGO_ENABLED=0 go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@9f61b0f53f80672872fced07b6874397c3ed197b
|
||||||
# Force BuildKit to run the lint stage before proceeding. BuildKit runs
|
|
||||||
# stages in parallel by default; without this no-op copy a lint failure
|
|
||||||
# would not gate compilation.
|
|
||||||
COPY --from=lint /src/go.sum /dev/null
|
|
||||||
|
|
||||||
|
WORKDIR /repo/backend
|
||||||
COPY backend/go.mod backend/go.sum ./
|
COPY backend/go.mod backend/go.sum ./
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
|
COPY .git /repo/.git
|
||||||
COPY backend/ .
|
COPY backend/ .
|
||||||
|
|
||||||
RUN make test
|
RUN make check
|
||||||
|
RUN make build
|
||||||
|
|
||||||
# The build is driven through the Makefile so there is exactly one
|
|
||||||
# definition of the build command; it expands to
|
|
||||||
# CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X main.Version=... -X main.Buildarch=..."
|
|
||||||
# VERSION is handed over in the environment rather than as a make
|
|
||||||
# variable so it still reaches the build if the target is ever turned
|
|
||||||
# into a shim around a script.
|
|
||||||
ARG VERSION=dev
|
|
||||||
RUN VERSION="${VERSION}" make build
|
|
||||||
|
|
||||||
# Runtime stage
|
|
||||||
# alpine:3.23 (2026-02-27)
|
# alpine:3.23 (2026-02-27)
|
||||||
FROM alpine:3.23@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
|
FROM alpine:3.23@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
|
||||||
|
|
||||||
RUN apk add --no-cache ca-certificates
|
RUN apk add --no-cache ca-certificates
|
||||||
COPY --from=builder /src/netwatch-server /usr/local/bin/netwatch-server
|
COPY --from=builder /repo/backend/netwatch-server /usr/local/bin/netwatch-server
|
||||||
|
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
ENTRYPOINT ["netwatch-server"]
|
ENTRYPOINT ["netwatch-server"]
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
4
TODO.md
4
TODO.md
@@ -22,10 +22,6 @@ files, so merging it also closes most compliance gaps.
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
- 2026-08-09: `Dockerfile.backend` reworked to the mandated Go multistage
|
|
||||||
lint-stage pattern: separate `lint` stage on the hash-pinned
|
|
||||||
`golangci/golangci-lint` image, `COPY --from=lint` stage dependency,
|
|
||||||
`CGO_ENABLED=0` static build driven by `ARG VERSION`, and no more `COPY .git`
|
|
||||||
- 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
|
||||||
|
|||||||
@@ -1,22 +1,25 @@
|
|||||||
# VERSION is overridable (the Dockerfile passes its ARG VERSION in) and
|
UNAME_S := $(shell uname -s)
|
||||||
# degrades to "dev" when git is unavailable or there is no .git — the
|
VERSION := $(shell git describe --always --dirty)
|
||||||
# build must not depend on the repository history being in the build
|
|
||||||
# context.
|
|
||||||
VERSION ?= $(shell { git describe --always --dirty; } 2>/dev/null || echo dev)
|
|
||||||
BUILDARCH := $(shell uname -m)
|
BUILDARCH := $(shell uname -m)
|
||||||
BINARY := netwatch-server
|
BINARY := netwatch-server
|
||||||
|
|
||||||
GOLDFLAGS += -s -w
|
|
||||||
GOLDFLAGS += -X main.Version=$(VERSION)
|
GOLDFLAGS += -X main.Version=$(VERSION)
|
||||||
GOLDFLAGS += -X main.Buildarch=$(BUILDARCH)
|
GOLDFLAGS += -X main.Buildarch=$(BUILDARCH)
|
||||||
|
|
||||||
|
ifeq ($(UNAME_S),Darwin)
|
||||||
|
GOFLAGS := -ldflags "$(GOLDFLAGS)"
|
||||||
|
else
|
||||||
|
GOFLAGS = -ldflags "-linkmode external -extldflags -static $(GOLDFLAGS)"
|
||||||
|
endif
|
||||||
|
|
||||||
.PHONY: all build test lint fmt fmt-check check docker hooks run clean
|
.PHONY: all build test lint fmt fmt-check check docker hooks run clean
|
||||||
|
|
||||||
all: build
|
all: build
|
||||||
|
|
||||||
build:
|
build: ./$(BINARY)
|
||||||
CGO_ENABLED=0 go build -trimpath -ldflags "$(GOLDFLAGS)" \
|
|
||||||
-o ./$(BINARY) ./cmd/netwatch-server/
|
./$(BINARY): $(shell find . -name '*.go' -type f) go.mod go.sum
|
||||||
|
go build -o $@ $(GOFLAGS) ./cmd/netwatch-server/
|
||||||
|
|
||||||
test:
|
test:
|
||||||
timeout 30 go test ./...
|
timeout 30 go test ./...
|
||||||
|
|||||||
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