2dda26a911
feat: infra/devops batch — CI/CD, installer, nginx docs, cf-orch agent (v0.3.0)
...
CI / Frontend typecheck + tests (push) Waiting to run
CI / Python tests (push) Waiting to run
Mirror / mirror (push) Has been cancelled
Release / release (push) Has been cancelled
Closes #15 , #22 , #24 , #25 . Closes #1 and #27 (already shipped in 0.2.0).
## CI/CD (#22 )
- .forgejo/workflows/ci.yml — Python lint (ruff) + pytest + Vue typecheck + vitest
on every PR/push. Installs cf-core from GitHub mirror for the CI runner.
- .forgejo/workflows/release.yml — Docker build/push (api + web) to Forgejo registry
on v* tags; git-cliff changelog; multi-arch amd64+arm64.
- .forgejo/workflows/mirror.yml — push to GitHub + Codeberg mirrors.
## Self-hosted installer (#25 )
- install.sh rewritten to match CF installer pattern: coloured output, named
functions, --docker / --bare-metal / --help flags, auto-detect Docker/conda/
Python/Node/Chromium/Xvfb, license key prompting with format validation.
## Nginx docs (#24 )
- docs/nginx-self-hosted.conf — sample nginx config: SPA fallback, SSE proxy
(proxy_buffering off), long-term asset cache headers.
- docs/getting-started/installation.md — bare-metal install section with nginx
setup, Chromium/Xvfb note, serve-ui.sh vs nginx trade-off.
## cf-orch agent (#15 )
- compose.override.yml — cf-orch-agent sidecar service (profiles: [orch]).
Starts only with docker compose --profile orch. Registers with coordinator at
CF_ORCH_COORDINATOR_URL (default 10.1.10.71:7700).
- .env.example — CF_ORCH_URL / CF_ORCH_COORDINATOR_URL comments expanded.
## Docs
- mkdocs.yml + full docs/ tree (getting-started, reference, user-guide) staged
from prior session work.
Bump version 0.2.0 → 0.3.0.
2026-04-14 06:19:25 -07:00
bdbcb046cc
fix: detect eBay condition field for parts/repair listings; add clear-filters btn
...
- aggregator: also check listing.condition against damage keywords so listings
with eBay condition "for parts or not working" flag scratch_dent_mentioned
even when the title looks clean
- aggregator: add "parts/repair" (slash) + "parts or not working" to keyword set
- trust/__init__.py: pass listing.condition into aggregate()
- 3 new regression tests (synthetic fixtures, 17 total passing)
- SearchView: extract DEFAULT_FILTERS const + resetFilters(); add "Clear filters"
button that shows only when activeFilterCount > 0 with count badge
- .env.example: document LLM inference env vars (ANTHROPIC/OPENAI/OLLAMA/CF_ORCH_URL)
and cf-core wiring notes; closes #17
2026-04-04 22:42:56 -07:00
0430454dad
feat: eBay affiliate link builder (Option B — user-configurable, CF fallback)
...
- _affiliate_url() helper appends EPN params when EBAY_AFFILIATE_CAMPAIGN_ID set
- Clean /itm/ URLs by default (no affiliate tracking without explicit opt-in)
- affiliate_active flag in search response drives frontend disclosure
- SearchView shows 'Links may include an affiliate code' when active
- .env.example documents EBAY_AFFILIATE_CAMPAIGN_ID with EPN registration link
- Closes #19
2026-04-03 22:06:41 -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
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
98695b00f0
feat(snipe): eBay trust scoring MVP — search, filters, enrichment, comps
...
Core trust scoring:
- Five metadata signals (account age, feedback count/ratio, price vs market,
category history), composited 0–100
- CV-based price signal suppression for heterogeneous search results
(e.g. mixed laptop generations won't false-positive suspicious_price)
- Expanded scratch/dent title detection: evasive redirects, functional problem
phrases, DIY/repair indicators
- Hard filters: new_account, established_bad_actor
- Soft flags: low_feedback, suspicious_price, duplicate_photo, scratch_dent,
long_on_market, significant_price_drop
Search & filtering:
- Browse API adapter (up to 200 items/page) + Playwright scraper fallback
- OR-group query expansion for comprehensive variant coverage
- Must-include (AND/ANY/groups), must-exclude, category, price range filters
- Saved searches with full filter round-trip via URL params
Seller enrichment:
- Background BTF /itm/ scraping for account age (Kasada-safe headed Chromium)
- On-demand enrichment: POST /api/enrich + ListingCard ↻ button
- Category history derived from Browse API categories field (free, no extra calls)
- Shopping API GetUserProfile inline enrichment for API adapter
Market comps:
- eBay Marketplace Insights API with Browse API fallback (catches 403 + 404)
- Comps prioritised in ThreadPoolExecutor (submitted first)
Infrastructure:
- Staging DB fields: times_seen, first_seen_at, price_at_first_seen, category_name
- Migrations 004 (staging tracking) + 005 (listing category)
- eBay webhook handler stub
- Cloud compose stack (compose.cloud.yml)
- Vue frontend: search store, saved searches store, ListingCard, filter sidebar
Docs:
- README fully rewritten to reflect MVP status + full feature documentation
- Roadmap table linked to all 13 Forgejo issues
2026-03-26 23:37:09 -07:00
68a9879191
feat: add scraper adapter with auto-detect fallback and partial score logging
2026-03-25 14:12:29 -07:00
ac114da5e7
feat: scaffold snipe repo
2026-03-25 13:08:54 -07:00