Files
app/CLAUDE.md
T
Coupon Carl cfda1b544d feat: migrate authentication to Better-Auth (Phase 1)
Replace hand-rolled JWT auth with Better-Auth session-based authentication.

- Scaffold auth/ Node.js service with Better-Auth, bcrypt password compat,
  Postgres adapter mapped to existing users table
- Add Alembic migration (002) creating sessions, accounts, verifications
  tables and migrating password hashes to accounts table
- Update FastAPI auth dependency to validate sessions via shared DB
  (supports both cookie and Bearer token)
- Remove registration/login/refresh endpoints from API gateway (now
  handled by Better-Auth service)
- Update frontend to use better-auth/react client with httpOnly cookies
  (no tokens in localStorage or memory)
- Rewrite auth store, Login, Register, Dashboard, Settings, ProtectedRoute
  to use session-based auth
- Update all tests to create sessions directly in DB instead of JWT tokens

Resolves CAR-27
See plan: CAR-26#document-plan

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-28 04:46:10 +00:00

9.3 KiB

CartSnitch Monorepo

Project Context

CartSnitch is a self-hosted grocery price intelligence platform. This repo (cartsnitch/cartsnitch) is the monorepo containing the flagship frontend PWA and core backend services.

GitHub org: github.com/cartsnitch Domain: cartsnitch.com

Monorepo Layout

Directory Service Purpose
/ (root) Frontend React PWA, mobile-first (this directory)
auth/ Auth Better-Auth Node.js service (session management, email/password, OAuth)
api/ API Gateway Frontend-facing REST API
common/ Common Shared Python models, schemas, Alembic migrations
receiptwitness/ ReceiptWitness Purchase data ingestion via retailer scrapers

Other CartSnitch Repos (still separate)

Repo Service Purpose
cartsnitch/stickershock StickerShock Price increase detection & CPI comparison
cartsnitch/shrinkray ShrinkRay Shrinkflation monitoring
cartsnitch/clipartist ClipArtist Coupon/deal watching & shopping optimization
cartsnitch/infra K8s manifests, Flux kustomizations

What This App Does

The frontend is a mobile-first PWA that serves as the primary user interface for CartSnitch. Users interact with it to:

  1. Connect store accounts — link their Meijer, Kroger, Target loyalty accounts
  2. View purchase history — see all purchases across stores in one timeline
  3. Browse products — search the normalized product catalog
  4. Track prices — view per-item price charts across stores over time
  5. Get alerts — see price increase and shrinkflation notifications
  6. View coupons/deals — browse active coupons relevant to their shopping
  7. Generate optimized shopping lists — input what they need, get a store-split plan with coupon instructions
  8. Public dashboards — shareable price transparency views (store comparisons, inflation tracking)

Tech Stack

  • React 18+ (or Next.js if SSR/SSG is valuable for public pages)
  • TypeScript
  • Tailwind CSS (mobile-first responsive design)
  • Workbox (service worker, offline caching, PWA manifest)
  • Recharts or Chart.js (price trend visualizations)
  • TanStack Query (React Query) for data fetching and caching
  • React Router (client-side routing)
  • Zustand or Jotai (lightweight state management)
  • Vite (build tool)

Repo Structure

