Commit Graph

216 Commits

Author SHA1 Message Date
720460852c feat(20-02): add FabMenu and CatalogSearchOverlay components
- FabMenu with animated mini menu (Add to Collection, Start Thread, New Setup)
- CatalogSearchOverlay with debounced search, tag chip filtering, result cards
- Loading skeleton grid and empty state
- Framer Motion animations for menu entrance/exit and overlay transitions
2026-04-06 08:02:59 +02:00
62249b5b48 Merge branch 'worktree-agent-adbc35a5' into Develop
# Conflicts:
#	.planning/STATE.md
#	drizzle-pg/meta/0002_snapshot.json
#	drizzle-pg/meta/_journal.json
#	src/db/schema.ts
2026-04-06 08:00:04 +02:00
67facea338 feat(20-01): extend UIStore with FAB/catalog state, add useTags hook, update useGlobalItems
- Add fabMenuOpen, openFabMenu, closeFabMenu to UIStore
- Add catalogSearchOpen, catalogSearchMode, openCatalogSearch, closeCatalogSearch
- openCatalogSearch also closes FAB menu (natural flow)
- Create useTags hook with 5-min staleTime cache
- Add optional tags parameter to useGlobalItems for tag filtering
2026-04-06 07:57:47 +02:00
2ec1276849 feat(20-01): add tags table, tag service/route, register global-items route
- Create tags table in schema with id, name (unique), createdAt
- Generate migration for tags table
- Create tag.service.ts with getAllTags (id+name, alphabetical order)
- Create tags.ts route with GET / handler
- Register /api/global-items and /api/tags routes in index.ts
- Add auth skip for GET /api/tags and GET /api/global-items
2026-04-06 07:56:40 +02:00
0a233c754d feat(19-03): add COALESCE merge for reference items in secondary services
- Setup service: LEFT JOIN globalItems in getAllSetups totals and getSetupWithItems
- Totals service: LEFT JOIN globalItems in getCategoryTotals and getGlobalTotals
- Profile service: LEFT JOIN globalItems in getPublicProfile totals and getPublicSetupWithItems
- CSV service: LEFT JOIN globalItems in exportItemsCsv for merged name/weight/price
2026-04-06 00:26:13 +02:00
ecc6ac689a feat(19-03): add tag filtering to global item search and migrate owner count
- searchGlobalItems now accepts tagNames param with AND intersection logic
- Owner count uses items.globalItemId instead of removed itemGlobalLinks
- Removed linkItemToGlobal and unlinkItemFromGlobal functions
- Route handlers now async with tags query param support
- Rewrote tests to async PGlite pattern, added tag filtering tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 20:55:36 +02:00
8a5ee731d0 feat(19-02): add catalog-linked candidates, branched resolution, remove link/unlink routes
- getThreadWithCandidates LEFT JOINs globalItems with COALESCE for name, weight, price, image
- createCandidate accepts and stores globalItemId
- resolveThread branches: reference item (globalItemId set) vs standalone (full data copy)
- Removed link/unlink endpoints from items route (replaced by direct globalItemId FK)
- 6 new tests for catalog-linked candidates and branched resolution
2026-04-05 20:49:56 +02:00
d1ffd79bbb feat(19-02): add COALESCE merge for reference items in item service
- getAllItems and getItemById LEFT JOIN globalItems with COALESCE for name, weight, price, image
- createItem accepts globalItemId and purchasePriceCents, stores brand+model as fallback name
- duplicateItem preserves globalItemId and purchasePriceCents
- updateItem type includes globalItemId and purchasePriceCents
- 10 new tests for reference item creation and merged data retrieval
2026-04-05 20:34:54 +02:00
e9baa8d7e0 feat(19-01): update Zod schemas, types, and seed script for reference model
- Add globalItemId and purchasePriceCents to createItemSchema
- Add globalItemId to createCandidateSchema
- Add tags param to searchGlobalItemsSchema
- Remove linkItemSchema from schemas and types
- Replace ItemGlobalLink with Tag and GlobalItemTag types
- Convert seedGlobalItems to async, add seedTags with 30 curated tags
2026-04-05 20:27:51 +02:00
5df513c138 feat(19-01): update schema with reference item model and tags tables
- Add globalItemId and purchasePriceCents columns to items table
- Add globalItemId column to threadCandidates table
- Add tags and globalItemTags tables for tag system
- Remove itemGlobalLinks table (replaced by direct FK)
- Generate migration with data migration step before table drop
2026-04-05 20:25:59 +02:00
574a12e6fa fix: OIDC auth flow, Vite proxy, and PostgreSQL query compat
- Add auth redirect in root layout for unauthenticated users
- Proxy OIDC routes (/login, /callback, /logout) through Vite dev server
- Strip Secure flag from OIDC cookies in dev mode (HTTP localhost)
- Disable retry on auth query to prevent stale cookie loops
- Fix SQLite .get()/.all()/.run() calls in category and global-item
  services for PostgreSQL compatibility
