This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Package Tracker is a self-hosted app that connects to email inboxes via IMAP, analyzes emails using LLMs (via LiteLLM), extracts purchase/shipment info, and tracks orders in a web dashboard. Supports multiple users with admin capabilities.
- Backend: FastAPI (Python 3.12+, fully async), SQLAlchemy 2.0 async ORM, PostgreSQL 16, LiteLLM
- Frontend: Vue 3 (Composition API,
<script setup>, TypeScript), Vite, Tailwind CSS 4, Pinia, Vue Router, Axios - Deployment: Docker Compose (dev and prod configs)
docker compose up # Dev: backend:8000, frontend:5173, db:5432
docker compose -f docker-compose.prod.yaml up # Prod: frontend on port 80pip install -e ".[dev]" # Install with dev deps
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload # Run dev server
pytest tests/ -v # Run all tests
pytest tests/test_orders.py::test_list_orders -v # Run single testnpm install # Install deps
npm run dev # Vite dev server
npm run build # Type-check + production build
npm run lint # ESLint with --fix
npm run format # Prettier
npm run type-check # vue-tscAll API routes are under /api/v1/. Routers live in api/, each included in main.py.
Request flow: API router (api/) → dependency injection for auth/db (api/deps.py) → service layer (services/) → ORM models (models/) → database
Key services:
imap_worker.py— async IMAP IDLE/polling per watched folder, started/stopped via app lifespan inmain.pyemail_processor.py— orchestrates: analyze email (LLM) → match to existing order → create/update order + eventllm_service.py— calls LiteLLM with a system prompt to extract structuredEmailAnalysisfrom email textorder_matcher.py— 3-tier matching: exact order_number → exact tracking_number → fuzzy vendor+items
Auth: JWT (HS256) via HTTPBearer. Passwords hashed with bcrypt. IMAP passwords encrypted with Fernet. All sensitive config uses PT_ env prefix (see config.py).
Models: User → has many EmailAccount → has many WatchedFolder. User → has many Order → has many OrderEvent. LLMConfig is a global singleton. OrderEvent stores llm_raw_response for debugging.
Routing: router/index.ts with guards — requiresAuth, requiresAdmin, guest meta flags. Public: /login, /setup. Protected: /dashboard, /orders, /accounts, /settings. Admin: /admin/*.
State: Pinia stores in stores/ — auth.ts (JWT in localStorage, user state), orders.ts, accounts.ts.
API client: api/client.ts — Axios instance, request interceptor adds JWT, response interceptor handles 401 → logout.
Layout: AppLayout.vue wraps all authenticated views with sidebar + mobile-responsive top bar.
i18n: Vue-i18n with locale files in i18n/locales/ (en.json, de.json). Never hardcode user-visible strings in templates or scripts. Always use $t('key') (templates) or t('key') (script setup with const { t } = useI18n()). Module manifests store i18n keys (e.g., 'modules.llm.title') that are resolved at render time via $t(). When adding new UI text, add translation keys to both en.json and de.json.
In dev, Vite proxies /api requests to http://backend:8000 (Docker service name). When running outside Docker, update vite.config.ts proxy target to http://localhost:8000.
Always use Alembic for schema changes. Never execute DDL directly against the database. Migrations are applied automatically on application startup.
# From backend/
alembic revision --autogenerate -m "description of change" # Generate migrationWhen modifying SQLAlchemy models, generate an Alembic migration and commit it. Do not use CREATE TABLE, ALTER TABLE, or similar SQL directly.
Backend uses pytest with pytest-asyncio (asyncio_mode = "auto" in pyproject.toml). Tests use in-memory SQLite via aiosqlite. Fixtures in tests/conftest.py provide db_session and client (httpx AsyncClient with ASGI transport). FastAPI dependency overrides swap the real DB for the test DB.
All configuration lives in .env (required — copy .env.example to get started). Both dev and prod compose files use env_file: .env to pass vars directly into containers — no environment: sections or inline variable construction.
Database:
POSTGRES_USER— PostgreSQL username (default:tracker)POSTGRES_PASSWORD— PostgreSQL passwordPOSTGRES_DB— PostgreSQL database name (default:tracker)
Backend (Pydantic Settings with PT_ prefix in config.py):
PT_SECRET_KEY— JWT signing key (required, no default)PT_ENCRYPTION_KEY— Fernet key for IMAP password encryption (required, no default)PT_DATABASE_URL— PostgreSQL connection string (asyncpg)