2 Commits

Author SHA1 Message Date
485aa4194a Merge branch 'main' into feature/mobile-detection-message
All checks were successful
check / check (push) Successful in 24s
2026-07-25 17:49:06 +07:00
user
a279cf8583 feat: add mobile viewport detection with friendly unavailable message
Some checks failed
check / check (push) Failing after 3m26s
Detect mobile viewport (window.innerWidth < 768) at startup and show a
centered 'Not yet available on mobile' message instead of the full
monitoring UI. All polling, gateway detection, and network requests are
skipped entirely on mobile viewports.

Desktop behavior is completely unchanged — the mobile check is the very
first thing in init() and returns early before any other setup runs.
2026-03-16 21:18:55 -07:00
28 changed files with 114 additions and 388 deletions

View File

@@ -6,6 +6,5 @@ jobs:
steps: steps:
# actions/checkout v4.2.2, 2026-02-22 # actions/checkout v4.2.2, 2026-02-22
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
# script/cibuild builds both images; it is the only build
# step, so how the repo builds stays defined in script/.
- run: script/cibuild - run: script/cibuild
- run: docker build -f Dockerfile.backend .

View File

@@ -3,16 +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-frontend runs script/frontend-check (test + lint + RUN yarn build
# fmt-check for the frontend); its test step 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. It is the
# frontend half of `make check` rather than all of it because this stage
# is a node image with no Go toolchain; the backend half is gated by
# Dockerfile.backend, and script/cibuild builds both images.
RUN make check-frontend
# nginx:stable-alpine as of 2026-02-22 # nginx:stable-alpine as of 2026-02-22
FROM nginx@sha256:15e96e59aa3b0aada3a121296e3bce117721f42d88f5f64217ef4b18f458c6ab FROM nginx@sha256:15e96e59aa3b0aada3a121296e3bce117721f42d88f5f64217ef4b18f458c6ab

View File

@@ -1,10 +1,8 @@
.PHONY: bootstrap setup dev test lint fmt fmt-check check check-frontend \ .PHONY: bootstrap setup dev test lint fmt fmt-check check docker hooks
check-backend docker hooks
# Standard targets are thin shims; the implementations live in script/ # Standard targets are thin shims; the implementations live in script/
# per the scripts-to-rule-them-all pattern (see the Entrypoints section # per the scripts-to-rule-them-all pattern (see the Entrypoints section
# of README.md). test, lint, fmt, fmt-check and check all cover the # of README.md).
# whole repo: the frontend at the root and the Go backend in backend/.
bootstrap: bootstrap:
@script/bootstrap @script/bootstrap
@@ -30,14 +28,6 @@ fmt-check:
check: check:
@script/check @script/check
# Half-repo gates. Used by the two Dockerfiles, whose build stages only
# have the toolchain for their own half; prefer `make check` otherwise.
check-frontend:
@script/frontend-check
check-backend:
@backend/script/check
docker: docker:
@script/docker @script/docker

View File

@@ -28,55 +28,23 @@ docker run -p 8080:8080 netwatch
This repository adheres to the This repository adheres to the
[Scripts to Rule Them All](https://github.com/github/scripts-to-rule-them-all) [Scripts to Rule Them All](https://github.com/github/scripts-to-rule-them-all)
standard: normalized scripts in `script/` are the entrypoints for the standard: normalized scripts in `script/` are the entrypoints for the
development workflow, and the Makefile targets are thin shims that call them. development workflow, and the Makefile targets are thin shims that call them. We
provide:
The repo holds two projects: the frontend at the repo root and the Go backend in
`backend/`, which has its own `script/` directory and its own shim Makefile. The
root scripts cover both, so `make check` at the root fails if either half is
broken. We provide:
- `script/bootstrap` — install all dependencies (pinned node via nvm if needed, - `script/bootstrap` — install all dependencies (pinned node via nvm if needed,
yarn via corepack, `yarn install --frozen-lockfile`) yarn via corepack, `yarn install --frozen-lockfile`)
- `script/setup` — make a fresh clone ready for development: bootstrap plus the - `script/setup` — make a fresh clone ready for development: bootstrap plus the
git pre-commit hook git pre-commit hook
- `script/projectname` — print the project name (used for the Docker image tags) - `script/projectname` — print the project name (used for the Docker image tag)
- `script/test` — run the whole repo's tests: `script/frontend-test`, then - `script/test` — run the production build as the test (no unit tests yet)
`backend/script/test` - `script/lint` — run prettier in check mode
- `script/lint`lint the whole repo: `script/frontend-lint`, then - `script/fmt`format all files (writes)
`backend/script/lint` - `script/fmt-check` — check formatting (read-only)
- `script/fmt` — format the whole repo (writes): `script/frontend-fmt`, then - `script/check` — run test, lint, and fmt-check
`backend/script/fmt` - `script/docker` — build the Docker image tagged via `script/projectname`
- `script/fmt-check` — check formatting across the whole repo (read-only) - `script/cibuild` — CI entrypoint: plain `docker build .`
- `script/check` — run test, lint, and fmt-check; this is the repo-wide gate - `script/precommit` — run by the git pre-commit hook; runs `script/check`
- `script/frontend-test` — the frontend's test: the production build (no unit - `script/install-precommit` — install the git pre-commit hook
tests yet)
- `script/frontend-lint` — run prettier in check mode
- `script/frontend-fmt` — format everything prettier understands (writes)
- `script/frontend-fmt-check` — check prettier formatting (read-only)
- `script/frontend-check` — the frontend half of `script/check`, used by
`Dockerfile`, whose build stage is a node image with no Go toolchain
- `script/docker` — build both images, tagged via `script/projectname`:
`netwatch` from `Dockerfile` and `netwatch-server` from `Dockerfile.backend`
- `script/cibuild` — CI entrypoint: builds both images; the only build step in
the Gitea workflow
- `script/precommit` — run by the git pre-commit hook; runs `script/check`, so a
commit is gated on both halves of the repo
- `script/install-precommit` — install the git pre-commit hook; this is the
repo's only pre-commit hook installer
The backend's scripts are shimmed by `backend/Makefile` and are also called by
the root scripts above:
- `backend/script/build` — compile `netwatch-server` with version and
architecture stamped in
- `backend/script/test` — run the Go tests under a 30-second timeout
- `backend/script/lint` — assert `.golangci.yml` still matches its pinned
sha256, then run golangci-lint
- `backend/script/fmt` — format the Go sources (writes)
- `backend/script/fmt-check` — check Go formatting (read-only)
- `backend/script/check` — run the backend's test, lint, and fmt-check
- `backend/script/run` — build and run the server locally
- `backend/script/clean` — remove build artifacts
## Rationale ## Rationale
@@ -157,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
@@ -180,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.

View File

@@ -22,11 +22,6 @@ files, so merging it also closes most compliance gaps.
# Completed Steps # Completed Steps
- 2026-08-09: unified the gate: the root `make check` now covers the Go backend
as well as the frontend, the backend moved onto scripts-to-rule-them-all
(`backend/script/*` with `backend/Makefile` as thin shims), the duplicate
pre-commit hook installer in `backend/Makefile` was removed, and
`script/cibuild` now builds both images as the workflow's only build step
- 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

View File

@@ -1,38 +1,53 @@
# Standard targets are thin shims; the implementations live in UNAME_S := $(shell uname -s)
# backend/script/ per the scripts-to-rule-them-all pattern (see the VERSION := $(shell git describe --always --dirty)
# Entrypoints section of README.md). BUILDARCH := $(shell uname -m)
# BINARY := netwatch-server
# There is no `hooks` target here: the repo has exactly one pre-commit
# hook installer, the root `script/install-precommit`, and the hook it
# installs gates both halves of the repo. There is no `docker` target
# either: the backend image is built from Dockerfile.backend with the
# repo root as its context, so it belongs to the root `make docker` and
# `script/cibuild`.
.PHONY: all build test lint fmt fmt-check check run clean GOLDFLAGS += -X main.Version=$(VERSION)
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
all: build all: build
build: build: ./$(BINARY)
@script/build
./$(BINARY): $(shell find . -name '*.go' -type f) go.mod go.sum
go build -o $@ $(GOFLAGS) ./cmd/netwatch-server/
test: test:
@script/test timeout 30 go test ./...
lint: lint:
@script/lint golangci-lint run ./...
fmt: fmt:
@script/fmt go fmt ./...
fmt-check: fmt-check:
@script/fmt-check @test -z "$$(gofmt -l .)" || \
(echo "Files not formatted:"; gofmt -l .; exit 1)
check: check: test lint fmt-check
@script/check
run: docker:
@script/run timeout 300 docker build -t netwatch-server -f ../Dockerfile.backend ..
hooks:
@printf '#!/bin/sh\ncd backend && make check\n' > \
$$(git rev-parse --show-toplevel)/.git/hooks/pre-commit
@chmod +x \
$$(git rev-parse --show-toplevel)/.git/hooks/pre-commit
@echo "Pre-commit hook installed"
run: build
./$(BINARY)
clean: clean:
@script/clean rm -f ./$(BINARY)

View File

@@ -11,38 +11,11 @@ make run
# Run tests, lint, and format check # Run tests, lint, and format check
make check make check
# Docker (from the repo root; the image's build context is the repo root) # Docker
make docker docker build -t netwatch-server .
docker run -p 8080:8080 netwatch-server docker run -p 8080:8080 netwatch-server
``` ```
## Entrypoints
This project follows the same
[Scripts to Rule Them All](https://github.com/github/scripts-to-rule-them-all)
pattern as the repo root: the implementations live in `backend/script/` and the
targets in `backend/Makefile` are thin shims that call them. The repo root's
`script/test`, `script/lint`, `script/fmt` and `script/fmt-check` call these
too, so the root `make check` covers the backend.
- `script/build` — compile `netwatch-server` with the version and architecture
stamped in via ldflags (statically linked on Linux)
- `script/test` — run the Go tests under a 30-second timeout
- `script/lint` — assert `.golangci.yml` still matches its pinned sha256, then
run golangci-lint
- `script/fmt` — format the Go sources (writes)
- `script/fmt-check` — check Go formatting (read-only)
- `script/check` — run test, lint, and fmt-check
- `script/run` — build and run the server locally
- `script/clean` — remove build artifacts
There is deliberately no `hooks` target here: the repo has exactly one
pre-commit hook installer, the root `script/install-precommit`, and the hook it
installs runs the root `script/check`, which gates both halves of the repo.
There is no `docker` target either: `Dockerfile.backend` lives at the repo root
and builds with the repo root as its context, so the backend image is built by
the root `make docker` and by `script/cibuild`.
## Rationale ## Rationale
The NetWatch frontend collects latency measurements from the browser but has no The NetWatch frontend collects latency measurements from the browser but has no

View File

@@ -1,27 +0,0 @@
#!/bin/sh
# script/build: compile the netwatch-server binary into the backend
# project root. Version and architecture are stamped into the binary via
# ldflags; on Linux the binary is statically linked.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
BINARY="netwatch-server"
main() {
cd "$ROOT"
# git describe fails outside a working repo (e.g. a source tarball),
# which must not abort the build.
version="$(git describe --always --dirty 2>/dev/null || echo unknown)"
buildarch="$(uname -m)"
ldflags="-X main.Version=$version -X main.Buildarch=$buildarch"
if [ "$(uname -s)" != "Darwin" ]; then
ldflags="-linkmode external -extldflags -static $ldflags"
fi
go build -o "$BINARY" -ldflags "$ldflags" ./cmd/netwatch-server/
}
main "$@"

View File

@@ -1,15 +0,0 @@
#!/bin/sh
# script/check: run all backend checks (test, lint, fmt-check). Must not
# modify any files. The root script/check calls this, so the repo-wide
# gate covers the backend.
set -eu
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
main() {
"$SCRIPT_DIR/test"
"$SCRIPT_DIR/lint"
"$SCRIPT_DIR/fmt-check"
}
main "$@"

View File

@@ -1,12 +0,0 @@
#!/bin/sh
# script/clean: remove build artifacts.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() {
cd "$ROOT"
rm -f netwatch-server
}
main "$@"

View File

@@ -1,12 +0,0 @@
#!/bin/sh
# script/fmt: format all Go sources in the backend (writes).
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() {
cd "$ROOT"
go fmt ./...
}
main "$@"

View File

@@ -1,18 +0,0 @@
#!/bin/sh
# script/fmt-check: check Go formatting (read-only). Same scope as
# script/fmt, but fails instead of writing.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() {
cd "$ROOT"
unformatted="$(gofmt -l .)"
if [ -n "$unformatted" ]; then
echo "Files not formatted:"
echo "$unformatted"
exit 1
fi
}
main "$@"

