Backend: - Add auth.py for JWT token verification - Update main.py to protect all routes with auth middleware - Remove dashboard router (frontend handles aggregation) - Add Docker support with Dockerfile and docker-compose.yml Frontend: - Add OIDC authentication using oidc-client-ts with PKCE flow - Create router.js with auth guards for automatic login/logout - Add api.js for unified Axios instance with auth headers - Add composables: useAuth.js, useVehicleData.js for caching - Add views/Main.vue as main application page - Simplify App.vue to router-view container - Add deploy-web.sh deployment script Documentation: - Update AGENTS.md with new architecture and auth flow
5.9 KiB
5.9 KiB
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):
router.beforeEachchecks authentication status- Unauthenticated → redirect to Authentik OIDC login
- Callback
/auth/callback→ process code, exchange for token - Token stored in localStorage, set to Axios headers
- All API requests include
Authorization: Bearer <token>
Backend (FastAPI JWT):
auth.pyvalidates JWT token from Authentik- All routes protected by
get_current_userdependency - Returns 401 for invalid/missing tokens
API Routing Convention
All API routes include /carcost prefix:
GET/POST /carcost/vehicles/*- Vehicle CRUDGET/POST /carcost/fuel-records/*- Fuel recordsGET/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 flowweb/src/api.js- Unified Axios instance with auth header managementweb/src/composables/useAuth.js- OIDC client configuration
Backend Auth:
api/auth.py- JWT token verificationapi/main.py- Protected routes with auth dependency
Database Models
Vehicle
id,name,purchase_date,initial_mileageis_deleted- Soft delete flagcreated_at,updated_at
FuelRecord
id,vehicle_id,date,mileagefuel_amount- Litersfuel_price- Price per literdisplay_cost- Machine display amount (机显总价)actual_cost- Actual paid amount (实付金额)is_full_tank- Booleannotes- Gas station name etc.is_deleted- Soft delete flag
Cost
id,vehicle_id,date,type,amountmileage- Optionalnotesis_installment- Whether to spread across monthsinstallment_months- Number of months to spreadis_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
# 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/)
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/)
cd web
npm install
# Dev server
npm run dev
# Build for production
npm run build
# Preview production build
npm run preview
Deployment
# 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
<script setup> - Element Plus for UI components
- Unified Axios instance (
api.js) with automatic auth header - Vue Router guards handle all auth logic before component render
useVehicleDatacomposable for global data caching
Backend
- FastAPI with JWT token verification
- All routes protected by default
- Manual session management
- SQLAlchemy 2.0 style
Important Notes
- Port: Backend runs on 7030, frontend dev server on 5173
- CORS: Backend allows all origins (
["*"]) for dev - Soft Deletes: All entities use
is_deletedflag - Frontend Max Width: Content constrained to 900px centered
- Authentication: Fully automatic, no login/logout buttons in UI