Commit graph

513 commits

Author SHA1 Message Date
Jon Seager
19e191e922
perf: parallelize independent async calls in detail pages
Use tokio::try_join! to run independent repo queries and image URL
lookups concurrently in bag, brew, and checkin page handlers, and for
gear option loading in the brew form data loader.
2026-02-13 13:06:10 +00:00
Jon Seager
b8a26bbeca
feat: add entity counts to stats cache for home page
Add EntityCounts struct and StatsRepository::entity_counts() to query
per-entity row counts. Include them in CachedStats so the home page can
read counts from cache instead of issuing six list queries with LIMIT 1.
Derive Default on StatsView for the fallback case.
2026-02-13 13:06:04 +00:00
Jon Seager
9d25873243
refactor: reuse ListQuery for timeline page
Replace the hand-rolled TimelineQuery struct with the shared ListQuery
type and its into_request_and_search_with_default method.
2026-02-13 13:05:56 +00:00
Jon Seager
a1aefda860
refactor: use update_response helper in update handlers
Replace duplicated datastar/form/json response branching in brew, cup,
and roast update handlers with the shared update_response helper.
2026-02-13 13:05:50 +00:00
Jon Seager
167af65943
fix: prevent XSS in redirect script via JSON encoding
JSON-encode the URL in render_redirect_script instead of interpolating
it into a single-quoted string, preventing injection via crafted URLs.
2026-02-13 13:05:44 +00:00
Jon Seager
ff2c602139
fix: validate cafe URL scheme on create and update
Make is_valid_url_scheme pub(crate) and use it to filter cafe website
URLs on both create and update, matching roaster validation behavior.
Add UpdateCafe::normalize() method for consistent sanitization.
2026-02-13 13:05:29 +00:00
Jon Seager
b5d5949adf
refactor: simplify admin page auth with direct session lookup
Replace the private extract_user_from_session helper with inline
session/user lookup, reusing the shared SESSION_COOKIE_NAME constant.
Adds tracing instrumentation to the handler.
2026-02-13 13:05:23 +00:00
Jon Seager
0bf30d3475
fix: verify authenticated user in passkey add finish
Add AuthenticatedUser extractor to passkey_add_finish and verify the
session user matches the challenge owner, preventing one user from
completing another user's passkey registration.
2026-02-13 13:05:14 +00:00
Jon Seager
55f2de1e92
refactor: deduplicate SESSION_COOKIE_NAME constant
Make the constant pub(crate) in application::auth and remove the
duplicate definition from routes::app::auth.
2026-02-13 13:04:34 +00:00
Jon Seager
78cc66972d
refactor: extract static_asset! macro for static file handlers
Replace 16 near-identical async functions with a macro that generates
static-asset handlers with embedded content and one-week cache headers.
2026-02-13 13:03:14 +00:00
Ubuntu
7e0b910008
fix: validate CLI callback URLs to localhost-only
Reject non-localhost CLI callback URLs in the WebAuthn auth start
endpoint to prevent token exfiltration to external servers.
2026-02-13 13:01:12 +00:00
Jon Seager
24d9d75145
feat: add home screen icons for iOS and Android 2026-02-13 11:41:16 +00:00
Jon Seager
21696a02f0
chore: update gitignore 2026-02-13 11:27:02 +00:00
Jon Seager
2e41c28429
fix: apply EXIF orientation to uploaded photos
iPhone photos were displayed rotated because EXIF orientation metadata
was not being applied. Fix both the client-side canvas conversion
(use createImageBitmap which respects EXIF) and the server-side image
processing (read and apply EXIF orientation before resizing).
2026-02-12 18:07:52 +00:00
Jon Seager
98053a8679
docs: add note about Claude usage in README 2026-02-12 15:55:57 +00:00
Jon Seager
f1548d7e7a
build: fail-fast pre-commit hooks for faster iteration 2026-02-12 15:30:57 +00:00
Jon Seager
67f218495f
feat: add cancel buttons to all add forms
Match the edit form pattern by adding a Cancel button (history.back())
below each Save button in the sticky submit bar.
2026-02-11 10:24:28 +00:00
Jon Seager
720ebcbad3
feat: add sticky submit buttons to all edit forms on mobile
Add sticky-submit class to the shared edit_form_actions() macro and
pb-16 bottom padding to all seven edit form templates, matching the
add form pattern for a consistent mobile experience.
2026-02-11 10:20:30 +00:00
Jon Seager
1eb5647b76
feat: add sticky submit button to all mobile add forms
Apply the existing sticky-submit pattern (fixed bottom bar on mobile,
inline on desktop) to roaster, roast, bag, gear, cafe, and cup add
forms, matching the brew form behavior.
2026-02-11 10:15:40 +00:00
Jon Seager
1f21dc27af
fix: prevent iOS auto-zoom on input focus
Bump .input-field font-size to 1rem (16px) on mobile so iOS Safari
does not auto-zoom the viewport when focusing text inputs, number
inputs, or searchable selects.
2026-02-11 10:11:02 +00:00
Jon Seager
55addb200c
fix: prevent double-tap zoom on mobile stepper buttons
Add touch-action: manipulation to .btn-adjust so rapid taps on +/-
buttons no longer trigger the browser's double-tap-to-zoom gesture.
2026-02-11 10:07:58 +00:00
Jon Seager
049e9d68a8
fix: mobile fixed nav bar and detail page polish
- Make nav bar fixed on mobile with safe-area-inset-top support for
  notched devices (10% reduction) and 1rem minimum for non-notched
