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
200 lines
5.9 KiB
Markdown
200 lines
5.9 KiB
Markdown
# 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 <token>`
|
|
|
|
**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 `<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
|
|
- `useVehicleData` composable 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_deleted` flag
|
|
- **Frontend Max Width**: Content constrained to 900px centered
|
|
- **Authentication**: Fully automatic, no login/logout buttons in UI
|