feat(finlab): dashboard web (graphiques/portefeuille/actions) + fix seed NFS (#65)

Ajoute une interface graphique à finlab, en plus de la console Claude Code (les
deux cohabitent sur finance.lab.local) :

- dashboard/ : backend FastAPI (sans LLM/clé) exposant les données finlab en JSON
  (/api/portfolio, /api/scan, /api/ohlc, /api/plan, /api/alerts) + front statique
  (lightweight-charts) : graphiques chandeliers MM50/200 + volume, portefeuille
  (P&L/poids/secteurs), watchlists thématiques scannées, alertes, plan de trade R:R
- Découplage code/data : finlab lit ses configs depuis FINLAB_HOME si défini
  (workspace persistant) ; repli sur le dossier du package en dév local
- Image double usage (un build, deux conteneurs) : code → /opt/app, seed minimal
  (configs + persona + MCP) → workspace
- Fix seed NFS (cause racine du workspace vide) : WORKDIR n'est plus sur le volume
  (le runtime le créait en root sur NFS) ; seed déplacé dans un initContainer
  robuste (recrée le dossier s'il est non-inscriptible)
- Deployment : initContainer seed + 2 conteneurs (console ttyd -b /console, dashboard
  uvicorn) ; Service 2 ports ; IngressRoute 2 routes (/console + dashboard), basicAuth

Validé en local (podman) : seed OK, dashboard /api/portfolio → 16k€/10 positions
via FINLAB_HOME, endpoints ohlc/scan/plan/alerts OK, ttyd/claude/finlab présents.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ALI YESILKAYA 2026-06-30 00:57:23 +02:00 committed by GitHub
parent 607e5e6c0a
commit 56cb32eacc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 700 additions and 149 deletions

View file

@ -16,12 +16,9 @@ spec:
labels:
app: finlab
spec:
# Image privée sur GHCR → secret de pull créé manuellement dans le ns ai
# (cf. README : kubectl create secret docker-registry ghcr-pull ...).
imagePullSecrets:
- name: ghcr-pull
# Conformité PodSecurity « restricted » (namespace ai). fsGroup 1000 → le PVC NFS monté
# en /home/finlab (HOME : ~/.claude du login abonnement + workspace) est inscriptible.
# PodSecurity « restricted » (ns ai). fsGroup 1000 → PVC NFS monté en /home/finlab.
securityContext:
runAsNonRoot: true
runAsUser: 1000
@ -29,40 +26,72 @@ spec:
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: finlab
# Tag bumpé par la CI (build-finlab.yml) sur main.
# initContainer : prépare le workspace (seed robuste NFS) AVANT console + dashboard,
# pour qu'il soit peuplé et inscriptible quand les deux conteneurs démarrent.
initContainers:
- name: seed
image: ghcr.io/alkatrazz24/funk-finlab:sha-54f6f7c
command: ["/usr/local/bin/entrypoint-seed.sh"]
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
volumeMounts:
- name: home
mountPath: /home/finlab
containers:
# ── CONSOLE : Claude Code dans un terminal web (ttyd), sous /console ──
- name: console
image: ghcr.io/alkatrazz24/funk-finlab:sha-54f6f7c
command: ["/usr/local/bin/entrypoint-console.sh"]
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
ports:
- name: console
containerPort: 7681
readinessProbe:
tcpSocket: { port: 7681 }
initialDelaySeconds: 10
periodSeconds: 15
livenessProbe:
tcpSocket: { port: 7681 }
initialDelaySeconds: 30
periodSeconds: 30
resources:
requests: { cpu: 150m, memory: 512Mi }
limits: { cpu: "1", memory: 1536Mi }
volumeMounts:
- name: home
mountPath: /home/finlab
# ── DASHBOARD : interface web (FastAPI), graphiques + données finlab ──
- name: dashboard
image: ghcr.io/alkatrazz24/funk-finlab:sha-54f6f7c
command:
- /opt/venv/bin/uvicorn
- dashboard.server:app
- --host=0.0.0.0
- --port=8800
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
ports:
- name: web
containerPort: 7681
env:
- name: TZ
value: "Europe/Paris"
# ttyd écoute en TCP : sonde TCP (pas de endpoint HTTP de santé dédié). Démarrage
# rapide, mais on laisse de la marge (seed du workspace au 1er boot).
containerPort: 8800
# FINLAB_HOME/PYTHONPATH viennent de l'image ; FINLAB_HOME = workspace (configs live).
readinessProbe:
tcpSocket:
port: 7681
initialDelaySeconds: 10
httpGet: { path: /healthz, port: 8800 }
initialDelaySeconds: 8
periodSeconds: 15
livenessProbe:
tcpSocket:
port: 7681
initialDelaySeconds: 30
httpGet: { path: /healthz, port: 8800 }
initialDelaySeconds: 20
periodSeconds: 30
# Node + Claude Code peuvent consommer ; nœuds 8 Go (OOM actif) → limite raisonnable.
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: "1"
memory: 1536Mi
requests: { cpu: 100m, memory: 256Mi }
limits: { cpu: 500m, memory: 768Mi }
volumeMounts:
- name: home
mountPath: /home/finlab