- Adjust timeline sticky month headings to sit flush below the fixed
  nav with matching top/bottom padding
- Update IntersectionObserver rootMargin to account for fixed nav height
  so stuck-state accent border triggers correctly
- Increase main content top padding on mobile for balanced spacing on
  detail pages
- Add remaining field to bag edit form with +/- stepper buttons
- Make admin page passkey/token forms responsive with icon-only mobile
  delete/revoke buttons
- Make detail page action buttons stack vertically on mobile with equal
  width
2026-02-11 09:53:43 +00:00
Jon Seager
705144bcf7
fix: make detail page action buttons responsive on mobile 2026-02-11 09:05:58 +00:00
Jon Seager
76445f49ae
fix: improve admin page forms for mobile
Stack passkey and token input forms vertically on mobile with full-width
fields and buttons. Collapse Delete/Revoke buttons to icon-only squares
on mobile, expanding to show text labels on larger screens.
2026-02-11 08:59:52 +00:00
Jon Seager
3a32d9725a
feat: add remaining amount field to bag edit form 2026-02-11 08:53:56 +00:00
Jon Seager
4190fc2620
feat: log payload fields in trace spans by redacting image data
Add ImageData newtype that wraps Option<String> with a custom Debug impl
showing Some(<image>)/None instead of raw base64. Replace image fields on
all 14 submission structs and remove payload from tracing skip lists so
textual/numeric fields appear in spans.
2026-02-11 08:37:03 +00:00
Jon Seager
56a1637d63
style: use accented color for links on timeline 2026-02-10 20:35:42 +00:00
Jon Seager
a38e9aae5b
fix: use shortened dates in expanded timeline cards 2026-02-10 20:33:24 +00:00
Jon Seager
7ec422f7ce
style: short month names on expanded timeline cards 2026-02-10 20:32:13 +00:00
Jon Seager
2cd579a574
refactor: deduplicate update handlers and edit templates
Add HasChanges trait with impl_has_changes! macro, validate_update()
and update_response() helpers to reduce boilerplate across all 7 entity
update handlers. Extract edit form actions (error, spinner, buttons)
into a shared Askama macro. Also adds missing no-changes validation
to the bag update handler.
2026-02-10 20:04:24 +00:00
Jon Seager
8ced58039b
fix: add x icon to cancel button and saving spinner to edit forms
Replace both buttons with a centered "Saving..." spinner when the
form is submitting. Add x_mark icon to the cancel button for clarity.
2026-02-10 19:43:10 +00:00
Jon Seager
0434b127e0
test: add E2E chromedriver tests for edit forms
Cover text fields, numeric fields, +/- adjuster buttons, searchable
selects, image upload/replace/remove, deferred image save, and the
cancel button across roaster, gear, cafe, brew, and cup entities.

Add wait_for_url_not_contains helper to correctly detect post-submit
redirects when the edit page URL shares a prefix with the detail page.
2026-02-10 19:43:06 +00:00
Jon Seager
4ee8cf7964
fix: polish edit forms and detail pages
- Skip payload in tracing::instrument to avoid logging base64 image data
- Add blob: to CSP img-src for image preview support
- Add deferred_upload_with_preview macro for edit form image previews
  with Replace/Remove buttons and proper DOM cleanup on replacement
- Fix datastar-fetch finished handler (evt.detail.response is undefined
  for redirect scripts)
- Display brew time in M:SS format on edit form
- Add full-width Save Changes button with check icon and Cancel button
  to all edit forms