frontend/
├── CLAUDE.md
├── README.md
├── package.json
├── tsconfig.json
├── vite.config.ts
├── tailwind.config.ts
├── Dockerfile                  # Multi-stage: build + nginx serve
├── docker-compose.yml          # Local dev
├── public/
│   ├── manifest.json           # PWA manifest
│   ├── sw.js                   # Service worker (Workbox generated)
│   ├── icons/                  # PWA icons (192, 512, maskable)
│   └── favicon.ico
├── src/
│   ├── main.tsx
│   ├── App.tsx
│   ├── api/
│   │   ├── client.ts           # Axios/fetch wrapper, JWT interceptor
│   │   ├── auth.ts             # Auth API calls
│   │   ├── purchases.ts
│   │   ├── products.ts
│   │   ├── prices.ts
│   │   ├── coupons.ts
│   │   ├── shopping.ts
│   │   └── alerts.ts
│   ├── hooks/
│   │   ├── useAuth.ts
│   │   ├── usePurchases.ts
│   │   ├── useProducts.ts
│   │   ├── usePrices.ts
│   │   └── useAlerts.ts
│   ├── pages/
│   │   ├── Login.tsx
│   │   ├── Register.tsx
│   │   ├── Dashboard.tsx        # Home — summary, recent purchases, alerts
│   │   ├── Purchases.tsx        # Purchase history timeline
│   │   ├── PurchaseDetail.tsx   # Single receipt view
│   │   ├── Products.tsx         # Product catalog / search
│   │   ├── ProductDetail.tsx    # Product with cross-store price chart
│   │   ├── Prices.tsx           # Price trends overview
│   │   ├── Coupons.tsx          # Active deals
│   │   ├── ShoppingList.tsx     # Optimized shopping list builder
│   │   ├── Alerts.tsx           # Price increase / shrinkflation alerts
│   │   ├── StoreAccounts.tsx    # Manage connected stores
│   │   ├── Settings.tsx
│   │   └── public/
│   │       ├── PriceTrends.tsx  # Public shareable price dashboards
│   │       └── StoreComparison.tsx
│   ├── components/
│   │   ├── layout/
│   │   │   ├── AppShell.tsx     # Mobile nav, header, bottom tabs
│   │   │   ├── BottomNav.tsx
│   │   │   └── Header.tsx
│   │   ├── charts/
│   │   │   ├── PriceHistoryChart.tsx
│   │   │   ├── SpendingChart.tsx
│   │   │   └── InflationComparisonChart.tsx
│   │   ├── purchases/
│   │   │   ├── PurchaseCard.tsx
│   │   │   └── PurchaseItemRow.tsx
│   │   ├── products/
│   │   │   ├── ProductCard.tsx
│   │   │   └── StorePriceComparison.tsx
│   │   ├── coupons/
│   │   │   └── CouponCard.tsx
│   │   ├── shopping/
│   │   │   ├── ShoppingListEditor.tsx
│   │   │   └── OptimizedPlan.tsx
│   │   ├── alerts/
│   │   │   ├── PriceAlertCard.tsx
│   │   │   └── ShrinkflationCard.tsx
│   │   └── common/
│   │       ├── LoadingSpinner.tsx
│   │       ├── EmptyState.tsx
│   │       ├── StoreLogo.tsx
│   │       └── ErrorBoundary.tsx
│   ├── stores/                 # Zustand/Jotai state stores
│   │   ├── authStore.ts
│   │   └── uiStore.ts
│   ├── utils/
│   │   ├── formatCurrency.ts
│   │   ├── formatDate.ts
│   │   └── storeSlugs.ts
│   └── types/
│       ├── purchase.ts
│       ├── product.ts
│       ├── price.ts
│       ├── coupon.ts
│       └── alert.ts
└── tests/
    └── ...

Design Principles

  • Mobile-first. Primary use case is checking prices and managing lists on a phone in-store or on the couch. Design for 375px viewport first, scale up.
  • Fast and offline-capable. Service worker caches the app shell and recent data. Users should be able to browse their purchase history and shopping lists offline.
  • Minimal, opinionated UI. This isn't a generic dashboard. Every screen should answer a specific question: "What did I buy?" "Where should I buy this?" "Am I getting ripped off?"
  • Store branding. Use each retailer's brand colors for visual differentiation (Meijer red, Kroger blue, Target red — careful with the two reds, differentiate with icons/logos).
  • Charts that tell a story. Price history charts should make it immediately obvious when a price spiked. Annotations for "coupon available" or "inflation baseline" add context.

PWA Requirements

  • manifest.json with proper app name, icons (192x192, 512x512, maskable), theme color, background color, display: standalone
  • Service worker via Workbox: precache app shell, runtime cache API responses with stale-while-revalidate
  • Add to Home Screen support on iOS and Android
  • Offline fallback page

API Integration

All data comes from the CartSnitch API gateway (cartsnitch/api). Base URL configured via environment variable VITE_API_URL.

  • Authentication via Better-Auth (auth/ service). Sessions are managed via httpOnly cookies — no tokens in localStorage or memory.
    • Auth service URL configured via VITE_AUTH_URL (default: http://localhost:3001)
    • Frontend uses better-auth/react client for sign-in, sign-up, sign-out, and useSession() hook
    • API gateway validates sessions by querying the shared sessions table in Postgres
    • Both cookie-based and Bearer token auth are supported (cookies for web, Bearer for API clients)
  • TanStack Query handles caching, background refetching, and optimistic updates.
  • API client sends credentials: 'include' on all requests to forward session cookies.

Development Workflow

  • Never push directly to main. Always create feature branches and open PRs.
  • Branch naming: feature/<description> or fix/<description>
  • Use conventional commits: feat:, fix:, refactor:, docs:, chore:
  • npm run dev for local development with hot reload
  • npm run build for production build
  • Lint with ESLint, format with Prettier

Important Notes

  • The store account connection flow is the most complex UX. Users need to authenticate with each retailer. This likely involves opening a controlled browser window/iframe where they log in, and CartSnitch captures the resulting session. Design this flow carefully — it needs to feel safe and trustworthy.
  • Public price transparency pages should be SSR-friendly or statically generated for SEO if the "naming and shaming" feature is going to get organic traffic. Consider Next.js for these pages specifically, or a separate lightweight static site.
  • The optimized shopping list is the killer feature for retention. Make it dead simple: add items → see the split → go shop. No friction.
  • Push notifications (via service worker) for price alerts and deal notifications are a Phase 2+ feature but design the alert system with this in mind.