Commit Graph
9 Commits
Author SHA1 Message Date
davideandClaude Opus 5 25f4a1c6b6 Refuse to start half-configured, and keep failures machine-readable
Startup validation (B-15). An empty JWT_SECRET makes PyJWT raise InvalidKeyError
on every login and an empty XPRV_ENCRYPTION_KEY makes Fernet fail on the first
key derivation — either way the container came up looking healthy and broke the
moment a real user touched it. validate_runtime_secrets() reports every problem
at once and is called from the lifespan (wired in the next commit).

Deviation from the plan in BUGS.md, which proposed a Pydantic field_validator:
Settings is constructed at import time by every module that reads config,
including the test suite, which has no .env and no business holding real secrets
— a validator there would fail a fresh clone at collection. At startup the
guarantee that matters is unchanged (the server won't serve traffic
half-configured) without coupling imports to a gitignored file. An empty
ADMIN_TOKEN is deliberately non-fatal: require_admin already denies everything,
so the effect is a locked panel, not an open one.

Unhandled errors answer the documented shape (B-24). The catch-all returned a
bare-string `detail` while app/api/errors.py documents
{"code", "message", "params"}, leaving clients to special-case exactly the
responses they understand least. It now returns internal_error in that shape,
with the exception text staying in logs/app.log and out of the response body.

GET /guida no longer crashes (B-16). It reads docs/guida-utente.md, which the
Dockerfile doesn't ship, so in every real deployment that navbar link was a 500 —
confirmed in the deployed log, which holds two of them from earlier today ending
in "RuntimeError: File at path docs/guida-utente.md does not exist." It now
checks the file and answers a structured 404 (guide_unavailable) with an error
logged. Shipping docs/ in the image was written and then reverted on request: the
guide is being reworked first, so /guida answers 404 in Docker for now, which is
an accepted state rather than an oversight.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 00:34:33 +02:00
davideandClaude Sonnet 5 c4f6f2eed7 Add a help link and bug-report button to the user navbar
Serves docs/guida-utente.md as plain text at GET /guida (no markdown
rendering, kept simple) and links it from the navbar next to a "Segnala un
bug" button. The bug-report link is a placeholder GitHub issues URL
(REPLACE_ME) until a real repo exists.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 14:34:03 +02:00
davide f27fe6243c Harden session handling, add password reset/change, and firm up round polling
Session hardening: / and /admin now respond with Cache-Control: no-store, and
both pages re-derive their auth state on pageshow (event.persisted) as a
safety net against bfcache showing a stale logged-in/out view across
back/forward navigation. The user page also syncs logout across tabs via the
storage event, since localStorage is shared but in-memory JS state isn't.

Password recovery: admin gets a "Reset" button per user (POST
/admin/users/{id}/reset-password) that generates and sets a new password,
shown once — passwords are Argon2-hashed and can never be recovered, only
replaced. Users get self-service password change (POST
/users/me/change-password, requires the current password) under a new
Profilo tab, alongside read-only account info (username, address, balance,
join date).

Round display robustness: the user dashboard now refreshes immediately on
tab visibility change (background tabs get their timers throttled hard),
shows an explicit "connessione persa" state after repeated failed polls
instead of silently freezing on stale data, and polls faster both right when
the countdown hits zero and through the gap where the round is past its
deadline but still waiting for in-flight bets to confirm before the server
actually closes it.
2026-07-22 12:00:09 +02:00
davideandClaude Sonnet 5 7f2d0bcec6 Add a public current-round status endpoint
GET /rounds/current returns the active round's id/status, opened_at/
closes_at (derived from ROUND_DURATION_SECONDS), participant count and
jackpot (participant_count * bet_amount_sats). No active round still
returns bet_amount_sats so clients can render a fixed-entry hint either
way.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 10:57:31 +02:00
davideandClaude Sonnet 5 dd45ec75c6 Add a QR code endpoint for PLM addresses
GET /qr/{address} renders the address as a PNG QR code (qrcode[pil]),
gated by a bech32-shaped regex since it's otherwise unauthenticated —
the address itself isn't sensitive, but this keeps it from being used
as an arbitrary text-to-QR service.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 10:51:56 +02:00
davideandClaude Sonnet 5 7e5c372833 Log to a file instead of only stdout
All app and uvicorn logging now goes to logs/app.log (rotating,
10MB x5), and a catch-all exception handler logs full tracebacks there
before returning a generic 500 — so an error is traceable to its cause
without depending on how the process was launched. logs/ is gitignored,
like the other runtime artifacts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 10:46:47 +02:00
davideandClaude Sonnet 5 a6dbe48457 Serve the admin panel at its own unlinked /admin page
Moves the fee_address/bet_amount_sats config form out of the main test
UI into a dedicated admin.html, served by a GET /admin route
(registered ahead of the StaticFiles mount so it doesn't shadow the
existing GET/PUT /admin/config API). Deliberately not linked from the
test UI in either direction: reachable only by knowing the URL.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 10:36:55 +02:00
davideandClaude Sonnet 5 ac5ee2ac2c Add static test UI for manual QA
Single-page vanilla HTML/JS frontend (register/login, balance,
place bet, withdraw) served by FastAPI at the same origin so it can
exercise the live API without CORS setup. Manual-testing aid only,
not part of the MVP spec.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 10:27:05 +02:00
davideandClaude Sonnet 5 c45bf543c0 Wire up the FastAPI app and master-key bootstrap script
app/main.py assembles the lifespan-managed background tasks (Electrum
listener, round scheduler, confirmation poller, RBF bumper) and mounts
all routers behind a /health check. generate_master_key.py is the
one-time ops script that creates and Fernet-encrypts the server's
master xprv before first launch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 10:26:42 +02:00