- Fix country flag emoji spacing on cafe and cup detail pages
- Add "View on Map" Google Maps link to cafe and cup detail pages
2026-02-10 19:43:03 +00:00
Jon Seager
9650735057
fix: wire edit forms for Datastar submission and recompute bag remaining
Add {contentType: 'form'} to all edit template @put() calls so form
inputs are submitted by name rather than as Datastar signals. Replace
the brew edit quick notes text input with toggle pill buttons. Recompute
bag remaining when amount is updated to account for brew deductions.
Add CLI tests for brew and cup update commands.
2026-02-10 19:43:00 +00:00
Jon Seager
cb28a6f8bb
fix: wire edit forms for Datastar form submission and add CLI tests
Add {contentType: 'form'} to all edit template @put() calls so form
inputs are submitted by name rather than as Datastar signals. Replace
the quick notes text input on the brew edit page with toggle pill
buttons matching the add form. Add CLI tests for brew and cup update
commands.
2026-02-10 19:42:58 +00:00
Jon Seager
5f99ca0953
feat: add CLI update commands for brews and cups
Add UpdateBrewCommand and UpdateCupCommand to the CLI with all
updatable fields. Add corresponding update() methods to BrewsClient
and CupsClient.
2026-02-10 19:42:55 +00:00
Jon Seager
931859938a
test: add integration and E2E tests for edit functionality
Add form-based PUT update tests via define_form_update_tests macro for all
7 entities. Add hand-written datastar update tests for roasters, roasts,
cafes, brews, and cups. Add brew/cup update API tests (CRUD + auth).
Add put_form, put_form_datastar, create_default_brew, create_default_cup
test helpers.
2026-02-10 19:42:52 +00:00
Jon Seager
1a1b28559c
feat: add edit page route handlers, template structs, and edit templates
Add edit page handlers for all 7 entities (roaster, roast, bag, brew,
cafe, cup, gear) with authentication, data pre-loading, and image URL
resolution. Register edit routes in app router. Add corresponding
template structs and HTML templates with pre-populated forms.
2026-02-10 19:42:49 +00:00
Jon Seager
94088f1f4b
feat: add edit button to all detail pages
Add edit_button and edit_delete_buttons macros to detail_cards.html.
All 7 entity detail pages now show an Edit button next to Delete when
authenticated. Each detail template struct receives a pre-computed
edit_url from the route handler.
2026-02-10 19:42:47 +00:00
Jon Seager
1c029f52f4
feat: add update endpoints for brews and cups
Add UpdateBrewSubmission and UpdateCupSubmission types with image
support. Register PUT handlers on /brews/{id} and /cups/{id} with
three-way response pattern (Datastar/form/JSON).
2026-02-10 19:42:45 +00:00
Jon Seager
bc449e520e
feat: upgrade update handlers to FlexiblePayload with three-way response
All five existing update handlers (roaster, roast, cafe, gear, bag) now
accept FlexiblePayload with UpdateSubmission types that separate image
data from domain structs. Each returns Datastar redirect scripts, form
redirects, or JSON depending on request type. Image save support added
to all update paths.
2026-02-10 19:42:43 +00:00
Jon Seager
77cb61f91b
feat: add UpdateBrew, UpdateCup structs and expand UpdateBag
Add domain update structs for brew and cup entities, add update()
methods to their repository traits and SQL implementations, and
expand UpdateBag with roast_id, roast_date, and amount fields.
2026-02-10 19:42:39 +00:00
Jon Seager
fbf5a288c1
feat: support HEIF/HEIC image uploads via client-side Canvas conversion
Convert all image files to JPEG on the client before uploading, allowing
iPhone HEIF/HEIC photos to work without server-side libheif dependency.
2026-02-10 17:56:48 +00:00
Jon Seager
bb4aa103fb
fix: add success logging for deferred image saves
Log at info level when a deferred image is successfully saved, matching
the project convention that every create/update operation logs with
entity context.
2026-02-10 17:50:46 +00:00
Jon Seager
dfd9814fa5
fix: add concurrency semaphore for image processing
Limit concurrent image processing tasks to 4 via a tokio::Semaphore on
AppState. Acquired before spawn_blocking in both upload_image() and
save_deferred_image() to prevent CPU/memory exhaustion from concurrent
image uploads.
2026-02-10 17:50:42 +00:00
Jon Seager
3675a2cdc6
fix: add decompression bomb protection and MIME type validation
Set image decoder limits (10000x10000 max dimensions, 256MB max alloc)
to prevent crafted images from causing OOM via decompression bombs.
Validate data URL MIME types against an allowlist (jpeg/png/webp)
before decoding, rejecting non-image content types early.
2026-02-10 17:50:38 +00:00
Jon Seager
082b582cb3
fix: move image processing off async runtime with spawn_blocking
CPU-intensive image operations (decode, Lanczos3 resize, JPEG encode)
were running directly on the async worker thread, blocking all other
requests for 100-500ms per upload. Wrap in spawn_blocking in both
upload_image() and save_deferred_image().
2026-02-10 17:50:34 +00:00
Jon Seager
cf56ac28f7
test: add integration and e2e tests for image API
Cover all image entry points: upload, get, thumbnail, delete, auth
guards, validation, datastar responses, deferred image saves via
roaster create, checkin (cafe/cup), and scan flows including the
matched-roast guard that prevents overwriting existing images.
2026-02-10 17:50:30 +00:00
Jon Seager
7bacd30854
feat: improve image loading states on detail pages
Replace "Uploading..." text with animated spinner when uploading via
thumbnail, and show placeholder icon while image loads from server.
2026-02-10 17:50:27 +00:00
Jon Seager
b28234b52f
feat: include entity images in backup, restore, and reset
Images were not included in the backup/restore system, so a backup
would lose all uploaded images and a reset would orphan them.

Add BackupImage with base64-encoded binary data to BackupData,
export/restore/reset the entity_images table, and verify it in
the empty-database check. Backward-compatible with existing v2
backups via #[serde(default)].
2026-02-10 17:50:23 +00:00