# CarCost - Agent Instructions ## Project Overview Full-stack vehicle expense tracking application with OIDC authentication. - **Frontend**: Vue 3 + Vite + Element Plus + ECharts (`web/`) - **Backend**: FastAPI + SQLAlchemy + PostgreSQL (`api/`) - **Authentication**: Authentik OIDC with PKCE flow ## Architecture ### Monorepo Structure ``` carcost/ ├── deploy-web.sh # Frontend deployment script ├── web/ │ ├── src/ │ │ ├── App.vue # Root container (router-view only) │ │ ├── main.js # Entry with Element Plus + ECharts │ │ ├── router.js # Vue Router with OIDC auth guard │ │ ├── api.js # Unified Axios instance with auth │ │ ├── composables/ # Vue composables │ │ │ ├── useVehicleData.js │ │ │ └── useAuth.js # OIDC configuration (userManager only) │ │ ├── views/ # Page views │ │ │ └── Main.vue # Main application page │ │ └── components/ # Reusable components │ └── dist/ # Build output │ ├── api/ │ ├── main.py # FastAPI entry with auth middleware │ ├── auth.py # JWT verification module │ ├── config.py # Configuration │ ├── database.py # Database connection │ ├── models.py # SQLAlchemy models │ ├── schemas.py # Pydantic schemas │ └── routers/ │ ├── vehicles.py # Vehicle CRUD │ ├── fuel_records.py # Fuel record CRUD │ └── costs.py # Cost record CRUD ``` ### Authentication Flow **Frontend (Vue Router Guard)**: 1. `router.beforeEach` checks authentication status 2. Unauthenticated → redirect to Authentik OIDC login 3. Callback `/auth/callback` → process code, exchange for token 4. Token stored in localStorage, set to Axios headers 5. All API requests include `Authorization: Bearer ` **Backend (FastAPI JWT)**: 1. `auth.py` validates JWT token from Authentik 2. All routes protected by `get_current_user` dependency 3. Returns 401 for invalid/missing tokens ### API Routing Convention All API routes include `/carcost` prefix: - `GET/POST /carcost/vehicles/*` - Vehicle CRUD - `GET/POST /carcost/fuel-records/*` - Fuel records - `GET/POST /carcost/costs/*` - Cost records Frontend API base: `https://api.yuany3721.site/carcost` ### Key Files **Frontend Auth**: - `web/src/router.js` - Route guards handle OIDC flow - `web/src/api.js` - Unified Axios instance with auth header management - `web/src/composables/useAuth.js` - OIDC client configuration **Backend Auth**: - `api/auth.py` - JWT token verification - `api/main.py` - Protected routes with auth dependency ### Database Models #### Vehicle - `id`, `name`, `purchase_date`, `initial_mileage` - `is_deleted` - Soft delete flag - `created_at`, `updated_at` #### FuelRecord - `id`, `vehicle_id`, `date`, `mileage` - `fuel_amount` - Liters - `fuel_price` - Price per liter - `display_cost` - Machine display amount (机显总价) - `actual_cost` - Actual paid amount (实付金额) - `is_full_tank` - Boolean - `notes` - Gas station name etc. - `is_deleted` - Soft delete flag #### Cost - `id`, `vehicle_id`, `date`, `type`, `amount` - `mileage` - Optional - `notes` - `is_installment` - Whether to spread across months - `installment_months` - Number of months to spread - `is_deleted` - Soft delete flag **Cost Types**: 保养/维修/保险/停车/过路费/洗车/违章/其他 All models use soft delete (`is_deleted` boolean). ## Authentication (Authentik OIDC) ### Authentik Configuration **OIDC Discovery URL**: `https://auth.yuany3721.site/application/o/car-cost/.well-known/openid-configuration` **Client ID**: `27UljrOp2LfCg3fjEo1n8vOiRyCFzqEcnBFiUK59` **Endpoints**: - **Authorization**: `https://auth.yuany3721.site/application/o/authorize/` - **Token**: `https://auth.yuany3721.site/application/o/token/` - **UserInfo**: `https://auth.yuany3721.site/application/o/userinfo/` - **End Session**: `https://auth.yuany3721.site/application/o/car-cost/end-session/` ### Environment Variables ```bash # api/.env DATABASE_URL=postgresql://user:pass@host:port/carcost DEBUG=true AUTHENTIK_ISSUER=https://auth.yuany3721.site/application/o/car-cost/ AUTHENTIK_CLIENT_ID=27UljrOp2LfCg3fjEo1n8vOiRyCFzqEcnBFiUK59 ``` ## Development Commands ### Backend (api/) ```bash cd api source venv/bin/activate # Run dev server python main.py # or uvicorn main:app --host 0.0.0.0 --port 7030 --reload # Import fuel records from CSV python import_fuel_records.py ``` ### Frontend (web/) ```bash cd web npm install # Dev server npm run dev # Build for production npm run build # Preview production build npm run preview ``` ### Deployment ```bash # Deploy frontend (from carcost root) ./deploy-web.sh ``` ## Key Configuration Files ### api/.env ``` DATABASE_URL=postgresql://user:pass@host:port/carcost DEBUG=true AUTHENTIK_ISSUER=https://auth.yuany3721.site/application/o/car-cost/ AUTHENTIK_CLIENT_ID=27UljrOp2LfCg3fjEo1n8vOiRyCFzqEcnBFiUK59 ``` ### web/vite.config.js - `server.allowedHosts: ['yuany3721.site', '.yuany3721.site']` ## Code Patterns ### Frontend - Vue 3 Composition API with `