View File

@@ -1,46 +0,0 @@
#!/bin/sh
# script/lint: run the Go linter over the backend.
#
# .golangci.yml is standardized org-wide and must never be edited here
# (REPO_POLICIES.md). Its last silent drift replaced the v2 schema with
# v1 keys, which left every threshold in the file inert while the build
# stayed green. This script therefore asserts the file still matches the
# pinned copy byte for byte before the linter runs. The check is a local
# hash comparison: no network, no remote schema, nothing unpinned in the
# build path.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
# sha256 of the pinned backend/.golangci.yml.
GOLANGCI_CONFIG_SHA256="33ba2bf7fe4a44779d09b0fb31d6daf03685f8dc9d2bc417f963d7aabb0d17dc"
# sha256 <file>: print the file's sha256, coreutils or Darwin/busybox.
sha256() {
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$1" | cut -d' ' -f1
else
shasum -a 256 "$1" | cut -d' ' -f1
fi
}
check_config_hash() {
actual="$(sha256 .golangci.yml)"
if [ "$actual" != "$GOLANGCI_CONFIG_SHA256" ]; then
echo ".golangci.yml has drifted from the pinned config."
echo " expected $GOLANGCI_CONFIG_SHA256"
echo " actual $actual"
echo "Restore it verbatim from sneak/prompts; do not edit it."
echo "Only update GOLANGCI_CONFIG_SHA256 in this script when the"
echo "pinned config is deliberately replaced with a new standard."
exit 1
fi
}
main() {
cd "$ROOT"
check_config_hash
golangci-lint run ./...
}
main "$@"

