5.1 KiB
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 endpointsGET /api/getTokenInfo,GET /api/version/android,GET /api/getDapps,POST /api/increaseDappCounter(headerX-Api-Key= envIDRS_SECRET, fail-closed 401 if unset/wrong),GET /img/token/<filename>,GET /img/dapps/<filename>+/public/img/...aliases (seed iconUrl path); mounts/static; startup callsdb.init_db().app/admin.py— sqladmin panel:TokenAdmin(custom WTForms form,on_model_change),VersionAdmin,DappAdmin(URL field +imageFileupload, autocreated_at/updated_at,idnot writable),WalletAdmin(unique account, dup rejected, autocreated_at,idnot writable),create_admin(app). Upload icons saved underapp/static/icons/token/withiconUrl = {IDRS_PUBLIC_URL}/img/token/<filename>; dapp uploads undericons/dapps/.app/models.py— SQLAlchemyToken/Version/Dapp/Wallet, engine,SessionLocal,ICON_DIR,DB_PATH(envIDRS_DB_PATH).app/db.py— seed fromtokenList.json/versionAndroid.json/dapps.json(once, empty DB only). Dapp seed mirrors eachimageintoicons/dapps/and rewrites to{IDRS_PUBLIC_URL}/img/dapps/<file>(gate:image/*only, sniffoctet-stream,text/html→skip; on failure keep original URL; collision →-Nsuffix). 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 envIDRS_VERIFY_TOKENS(tokeniconUrl, absent/0/false/off → disabled, default) andIDRS_VERIFY_DAPPS(dapplink, enabled by default;0/false/offdisables). When enabled,is_live= reachability (local file stat undericons/token/, or external HTTP HEAD/GET 2xx; dapp link strict 2xx, whitespace-stripped); empty → false; flips after 2 consecutive failed passes, auto-recovers; interval envIDRS_VERIFY_INTERVAL(default 600s). When disabled,is_liveis admin-owned (editable form field).IDRS_VERIFY_TOKENSonly. Dapp admin-setis_live=falseis sticky viais_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(defaulthttps://idrs.databisnis.id),IDRS_SECRET(dapp counter endpoint; unset → always 401). Test credsadmin/testpass123— change before exposure..session_secret(gitignored, auto-generated 0600) ifIDRS_SESSION_SECRETunset.- NEVER bake
.envinto the Docker image; pass via--env-file/-e. - Untracked, never commit:
.env,.session_secret,idrs.db*,venv/,app/static/icons/*.
Run (local)
./run.shloads.envthenuvicorn 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(registrygit.proit.id, projectproitlab).- Deploy (Swarm):
docker stack deploy -c docker-compose.yml idrs— serviceidrs-api, traefik labels (Hostidrs.databisnis.id,entrypoints=websecure,certresolver=letsencrypt), constraintnode.hostname != server2U, external overlaytraefik-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 >1breaks login). - Behind traefik, uvicorn must trust the proxy scheme or admin assets come out
http://and the panel is unstyled (mixed content): stack env setsFORWARDED_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/loginwith-c/-bcookie jar; then curl admin endpoints / uploads with the jar. - Assert
/img/token/<f>returnsimage/png; HTML/oversize uploads rejected (400).
Operation gotchas
- NEVER
pkill -f uvicornbroadly — 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.