chore: update
ci-api / test (push) Successful in 21m54s
ci-web / test (push) Successful in 17m37s

This commit is contained in:
Xor290
2026-08-02 15:23:43 +02:00
parent 18707e2b3e
commit b82deee4fb
7 changed files with 144 additions and 24 deletions
+9 -3
View File
@@ -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
+21 -13
View File
@@ -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 {
@@ -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,
@@ -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 }}
@@ -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
+41
View File
@@ -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<StorageDriver>('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
/>
</FormControl>
</HStack>
<HStack spacing={3} align="start">
<FormControl isDisabled={submitting}>
<FormLabel fontSize="sm">Stratégie de répartition</FormLabel>
<Select
value={lbStrategy}
onChange={(e) => setLbStrategy(e.target.value as typeof lbStrategy)}
>
<option value="failover">Failover</option>
<option value="roundrobin">Round-robin</option>
<option value="leastconn">Moins de connexions</option>
</Select>
</FormControl>
<FormControl isDisabled={submitting}>
<FormLabel fontSize="sm">TTL JWT (secondes)</FormLabel>
<Input
placeholder="300"
value={lbJwtTtlSeconds}
onChange={(e) => setLbJwtTtlSeconds(e.target.value)}
type="number"
/>
</FormControl>
<FormControl isDisabled={submitting}>
<FormLabel fontSize="sm">Intervalle health-check (s)</FormLabel>
<Input
placeholder="30"
value={lbHealthCheckInterval}
onChange={(e) => setLbHealthCheckInterval(e.target.value)}
type="number"
/>
</FormControl>
</HStack>
</Stack>
)}
+6
View File
@@ -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,
}),