Commit graph

28 commits

Author SHA1 Message Date
Jon Seager
ac7a4f9cf9
feat(cups): add migration and domain layer for cup entity
- Create cups table with roast_id/cafe_id FKs, optional notes and rating
- Update timeline_events CHECK constraint to include 'cup' entity type
- Add CupId typed wrapper, Cup/CupWithDetails/NewCup/UpdateCup structs
- Add CupFilter, CupSortKey, and CupRepository trait
2026-02-03 16:35:56 +00:00
Jon Seager
c6ad6a02c7
feat(cafes): add migration and domain layer for cafe entity
Add cafes table with name, slug, city, country, latitude, longitude,
website, and notes fields. Define domain types (Cafe, NewCafe, UpdateCafe),
CafeId typed wrapper, CafeRepository trait, and CafeSortKey enum.
2026-02-03 14:52:37 +00:00
Jon Seager
f616958e3c
feat(search): add search parameter to repository traits
Add `search: Option<&str>` to list methods on RoasterRepository,
RoastRepository, BagRepository, GearRepository, and BrewRepository.
Default trait methods pass None for backwards compatibility.
2026-02-03 12:51:19 +00:00
Jon Seager
0b967cbb14
feat(gear): add filter paper category and optional brew association
- Add migration to extend gear CHECK constraint with 'filter_paper'
  and add nullable filter_paper_id column to brews
- Add FilterPaper variant to GearCategory enum
- Add filter_paper_id to Brew, NewBrew, and BrewWithDetails
- Add filter_paper_id to TimelineBrewData
2026-02-03 10:13:35 +00:00
Jon Seager
949f65ac16
refactor(timeline): denormalize data and simplify frontend
Database:
- Add slug, roaster_slug, brew_data_json columns to timeline_events
- Migration backfills existing data from related tables
- Remove 9-way LEFT JOIN from list query, read directly from columns

Frontend:
- Use CSS :nth-of-type(odd/even) for alternating timeline layout
- Remove JavaScript class manipulation when appending month events
- Simplify infinite scroll month-merging logic

This eliminates query-time JOINs across 5 tables and ~20 lines of
client-side JavaScript for pattern maintenance.
2026-02-03 08:53:29 +00:00
Jon Seager
b12ad2bf6d
feat(timeline): add "brew again" button to timeline
- Add TimelineBrewData struct to domain layer to carry raw brew
  parameters needed for repeating a brew
- Extend timeline SQL query to LEFT JOIN with brews table and fetch
  brew data (bag_id, grinder_id, brewer_id, coffee_weight, etc.)
- Add brew_data field to TimelineEventView for template access
- Add is_authenticated to TimelineChunkTemplate for auth-aware chunks
- Add "brew again" button to timeline month partial, visible only for
  authenticated users on brew events
- Server returns reload script when brew is created from timeline page,
  using Datastar's fragment patching to append script to body
2026-02-02 20:14:51 +00:00
Jon Seager
91bd3172ea
feat(brews): add domain layer and database migration
- Add BrewId typed wrapper
- Add Brew, BrewWithDetails, NewBrew, BrewFilter, BrewSortKey
- Add BrewRepository trait with transactional insert semantics
- Add brews table with foreign keys to bags and gear
- Update timeline_events constraint to include 'brew' entity type
2026-02-02 19:29:23 +00:00
Jon Seager
2bd008a8d7
chore(lint): enable clippy pedantic and restriction lints
- Add [lints.clippy] section to Cargo.toml with pedantic group
- Cherry-pick restriction lints: dbg_macro (deny), todo, unwrap_used, expect_used (warn)
- Allow noisy pedantic lints (missing_errors_doc, module_name_repetitions, etc.)
- Fix unnecessary Result wrappers in token and user repository to_domain functions
- Merge duplicate match arms in TimelineEventViewModel
- Add justified #[allow] attributes for startup code and tests
2026-02-02 17:55:16 +00:00
Jon Seager
b3c13faacf
fix(api): make all roast and bag endpoints return enriched types
Previously, list endpoints returned enriched types (RoastWithRoaster,
BagWithRoast) with related entity names, while get/create/update
endpoints returned bare types without this information.

This change makes all endpoints consistent by returning enriched types:
- Added get_with_roaster and get_with_roast repository methods
- Created define_enriched_get_handler! macro for custom getter methods
- Updated create and update handlers to fetch enriched data after write
- Updated CLI client to expect enriched types
2026-02-02 16:43:31 +00:00
Jon Seager
bee94d2a2e
fix(api): flatten RoastWithRoaster JSON response for consistency
The list-roasts API was returning nested objects with a "roast" field,
which was inconsistent with other list APIs in the codebase. Added
serde's #[serde(flatten)] attribute to make the response flat while
maintaining the internal struct composition.
2026-02-02 16:32:56 +00:00
Jon Seager
6bbbafbef4
refactor(gear): remove notes field from Gear entity
The notes field was not providing enough value to justify its presence.
Simplified the Gear entity by removing notes from:
- Domain structs (Gear, NewGear, UpdateGear)
- SQL repository queries and GearRecord
- HTTP client methods
- CLI commands (--notes flag)
- Web views and templates
- All related tests

