Commit graph

490 commits

Author SHA1 Message Date
Jon Seager
f2fcada799
perf(auth): fix N+1 queries in WebAuthn authentication
Add list_all() to PasskeyCredentialRepository so auth_start and
auth_finish fetch all credentials in a single query instead of
looping through every user with a per-user query.
2026-02-06 18:18:58 +00:00
Jon Seager
a3cf7970d1
ci: split CI and deploy into separate workflows
Move test running to a dedicated push.yml workflow that triggers on both
push and PR, and restrict deploy.yml to push-to-main only.
2026-02-06 18:08:18 +00:00
Jon Seager
5bcd01d358
docs: document SQLite config and HTTP middleware stack in CLAUDE.md 2026-02-06 18:05:21 +00:00
Jon Seager
5c75c39883
perf(build): add release profile, lld linker, and defer script tags
- Add [profile.release] with thin LTO and symbol stripping
- Configure lld linker via .cargo/config.toml for faster linking
- Add clang and lld to Nix devShell
- Add defer attribute to component script tags to unblock HTML parsing
2026-02-06 18:05:16 +00:00
Jon Seager
4fb2e945a4
perf(http): add gzip compression and static asset cache headers
- Add CompressionLayer with gzip to the middleware stack
- Add Cache-Control: public, max-age=604800 (1 week) to all static
  asset routes (CSS, JS, favicons)
2026-02-06 18:05:09 +00:00
Jon Seager
494e0346bf
perf(db): enable WAL mode, add tuning pragmas and missing indexes
- Enable WAL journal mode for concurrent reads during writes
- Set synchronous=NORMAL, cache_size=8MB, temp_store=MEMORY, busy_timeout=5s
- Add indexes on brews.grinder_id, brews.brewer_id, brews.filter_paper_id
  to speed up the 6-table brew list JOIN
- Gitignore WAL sidecar files (*.db-shm, *.db-wal)
2026-02-06 18:04:58 +00:00
Jon Seager
1bcadf1d3e
ci: don't build twice in the test phase 2026-02-06 17:57:47 +00:00
Jon Seager
a8465826b5
docs: add security gotchas to CLAUDE.md
Document CSP update requirements, cookie Secure default, URL scheme
validation pattern, and Datastar fragment target checking so future
changes don't regress the security hardening.
2026-02-06 17:55:51 +00:00
Jon Seager
b4f9c02e5a
fix(brews): return reload response for brew-again from non-brew pages
The create_brew handler returned a #brew-list fragment for all Datastar
requests, but that element only exists on the brews data page. Requests
from the homepage or timeline now get a reload-script response instead,
fixing the PatchElementsNoTargetsFound error.
2026-02-06 17:52:28 +00:00
Jon Seager
835423f6ca
chore: remove unused ADMIN env vars, document BREWLOG_INSECURE_COOKIES
BREWLOG_ADMIN_USERNAME and BREWLOG_ADMIN_PASSWORD were referenced in
docs but never used in the codebase (WebAuthn-only auth). Replace with
the new BREWLOG_INSECURE_COOKIES variable.
2026-02-06 17:52:21 +00:00
Jon Seager
830d424297
fix(security): add server-side URL scheme validation for roaster homepage
Reject non-http(s) URL schemes (javascript:, data:, etc.) in both
NewRoaster::normalize() and new UpdateRoaster::normalize(). The HTML
input type="url" provides browser-side validation, but the API path
had no server-side check.
2026-02-06 17:52:15 +00:00
Jon Seager
1f2ec0fc14
fix(security): replace string interpolation with static match in gear SQL filter
GearCategory is a Rust enum so this was not exploitable, but the
format!() pattern is a code smell. Use a match returning &'static str
literals instead for consistency with the rest of the parameterised
query codebase.
2026-02-06 17:52:08 +00:00
Jon Seager
533a0a067f
fix(security): make session insert synchronous and default cookie Secure flag to on
- Await session DB insert before setting cookie, preventing orphaned sessions
- Invert cookie Secure default: now on by default, opt out via
  BREWLOG_INSECURE_COOKIES=true for local HTTP development
2026-02-06 17:52:01 +00:00
Jon Seager
85946ba9a4
fix(security): add security response headers and request body size limit
- Add Content-Security-Policy, X-Frame-Options, X-Content-Type-Options,
  Referrer-Policy, and Strict-Transport-Security response headers
