carcost/AGENTS.md
yuany3721 71e11eaf30 feat: Add OIDC authentication with Authentik and refactor project structure
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
2026-04-12 13:31:27 +08:00

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):

  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

# 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
  • 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