View File

@@ -1,14 +0,0 @@
#!/bin/sh
# script/run: build and run netwatch-server locally.
set -eu
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
main() {
"$SCRIPT_DIR/build"
cd "$ROOT"
exec ./netwatch-server "$@"
}
main "$@"

View File

@@ -1,12 +0,0 @@
#!/bin/sh
# script/test: run the backend test suite.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() {
cd "$ROOT"
timeout 30 go test ./...
}
main "$@"

View File

@@ -1,7 +1,6 @@
#!/bin/sh #!/bin/sh
# script/check: run all checks (test, lint, fmt-check) across the whole # script/check: run all checks (test, lint, fmt-check). Our own
# repo, frontend and backend. Our own extension to # extension to scripts-to-rule-them-all. Must not modify any files.
# scripts-to-rule-them-all. Must not modify any files.
set -eu set -eu
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"

View File

@@ -1,24 +1,13 @@
#!/bin/sh #!/bin/sh
# script/cibuild: run the CI build. It builds every image in the repo: # script/cibuild: run the CI build. The Dockerfile runs make check, so
# the frontend image from Dockerfile and the backend image from # a successful build implies all checks pass.
# Dockerfile.backend. Each Dockerfile runs its half of make check as a
# build step, so a successful cibuild implies the whole repo is green.
# This is the only build step the Gitea workflow runs.
set -eu set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
# build_image <dockerfile>: build one image from the repo root context.
# Every docker build CI performs goes through here, so build-wide flags
# only ever have to be added in one place.
build_image() {
timeout 300 docker build -f "$1" .
}
main() { main() {
cd "$ROOT" cd "$ROOT"
build_image Dockerfile docker build .
build_image Dockerfile.backend
} }
main "$@" main "$@"