Added migration 0008_remove_gear_notes.sql to drop the column.
2026-02-02 16:16:54 +00:00
Jon Seager
708d89d452
fix: resolve clippy warnings for argument count and FromStr trait
- Allow too_many_arguments for AppState::new since 8 repos are needed
- Implement FromStr trait for GearCategory instead of custom from_str
  method to follow Rust conventions
- Update callers to use map_err for Result handling
2026-02-02 16:12:13 +00:00
Jon Seager
b794d8a63f
feat(domain): add Gear entity with database migrations
Add Gear entity to track brewing equipment (grinders and brewers) with complete domain layer implementation.

Database changes:
- migrations/0006_add_gear.sql: Create gear table with category CHECK constraint and indexes
- migrations/0007_update_timeline_for_gear.sql: Document 'gear' as valid timeline entity type

Domain layer:
- Add GearId typed ID wrapper
- Create domain/gear.rs with:
  - GearCategory enum (Grinder/Brewer) with string conversion methods
  - Gear entity with make, model, notes fields
  - NewGear and UpdateGear DTOs
  - GearFilter for category-based filtering
  - GearSortKey with Make (default), Model, Category, CreatedAt options
- Add GearRepository trait to domain/repositories.rs with standard CRUD operations
- Register gear module in domain/mod.rs

This follows the same architectural pattern as the Bag entity.
2026-02-02 15:14:23 +00:00
Jon Seager
3a9fb16793
refactor(bags): replace multiple list methods with composable filter
- Add BagFilter struct with constructor methods (all, open, closed, for_roast)
- Replace 5 repository methods with single list(filter, request) method
- Add build_where_clause helper for dynamic WHERE clause construction
- Update all callers in bags and roasts routes

This eliminates method explosion when adding new filters - now only
BagFilter and build_where_clause need updating instead of adding
new repository methods.
2026-02-02 14:15:18 +00:00
Jon Seager
6611643424
chore: move RepositoryError into dedicated file 2026-02-02 12:57:15 +00:00
Jon Seager
f750107bb1
fix: add an action field to timeline events 2025-11-27 14:52:13 +00:00
Jon Seager
673f76f09a
fix: ensure the list-bags command with no id lists all bags 2025-11-27 14:32:48 +00:00
Jon Seager
14c3079600
feat: add bags domain and repositories 2025-11-27 13:56:06 +00:00
Jon Seager
f88a880d07
feat!: update to a more human friendly url structure for roasters/roasts 2025-11-25 21:34:08 +00:00
Jon Seager
42d0f71eb1
feat!: use numeric, database-generated IDs throughout 2025-11-25 18:21:04 +00:00
Jon Seager
6e1053be8f
chore: clean up unused domain models and tables 2025-11-25 16:53:38 +00:00
copilot-swe-agent[bot]
e710c6dc62
refactor: improve code quality and add comprehensive documentation
Code Quality Improvements:
- Fix hex literal grouping in ID generator (0xB10C_1D -> 0x00B1_0C1D)
- Rename ListQuery::default() to default_query() to avoid confusion with Default trait
- Use div_ceil() instead of manual ceiling division
- Remove unnecessary borrows in auth token generation and hashing
- Simplify nested if statements in error handling

Documentation:
- Add comprehensive authentication section to README
- Document environment variables for server and CLI
- Add security best practices and considerations
- Document password hashing (Argon2id), token storage (SHA-256), and session management
- Include step-by-step authentication setup guide
- Add production deployment recommendations

All 70 tests pass (8 unit + 46 server + 16 CLI) 

Co-authored-by: jnsgruk <668505+jnsgruk@users.noreply.github.com>
2025-11-25 16:28:51 +00:00
copilot-swe-agent[bot]
4c040f2c58
fix(security): validate session tokens against database
- Add sessions table to store session tokens with expiration
- Create Session domain model and SessionRepository trait
- Implement SqlSessionRepository for session persistence
- Update is_authenticated() to validate tokens against database
- Sessions expire after 30 days
- Session tokens hashed with SHA-256 before storage
- Delete sessions from database on logout
- Update all page handlers to properly validate sessions

This prevents session hijacking by ensuring only valid, unexpired
tokens stored in the database can authenticate requests.

Co-authored-by: jnsgruk <668505+jnsgruk@users.noreply.github.com>
2025-11-25 16:28:48 +00:00
copilot-swe-agent[bot]
6d0ee869f7
feat(api): add authentication middleware and token management routes
Co-authored-by: jnsgruk <668505+jnsgruk@users.noreply.github.com>
2025-11-25 16:28:10 +00:00
copilot-swe-agent[bot]
ca5d25ea10
feat(domain): add auth database schema and domain models
Co-authored-by: jnsgruk <668505+jnsgruk@users.noreply.github.com>
2025-11-25 16:27:51 +00:00
Jon Seager
4f69d20bb1
feat: add pagination/infinite scroll to timeline 2025-11-24 21:45:59 +00:00
Jon Seager
fb9fb6cc23
refactor(roasts): require roast metadata 2025-11-24 11:44:16 +00:00
Jon Seager
3241f3c961
feat: bootstrap brewlog platform 2025-11-24 11:44:12 +00:00