Python Backend
The Python backend uses FastAPI + SQLAlchemy 2.0 (async) + SQLite, organizing code by business domain, with infrastructure kept together under app/core/.
Directory Organization
Section titled “Directory Organization”app/├── main.py # Application assembly: CORS, audit middleware, route registration├── core/ # Infrastructure│ ├── config.py # Configuration and JWT key validation│ ├── database.py # async engine and session│ ├── security.py # bcrypt and JWT signing/verification│ ├── deps.py # Dependencies: current user, permission checks│ ├── audit.py # Audit middleware│ ├── permissions_catalog.py # Permission code catalog (single source of truth)│ ├── menu_catalog.py # Built-in menu and group catalog│ ├── app_state.py # Session epoch and menu layout version│ ├── seed.py # Seed data│ └── schemas.py # ApiResponse / PaginatedData├── auth/ user/ role/ permission/ menu/├── system/ ai_model/ audit/ data_management/ dashboard/ public/└── ... # each domain contains models / schemas / crud / routerStartup Flow
Section titled “Startup Flow”On application startup (lifespan), the following run in order:
init_db()— create tables (create_all).seed_data()— write default roles, permissions, and users (idempotent: skipped if data already exists).sync_permissions()— sync the permission catalog with the database and fill in permissions missing from theadminrole.sync_menu_catalog()— fill in built-in groups and items without overwriting saved order.- Initialize
app_stateand the menu layout version. init_default_presets()— initialize AI model presets (when the table is empty).
Security
Section titled “Security”- Passwords: bcrypt hashing.
- Tokens: PyJWT, HS256, valid for 1440 minutes (24 hours) by default, with
subas the user ID. - Session epoch: JWTs contain an
epoch. A successful data import rotatesapp_state.session_epoch, immediately invalidating every old token. - JWT key validation: if
JWT_SECRET_KEYis empty or belongs to the weak-key set (such assecret,123456,changeme), startup is refused; a length under 32 triggers a warning but still starts. - Rate limiting: login and registration are limited by IP to at most 5 times per minute; exceeding this returns a Chinese message.
- Permissions: endpoints authorize by permission code via the
require_permission(...)dependency, not by checking role names.
Configuration
Section titled “Configuration”Configuration is read from environment variables and backend/.env (based on pydantic-settings). See Configuration and Environment Variables for the complete list:
| Variable | Description |
|---|---|
DATABASE_URL |
Defaults to sqlite+aiosqlite:///./app.db; points to /app/data/app.db in Docker |
JWT_SECRET_KEY |
Strong random key; required and must not be a weak value |
JWT_ALGORITHM |
Defaults to HS256 |
JWT_ACCESS_TOKEN_EXPIRE_MINUTES |
Defaults to 1440 |
CORS_ORIGINS |
Allowed frontend origins, as a JSON array |
DATA_TMP_DIR |
Temporary import/export directory, default ./data/tmp |
MAX_IMPORT_SIZE_BYTES |
Import archive size limit, default 1 GB |
Starting Manually
Section titled “Starting Manually”Without using dev.sh:
cd backenduv syncJWT_SECRET_KEY="$(openssl rand -base64 48)" uv run uvicorn app.main:app --reload --host 0.0.0.0 --port 8088After startup you can access:
- Frontend API prefix:
http://localhost:8088/api - Swagger docs:
http://localhost:8088/docs - OpenAPI:
http://localhost:8088/openapi.json