From b82deee4fb3899122ff620aa10d4aba98eaeb78e Mon Sep 17 00:00:00 2001 From: Xor290 Date: Sun, 2 Aug 2026 15:23:43 +0200 Subject: [PATCH] chore: update --- control-plane/api/internal/demos/demos.go | 12 ++++-- control-plane/api/internal/demos/handler.go | 34 +++++++++------ .../api/internal/demos/helm_provisioner.go | 36 ++++++++++++---- .../lbtelegram/templates/ingressroute.yaml | 33 +++++++++++++++ deploy/chart-gestion/lbtelegram/values.yaml | 6 +++ web/src/components/CreateDemoModal.tsx | 41 +++++++++++++++++++ web/src/lib/api.ts | 6 +++ 7 files changed, 144 insertions(+), 24 deletions(-) create mode 100644 deploy/chart-gestion/lbtelegram/templates/ingressroute.yaml diff --git a/control-plane/api/internal/demos/demos.go b/control-plane/api/internal/demos/demos.go index f92ce16..41c416e 100644 --- a/control-plane/api/internal/demos/demos.go +++ b/control-plane/api/internal/demos/demos.go @@ -56,13 +56,19 @@ type ProvisionConfig struct { S3Endpoint string // Load-balancer Telegram (chart lbtelegram) : installé seulement si au - // moins un bot est renseigné. Le reste de la config du chart (DSN - // postgres/redis, secrets JWT/webhook/backend-link) est généré par le - // provisioner, pas saisi par l'admin. + // moins un bot est renseigné. GATEWAY_URL (URL publique de la démo) et + // les DSN/secrets sont générés par le provisioner. Les 3 réglages + // ci-dessous sont optionnels — laissés vides, lbtelegram applique ses + // propres défauts (failover, 300s, 30s — voir internal/config/config.go + // du service lbtelegram). LBBot1Username string LBBot1Token string LBBot2Username string LBBot2Token string + // LBStrategy : "failover" (défaut) | "roundrobin" | "leastconn" + LBStrategy string + LBJWTTTLSeconds string + LBHealthCheckInterval string // secondes // Compte admin de l'application déployée (pas le compte omnex de // l'admin qui lance la démo) — requis : sans lui, personne ne peut se diff --git a/control-plane/api/internal/demos/handler.go b/control-plane/api/internal/demos/handler.go index b6fbb99..dc07b66 100644 --- a/control-plane/api/internal/demos/handler.go +++ b/control-plane/api/internal/demos/handler.go @@ -35,6 +35,11 @@ type createRequest struct { LBBot1Token string `json:"lb_bot1_token" binding:"omitempty"` LBBot2Username string `json:"lb_bot2_username" binding:"omitempty,max=64"` LBBot2Token string `json:"lb_bot2_token" binding:"omitempty"` + // LBStrategy : "failover" | "roundrobin" | "leastconn" (vide => failover, + // défaut appliqué par lbtelegram lui-même). + LBStrategy string `json:"lb_strategy" binding:"omitempty,oneof=failover roundrobin leastconn"` + LBJWTTTLSeconds string `json:"lb_jwt_ttl_seconds" binding:"omitempty,numeric"` + LBHealthCheckInterval string `json:"lb_health_check_interval" binding:"omitempty,numeric"` // Compte admin de l'application déployée — requis, sans lui personne ne // peut se connecter au backoffice de la démo. @@ -63,19 +68,22 @@ func (h *Handler) Create(c *gin.Context) { return } cfg := ProvisionConfig{ - TelegramBotUsername: req.TelegramBotUsername, - TelegramBotToken: req.TelegramBotToken, - NowPaymentsAPIKey: req.NowPaymentsAPIKey, - NowPaymentsIPNSecret: req.NowPaymentsIPNSecret, - StorageDriver: req.StorageDriver, - S3Bucket: req.S3Bucket, - S3Endpoint: req.S3Endpoint, - LBBot1Username: req.LBBot1Username, - LBBot1Token: req.LBBot1Token, - LBBot2Username: req.LBBot2Username, - LBBot2Token: req.LBBot2Token, - AdminUsername: req.AdminUsername, - AdminPassword: req.AdminPassword, + TelegramBotUsername: req.TelegramBotUsername, + TelegramBotToken: req.TelegramBotToken, + NowPaymentsAPIKey: req.NowPaymentsAPIKey, + NowPaymentsIPNSecret: req.NowPaymentsIPNSecret, + StorageDriver: req.StorageDriver, + S3Bucket: req.S3Bucket, + S3Endpoint: req.S3Endpoint, + LBBot1Username: req.LBBot1Username, + LBBot1Token: req.LBBot1Token, + LBBot2Username: req.LBBot2Username, + LBBot2Token: req.LBBot2Token, + LBStrategy: req.LBStrategy, + LBJWTTTLSeconds: req.LBJWTTTLSeconds, + LBHealthCheckInterval: req.LBHealthCheckInterval, + AdminUsername: req.AdminUsername, + AdminPassword: req.AdminPassword, } d, err := h.svc.Create(req.LeadID, req.Username, cfg) if err != nil { diff --git a/control-plane/api/internal/demos/helm_provisioner.go b/control-plane/api/internal/demos/helm_provisioner.go index 2cca993..6baa477 100644 --- a/control-plane/api/internal/demos/helm_provisioner.go +++ b/control-plane/api/internal/demos/helm_provisioner.go @@ -620,6 +620,31 @@ func (h *HelmProvisioner) buildLBTelegramValues(d Demo, cfg ProvisionConfig, bac dbURL := fmt.Sprintf("postgres://postgres:demo-postgres-pass@%s-postgresql-postgresql:5432/demo_db?sslmode=disable", d.Namespace) redisURL := fmt.Sprintf("redis://:demo-redis-pass@%s-redis-redis:6379/0", d.Namespace) lbRepo, lbTag := parseImage(h.lbtelegramImage) + host := fmt.Sprintf("%s.%s", d.Namespace, h.baseDomain) + + env := map[string]string{ + "PORT": "8081", + "ENV": "production", + // URL publique HTTPS de cette démo : requise par lbtelegram (panic + // sinon), utilisée pour enregistrer les webhooks Telegram de chaque + // bot (GATEWAY_URL + "/webhook/" + botID) — voir + // deploy/chart-gestion/lbtelegram/templates/ingressroute.yaml pour + // le routage public correspondant. + "GATEWAY_URL": "https://" + host, + "BOT_COUNT": fmt.Sprintf("%d", botCount), + "BOT1_USERNAME": cfg.LBBot1Username, + "BOT2_USERNAME": cfg.LBBot2Username, + "BACKEND_LINK_URL": backendURL, + } + if cfg.LBStrategy != "" { + env["LB_STRATEGY"] = cfg.LBStrategy + } + if cfg.LBJWTTTLSeconds != "" { + env["JWT_TTL_SECONDS"] = cfg.LBJWTTTLSeconds + } + if cfg.LBHealthCheckInterval != "" { + env["HEALTH_CHECK_INTERVAL"] = cfg.LBHealthCheckInterval + } return map[string]interface{}{ "image": map[string]interface{}{ @@ -627,14 +652,9 @@ func (h *HelmProvisioner) buildLBTelegramValues(d Demo, cfg ProvisionConfig, bac "tag": lbTag, "pullPolicy": "Always", }, - "env": map[string]string{ - "PORT": "8081", - "ENV": "production", - "BOT_COUNT": fmt.Sprintf("%d", botCount), - "BOT1_USERNAME": cfg.LBBot1Username, - "BOT2_USERNAME": cfg.LBBot2Username, - "BACKEND_LINK_URL": backendURL, - }, + "host": host, + "traefikNamespace": sharedTraefikNamespace, + "env": env, "secrets": map[string]string{ "BOT1_TOKEN": cfg.LBBot1Token, "BOT2_TOKEN": cfg.LBBot2Token, diff --git a/deploy/chart-gestion/lbtelegram/templates/ingressroute.yaml b/deploy/chart-gestion/lbtelegram/templates/ingressroute.yaml new file mode 100644 index 0000000..8d2faf5 --- /dev/null +++ b/deploy/chart-gestion/lbtelegram/templates/ingressroute.yaml @@ -0,0 +1,33 @@ +{{- if .Values.host }} +# Webhooks Telegram des bots du load-balancer (POST /webhook/bot1, +# /webhook/bot2, ...), voir internal/bot/registry.go du service lbtelegram : +# webhookURL = GATEWAY_URL + "/webhook/" + botID. Sans cette route, lbtelegram +# enregistre bien le webhook auprès de Telegram (GATEWAY_URL requis au +# démarrage) mais ne reçoit jamais rien : personne ne route /webhook/bot* +# vers son Service. +# +# Priorité 25 : au-dessus de la route générique /webhook/ -> backend +# (priority 20, voir deploy/chart-gestion/ingressroute/templates/ingressroute.yaml) +# pour ne pas être captée par elle. +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: {{ .Release.Namespace }}-lbtelegram-routes + namespace: {{ .Release.Namespace }} + labels: + {{- include "lbtelegram.labels" . | nindent 4 }} +spec: + entryPoints: + - websecure + routes: + - match: Host(`{{ .Values.host }}`) && PathPrefix(`/webhook/bot`) + kind: Rule + priority: 25 + middlewares: + - name: waf-chain + namespace: {{ .Values.traefikNamespace }} + services: + - name: {{ include "lbtelegram.fullname" . }} + port: {{ .Values.service.port }} + tls: {} +{{- end }} diff --git a/deploy/chart-gestion/lbtelegram/values.yaml b/deploy/chart-gestion/lbtelegram/values.yaml index 2f07c00..72a37dc 100644 --- a/deploy/chart-gestion/lbtelegram/values.yaml +++ b/deploy/chart-gestion/lbtelegram/values.yaml @@ -54,3 +54,9 @@ securityContext: service: type: ClusterIP port: 8081 + +# Routage public des webhooks Telegram (/webhook/bot1, /webhook/bot2...) via +# le Traefik partagé. host vide => pas d'IngressRoute créée (voir +# templates/ingressroute.yaml). +host: "" +traefikNamespace: traefik diff --git a/web/src/components/CreateDemoModal.tsx b/web/src/components/CreateDemoModal.tsx index 8cdaba1..55b06a8 100644 --- a/web/src/components/CreateDemoModal.tsx +++ b/web/src/components/CreateDemoModal.tsx @@ -14,6 +14,7 @@ import { ModalOverlay, Radio, RadioGroup, + Select, Stack, Switch, Text, @@ -51,6 +52,9 @@ export function CreateDemoModal({ isOpen, onClose, onCreated, leadId, lockedUser const [lbBot1Token, setLbBot1Token] = useState('') const [lbBot2Username, setLbBot2Username] = useState('') const [lbBot2Token, setLbBot2Token] = useState('') + const [lbStrategy, setLbStrategy] = useState<'failover' | 'roundrobin' | 'leastconn'>('failover') + const [lbJwtTtlSeconds, setLbJwtTtlSeconds] = useState('') + const [lbHealthCheckInterval, setLbHealthCheckInterval] = useState('') const [storageDriver, setStorageDriver] = useState('local') const [s3Bucket, setS3Bucket] = useState('') @@ -72,6 +76,9 @@ export function CreateDemoModal({ isOpen, onClose, onCreated, leadId, lockedUser setLbBot1Token('') setLbBot2Username('') setLbBot2Token('') + setLbStrategy('failover') + setLbJwtTtlSeconds('') + setLbHealthCheckInterval('') setStorageDriver('local') setS3Bucket('') setS3Endpoint('') @@ -120,6 +127,9 @@ export function CreateDemoModal({ isOpen, onClose, onCreated, leadId, lockedUser lbBot1Token: lbBot1Token.trim() || undefined, lbBot2Username: lbBot2Username.trim() || undefined, lbBot2Token: lbBot2Token.trim() || undefined, + lbStrategy, + lbJwtTtlSeconds: lbJwtTtlSeconds.trim() || undefined, + lbHealthCheckInterval: lbHealthCheckInterval.trim() || undefined, } : {}), } @@ -311,6 +321,37 @@ export function CreateDemoModal({ isOpen, onClose, onCreated, leadId, lockedUser /> + + + Stratégie de répartition + + + + TTL JWT (secondes) + setLbJwtTtlSeconds(e.target.value)} + type="number" + /> + + + Intervalle health-check (s) + setLbHealthCheckInterval(e.target.value)} + type="number" + /> + + )} diff --git a/web/src/lib/api.ts b/web/src/lib/api.ts index 8ea466c..872c580 100644 --- a/web/src/lib/api.ts +++ b/web/src/lib/api.ts @@ -88,6 +88,9 @@ export interface CreateDemoParams { lbBot1Token?: string lbBot2Username?: string lbBot2Token?: string + lbStrategy?: 'failover' | 'roundrobin' | 'leastconn' + lbJwtTtlSeconds?: string + lbHealthCheckInterval?: string adminUsername: string adminPassword: string } @@ -185,6 +188,9 @@ export const api = { : {}), ...(params.lbBot1Username ? { lb_bot1_username: params.lbBot1Username, lb_bot1_token: params.lbBot1Token } : {}), ...(params.lbBot2Username ? { lb_bot2_username: params.lbBot2Username, lb_bot2_token: params.lbBot2Token } : {}), + ...(params.lbStrategy ? { lb_strategy: params.lbStrategy } : {}), + ...(params.lbJwtTtlSeconds ? { lb_jwt_ttl_seconds: params.lbJwtTtlSeconds } : {}), + ...(params.lbHealthCheckInterval ? { lb_health_check_interval: params.lbHealthCheckInterval } : {}), admin_username: params.adminUsername, admin_password: params.adminPassword, }),