- Add userId scoping to category service functions
- Add OIDC error logging in auth middleware
- Apply linter auto-formatting across affected files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:25:31 +02:00
b2936b098e Merge branch 'worktree-agent-af80e237' into Develop
# Conflicts:
#	.planning/REQUIREMENTS.md
#	.planning/STATE.md
2026-04-05 13:21:56 +02:00
a9956681ba feat(18-05): add public profile page and setup visibility toggle
- Create public profile page at /users/$userId with avatar, name, bio, setups
- Create PublicSetupCard component for profile page setup listing
- Add isPublic toggle button on setup detail page
- Add Public badge to SetupCard in list view
- Update useSetups hook with isPublic field on interfaces
2026-04-05 13:19:36 +02:00
f5233d075f feat(18-04): add LinkToGlobalItem component for catalog linking
- Search-based dropdown to find and link global catalog items
- Shows linked status with link to global item detail page
- Unlink button to remove association
- Debounced search with loading and empty states
2026-04-05 13:18:24 +02:00
f53f66d321 feat(18-04): add global item hooks, catalog browse page, and detail page
- useGlobalItems/useGlobalItem/useLinkItem/useUnlinkItem hooks
- Global catalog browse page with search, debounce, and skeleton loading
- Global item detail page with owner count badge
- GlobalItemCard component with brand, model, specs badges
2026-04-05 13:17:39 +02:00
f120d179f7 feat(18-05): add profile hooks and profile edit UI in settings
- Create usePublicProfile and useUpdateProfile hooks
- Create ProfileSection component with avatar upload, display name, bio
- Add Profile section to settings page (visible when authenticated)
2026-04-05 13:17:31 +02:00
2843351d90 Merge branch 'worktree-agent-a86c0a6d' into Develop
# Conflicts:
#	.planning/REQUIREMENTS.md
#	.planning/STATE.md
#	src/db/schema.ts
#	src/db/seed.ts
#	src/server/index.ts
#	src/server/routes/setups.ts
#	src/server/services/category.service.ts
#	src/server/services/setup.service.ts
#	src/shared/schemas.ts
#	src/shared/types.ts
2026-04-05 13:13:34 +02:00
465297c398 Merge branch 'worktree-agent-a7e6e4b2' into Develop
# Conflicts:
#	.planning/REQUIREMENTS.md
#	.planning/ROADMAP.md
#	.planning/STATE.md
#	drizzle/meta/_journal.json
#	src/db/schema.ts
#	src/db/seed.ts
#	src/shared/schemas.ts
#	src/shared/types.ts
2026-04-05 13:13:26 +02:00
eb8f4b7cb2 feat(18-03): add profile routes, public setup endpoint, and auth middleware updates
- GET /api/users/:id/profile: public profile with public setups (no auth)
- PUT /api/auth/profile: update own profile (requires auth)
- GET /api/setups/:id/public: public setup view with items (no auth)
- Auth middleware skips public profile and public setup GET endpoints
- Register profileRoutes at /api/users in index.ts
- Add getOrCreateUncategorized to category service (Rule 3 fix)
- 10 route tests covering auth, public access, and 404 cases
2026-04-05 13:10:13 +02:00
d97d5d92ba feat(18-02): add global item routes, item link/unlink endpoints, and route tests
- GET /api/global-items with optional q search parameter
- GET /api/global-items/:id with ownerCount
- POST /api/items/:id/link to link user item to global item
- DELETE /api/items/:id/link to unlink
- Route registered in index.ts
- 10 route tests covering all endpoints
2026-04-05 13:07:26 +02:00
854811dd6b feat(18-03): add profile service and setup isPublic support
- updateProfile: update displayName, avatarUrl, bio for a user
- getPublicProfile: return user info with only public setups
- getPublicSetupWithItems: return setup details only if isPublic is true
- createSetup now accepts and persists isPublic field
- updateSetup can toggle isPublic
- getAllSetups includes isPublic in response
2026-04-05 13:06:44 +02:00
60dd9f4934 feat(18-02): implement global item service, seed script, and seed integration
- searchGlobalItems with LIKE-based case-insensitive search and wildcard escaping
- getGlobalItemWithOwnerCount with owner count from junction table
- linkItemToGlobal/unlinkItemFromGlobal for item-global linking
- seedGlobalItems idempotent seed from JSON catalog
- Integrated seed into seedDefaults startup
2026-04-05 13:06:07 +02:00
3a6876f7e8 test(18-02): add failing tests for global item service and seed
- 10 test cases covering search, owner count, link/unlink, seed idempotency
- Added globalItems/itemGlobalLinks tables to SQLite schema
- Added Zod schemas and types for global items
- Created 18-item bikepacking gear seed data JSON
2026-04-05 13:05:28 +02:00
89b0496845 chore(18-03): apply 18-01 schema foundation as dependency baseline 2026-04-05 13:04:09 +02:00
81b70a72ac feat(18-01): add Zod schemas, types, and global items seed data
- Add searchGlobalItemsSchema, linkItemSchema, updateProfileSchema to schemas.ts
- Add isPublic field to createSetupSchema and updateSetupSchema
- Add GlobalItem, ItemGlobalLink, SearchGlobalItems, LinkItem, UpdateProfile types
- Create global-items-seed.json with 18 bikepacking gear items across 7 categories
- Format fix in schema.ts (pre-existing biome formatting)
2026-04-05 12:59:21 +02:00
82657038cc feat(18-01): add globalItems, itemGlobalLinks tables and user profile/setup visibility columns
- Add globalItems table with brand, model, category, weightGrams, priceCents, imageUrl, description
- Add itemGlobalLinks junction table linking user items to global items (unique per item)
- Add displayName, avatarUrl, bio nullable columns to users table
- Add isPublic boolean column to setups table (default false)
- Import boolean from drizzle-orm/pg-core
- Generate migration 0001_tough_boomerang.sql
2026-04-05 12:57:49 +02:00
8c64bf9fbf feat(17-03): update client components to use imageUrl from API responses
- Replace all /uploads/ path construction with imageUrl presigned URLs
- Add imageUrl prop to ItemCard, CandidateCard, CandidateListItem, ComparisonTable
- Update ImageUpload to use presigned URLs + local preview for new uploads
- Pass imageUrl through from parent components (CollectionView, forms, routes)
2026-04-05 12:27:34 +02:00
f5d79072f2 feat(17-02): wire storage service into all routes and MCP tools, remove static /uploads/*
- Replace unlink() with deleteImage() in items and threads routes
- Add withImageUrl/withImageUrls to item, thread, setup GET responses
- Enrich MCP tool responses with presigned image URLs
- Remove /uploads/* static file serving from server index
- Update MCP image tool description (local -> storage)
2026-04-05 12:22:41 +02:00
5ce3f92a78 feat(17-02): refactor image service and routes to use S3 storage service
- Replace Bun.write/mkdir with uploadImage() from storage.service
- Remove uploadsDir parameter from fetchImageFromUrl
- Update tests to mock storage service instead of checking filesystem
2026-04-05 12:20:31 +02:00
544dd5bcd9 Merge branch 'worktree-agent-a402d11d' into Develop
# Conflicts:
#	.env.example
#	.planning/STATE.md
#	bun.lock
#	docker-compose.dev.yml
#	docker-compose.yml
#	package.json
2026-04-05 12:17:35 +02:00
f845f878fe feat(17-01): add S3 storage service with upload, delete, and presigned URL support
- Create storage.service.ts wrapping @aws-sdk/client-s3 with forcePathStyle for MinIO
- Export uploadImage, deleteImage, getImageUrl, withImageUrl, withImageUrls
- Add unit tests with mocked S3Client (8 tests passing)
- Install @aws-sdk/client-s3 and @aws-sdk/s3-request-presigner
2026-04-05 12:15:09 +02:00
d4bf4f5c16 feat(16-03): wire userId into MCP server and tool registrations
- Update createMcpServer signature to accept (db, userId)
- MCP auth middleware resolves userId from API key and Bearer token
- Store userId alongside transport in session map
- All 4 tool registration functions accept and pass userId
- Collection summary resource passes userId to all service calls
2026-04-05 10:52:43 +02:00
e78002208a feat(16-03): wire userId from context into all route handlers
- Extract userId via c.get('userId') in every route handler
- Pass userId to all service function calls as second argument
- Update settings routes to use composite key [userId, key]
- Update Env type to include userId in Variables
- Auth routes pass userId to API key management functions
2026-04-05 10:49:51 +02:00
242cacea7c feat(16-02): add userId scoping to thread, setup, and auth services
- All functions accept userId, no more prodDb defaults
- Thread operations verify ownership via and(eq(id), eq(userId))
- Candidate operations verify parent thread ownership before proceeding
- resolveThread includes userId in new item insert and verifies category ownership
- Setup operations use and() for composite id+userId conditions
- syncSetupItems validates both setup and item ownership via inArray
- updateItemClassification and removeSetupItem verify setup ownership
- Auth service: reordered createApiKey params to (db, userId, name)
- verifyApiKey unchanged (already returns { userId } from Plan 01)
2026-04-05 10:43:38 +02:00
8d85d2839e feat(16-02): add userId scoping to item, category, totals, and CSV services
- All functions accept userId as second parameter, no more prodDb defaults
- All queries filter by eq(table.userId, userId) for data isolation
- Get-by-id, update, delete use and() for composite id+userId conditions
- deleteCategory uses dynamic getOrCreateUncategorized(db, userId) not hardcoded ID
- CSV import scopes category lookup/creation and item creation to userId
- CSV export filters items by userId
- Category service converted from sync SQLite to async Postgres patterns
2026-04-05 10:41:59 +02:00
ad309510af Merge branch 'worktree-agent-a9a8b0dc' into Develop
# Conflicts:
#	.planning/REQUIREMENTS.md
#	.planning/ROADMAP.md
#	.planning/STATE.md
#	drizzle-pg/meta/0000_snapshot.json
#	drizzle-pg/meta/_journal.json
#	src/db/schema.ts
#	src/db/seed.ts
#	src/server/middleware/auth.ts
#	src/server/services/auth.service.ts
#	src/server/services/category.service.ts
#	src/server/services/oauth.service.ts
#	tests/helpers/db.ts
2026-04-05 10:38:29 +02:00
b6d562f082 feat(16-01): update auth middleware and services to resolve userId
- verifyApiKey returns { userId } | null instead of boolean
- verifyAccessToken returns { userId } | null instead of boolean
- Add getOrCreateUser upsert function in auth.service
- Add getOrCreateUncategorized helper in category.service
- requireAuth sets userId on Hono context for all 3 auth methods
- Remove GET bypass: all API routes require auth for userId resolution
- Keep bypass for /api/auth and /api/health paths
2026-04-05 10:34:19 +02:00
91e93a31a5 feat(16-01): migrate schema to pgTable and add users table with userId columns
- Rewrite schema.ts from sqlite-core to pg-core (pgTable, serial, timestamp, doublePrecision)
- Add users table with id, logtoSub (unique), createdAt
- Add userId FK column to items, categories, threads, setups, apiKeys, oauthTokens
- Add composite unique constraint on categories(userId, name)
- Change settings PK to composite (userId, key)
- Remove global Uncategorized seed from seed.ts (now per-user lazy)
- Generate Drizzle pg migration
2026-04-05 10:32:51 +02:00
6be9a2b168 fix(15): update oauth routes/tests for async + OIDC session auth
- Add await to all oauth service calls in routes (registerClient, getClient, etc.)
- Rewrite oauth tests to use mocked OIDC session instead of createUser/password
- Test consent-based authorize flow instead of credential-based flow

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 21:43:06 +02:00
59e7f4be8a fix(15): convert auth service/tests to async PGlite pattern
The executor agents wrote sync SQLite-style calls (.get(), .all(), .run())
instead of the async Postgres pattern established in Phase 14. Fixed:
- auth.service.ts: use await + destructuring for all DB operations
- auth routes: await listApiKeys
- All auth test files: async createTestDb(), await service calls

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 21:40:12 +02:00
72eefd1a06 Merge branch 'worktree-agent-a7f7c229' into Develop
# Conflicts:
#	.planning/REQUIREMENTS.md
#	.planning/ROADMAP.md
#	.planning/STATE.md
#	tests/routes/auth.test.ts
#	tests/services/auth.service.test.ts
2026-04-04 20:56:29 +02:00
79b27b6bcc feat(15-03): rewrite login page and auth hooks for OIDC
- Login page redirects to Logto instead of showing credential form
- AuthState uses string id (Logto sub claim) instead of number
- Remove useLogin, useSetup, useChangePassword hooks
- useLogout redirects to /logout (server-side OIDC logout)
- Remove ChangePasswordSection from settings page
- Update UserMenu to use new useLogout API
- Settings page shows API keys section when authenticated
2026-04-04 20:52:58 +02:00
3158274c6a Merge branch 'worktree-agent-a9901af2' into Develop
# Conflicts:
#	.planning/REQUIREMENTS.md
#	.planning/ROADMAP.md
#	.planning/STATE.md
#	bun.lock
#	package.json
#	src/server/middleware/auth.ts
#	src/server/routes/auth.ts
#	src/server/routes/oauth.ts
#	src/server/services/auth.service.ts
2026-04-04 20:48:38 +02:00
c0e6db5aa6 feat(15-02): update MCP OAuth and MCP middleware for OIDC
- Replace verifyPassword with getAuth in OAuth authorize routes
- Replace login form with consent-only form (no credential fields)
- Remove getUserCount bypass from MCP auth middleware
- GET/POST /authorize redirect to /login if no OIDC session
2026-04-04 20:46:23 +02:00
1b6a65b4d5 feat(15-02): rewrite auth routes for OIDC login/callback/logout
- Add top-level /login, /callback, /logout OIDC routes in index.ts
- Strip auth.ts to /me (OIDC claims) and API key CRUD only
- Remove credential-based login, setup, password change routes
- Remove all cookie/session handling from auth routes
2026-04-04 20:44:46 +02:00
259dc2bc8c feat(15-02): install OIDC deps, rewrite auth middleware and service
- Install @hono/oidc-auth and jose for OIDC integration
- Rewrite requireAuth middleware with three-way auth: API key, MCP Bearer, OIDC session
- Strip auth.service.ts to API key functions only (remove user/session management)
- Remove all references to getUserCount, getSession, refreshSession from middleware
2026-04-04 20:43:52 +02:00
e3659a23f1 Merge branch 'worktree-agent-ae56a15a' into Develop
# Conflicts:
#	.planning/ROADMAP.md
#	.planning/STATE.md
#	docker-compose.dev.yml
#	docker-compose.yml
#	src/db/schema.ts
2026-04-04 20:41:11 +02:00
0fe231ff1c feat(15-01): remove users and sessions tables from schema
- Delete users and sessions table definitions from src/db/schema.ts
- Generate Drizzle migration to drop both tables
- Retain apiKeys, oauthClients, oauthCodes, oauthTokens tables
2026-04-04 20:38:38 +02:00
f30d375544 feat(14-06): convert route tests + MCP tests to async PGlite
- All 8 route test files: async createTestApp(), async beforeEach
- MCP tools test: await createTestDb(), await getCollectionSummary()
- Fixed MCP tool files: added await to all service calls in items, categories, threads, setups tools
- Fixed MCP collection resource: made getCollectionSummary async
- Fixed MCP index.ts: await getCollectionSummary call
- Increased test timeout to 30s in bunfig.toml for PGlite WASM overhead
- Zero SQLite references remain in tests/

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 15:40:14 +02:00
458b33f1c7 feat(14-06): convert all 9 service test files to async PGlite
- All beforeEach now use async/await createTestDb()
- All service calls in tests now awaited
- All direct DB calls (.run()/.all()) replaced with await
- All test callbacks made async
- Fixed PostgreSQL GROUP BY strictness in totals.service.ts (categories.name and categories.icon added to groupBy)
- db type changed to 'any' to accommodate PGlite type differences

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 13:11:52 +02:00