From 9b840274f014b935c3ee61a27918cab357a6eeab Mon Sep 17 00:00:00 2001 From: BaranDev Date: Tue, 20 Oct 2026 17:42:53 +0300 Subject: [PATCH 1/3] Readable logs, a public health page and a private one - One plain line per request: what was asked, the answer, whether it came from the memory cache, the disk cache or was drawn (with draw time), size, country, kind of client and Cloudflare ray id. Client IPs are never written. - Lines for queue decisions, GitHub fetches (with quota and refill time), retries, pauses, cache sweeps, the start, and a status line every 15 minutes. - /health and the new /status page show only public state: up, whether GitHub fetching runs, whether new users get in, with times in minutes and UTC. - /admin/health and /admin/status show every count and capacity, answer only on questlog.cc (or locally) and are never cached; they go behind Cloudflare Access. - Health link in the footer; Docker log size capped at 5 x 20 MB. --- README.md | 2 + docker-compose.vps.yml | 3 + site/app/admin/status/page.tsx | 20 ++ site/app/status/page.tsx | 31 +++ site/components/Footer.tsx | 1 + site/components/Health.module.css | 243 +++++++++++++++++++ site/components/Health.tsx | 376 ++++++++++++++++++++++++++++++ src/cache.rs | 9 +- src/github.rs | 10 +- src/lib.rs | 1 + src/log.rs | 66 ++++++ src/main.rs | 23 +- src/queue.rs | 85 ++++++- src/server/cards.rs | 36 ++- src/server/mod.rs | 161 ++++++++++++- 15 files changed, 1037 insertions(+), 30 deletions(-) create mode 100644 site/app/admin/status/page.tsx create mode 100644 site/app/status/page.tsx create mode 100644 site/components/Health.module.css create mode 100644 site/components/Health.tsx create mode 100644 src/log.rs diff --git a/README.md b/README.md index 659f1510..7d424da9 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,8 @@ docker run -d -p 8080:8080 --env-file .env -v questlog-data:/data questlog `.env` holds `GITHUB_TOKEN=` with a fine-grained token that can read public repositories (see `.env.example`). `NEW_LOGINS_PER_HOUR` (600 by default) caps how many logins are fetched for the first time each hour; raise it for a busy day, GitHub's own limit still pauses fetching when it runs low. The server listens on `PORT` (8080) and keeps its cache in `DATA_DIR` (`/data`). With your own token, your own instance can count your private contributions too. +**Watching it.** The server writes one plain line per request and per background event to stderr (`docker logs questlog`): what was asked, whether the card came from the memory cache, the disk cache or was drawn (and how long that took), each GitHub fetch with the quota left, queue decisions, and a status line every 15 minutes. Client IPs are never written; the Cloudflare ray id links a line to Cloudflare's own logs. `/status` and `/health` show the public state (up, whether GitHub fetching runs, whether new users get in, with times in UTC). `/admin/status` and `/admin/health` show every count and capacity; they answer only on the `questlog.cc` host (or locally), so put them behind a login such as Cloudflare Access. + ## Development The pages at `/`, `/builder` and `/wiki` come from `site/`, a Next.js static export that the binary embeds. Build it before `cargo build`: `cd site && npm ci && npm run build` (it writes `static/`). diff --git a/docker-compose.vps.yml b/docker-compose.vps.yml index 3ec09fcb..b580095b 100644 --- a/docker-compose.vps.yml +++ b/docker-compose.vps.yml @@ -12,6 +12,9 @@ services: cap_drop: [ALL] security_opt: ["no-new-privileges:true"] env_file: .env + logging: # one readable line per request and event; capped so the log can't fill the disk + driver: json-file + options: { max-size: "20m", max-file: "5" } volumes: - questlog-data:/data diff --git a/site/app/admin/status/page.tsx b/site/app/admin/status/page.tsx new file mode 100644 index 00000000..2b9f52dd --- /dev/null +++ b/site/app/admin/status/page.tsx @@ -0,0 +1,20 @@ +import type { Metadata } from "next"; +import Nav from "@/components/Nav"; +import Health from "@/components/Health"; + +// Behind Cloudflare Access: every number, for Baran. Not indexed, not linked from the site. +export const metadata: Metadata = { + title: "questlog health, every number", + robots: { index: false, follow: false }, +}; + +export default function AdminStatusPage() { + return ( + <> +