5.2 KiB
AGENTS.md
Project overview
- Full-stack todo app: Vue 3 + Element Plus (WebUI/) + FastAPI + SQLAlchemy (api/)
- Python 3.10+, Node 18+; SQLite database
- Single
masterbranch, no CI/CD
Quick commands
# Install Python deps (required before first run)
pip install -r requirements.txt
# Full-stack one-shot (builds frontend, then starts backend on :23994)
python main.py
# Frontend dev only (hot-reload on :5173, proxies /api → :23994)
cd WebUI; npm run dev
# Backend only
cd api; uvicorn app.main:app --host 0.0.0.0 --port 23994
# Type-check frontend (noEmit; uses project references tsconfig)
cd WebUI; npm run typecheck
# Run the one hand-written test (backend must be running on :23994)
python tests/test_accounts.py
Build order matters: python main.py automatically compiles WebUI (npm install + npm run build), copies WebUI/dist/ → api/webui/, then starts uvicorn. It checks timestamps to skip rebuilds when frontend is unchanged.
Import path: main.py injects api/ into sys.path (no os.chdir). Backend imports resolve relative to api/ — e.g. from app.config import ..., not from api.app.config import ....
Port conflict: main.py will auto-kill any process already listening on port 23994 before starting. If you have another instance running, it will be terminated without warning.
Architecture
Backend (api/app/)
- Config is hardcoded in
api/app/config.py— no.env, noos.getenv(). Port23994, CORS origins["http://localhost:5173", "http://localhost:23994"]. Note:pydantic-settingsis inrequirements.txtbut unused. - SQLite path is computed relative to
api/via__file__— safe regardless of cwd.connect_args={"check_same_thread": False}for FastAPI async compatibility. database.py:init_db()auto-creates tables on startup (create_all) and auto-adds missing columns viaALTER TABLE ADD COLUMN(no Alembic). Columns that are non-nullable with no default are skipped.- UserSettings is a singleton: always id=1, auto-created on first
GET. - Account balance changes auto-create
AccountHistoryrecords inupdate_balance(). - Habit checkins for the same day accumulate count (not new rows), enforced by a
(habit_id, checkin_date)unique constraint. - Anniversaries / DebtInstallments have computed fields (
next_date,days_until,year_count/remaining_periods) calculated at request time, not stored in DB. task_tagsM2M table is defined inmodels/tag.py(notmodels/task.py).- Update schemas use
clearable_fields+exclude_unset=Trueto distinguish "field not sent" from "field sent as null". - JWT authentication — mandatory for all
/api/*routes except/api/auth/*and/health. Default password:elysia. Login viaPOST /api/auth/login. Token key in localStorage:elysia_auth_token. Frontend axios interceptor auto-attachesAuthorization: Bearer <token>.
Frontend (WebUI/)
- Vue Router uses
createWebHistory()(HTML5 history mode) — requires the backend SPA fallback (/{full_path:path}→index.html). - Vite dev proxy forwards
/api→http://localhost:23994. @alias maps tosrc/.- Global styles in SCSS (
src/styles/). - 8 Pinia stores; Element Plus icons registered globally in
main.ts. - Element Plus uses Chinese locale (
zh-cn).
Route registration order matters
The /health endpoint must be registered before the /{full_path:path} SPA fallback catch-all, otherwise /health requests return index.html. This is enforced in api/app/main.py:114 — do not reorder these registrations.
Docker
Dockerfilecopies pre-builtapi/webui/— you must build frontend beforedocker build.- Docker CMD is an inline
python -cone-liner that injectsapi/intosys.pathand starts uvicorn. No separate entrypoint script. docker-compose.ymlmountsapi/data/andapi/logs/for persistence;api/webui/is read-only.
Testing quirks
- Only one test file:
tests/test_accounts.py— hand-written, no framework (not pytest). It counts pass/fail manually and usesrequestsdirectly againstlocalhost:23994. - Backend must be running before executing tests.
- The test permanently mutates the database — Section 15 deliberately leaves test data in place for UI display. Run it on a disposable database copy or reset manually if you need a clean state.
- The test sends no auth headers and will fail with 401 if JWT auth is enforced. You must first
POST /api/auth/loginwith{"password": "elysia"}to get a token, then includeAuthorization: Bearer <token>in requests, or temporarily comment out the auth middleware for testing. - No test coverage for tasks, habits, anniversaries, or tags.
What's missing (agents should not assume)
- No linter, formatter, pre-commit hooks, or CI/CD
- No
.envor environment variable loading - No database migrations framework (Alembic)
Additional notes
- Swagger UI at
/docswhen backend is running — the live, auto-generated API reference. python-multipartinrequirements.txtis required for FastAPI to parse form data (not optional).sassis a runtimedependency(notdevDependency) inpackage.json— unusual, but intentional.