add docker-compose swarm stack (traefik, NFS binds); SPEC/AGENTS docs

This commit is contained in:
proitlab committed 2026-08-16 20:31:35 +07:00
1 parent 73eeab7f63
commit e737cd0b63
3 files changed
+32 -3

No files matched your search

+4 -3
View File
@@ -22,10 +22,11 @@ Project conventions and operational gotchas for agents working in this repo.
- Sanity-import: `./venv/bin/python -c "from app import main; print('IMPORT_OK')"`.
## Docker
- `docker build -t idrs-api .`
- Run with volumes: `-v idrs-data:/app/data -v idrs-icons:/app/app/static/icons`, env via `--env-file .env`.
- `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).
- DB + uploaded icons persist in the volumes across container recreate; code changes need an image rebuild.
- 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.
+2
View File
@@ -16,6 +16,7 @@ FastAPI + SQLite server mirroring idrs-api live endpoints — `GET /api/getToken
- out of scope: multi-user table, roles, 2FA, password reset, on-chain calls, audit trail, change history
- data survival: existing `idrs.db` file + schema kept (migration-free); SQLAlchemy reads the same file
- run: `./venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000` + `run.sh` wrapper
- deploy: `docker-compose.yml` → Docker Swarm stack (`docker stack deploy -c docker-compose.yml`); traefik edge (Host `idrs.databisnis.id`, websecure, letsencrypt); NFS bind mounts for DB (`/app/data`) + static (`/app/app/static`); single uvicorn worker
## §I — Interfaces
api: GET `/api/getTokenInfo` → 200 `{tokenList:[{idfToken:int, position:int, typeBlockchain:string, name:string, symbol:string, contractAddr:string, decimals:int, iconUrl:string, filename:string, is_live:bool}]}` (position ASC)
@@ -70,6 +71,7 @@ T19|x|form: hide `idfToken` (auto) + `typeBlockchain` (fixed Vexanium); `positio
T20|x|Dockerfile (python:3.12-slim, uvicorn single worker, non-root) + .dockerignore; DB (`/app/data`) and icons (`/app/app/static/icons`) volumes persist|§C
T21|x|`tokenList.json`: replace seed iconUrl domain `idrs.kriptoteknologi.io` → `idrs.databisnis.id` (14 rows; existing DBs need manual update)|V5
T22|x|icon upload → `token/` subdir + `/img/token/<filename>` route (flat route removed); migrate `ayam-logo.png` into `token/`, backfill token1 iconUrl|V15
T23|x|docker-compose.yml: swarm stack — service `idrs-api` (registry image `git.proit.id/proitlab/idrs-api`), traefik labels (Host `idrs.databisnis.id`, websecure, letsencrypt), constraint `node.hostname != server2U`, external `traefik-net`, NFS bind mounts (`data` + `static`)|§C
## §B — Bug log
id|date|cause|fix
+26
View File
@@ -0,0 +1,26 @@
services:
idrs-api:
image: git.proit.id/proitlab/idrs-api:latest
env_file:
- .env
volumes:
- /mnt/nfs/server5.saltis.id/data/idrs/data:/app/data
- /mnt/nfs/server5.saltis.id/data/idrs/static:/app/app/static
networks:
- traefik-net
deploy:
placement:
constraints:
- node.hostname != server2U
restart_policy:
condition: any
labels:
- traefik.enable=true
- traefik.http.routers.idrs-api.rule=Host(`idrs.databisnis.id`)
- traefik.http.routers.idrs-api.entrypoints=websecure
- traefik.http.routers.idrs-api.tls.certresolver=letsencrypt
- traefik.http.services.idrs-api.loadbalancer.server.port=8000
networks:
traefik-net:
external: true