# Polaris — Reseller Platform · Module Contract (agents read this) Single source of truth for the 3 parallel build agents. Do NOT drift from these interfaces — the other agents depend on them exactly as written. ## Stack (locked) - Backend: **FastAPI** (Python 3.12) in `/backend`, SQLAlchemy 2.0 ORM, Pydantic v2 - DB: **PostgreSQL 16** (schema in `db/schema.sql` — DO NOT modify it, build against it) - Queue: **Celery** + **Redis** (`redis://redis:6379/0`) - Frontend: **React 18 + Vite + TypeScript + Tailwind** in `/frontend` - Deploy: Docker Compose (root `docker-compose.yml` already has postgres+redis) ## Service URLs (inside Docker network) - postgres: `postgres://reseller:${POSTGRES_PASSWORD}@postgres:5432/reseller` - redis: `redis://redis:6379/0` - api listens on `0.0.0.0:8000` ## BACKEND agent owns: /backend - `app/main.py` — FastAPI app, CORS allow-all (MVP), mounts routers under `/api` - `app/models.py` — SQLAlchemy models mirroring `db/schema.sql` EXACTLY (table + column names) - `app/database.py` — engine from `DATABASE_URL` env, `SessionLocal`, `Base` - `app/schemas.py` — Pydantic v2 request/response models - `app/engines/pricing.py` — `calculate_price(cost, shipping, fees)` -> returns retail, margin, profit using active `price_rules`; `estimate_net_profit(...)`; never below min_price - `app/engines/suppliers/base.py` — `SupplierAdapter` ABC: get_products / get_inventory / get_price / create_order / get_order_status / get_tracking / cancel_order - `app/engines/suppliers/csv_adapter.py` — CSV feed adapter (reads supplier CSV: sku,title,cost,inventory,shipping_cost,shipping_time,category,image_url) - `app/engines/suppliers/sample.py` — sample supplier seeded with ~25 realistic products ($50-$500, home/gadgets/office niche) - `app/engines/importer.py` — import feed -> normalize -> validate -> upsert products/supplier_products; never auto-publish invalid - `app/engines/order_router.py` — `select_supplier(product_id, qty)` highest-score eligible supplier; `route_order(order)` - `app/engines/scoring.py` — supplier score = 40% price + 20% inventory + 15% speed + 10% fulfillment + 10% returns + 5% history - `app/engines/inventory.py` — sync supplier_products inventory, mark 0/unavailable, alert flags - `app/tasks.py` — Celery: sync_inventory (every 5min), sync_tracking, recalc_prices - `app/routers/` — products.py, suppliers.py, orders.py, customers.py, admin.py, analytics.py, health.py - `alembic/` — optional; MVP may `Base.metadata.create_all()` instead (schema.sql is canonical) ## API endpoints (frontend depends on these — do not rename) - `GET /api/health` -> {status, version} - `GET /api/products` (filters: ?status=&category=&search=&limit=&offset=) - `GET /api/products/{id}` - `POST /api/products/{id}/publish` - `POST /api/products/{id}/pause` - `GET /api/products/{id}/price-history` - `POST /api/products/import` (body: {supplier_id, feed_type, data_or_url}) - `GET /api/suppliers` / `POST /api/suppliers` - `GET /api/suppliers/{id}/performance` - `POST /api/orders` (body: {customer_email, items:[{product_id,qty}]}) -> creates+routes order - `GET /api/orders` / `GET /api/orders/{id}` - `POST /api/orders/{id}/tracking` (simulate supplier tracking push) - `GET /api/analytics/summary` -> today revenue/orders/profit/margin/aov/conversion - `GET /api/analytics/top-products` - `GET /api/analytics/supplier-performance` - `POST /api/auth/login` (admin, simple: env ADMIN_EMAIL/ADMIN_PASSWORD_HASH, returns JWT) - `POST /api/auth/register` (customer) ## FRONTEND agent owns: /frontend React+Vite+TS+Tailwind. Two surfaces, shared API client (`src/lib/api.ts`): - `/` storefront: home, shop grid, category filter, product page, cart, checkout (mock Stripe/BTCPay button -> creates order), order-tracking page. ORIGINAL brand "Polaris". Dark premium aesthetic. BMAC footer link https://buymeacoffee.com/r26xrthzttg - `/admin` dashboard: today KPI cards (revenue/orders/gross+net profit/margin/aov/refunds), top products table, supplier performance table, import button, product lifecycle view, order list with status + tracking. Cache-Control no-store on all API fetches. - Env: `VITE_API_URL=/api` (nginx proxies). ## OPS agent owns: root deploy + docs - Extend `docker-compose.yml` with: api, worker, frontend, nginx services - `backend/Dockerfile`, `frontend/Dockerfile`, `nginx/nginx.conf` (proxy /api->api:8000, /->frontend) - `.env.example` (POSTGRES_PASSWORD, DATABASE_URL, REDIS_URL, SECRET_KEY, ADMIN_EMAIL, ADMIN_PASSWORD, BTCPAY_URL/KEY, STRIPE_KEY) - `README.md` (deploy steps), `docs/api.md` (endpoint reference), `tests/` (pytest smoke tests) - systemd unit `polaris.service` wrapping `docker compose up -d`, enabled+started ## Verification (the parent re-runs these — every agent must self-verify first) 1. `docker compose up -d` clean, all containers healthy 2. seed sample supplier + import -> products appear in GET /api/products 3. pricing returns sensible margin for a $80 cost item 4. POST /api/orders creates order, routes to supplier, records profit 5. frontend `/` and `/admin` render with live API data 6. README deploy steps reproducible from scratch ## Rules - Secrets ONLY via env vars, never hard-coded. - Backup any existing file before overwriting (cp x x.bak-). - Never delete the parent's work; only add/extend. - All code self-contained; report exact files changed + live curl proof.