View File

@@ -1,7 +1,6 @@
#!/bin/sh #!/bin/sh
# script/docker: build the repo's Docker images, tagged from # script/docker: build the Docker image tagged with the project name.
# script/projectname: the frontend image as <name> and the backend image # The tag comes from script/projectname.
# as <name>-server. Both build from the repo root as their context.
set -eu set -eu
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
@@ -9,9 +8,7 @@ ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
main() { main() {
cd "$ROOT" cd "$ROOT"
name="$("$SCRIPT_DIR/projectname")" timeout 300 docker build -t "$("$SCRIPT_DIR/projectname")" .
timeout 300 docker build -t "$name" -f Dockerfile .
timeout 300 docker build -t "$name-server" -f Dockerfile.backend .
} }
main "$@" main "$@"

View File

@@ -1,14 +1,12 @@
#!/bin/sh #!/bin/sh
# script/fmt: format the whole repo (writes): prettier over everything # script/fmt: format all files (writes).
# it understands, then gofmt over the Go backend.
set -eu set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() { main() {
cd "$ROOT" cd "$ROOT"
"$ROOT/script/frontend-fmt" yarn prettier --write .
"$ROOT/backend/script/fmt"
} }
main "$@" main "$@"

View File

@@ -1,14 +1,13 @@
#!/bin/sh #!/bin/sh
# script/fmt-check: check formatting across the whole repo (read-only). # script/fmt-check: check formatting (read-only). Same scope as
# Same scope as script/fmt, but fails instead of writing. # script/fmt, but fails instead of writing.
set -eu set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() { main() {
cd "$ROOT" cd "$ROOT"
"$ROOT/script/frontend-fmt-check" yarn prettier --check .
"$ROOT/backend/script/fmt-check"
} }
main "$@" main "$@"

