f8dd1d261d
feat: preferences store, community signals, a11y + API fixes
...
CI / Python tests (push) Waiting to run
CI / Frontend typecheck + tests (push) Waiting to run
Mirror / mirror (push) Waiting to run
- Store: add save_community_signal, get/set_user_preference, get_all_preferences
- App.vue: move skip link before nav (correct a11y order); bootstrap preferences store after session
- useTrustFeedback: prefix fetch URL with VITE_API_BASE (fixes menagerie routing)
- search.ts: guard v-model.number empty-string from cleared price inputs
- Python: isort import ordering pass across adapters, trust, tasks, tests
2026-04-14 16:15:09 -07:00
0617fc8256
feat: add in-app feedback FAB
...
- api/main.py: GET /api/feedback/status + POST /api/feedback — creates
Forgejo issues; disabled (503) when FORGEJO_API_TOKEN unset, 403 in
demo mode; includes view, version, platform context in issue body
- FeedbackButton.vue: 2-step modal (type → review → submit); probes
/api/feedback/status on mount, stays hidden until confirmed enabled
- App.vue: mount FeedbackButton with current route name as view context;
import useRoute for reactive route name tracking
- .env.example: document FORGEJO_API_TOKEN / FORGEJO_REPO / FORGEJO_API_URL
2026-04-03 21:42:26 -07:00
e93e3de207
feat: scammer blocklist, search/listing UI overhaul, tier refactor
...
**Scammer blocklist**
- migration 006: scammer_blocklist table (platform + seller_id unique key,
source: manual|csv_import|community)
- ScammerEntry dataclass + Store.add/remove/list_blocklist methods
- blocklist.ts Pinia store — CRUD, export CSV, import CSV with validation
- BlocklistView.vue — list with search, export/import, bulk-remove; sellers
show on ListingCard with force-score-0 badge
- API: GET/POST/DELETE /api/blocklist + CSV export/import endpoints
- Router: /blocklist route added; AppNav link
**Migration renumber**
- 002_background_tasks.sql → 007_background_tasks.sql (correct sequence
after blocklist; idempotent CREATE IF NOT EXISTS safe for existing DBs)
**Search + listing UI overhaul**
- SearchView.vue: keyword expansion preview, filter chips for condition/
format/price, saved-search quick-run button, paginated results
- ListingCard.vue: trust tier badge, scammer flag overlay, photo count
chip, quick-block button, save-to-search action
- savedSearches store: optimistic update on run, last-run timestamp
**Tier refactor**
- tiers.py: full rewrite with docstring ladder, BYOK LOCAL_VISION_UNLOCKABLE
flag, intentionally-free list with rationale (scammer_db, saved_searches,
market_comps free to maximise adoption)
**Trust aggregator + scraper**
- aggregator.py: blocklist check short-circuits scoring to 0/BAD_ACTOR
- scraper.py: listing format detection, photo count, improved title parsing
**Theme**
- theme.css: trust tier color tokens, badge variants, blocklist badge
2026-04-03 19:08:54 -07:00
9e20759dbe
feat: wire cloud session, Heimdall licensing, and split-store DB isolation
...
- api/cloud_session.py: new module — JWT validation (Directus HS256),
Heimdall provision+tier-resolve, CloudUser+SessionFeatures dataclasses,
compute_features() tier→feature-flag mapping, require_tier() dependency
factory, get_session() FastAPI dependency (local-mode transparent passthrough)
- api/main.py: remove _DB_PATH singleton; all endpoints receive session via
Depends(get_session); shared_store (sellers/comps) and user_store (listings/
saved_searches) created per-request from session.shared_db / session.user_db;
pages capped to features.max_pages; saved_searches limit enforced for free tier;
/api/session endpoint exposes tier+features to frontend; _trigger_scraper_enrichment
receives shared_db Path (background thread creates its own Store)
- app/platforms/ebay/adapter.py, scraper.py: rename store→shared_store parameter
(adapters only touch sellers+comps, never listings — naming reflects this)
- app/trust/__init__.py: rename store→shared_store (TrustScorer reads
sellers+comps from shared DB; listing staging fields come from caller)
- app/db/store.py: refresh_seller_categories gains listing_store param for
split-DB mode (reads listings from user_store, writes categories to self)
- web/src/stores/session.ts: new Pinia store — bootstrap() fetches /api/session,
exposes tier+features reactively; falls back to full-access local defaults
- web/src/App.vue: call session.bootstrap() on mount
- web/src/views/SearchView.vue: import session store; pages buttons disabled+greyed
above features.max_pages with upgrade tooltip
- compose.cloud.yml: add CLOUD_MODE=true + CLOUD_DATA_ROOT env; fix volume mount
- docker/web/nginx.cloud.conf: forward X-CF-Session header from Caddy to API
- .env.example: document cloud env vars (CLOUD_MODE, DIRECTUS_JWT_SECRET, etc.)
2026-03-27 02:07:06 -07:00
7a704441a6
feat(snipe): Vue 3 frontend scaffold + Docker web service
...
- web/: Vue 3 + Vite + UnoCSS + Pinia, dark tactical theme (amber/#0d1117)
- AppNav, ListingCard, SearchView with filters/sort, composables
(useSnipeMode, useKonamiCode, useMotion), Pinia search store
- Steal shimmer, auction countdown, Snipe Mode easter egg all native in Vue
- docker/web/: nginx + multi-stage Dockerfile (node build → nginx serve)
- compose.yml: api (8510) + web (8509) services
- Dockerfile CMD updated to uvicorn for upcoming FastAPI layer
- Clean build: 0 TS errors, 380 modules
2026-03-25 15:11:35 -07:00