Package-level slog.With() vars are evaluated at init time, before
observability.InitLogger() runs in main. They captured the original default
slog handler and bypassed the countingHandler that increments
log_messages_total — so warn/error calls from these packages weren't being
counted.
Convert to functions so each call resolves slog.Default() at use time and
inherits the active counting handler.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Convert log.Printf calls in webhook, reconcile, addresses, and auth handlers
to slog with a consistent `component` attribute. Severity now reflects
operational urgency:
- warn: signature failures, declined cards, unknown fingerprints, vendor
validation rejections, reconciler patching a missed charge
- error: DB write failures, malformed Stripe payloads, token generation
failures
- info: successful order/card events, normal reconciler ticks
Lets Loki/Prometheus filter by component (e.g. {component="webhook"}) and
gives the log-level dashboard panel real warn/error series instead of a
flat info line.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds Prometheus instrumentation across the checkout funnel, auth flow,
Stripe calls, rate limiters, and HTTP middleware (inflight gauge, request
and response sizes). Introduces a slog handler that counts log lines by
level so the dashboard can show error/warn rates; existing log.Printf
calls are routed through slog and counted as info.
Backs the new "terminalshop — Service Overview" Grafana dashboard.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Auth handler used FirstOrCreate which couldn't see soft-deleted keys
(GORM filters by deleted_at IS NULL) but also couldn't insert (unique
index still covers deleted rows), returning 500 on every login after
a key deletion. Use Unscoped lookup and restore soft-deleted keys.
Splash screen also got permanently stuck when auth failed because
splashViewInitCmd was never fired, leaving splash.dataReady false.
previously main.go handled auth directly, it connected to the database,
ran migrations, created a JWT manager and SSHAuthService, and
authenticated the SSH key before the TUI even started. this commit rips
all of that out
Auth now happens inside the TUI during the splash screen via
splashAuthCmd, which calls the /auth/token endpoint. if no user exists
or the fingerprint it auto-creates one using the SSH public ke, so there
is no longer a separate registartion flow. The token and user com back
in the response.
once authed splashViewInitCmd fires a single /view/init request that
loads everything needed to start the app, products, cart, addresses,
cards, and orders, before switching to the shop page
on the model side, User/*models.user and gossh.PublicKey were replaced
with plain Fingerprint and SSHPublicKeyStr strings, and NewModelWithAuth
was updated too
feat: implement server-side cart persistence with Stripe PaymentIntents
Replaced the client-only in-mem cart with a database-backed cart system
- Add Cart and CartItem GORM models (two-table design)
- Add 6 cart API endpoints (get, set item, set address, set card, clear,
conver)
- Switch from Stripe Charge API to PaymentIntents with confirm:true
- Add 9 API client methods for cart and card operations
- Refactor TUI cart from map[int] (slice index) to map[uint] (CoffeeID)
- Replace single-shot checkout with saveCardAndConvert multi-step flow
- Add CheckingOut state with "submitting order..." processing indicator
- Fix Stripe tokenization to use secret key for server-side SSH apps
- Remove admin product CRUD endpoints and old checkout handler