View File

@@ -1,17 +0,0 @@
#!/bin/sh
# script/frontend-check: run the frontend half of the checks only (test,
# lint, fmt-check). This exists for the frontend Dockerfile, whose build
# stage is a node image with no Go toolchain; the backend half is gated
# by Dockerfile.backend. Everywhere else, use script/check, which covers
# the whole repo. Must not modify any files.
set -eu
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
main() {
"$SCRIPT_DIR/frontend-test"
"$SCRIPT_DIR/frontend-lint"
"$SCRIPT_DIR/frontend-fmt-check"
}
main "$@"

View File

@@ -1,14 +0,0 @@
#!/bin/sh
# script/frontend-fmt: format the frontend and every other file prettier
# understands, repo-wide (writes). backend/ is in .prettierignore; Go
# sources are formatted by backend/script/fmt.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() {
cd "$ROOT"
yarn prettier --write .
}
main "$@"

View File

@@ -1,13 +0,0 @@
#!/bin/sh
# script/frontend-fmt-check: check prettier formatting (read-only). Same
# scope as script/frontend-fmt, but fails instead of writing.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() {
cd "$ROOT"
yarn prettier --check .
}
main "$@"

View File

@@ -1,12 +0,0 @@
#!/bin/sh
# script/frontend-lint: run the frontend linter (prettier in check mode).
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() {
cd "$ROOT"
yarn prettier --check .
}
main "$@"

View File

@@ -1,14 +0,0 @@
#!/bin/sh
# script/frontend-test: run the frontend test suite. The frontend has no
# unit tests; the production build serves as the test (fails on broken
# code).
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() {
cd "$ROOT"
timeout 30 yarn build
}
main "$@"

View File

@@ -1,14 +1,12 @@
#!/bin/sh #!/bin/sh
# script/lint: lint the whole repo: prettier over everything it # script/lint: run the linter (prettier in check mode).
# understands, then golangci-lint over the Go backend.
set -eu set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() { main() {
cd "$ROOT" cd "$ROOT"
"$ROOT/script/frontend-lint" yarn prettier --check .
"$ROOT/backend/script/lint"
} }
main "$@" main "$@"

View File

@@ -1,14 +1,13 @@
#!/bin/sh #!/bin/sh
# script/test: run the test suite for the whole repo: the frontend at # script/test: run the test suite. This repo has no unit tests; the
# the repo root, then the Go backend in backend/. # production build serves as the test (fails on broken code).
set -eu set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() { main() {
cd "$ROOT" cd "$ROOT"
"$ROOT/script/frontend-test" timeout 30 yarn build
"$ROOT/backend/script/test"
} }
main "$@" main "$@"

View File

@@ -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];