Files
SmartUp/CLAUDE.md
T

6.2 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project overview

SmartUp is a web dashboard for monitoring API upstream rate groups and sending change notifications via webhooks (generic JSON / DingTalk bot). It supports multiple upstream auth types, periodic rate checking with APScheduler, snapshot diffing, and balance tracking with cost analysis.

Tech stack

  • Backend: FastAPI (Python 3.12) + SQLAlchemy 2.0 (ORM, SQLite) + APScheduler
  • Frontend: Vue 3 + Element Plus + Vite + Pinia + ECharts
  • Deploy: Docker Compose single-container (multi-stage build: Node → Python)

Commands

# Backend dev
cd backend && source ../.venv/bin/activate
uvicorn app.main:app --reload --port 8000

# Run all backend tests
.venv/bin/pytest backend/ -q

# Run a single test file
.venv/bin/pytest backend/test_upstream_token_refresh.py -q

# Run a specific test
.venv/bin/pytest backend/test_upstream_token_refresh.py::test_bearer_proactive_refresh_when_expiry_approaching -q

# Frontend dev
cd frontend && npm run dev       # proxies to localhost:8000

# Docker (from repo root)
make up         # start existing container
make up-build   # rebuild image then start
make down log   # stop / view logs

The .venv at repo root is the backend venv. Frontend uses its own node_modules.

Architecture

Backend (backend/app/)

Entry point: main.py — FastAPI app with CORS, lifespan (init DB, create admin, start scheduler), mount SPA static files at /, API docs at /api/docs.

Layers:

  • routers/ — FastAPI route handlers (thin, call services). Prefixes: /api/upstreams, /api/websites, /api/webhooks, /api/logs, /api/auth, /api/custom-pages, /api/external-api-logs, /api/finance, /api/auth-capture
  • services/ — Business logic. Key services:
    • upstream_client.py (~1150 lines) — HTTP client for all upstream API calls. Auth types: none, bearer, api_key, login_password, new_api_token, nox_token, cookie. Handles rate/balance fetching, token refresh, proactive bearer refresh
    • scheduler.py — APScheduler background checks. Calls _check_upstream() which fetches rates, diffs snapshots, triggers webhooks
    • snapshot_service.py — Rate snapshot diffing and pruning
    • webhook_service.py — Send notifications (generic JSON / DingTalk with HMAC signature)
    • auth_config.py — Auth config masking, key whitelist per auth_type
    • finance_service.py — Daily cost tracking (usage_stats or balance_delta mode)
    • website_client.py / website_sync.py — Website group management and sync
  • models/ — SQLAlchemy 2.0 ORM (mapped_column style). Key models: Upstream, UpstreamRateSnapshot, WebhookConfig, NotificationLog, Website, CustomPage, UpstreamGeneratedKey, UpstreamRechargeEvent, FinanceDailySummary
  • schemas/ — Pydantic request/response schemas
  • utils/ — JWT auth (auth.py), DingTalk HMAC signature (dingtalk.py), number formatting

Database: SQLite with WAL mode, 64MB mmap, 20MB page cache. Schema migrations are handled inline in database.py (_migrate_* functions) — no Alembic. New columns/indexes are added via ALTER TABLE IF NOT EXISTS patterns.

Auth: JWT-based (python-jose). Admin user created on first startup from ADMIN_EMAIL/ADMIN_PASSWORD env vars. Each startup syncs the admin password hash from .env — changing .env admin password takes effect on restart.

Upstream auth types (in upstream_client.py):

Type Config fields Refresh behavior
bearer token, refresh_token, expires_in, token_expires_at, refresh_path Proactive 1h before expiry + 401 fallback
login_password email, password Refresh via refresh_token, fallback to re-login
api_key key, header None
new_api_token / nox_token token, user_id, provider None
cookie cookie_string None

Scheduler: Single-threaded BackgroundScheduler. Jobs are added/removed dynamically when upstreams change. Each upstream has its own check_interval_seconds.

Frontend (frontend/src/)

  • Router: Vue Router with auth guard (requiresAuth meta). Routes: /login, /upstreams, /websites, /webhooks, /logs, /external-api-logs, /custom-pages, /finance
  • State: Pinia authStore — token + email in localStorage
  • API: Axios with retry (3x exponential backoff, GET/HEAD only). 401 interceptor clears auth + redirects to login. Base URL / (same-origin in Docker)
  • UI: Element Plus component library, ECharts for charts, Lucide icons
  • Views: Each major feature has its own view component (Upstreams, Websites, Webhooks, Finance, etc.)

Deployment

  • Dockerfile: Two-stage — Node 20 builds Vue app, Python 3.12-slim runs uvicorn. Frontend dist copied to /app/static. tini as PID 1.
  • docker-compose.yml: Single smartup service, port SERVER_PORT (default 8899), volume mounts ./data for SQLite persistence, healthcheck on /healthz

Key patterns

  • Auth config persistence: UpstreamClient takes on_auth_config_update callback. The scheduler's callback persists refreshed tokens to DB via normalize_auth_config().
  • 401 retry in client: _send_request() catches 401 and calls the appropriate refresh method, then retries once. For bearer, this is _refresh_bearer_token(); for login_password, it tries refresh then re-login.
  • Snapshot diffing: Rates fetched from upstream → build_snapshot() → compare to latest snapshot via diff_snapshots() → fire webhooks on changes.
  • Secret masking: auth_config.py defines MASK sentinel. Sensitive fields are replaced with *** in API responses. normalize_auth_config() strips masks before saving.

Testing

Tests live alongside backend code as backend/test_*.py. They use pytest with monkeypatch for dependency injection. Test HTTP clients use a FakeHttpClient pattern (see test_upstream_token_refresh.py) that records calls and returns canned responses. No test database — tests mock at the HTTP layer.

Environment variables

See .env.example. Required: ADMIN_PASSWORD, JWT_SECRET. Optional: SERVER_PORT (8899), UNHEALTHY_THRESHOLD (3), TZ (Asia/Shanghai), CORS_ORIGINS.