- Set explicit 5 MB request body size limit via RequestBodyLimitLayer
- Enable tower-http "limit" and "set-header" features
2026-02-06 17:51:53 +00:00
Jon Seager
d35e1792da
ci: update to latest github checkout action 2026-02-06 17:35:26 +00:00
Jon Seager
95032f4014
chore: run cargo update 2026-02-06 17:34:57 +00:00
Jon Seager
0503fde8de
fix: ensure openssl linking works in clean build environments 2026-02-06 17:34:50 +00:00
Jon Seager
3fc21658a2
chore: update flake.lock file 2026-02-06 17:27:49 +00:00
Jon Seager
90d4b5eb74
ci: initialise ci configuration 2026-02-06 17:17:02 +00:00
Jon Seager
7a1fc314c8
feat: add fly.toml for production deployment 2026-02-06 17:14:56 +00:00
Jon Seager
59e9e8b318
chore: set the version to 1.0.0 2026-02-06 17:12:07 +00:00
Jon Seager
bd3e5df7ce
docs: drop copilot-instructions.md - CLAUDE.md is much more detailed 2026-02-06 17:07:22 +00:00
Jon Seager
2d7ebaebf3
chore: add *.env to gitignore 2026-02-06 16:50:26 +00:00
Jon Seager
195c4e5ef4
fix(ui): follow OS dark/light mode changes after manual toggle
Only persist theme to localStorage when the user's choice differs from
the OS preference. Clear it when they match so the prefers-color-scheme
listener remains active and automatically tracks system theme changes.
2026-02-06 16:49:16 +00:00
Jon Seager
837c548fd3
feat(nix): add Docker container build to flake
- Add brewlog-container package using dockerTools.buildImage
- Include cacert for TLS with /etc/ssl/certs and /bin linked
- Run as uid/gid 1000 with database at /data/brewlog.db
- Document Docker usage in README
2026-02-06 16:47:05 +00:00
Jon Seager
3e0c2ae771
feat(ui): add theme-aware SVG favicon that follows light/dark mode
- Create light (orange-700) and dark (orange-600) coffee cup SVG favicons
- Add explicit routes for both SVG variants
- Set correct favicon on initial load before body renders
- Swap favicon href on manual theme toggle
- Listen for OS prefers-color-scheme changes when no manual override set
2026-02-06 16:44:00 +00:00
Jon Seager
44174b4361
feat(ui): colour-code tasting note pills by SCA wheel category
Add view-layer mapping that categorises tasting notes into 9 colour
families (floral, fruity, citrus, sweet, nutty, spice, roasted, sour,
vegetal) based on the SCA Coffee Tasting Wheel. Matching uses
case-insensitive exact lookup against ~90 known terms, then substring
fallback, with pill-muted as the default for unrecognised notes.

- Add tasting_notes module with NoteCategory enum and categorize()
- Add 9 pill CSS variants with light and dark mode support
- Update roast list and timeline templates to render coloured pills
2026-02-06 16:26:29 +00:00
Jon Seager
dcaac1ee9c
feat(ui): add usage progress bars to bag cards and data table
- Add used_percent field to BagView computed from amount/remaining
- Display accent-colored progress bar on homepage bag cards
- Replace Open pill with progress bar in bag data table (desktop and mobile)
- Merge Weight column into Status column showing remaining/total
- Add full-width progress bar to mobile card view for open bags
- Sort Status column by remaining grams (closed bags treated as 10000g)
- Hide Finished field on mobile for open bags
- Use small-caps typography for remaining text labels
2026-02-06 16:08:41 +00:00
Jon Seager
3a960b9d14
feat(ui): enhance desktop tables and mobile card views
- Add spacious flat table CSS for desktop (transparent card wrapper,
  lighter dividers, visible hover, larger cell padding)
- Split roaster/cafe columns into separate Country and City columns
  with sortable headers
- Split cup Roast/Roaster into separate desktop columns with city
  sort support (full-stack: domain, repo, view, template)
- Add sortable Status column to bags with colored pills (green Open,
  amber Closed) and remaining weight subtext
- Style bag Finished column as date-only (NaiveDate) and Weight
  column with smaller text
