feat(monitor): admin-only latest.log stream at monitor.
Phase 1 of plan/23-monitor.md: a small Go service that tails the Minecraft server's latest.log into an in-memory ring buffer and serves it to an embedded single-page UI (autoscroll, substring/regex filter, level chips). Read-only by construction — /data is mounted :ro, there is no mutating route, and the container holds no credentials. It publishes NO port: caddy's basic_auth vhost is the only way in, which is the point, since the service has no auth of its own and the log carries player IPs, chat and admin commands. Two non-obvious choices, both to avoid prod-only failures: - Polling, not SSE. The chain is host nginx -> caddy -> app, and nginx buffers proxied responses by default, so an EventSource would stall on cochi (whose vhost is an untracked per-host file) and not reproduce locally. Delay was accepted at design time, so polling is strictly better. - The cursor is a sequence number, not a byte offset, and the tailer reopens on inode change or size shrink. itzg rotates latest.log on every server restart, i.e. every deploy — a naive tailer goes silent afterwards while still answering HTTP 200. Verified against rotate, truncate, delete/recreate, partial-line and ring-overflow cases. MONITOR_ENABLED (on|off) gates both layers per host, mirroring FILES_BROWSE. MONITOR_PASSWORD_HASH must be bcrypt AND single-quoted in .env: Compose interpolates $-sequences in .env values, so a bare hash is silently mangled ($2a$14$abc... -> $$2a$$14...) and every login fails with no hint why. Confirmed empirically, and it is the opposite of filestash, which needs $6$. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
8507ffd020
commit
fca6747bcc
13 files changed
+1241
-4
No files matched your search
+25
-2
@@ -22,6 +22,9 @@
|
||||
# the name from the port-80 redirect, so the host serves
|
||||
# nothing for it (no cert needed either). Same var gates the
|
||||
# caddy side in docker-compose.yml.
|
||||
# MONITOR_ENABLED on|off (default on) — emit the monitor.${BASE_DOMAIN}
|
||||
# admin log-stream vhost. Same on/off semantics as
|
||||
# FILES_BROWSE, same var gates the caddy side.
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/.." # repo root
|
||||
@@ -66,7 +69,27 @@ case "$FILES_BROWSE" in
|
||||
esac
|
||||
export FILES_BROWSE_NAME FILES_BROWSE_SERVER
|
||||
|
||||
rendered="$(envsubst '$BASE_DOMAIN $APP_DIR $CADDY_HTTP_PORT $FILES_BROWSE_NAME $FILES_BROWSE_SERVER' < "$TEMPLATE")"
|
||||
# monitor.${BASE_DOMAIN} — admin-only log stream (plan/23-monitor.md). Same
|
||||
# splice-or-comment trick as FILES_BROWSE above, and the same var gates the
|
||||
# caddy conf mount in docker-compose.yml. Turning it off here is what removes
|
||||
# the public name entirely; the container itself publishes no port either way.
|
||||
: "${MONITOR_ENABLED:=on}"
|
||||
MONITOR_TEMPLATE="${MONITOR_TEMPLATE:-nginx/monitor.server.tmpl}"
|
||||
case "$MONITOR_ENABLED" in
|
||||
on)
|
||||
[ -f "$MONITOR_TEMPLATE" ] || die "template not found: $MONITOR_TEMPLATE"
|
||||
MONITOR_NAME=" monitor.${BASE_DOMAIN}"
|
||||
MONITOR_SERVER="$(envsubst '$BASE_DOMAIN $APP_DIR' < "$MONITOR_TEMPLATE")"
|
||||
;;
|
||||
off)
|
||||
MONITOR_NAME=""
|
||||
MONITOR_SERVER="# monitor.${BASE_DOMAIN} disabled on this host (MONITOR_ENABLED=off in .env)."
|
||||
;;
|
||||
*) die "MONITOR_ENABLED must be 'on' or 'off' (got '$MONITOR_ENABLED')" ;;
|
||||
esac
|
||||
export MONITOR_NAME MONITOR_SERVER
|
||||
|
||||
rendered="$(envsubst '$BASE_DOMAIN $APP_DIR $CADDY_HTTP_PORT $FILES_BROWSE_NAME $FILES_BROWSE_SERVER $MONITOR_NAME $MONITOR_SERVER' < "$TEMPLATE")"
|
||||
|
||||
if [ "${1:-}" != "--install" ]; then
|
||||
printf '%s\n' "$rendered"
|
||||
@@ -78,7 +101,7 @@ command -v nginx >/dev/null 2>&1 || die "nginx not found on PATH"
|
||||
avail="/etc/nginx/sites-available/$SITE_NAME"
|
||||
enabled="/etc/nginx/sites-enabled/$SITE_NAME"
|
||||
|
||||
log "installing $TEMPLATE -> $avail (APP_DIR=$APP_DIR, CADDY_HTTP_PORT=$CADDY_HTTP_PORT, FILES_BROWSE=$FILES_BROWSE)"
|
||||
log "installing $TEMPLATE -> $avail (APP_DIR=$APP_DIR, CADDY_HTTP_PORT=$CADDY_HTTP_PORT, FILES_BROWSE=$FILES_BROWSE, MONITOR_ENABLED=$MONITOR_ENABLED)"
|
||||
printf '%s\n' "$rendered" | sudo tee "$avail" >/dev/null
|
||||
sudo ln -sf "$avail" "$enabled"
|
||||
sudo nginx -t
|
||||
|
||||
Reference in new issue
Block a user