Commit Graph
16 Commits
Author SHA1 Message Date
davideandClaude Sonnet 5 80c413ad3d Show the net prize as the jackpot instead of the gross pool
GET /rounds/current's jackpot_sats was the full pool (participant_count *
bet_amount_sats), which overstates what the winner actually receives once
the 30% commission is taken out at payout time. Apply the same 70% split
rounds/scheduler.py uses so the number shown during the round already
matches the real payout, without surfacing the fee itself in the UI/docs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 14:53:09 +02:00
davideandClaude Sonnet 5 7dcf6d2756 Sync round countdown across clients and enforce the bet cutoff on deadline, not scheduler tick
The round timer relied on each client's own wall clock, so two browsers with
skewed local clocks showed different countdowns for the same round; the
server now also returns server_time so the frontend can correct for clock
skew. Also drop out-of-order /rounds/current responses (multiple independent
triggers could resolve late and revert the UI to a stale drawing/result
state) and prune per-round bookkeeping maps on round transitions.

Separately, place_bet only checked status == "open", leaving a window (up to
the scheduler's 5s tick interval) after a round's timer hit zero where a new
bet could still be accepted. place_bet now checks the round's own deadline
directly (round_accepts_bets), acting as an immediate "yellow light" for new
entries while still letting already-broadcast bets confirm before the round
closes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 14:07:09 +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
davide 162a63d04a Add a maintenance pause/resume switch and a proper user navbar
RoundConfig gets a paused flag toggled via new POST /admin/pause and
/admin/resume endpoints (audit-logged, surfaced as a "Manutenzione" card in
the admin Parametri view). Pausing only stops the *next* round from opening
once the current one closes — rounds/service.py:open_new_round_if_needed
still lets an in-progress round finish, draw, and pay out its winner
normally. GET /rounds/current exposes lottery_paused so the user page shows
a maintenance banner (even while logged out) instead of silently going idle.

Also replaces the user dashboard's stacked account-bar card + bento-grid
menu with a single sticky navbar (identity row + Deposito/Bet/Prelievo
tabs), and moves the page content into a dedicated .app-shell container so
the navbar itself can span full width.
2026-07-22 10:36:36 +02:00
davide 78109aa4c4 Add a marketing landing hero and a live chain/round status strip
The user page's login screen was a bare test dashboard with no explanation
of how the lottery works; it now leads with a hero (3-step explainer, trust
pills) shown only while logged out, plus a bento-style nav and a glowing
round card during the draw.

Both the user and admin pages now show the current chain tip height and
lottery status (open / drawing / waiting for next round) via a small status
strip, polled from /rounds/current (extended with chain_tip_height sourced
from ElectrumListener.tip_height).
2026-07-22 10:16:51 +02:00
davideandClaude Sonnet 5 669c3fb714 Add draw_animation_seconds config and expose winner info during a round
RoundConfig gains draw_animation_seconds (default 20) — the minimum
time the frontend's "estrazione in corso" animation plays before
revealing a winner. It's purely a UI cue: the real draw still waits for
a confirmed block for its entropy (rounds/scheduler.py), which usually
takes much longer than this value, so it only ever extends the
animation, never truncates the real wait.

GET /rounds/current now also returns draw_animation_seconds,
winner_user_id and winner_amount_sats (all populated once the
scheduler sets them on the round, i.e. from "paying_out" onward) so a
client can determine and reveal the outcome. GET /users/me now returns
the user's own id, needed client-side to compare against winner_user_id.

Admin config CRUD refactored to a shared field tuple (_CONFIG_FIELDS)
instead of repeating the same 7-then-8 field list three times.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 16:04:02 +02:00
davideandClaude Sonnet 5 ed16e4d50b Add admin endpoints for round history, pending transactions, audit log
GET /admin/rounds: recent rounds with status, winner (joined username),
pool/winner/fee amounts, payout txid. GET /admin/pending-transactions:
in-flight bet/payout/withdrawal txs (RBF candidates). GET /admin/audit-log:
recent audit_log entries with parsed payload. All gated by the existing
require_admin dependency, feeding the new dashboard sections.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 15:05:49 +02:00
davideandClaude Sonnet 5 30bde96b6e Move all business/round parameters into DB config, out of env entirely
RoundConfig gains round_duration_seconds, round_cooldown_seconds,
min_amount_sats, fee_rate_sat_vb and rbf_timeout_seconds (plus a
hardcoded default for the pre-existing bet_amount_sats) as column
defaults on the model itself — get_round_config no longer seeds from
Settings at all. Every call site that read these from settings
(scheduler, bets, withdrawals, rounds service/route, RBF bumper) now
reads the DB-backed RoundConfig instead.

app/config.py now holds only true env-driven infra/secrets (database
URL, Electrum connection, master key, JWT, admin token) — no business
parameter has an env var anymore, matching an explicit decision to drop
the "seed from settings" indirection entirely rather than keep a env
fallback nobody should rely on.

Migration backfills the existing round_config row via server_default
(matching the old settings defaults) then drops the default, so future
rows go through the ORM/model defaults instead of a stale constant.

Tests updated: should_bump's timeout_seconds is now required (no
settings fallback); test_scheduler.py seeds a RoundConfig row directly
instead of monkeypatching settings; test_withdrawals.py and
test_rounds_service.py use local constants mirroring the model
defaults instead of reading them off settings.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 15:05:40 +02:00
davideandClaude Sonnet 5 39c6ea1950 Add admin endpoints to list users and export a user's private key
GET /admin/users lists id/username/address/balance_sats/created_at.
GET /admin/users/{id}/privkey derives and returns that user's raw WIF
private key, for manual intervention (e.g. sweeping funds back if
something's stuck) — this is already a custodial system, the server
holds the master key everything is derived from, so this doesn't grant
a new capability, just exposes an existing one through the API. Every
access is audit-logged (admin_privkey_accessed).

Also fixes a pre-existing test-isolation bug in test_admin.py's client
fixture: app.db.session.get_session had `from app.db.base import
AsyncSessionLocal`, a one-time reference copy at first import — later
tests reassigning db_base.AsyncSessionLocal never reached it, so any
test mixing direct DB writes with router calls silently read/wrote
against a stale, possibly-disposed engine from whichever test ran
first. Fixed by also rebinding app.db.session.AsyncSessionLocal in the
fixture on every run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 14:21:55 +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 01331c1e4c Add admin config and audit log
Bearer-token-gated admin endpoints to read/update the DB-backed
operational config (fee_address, bet_amount_sats) without a redeploy,
plus a lightweight audit log writer for round/payout/config events.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 10:26:31 +02:00
davideandClaude Sonnet 5 8380b80d12 Add withdrawal flow
Builds and broadcasts a user->external-address PSBT with change back to
the user's own address, fee deducted from the withdrawn amount, and
registers the confirmation handler that marks the withdrawal confirmed.
Shares the per-user lock with bets so a build never races a spend from
the same UTXO set.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 10:26:25 +02:00
davideandClaude Sonnet 5 492fc29eca Add bet flow
Places the fixed-cost bet into the current round: builds and broadcasts
the user->pool PSBT with change back to the user's own address, enforces
at most one active bet per user, and registers the confirmation handler
that marks a bet confirmed and adds the participant to the round.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 10:26:11 +02:00
davideandClaude Sonnet 5 107e592704 Add authentication and user profile endpoint
Argon2 password hashing, JWT session issuing/verification
(auth/security.py), register/login routes, the bearer-token
get_current_user dependency, and GET /users/me for address + balance.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 10:25:49 +02:00
davideandClaude Sonnet 5 d2db762d96 Scaffold project layout, DB schema and settings
Package skeleton, pyproject/alembic config, env-driven settings
(app/config.py), and the SQLAlchemy models + initial Alembic migration
covering users, UTXO events, rounds/participants, round config,
pending transactions, withdrawals and the audit log.

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