Files

5.1 KiB

AGENTS.md — IDRS API server

Project conventions and operational gotchas for agents working in this repo.

Layout

  • app/main.py — FastAPI app: public endpoints GET /api/getTokenInfo, GET /api/version/android, GET /api/getDapps, POST /api/increaseDappCounter (header X-Api-Key = env IDRS_SECRET, fail-closed 401 if unset/wrong), GET /img/token/<filename>, GET /img/dapps/<filename> + /public/img/... aliases (seed iconUrl path); mounts /static; startup calls db.init_db().
  • app/admin.py — sqladmin panel: TokenAdmin (custom WTForms form, on_model_change), VersionAdmin, DappAdmin (URL field + imageFile upload, auto created_at/updated_at, id not writable), WalletAdmin (unique account, dup rejected, auto created_at, id not writable), create_admin(app). Upload icons saved under app/static/icons/token/ with iconUrl = {IDRS_PUBLIC_URL}/img/token/<filename>; dapp uploads under icons/dapps/.
  • app/models.py — SQLAlchemy Token/Version/Dapp/Wallet, engine, SessionLocal, ICON_DIR, DB_PATH (env IDRS_DB_PATH).
  • app/db.py — seed from tokenList.json/versionAndroid.json/dapps.json (once, empty DB only). Dapp seed mirrors each image into icons/dapps/ and rewrites to {IDRS_PUBLIC_URL}/img/dapps/<file> (gate: image/* only, sniff octet-stream, text/html→skip; on failure keep original URL; collision → -N suffix). Also seeds first two whitelisted wallets (susukudaliar, vexessential) when the wallets table is empty.
  • app/verify.py — icon/link-reachability verifiers: daemon threads started on app startup, gated by env IDRS_VERIFY_TOKENS (token iconUrl, absent/0/false/off → disabled, default) and IDRS_VERIFY_DAPPS (dapp link, enabled by default; 0/false/off disables). When enabled, is_live = reachability (local file stat under icons/token/, or external HTTP HEAD/GET 2xx; dapp link strict 2xx, whitespace-stripped); empty → false; flips after 2 consecutive failed passes, auto-recovers; interval env IDRS_VERIFY_INTERVAL (default 600s). When disabled, is_live is admin-owned (editable form field). IDRS_VERIFY_TOKENS only. Dapp admin-set is_live=false is sticky via is_live_override (verifier skips overridden dapps). External hosts must be reachable from the server or those tokens/dapps flip false while enabled.
  • app/auth.py — AdminAuthBackend, fail-closed creds.
  • tokenList.json / versionAndroid.json / dapps.json — seed source (read-only after seed).

Environment / secrets

  • .env (gitignored) → ADMIN_USERNAME, ADMIN_PASSWORD, IDRS_PUBLIC_URL (default https://idrs.databisnis.id), IDRS_SECRET (dapp counter endpoint; unset → always 401). Test creds admin/testpass123 — change before exposure.
  • .session_secret (gitignored, auto-generated 0600) if IDRS_SESSION_SECRET unset.
  • NEVER bake .env into the Docker image; pass via --env-file / -e.
  • Untracked, never commit: .env, .session_secret, idrs.db*, venv/, app/static/icons/*.

Run (local)

  • ./run.sh loads .env then uvicorn app.main:app --host 127.0.0.1 --port 8000.
  • Start detached: setsid nohup ./run.sh < /dev/null > /tmp/opencode/idrs.log 2>&1 & disown (plain & hangs).
  • Sanity-import: ./venv/bin/python -c "from app import main; print('IMPORT_OK')".

Docker

  • docker build -t idrs-api . → docker tag idrs-api:latest git.proit.id/proitlab/idrs-api:latest → docker push git.proit.id/proitlab/idrs-api:latest (registry git.proit.id, project proitlab).
  • Deploy (Swarm): docker stack deploy -c docker-compose.yml idrs — service idrs-api, traefik labels (Host idrs.databisnis.id, entrypoints=websecure, certresolver=letsencrypt), constraint node.hostname != server2U, external overlay traefik-net (must pre-exist on manager), env_file: .env.
  • NFS bind mounts: /mnt/nfs/server5.saltis.id/data/idrs/data → /app/data (DB), /mnt/nfs/server5.saltis.id/data/idrs/static → /app/app/static (icons live at <static>/icons/; icons/ subdir auto-created on first upload).
  • MUST use a single uvicorn worker (in-memory starlette sessions → --workers >1 breaks login).
  • Behind traefik, uvicorn must trust the proxy scheme or admin assets come out http:// and the panel is unstyled (mixed content): stack env sets FORWARDED_ALLOW_IPS=*.
  • DB + uploaded icons persist in the volumes across container recreate; code changes need an image rebuild + re-push.

Verification

  • curl login: POST /admin/login with -c/-b cookie jar; then curl admin endpoints / uploads with the jar.
  • Assert /img/token/<f> returns image/png; HTML/oversize uploads rejected (400).

Operation gotchas

  • NEVER pkill -f uvicorn broadly — a separate user process runs uvicorn on port 7000. Kill only the port-8000 PID (ps -ef | grep "uvicorn app.main:app --host 127.0.0.1 --port 8000").
  • Local server binds 127.0.0.1:8000. Host ports 8080/8100 are taken by other services.

Spec

  • Canonical invariants and task status live in SPEC.md (§V invariants, §T tasks, §B bug log). Keep it updated when behavior changes; check §V before modifying.