- Show status pill next to mobile bag card actions menu
- Add city to cups mobile card view
- Use middle dot separator for brew recipe weight/volume
- Add tasting notes pills to roast list, brew notes to brew list
- Fix build.rs to use rerun-if-changed for CSS and templates
2026-02-06 15:40:40 +00:00
Jon Seager
c2cbbd58d7
docs: document test macros and generic helpers in CLAUDE.md 2026-02-06 14:27:51 +00:00
Jon Seager
1257630019
refactor(tests): add CLI test macros and deduplicate helpers
- Add `define_cli_auth_test!` macro for authentication-required tests
- Add `define_cli_list_test!` macro for unauthenticated list tests
- Replace 18 auth tests and 5 list tests with macro invocations
- Add `create_entity_cli()` generic, convert per-entity helpers to
  thin wrappers
- Move `create_bag` and `create_gear` from brews_cli.rs to helpers.rs
2026-02-06 14:27:43 +00:00
Jon Seager
dcdeaee7ca
refactor(tests): consolidate datastar tests with macro
- Add `define_datastar_entity_tests!` macro generating list-fragment,
  list-full-page, and delete-fragment tests per entity
- Add 5 setup functions that create entities and return IDs
- Replace 15 repetitive tests with 5 macro invocations
- Reduce datastar.rs from 858 to 576 lines
2026-02-06 14:27:30 +00:00
Jon Seager
ad4ee2617a
refactor(tests): add generic server helpers and CRUD test macros
- Add `paste` dev-dependency for macro identifier concatenation
- Add `create_entity<P, R>()` generic helper, convert per-entity
  creation helpers to thin wrappers
- Add `define_crud_tests!` macro generating nonexistent-GET/DELETE-404,
  empty-list-200, malformed-JSON-400, and missing-fields-400 tests
- Apply macro to roasters, cafes, cups, and roasts API tests
2026-02-06 14:26:54 +00:00
Jon Seager
f31f87d4e1
feat(ui): spacious flat desktop tables with accent hover
- Remove bordered card wrapper from table sections on desktop
- Strip thead background, rely on typography for header distinction
- Increase cell vertical padding from 0.75rem to 1rem
- Add white row backgrounds with explicit horizontal borders
- Add accent-tinted hover with left bar indicator
- Float pagination below table as un-bordered footer
- Shrink pagination prev/next to compact card-style buttons
2026-02-06 13:57:27 +00:00
Jon Seager
f8ffb37110
refactor(tests): use in-process server and shared AppState factory
Replace CLI test child process with in-process server on a background
thread, eliminating orphan processes after test runs. Simplify server
test helpers to use AppState::from_database() instead of manual repo
construction.
2026-02-06 13:48:15 +00:00
Jon Seager
9a46bb3613
refactor: update AppState imports to use state module
Mechanical change: all route handlers and auth middleware now import
AppState from application::state instead of application::server.
2026-02-06 13:48:09 +00:00
Jon Seager
4909d24a60
refactor(server): extract AppState into dedicated state module
Move AppState, AppStateConfig, and from_database() out of server.rs
into state.rs so server.rs only contains startup and shutdown logic.
2026-02-06 13:47:59 +00:00
Jon Seager
a6eaf26814
refactor: remove PostgreSQL feature-flagged support
- Remove sqlite/postgres feature flags from Cargo.toml
- Replace "any" sqlx feature with direct "sqlite" feature
- Remove all #[cfg] conditional compilation from database.rs
- Update README.md and CLAUDE.md to reflect SQLite-only support
2026-02-06 13:26:41 +00:00
Jon Seager
765acf5d6b
docs: rewrite README for clarity and brevity
Consolidate from ~250 lines to ~130, focusing on intro, quick start,
configuration reference, contributing, and license. Replace build-from-source
with cargo install instructions.
2026-02-06 13:21:04 +00:00
Jon Seager
eeeebc1572
docs: consolidate CLAUDE.md from 1212 to 607 lines
- Reorganise from single mega-section into layer-based structure
  (Backend, Datastar/Frontend, Design System, Tables/Lists)
