Releases: xxvcc/Tweet-Watcher
Releases · xxvcc/Tweet-Watcher
Release list
v3.2.0 — Monitoring cockpit + theme switch + bird auto-detect
Web panel redesigned as a monitoring cockpit, plus theme switching and bird-path auto-detection. The backend gains read-only per-account metrics to power the dashboard; monitoring and push behavior are unchanged.
Added
- Monitoring-cockpit panel: the panel opens on a live dashboard instead of a config form — per-account status cards (status light, last check / last push, most-recent pushed tweet, a push-count sparkline, and a next-check countdown), a top metrics strip (accounts, pushes today, uptime, next check), and a real-time activity feed. All configuration moved into a slide-over Settings drawer.
- Theme switching: dark / light / follow-system toggle in the header, remembered across sessions; auto-follows the OS when set to system.
- bird path auto-detection: a detect button (and auto-detect when the configured path is missing) locates the bird binary via common locations and
which, confirming it with--version; falls back to an install hint or manual entry. NewPOST /api/detect-bird(auth + CSRF, single-flight) and abirdOkflag on/api/config. - Read-only per-account metrics on
/api/status: pushes-today counter, per-account total pushes, last-pushed timestamp, last pushed tweet preview, and recent per-check push history (for the sparkline).
Changed
- Secret fields (auth_token / ct0 / bot token) no longer use
type=password(which made browser password managers autofill the login password); they mask via CSS with a readonly-until-focus guard, show a row of dots when a value is saved and blank when not, and reveal with 👁. - Account status cards update in place on each SSE event instead of rebuilding the grid — no flicker, smooth countdown.
Fixed
- Activity feed no longer shows duplicate rows after an SSE reconnect (log lines de-duplicated by timestamp).
- Never-checked accounts show "待检查" instead of a misleading "0:00" with a full progress bar.
- The "pushes today" counter rolls over on the display timezone (Asia/Shanghai) rather than UTC.
- Bird
--versionoutput is rendered via textContent, not innerHTML (defense-in-depth against HTML injection in the hint).
Verified with a jsdom end-to-end harness (login → dashboard render → SSE → settings → detect → save, 18/18) and an adversarial self-review of the diff.
v3.1.0 — Security & correctness hardening
Security and correctness hardening release following a full three-round audit. Backward-compatible for existing installs (which already have a password); only fresh setups and password resets now require a one-time setup token.
Security
- First-run takeover (TOFU) closed:
/api/setupnow requires a one-timesetup_tokenthat is printed to the server log at startup while no password exists, preventing an unauthenticated attacker from claiming the panel during the pre-setup window. A corruptpassword.jsonis treated fail-closed (as "password set") instead of silently re-opening setup. - Logout DoS closed:
/api/logoutonly bumps the session epoch (global revocation) when the request carries a valid session + CSRF token; unauthenticated requests can no longer force a global logout. - Login rate-limit bypass fixed: the failure counter is incremented synchronously before password verification, closing the concurrency window that let parallel requests slip past the lockout gate. The limiter is keyed on
req.ip(not the forgeableX-Real-IP), clears when the lockout expires, and is bounded (lazy eviction + hard cap). - Password hashing/verification moved to async bcrypt so bursts of login attempts no longer block the event loop.
bird_pathvalidation now also requires the basename to bebird, preventing an authenticated user from pointing it at/bin/shor other host binaries.- Added security response headers (CSP,
X-Content-Type-Options: nosniff,X-Frame-Options: DENY,Referrer-Policy) and disabledX-Powered-By. - The SSE stream re-verifies the session on each keepalive (closing on logout/expiry) and enforces a connection cap.
- Malformed cookies and session tokens now return 400/401 instead of leaking a 500 stack trace; a global error handler returns generic messages.
Fixed
- Silent mis-push on empty first fetch: a new account whose first fetch returns an empty list no longer records an empty baseline, which previously made the next fetch push the entire timeline.
- Fetch errors no longer masquerade as success: a non-zero exit / timeout from bird is treated as a failure instead of "no new tweets".
- JSON is extracted from stdout only, so bracket characters in bird's stderr diagnostics can no longer corrupt parsing and drop a successful fetch.
- Tweet IDs prefer the string
id_str/rest_idfields (avoiding 64-bit float rounding collisions that dropped tweets); object-typed IDs are skipped. - Telegram 429 responses honor
retry_after; permanent (4xx) errors are no longer retried. - Duplicate IDs within a single fetch (e.g. a pinned tweet appearing twice) are de-duplicated so they are not pushed twice.
- Offset-less tweet timestamps are interpreted as UTC instead of the server's local timezone.
- Usernames are de-duplicated case-insensitively; reserved names such as
__proto__no longer corrupt the dedup table (null-prototype maps). - The "checking…" badge no longer sticks on error paths (reset in
finally).
Changed
- Password reset now requires stop → delete
password.json→ restart, then reading the new one-time setup token from the logs. - Account checks run with bounded concurrency (max 4) so one slow/dead account no longer stalls the others; pause now takes effect immediately, mid-tick.
- Config saves return a
warningslist for silently-ignored invalid values; aMAX_ACCOUNTScap (100) is enforced and duplicate accounts are merged. - Per-account dedup is flushed once per check instead of per tweet, reducing write amplification.
Robustness
- Atomic writes now
fsyncthe file and the directory, so the "safe against power loss" guarantee actually holds; corrupt data files are surfaced (logged and preserved) rather than silently reset to defaults. uncaughtException/unhandledRejectionnow log andexit(1)so systemd restarts cleanly instead of the process limping on in an undefined state; the worker loop reschedules infinally, and stdout write failures can no longer stall it.- Added a
lastTickAtheartbeat and ahealthyflag on/api/statusto detect a silently dead worker.