- Promote 8 critical gotchas into dedicated top-level section
- Deduplicate signal naming (3x), route patterns (3x), form patterns
- Replace code examples with macro reference table pointing to source
- Trim design token table to name→class mapping (reference input.css)
- Consolidate error/logging rules into single section
- All patterns, rules, and conventions preserved
2026-02-06 13:12:18 +00:00
Jon Seager
c4914d6ec6
feat(ui): redesign account page and style CLI callback page
- Add page header, card-wrapped sections, and surface-alt item cards
  to the account page for visual consistency with the rest of the app
- Style CLI callback page with centered icon + heading layout matching
  the login page, with dark-mode-safe colors
- Add dark mode variants to all error/success alerts on account page
- Add check_circle icon for CLI callback success state
2026-02-06 12:33:01 +00:00
Jon Seager
db786c8387
fix(build): always regenerate CSS on every cargo build
Remove all rerun-if-changed directives so Cargo runs the build
script unconditionally, ensuring tailwindcss always regenerates
styles.css without manual steps.
2026-02-06 12:32:21 +00:00
Jon Seager
b68748da14
fix(ui): convert nav menu to floating dropdown and clean up icons
Replace the full-width inline menu with a compact right-aligned dropdown
that floats over page content. Adds click-outside-to-close via an
invisible backdrop, and uses z-50 to sit above timeline sticky headings.

- Replace database icon with squares (2x2 grid) for Data link
- Remove 5 unused icon macros: star, pen, login, sparkles, clipboard
- Add hover highlight (bg-surface-alt) to dropdown items
2026-02-06 12:28:36 +00:00
Jon Seager
202dafb9b9
feat(scan): show cards for existing roasters/roasts during bag scan
After AI extraction, check if the roaster and roast already exist by
slug. When matched, show compact summary cards instead of full edit
forms. Each card has a "Change" link to revert to the form if the
match is wrong.

- Add match_existing_entities() for slug-based roaster/roast lookup
- Return _matched-roaster-id and _matched-roast-id signals from extraction
- Add submit_existing_roast() path to skip creation when roast exists
- Dynamic submit buttons: Save Roaster & Roast / Save Roast / Open Bag
- Hidden inputs bound to signals handle all form submission cleanly
2026-02-06 12:27:25 +00:00
Jon Seager
4a7b054de1
feat(brews): pre-select bag when clicking Brew on homepage bag card
- Add initial-value attribute to searchable-select web component
- Update bag card Brew link to pass bag_id query parameter to add page
- Thread bag_id through route handler and template to pre-select the bag
2026-02-06 10:53:42 +00:00
Jon Seager
0030f87eed
refactor(timeline): extract event creation into service layer
Introduce application services that encapsulate entity creation +
timeline event recording. Route handlers call service.create() instead
of repo.insert() + inline timeline construction.

- Add define_simple_service! macro for roaster/cafe/gear services
- Add custom services for roasts, bags, brews, cups (need enrichment
  or cross-entity lookups)
- Add to_timeline_event() methods to all 7 domain entity types
- Remove timeline SQL from 4 repository insert() methods
- Delete brew_timeline_event() helper from brews route handler
- Update backup test to use services (timeline events created naturally)
- Document service layer pattern in CLAUDE.md
2026-02-06 10:42:52 +00:00
Jon Seager
97c46d034b
fix(db): add AUTOINCREMENT to prevent SQLite ROWID reuse
Without AUTOINCREMENT, SQLite can reassign the highest deleted ROWID to
a new row. This is a data integrity bug: a deleted entity's ID could be
assigned to a completely different entity, breaking bookmarks, API
caches, and URL stability.
2026-02-06 10:09:13 +00:00
Jon Seager
d9d87ef849
fix(ui): remove redundant signal resets and default tables to newest-first
Remove inline signal reset chains before extraction (server already
returns all signals) and before reload (reload destroys signal store).
Change gear and bags default sort from Make/RoastDate to CreatedAt so
data tables show newest entries first.
2026-02-05 21:59:06 +00:00
Jon Seager
4e4ac30332
refactor(account): convert token creation from imperative JS to Datastar
Replace fetch/classList DOM manipulation with Datastar signals and @post.
Server now returns signal patches for Datastar requests while preserving
the JSON API for CLI usage.
2026-02-05 21:58:57 +00:00
Jon Seager
325e4b611f
refactor(brews): deduplicate brew actions between desktop and mobile views
Extract brew-again form and delete button into a local Askama macro,
eliminating ~50 lines of duplicated markup.
2026-02-05 21:58:43 +00:00