chore: update
This commit is contained in:
@@ -104,12 +104,29 @@ func (h *HelmProvisioner) Provision(d Demo, resources []ExternalResource, cfg Pr
|
||||
return fmt.Errorf("création namespace %s: %w", d.Namespace, err)
|
||||
}
|
||||
|
||||
// Isolation réseau AVANT tout workload : deny-by-default, aucune
|
||||
// communication inter-démos, ingress limité au Traefik partagé (voir
|
||||
// deploy/chart-gestion/network-policy). Sans ça, tout pod d'une démo
|
||||
// peut atteindre le réseau de n'importe quelle autre démo.
|
||||
if err := h.installChart(d.Namespace, "network-policy", h.buildNetworkPolicyValues()); err != nil {
|
||||
h.deleteNamespace(d.Namespace)
|
||||
return fmt.Errorf("déploiement network-policy: %w", err)
|
||||
}
|
||||
|
||||
// Mot de passe postgres/redis généré par démo (jamais réutilisé d'une
|
||||
// démo à l'autre) : avec des identifiants partagés, un gap réseau
|
||||
// (CNI, règle manquante) donnerait un accès direct aux données de
|
||||
// n'importe quelle autre démo. Le NetworkPolicy ci-dessus est la
|
||||
// première ligne de défense, ce mot de passe unique la seconde.
|
||||
pgPassword := randomSecret()
|
||||
redisPassword := randomSecret()
|
||||
|
||||
// PVC dès la création de la démo (pas d'emptyDir) : si le client ne
|
||||
// souscrit pas, le teardown du namespace (TTL ou suppression admin)
|
||||
// supprime le PVC avec le reste. S'il souscrit, TransferToPaid n'a rien
|
||||
// d'autre à faire côté stockage — les données sont déjà en place.
|
||||
postgresValues := h.buildPostgresValues(d)
|
||||
redisValues := h.buildRedisValues(d)
|
||||
postgresValues := h.buildPostgresValues(d, pgPassword)
|
||||
redisValues := h.buildRedisValues(d, redisPassword)
|
||||
|
||||
// Secret partagé backend <-> lbtelegram (authentifie les appels de
|
||||
// lbtelegram vers backend) — généré une seule fois si le load-balancer
|
||||
@@ -119,7 +136,7 @@ func (h *HelmProvisioner) Provision(d Demo, resources []ExternalResource, cfg Pr
|
||||
backendLinkSecret = randomSecret()
|
||||
}
|
||||
|
||||
backendValues := h.buildBackendValues(d, resources, cfg, backendLinkSecret)
|
||||
backendValues := h.buildBackendValues(d, resources, cfg, backendLinkSecret, pgPassword, redisPassword)
|
||||
frontendValues := h.buildFrontendValues(d)
|
||||
ingressValues := h.buildIngressRouteValues(d)
|
||||
|
||||
@@ -157,7 +174,7 @@ func (h *HelmProvisioner) Provision(d Demo, resources []ExternalResource, cfg Pr
|
||||
// Load-balancer Telegram : optionnel, seulement si l'admin a renseigné
|
||||
// au moins un bot.
|
||||
if cfg.LBTelegramEnabled() {
|
||||
lbValues := h.buildLBTelegramValues(d, cfg, backendLinkSecret)
|
||||
lbValues := h.buildLBTelegramValues(d, cfg, backendLinkSecret, pgPassword, redisPassword)
|
||||
if err := h.installChart(d.Namespace, "lbtelegram", lbValues); err != nil {
|
||||
h.deleteNamespace(d.Namespace)
|
||||
return fmt.Errorf("déploiement lbtelegram: %w", err)
|
||||
@@ -174,7 +191,7 @@ func (h *HelmProvisioner) Provision(d Demo, resources []ExternalResource, cfg Pr
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := h.createGestionAdmin(context.Background(), d.Namespace, cfg.AdminUsername, cfg.AdminPassword); err != nil {
|
||||
if err := h.createGestionAdmin(context.Background(), d.Namespace, cfg.AdminUsername, cfg.AdminPassword, pgPassword); err != nil {
|
||||
log.Printf("Warning: création du compte admin échouée pour %s: %v", d.Namespace, err)
|
||||
}
|
||||
|
||||
@@ -244,19 +261,38 @@ func (h *HelmProvisioner) MigrateToPremiumNamespace(d Demo, newNamespace, newURL
|
||||
}
|
||||
lbValues, lbEnabled := h.getReleaseValuesOptional(oldNamespace, oldNamespace+"-lbtelegram")
|
||||
|
||||
// Mot de passe postgres actuel (pour le pg_dump) — jamais persisté nulle
|
||||
// part, relu directement depuis la release Helm en place.
|
||||
oldPostgresValues, err := h.getReleaseValues(oldNamespace, oldNamespace+"-postgresql")
|
||||
if err != nil {
|
||||
return fmt.Errorf("lecture config postgresql existante: %w", err)
|
||||
}
|
||||
oldPgPassword := asStringMap(oldPostgresValues["auth"])["password"]
|
||||
|
||||
if err := h.createNamespace(newNamespace); err != nil {
|
||||
return fmt.Errorf("création namespace %s: %w", newNamespace, err)
|
||||
}
|
||||
|
||||
// Isolation réseau AVANT tout workload, comme pour Provision.
|
||||
if err := h.installChart(newNamespace, "network-policy", h.buildNetworkPolicyValues()); err != nil {
|
||||
h.deleteNamespace(newNamespace)
|
||||
return fmt.Errorf("déploiement network-policy: %w", err)
|
||||
}
|
||||
|
||||
newDemo := d
|
||||
newDemo.Namespace = newNamespace
|
||||
newDemo.URL = newURL
|
||||
|
||||
if err := h.installChart(newNamespace, "postgresql", h.buildPostgresValues(newDemo)); err != nil {
|
||||
// Nouveau mot de passe postgres/redis pour le nouveau namespace (pas de
|
||||
// réutilisation de l'ancien) : la donnée est migrée, pas l'identifiant.
|
||||
pgPassword := randomSecret()
|
||||
redisPassword := randomSecret()
|
||||
|
||||
if err := h.installChart(newNamespace, "postgresql", h.buildPostgresValues(newDemo, pgPassword)); err != nil {
|
||||
h.deleteNamespace(newNamespace)
|
||||
return fmt.Errorf("déploiement postgresql: %w", err)
|
||||
}
|
||||
if err := h.installChart(newNamespace, "redis", h.buildRedisValues(newDemo)); err != nil {
|
||||
if err := h.installChart(newNamespace, "redis", h.buildRedisValues(newDemo, redisPassword)); err != nil {
|
||||
h.deleteNamespace(newNamespace)
|
||||
return fmt.Errorf("déploiement redis: %w", err)
|
||||
}
|
||||
@@ -265,7 +301,7 @@ func (h *HelmProvisioner) MigrateToPremiumNamespace(d Demo, newNamespace, newURL
|
||||
// vide). Redis (cache/sessions) n'est pas migré, reconstruit
|
||||
// naturellement — même convention que l'historique passage en
|
||||
// stockage persistant.
|
||||
if err := h.migratePostgresData(ctx, oldNamespace, newNamespace); err != nil {
|
||||
if err := h.migratePostgresData(ctx, oldNamespace, newNamespace, oldPgPassword, pgPassword); err != nil {
|
||||
h.deleteNamespace(newNamespace)
|
||||
return fmt.Errorf("migration données postgres: %w", err)
|
||||
}
|
||||
@@ -278,7 +314,7 @@ func (h *HelmProvisioner) MigrateToPremiumNamespace(d Demo, newNamespace, newURL
|
||||
}
|
||||
}
|
||||
|
||||
backendPatched := h.patchBackendValuesForNamespace(backendValues, newDemo, lbEnabled, backendLinkSecret)
|
||||
backendPatched := h.patchBackendValuesForNamespace(backendValues, newDemo, lbEnabled, backendLinkSecret, pgPassword, redisPassword)
|
||||
if err := h.installChart(newNamespace, "backend", backendPatched); err != nil {
|
||||
h.deleteNamespace(newNamespace)
|
||||
return fmt.Errorf("déploiement backend: %w", err)
|
||||
@@ -292,7 +328,7 @@ func (h *HelmProvisioner) MigrateToPremiumNamespace(d Demo, newNamespace, newURL
|
||||
return fmt.Errorf("déploiement ingressroute: %w", err)
|
||||
}
|
||||
if lbEnabled {
|
||||
lbPatched := h.patchLBTelegramValuesForNamespace(lbValues, newDemo, backendLinkSecret)
|
||||
lbPatched := h.patchLBTelegramValuesForNamespace(lbValues, newDemo, backendLinkSecret, pgPassword, redisPassword)
|
||||
if err := h.installChart(newNamespace, "lbtelegram", lbPatched); err != nil {
|
||||
h.deleteNamespace(newNamespace)
|
||||
return fmt.Errorf("déploiement lbtelegram: %w", err)
|
||||
@@ -391,12 +427,14 @@ func (h *HelmProvisioner) restartDeployment(ctx context.Context, namespace, name
|
||||
|
||||
// migratePostgresData copie les données postgres d'un namespace à l'autre
|
||||
// (pg_dump / psql restore), utilisé par MigrateToPremiumNamespace.
|
||||
func (h *HelmProvisioner) migratePostgresData(ctx context.Context, oldNamespace, newNamespace string) error {
|
||||
// oldPassword/newPassword : mots de passe respectifs de chaque instance
|
||||
// postgres (uniques par démo, jamais réutilisés — voir Provision).
|
||||
func (h *HelmProvisioner) migratePostgresData(ctx context.Context, oldNamespace, newNamespace, oldPassword, newPassword string) error {
|
||||
oldPod, err := h.findPod(ctx, oldNamespace, "postgresql")
|
||||
if err != nil {
|
||||
return fmt.Errorf("pod postgresql source introuvable: %w", err)
|
||||
}
|
||||
dumpCmd := fmt.Sprintf("PGPASSWORD=%s pg_dump -h localhost -U %s %s", demoDBPass, demoDBUser, demoDBName)
|
||||
dumpCmd := fmt.Sprintf("PGPASSWORD=%s pg_dump -h localhost -U %s %s", oldPassword, demoDBUser, demoDBName)
|
||||
dump, stderr, err := h.execInPod(ctx, oldNamespace, oldPod, "postgresql", []string{"sh", "-c", dumpCmd}, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("pg_dump: %w (%s)", err, stderr)
|
||||
@@ -409,7 +447,7 @@ func (h *HelmProvisioner) migratePostgresData(ctx context.Context, oldNamespace,
|
||||
if err != nil {
|
||||
return fmt.Errorf("pod postgresql cible introuvable: %w", err)
|
||||
}
|
||||
restoreCmd := fmt.Sprintf("PGPASSWORD=%s psql -h localhost -U %s %s", demoDBPass, demoDBUser, demoDBName)
|
||||
restoreCmd := fmt.Sprintf("PGPASSWORD=%s psql -h localhost -U %s %s", newPassword, demoDBUser, demoDBName)
|
||||
if _, stderr, err := h.execInPod(ctx, newNamespace, newPod, "postgresql", []string{"sh", "-c", restoreCmd}, strings.NewReader(dump)); err != nil {
|
||||
return fmt.Errorf("restore pg_dump: %w (%s)", err, stderr)
|
||||
}
|
||||
@@ -418,14 +456,19 @@ func (h *HelmProvisioner) migratePostgresData(ctx context.Context, oldNamespace,
|
||||
|
||||
// patchBackendValuesForNamespace réutilise les valeurs Helm existantes du
|
||||
// backend (image, secrets, réglages métier...) en ne recalculant que ce qui
|
||||
// référence le nom du namespace : DNS internes postgres/redis/lbtelegram et
|
||||
// URL du webhook Telegram (dépend de l'URL publique de la démo).
|
||||
func (h *HelmProvisioner) patchBackendValuesForNamespace(values map[string]interface{}, newDemo Demo, lbEnabled bool, backendLinkSecret string) map[string]interface{} {
|
||||
// référence le nom du namespace : DNS internes postgres/redis/lbtelegram,
|
||||
// URL du webhook Telegram, et les nouveaux mots de passe postgres/redis
|
||||
// (uniques au nouveau namespace, voir MigrateToPremiumNamespace).
|
||||
func (h *HelmProvisioner) patchBackendValuesForNamespace(values map[string]interface{}, newDemo Demo, lbEnabled bool, backendLinkSecret, pgPassword, redisPassword string) map[string]interface{} {
|
||||
env := asStringMap(values["env"])
|
||||
secrets := asStringMap(values["secrets"])
|
||||
|
||||
env["DB_HOST"] = fmt.Sprintf("%s-postgresql-postgresql", newDemo.Namespace)
|
||||
env["REDIS_HOST"] = fmt.Sprintf("%s-redis-redis", newDemo.Namespace)
|
||||
delete(env, "DB_PASSWORD") // désormais dans secrets, voir buildBackendValues
|
||||
delete(env, "REDIS_PASSWORD") // idem
|
||||
secrets["DB_PASSWORD"] = pgPassword
|
||||
secrets["REDIS_PASSWORD"] = redisPassword
|
||||
if lbEnabled {
|
||||
env["LBTELEGRAM_URL"] = fmt.Sprintf("http://%s-lbtelegram-lbtelegram.%s.svc.cluster.local:8081", newDemo.Namespace, newDemo.Namespace)
|
||||
secrets["BACKEND_LINK_SECRET"] = backendLinkSecret
|
||||
@@ -442,8 +485,8 @@ func (h *HelmProvisioner) patchBackendValuesForNamespace(values map[string]inter
|
||||
// patchLBTelegramValuesForNamespace réutilise les valeurs Helm existantes
|
||||
// du chart lbtelegram (bots, stratégie, tokens...) en ne recalculant que ce
|
||||
// qui référence le nom du namespace : host public, GATEWAY_URL, DSN
|
||||
// postgres/redis et URL interne du backend.
|
||||
func (h *HelmProvisioner) patchLBTelegramValuesForNamespace(values map[string]interface{}, newDemo Demo, backendLinkSecret string) map[string]interface{} {
|
||||
// postgres/redis (avec les nouveaux mots de passe) et URL interne du backend.
|
||||
func (h *HelmProvisioner) patchLBTelegramValuesForNamespace(values map[string]interface{}, newDemo Demo, backendLinkSecret, pgPassword, redisPassword string) map[string]interface{} {
|
||||
env := asStringMap(values["env"])
|
||||
secrets := asStringMap(values["secrets"])
|
||||
|
||||
@@ -452,8 +495,8 @@ func (h *HelmProvisioner) patchLBTelegramValuesForNamespace(values map[string]in
|
||||
env["BACKEND_LINK_URL"] = fmt.Sprintf("http://%s-backend-gestion-backend.%s.svc.cluster.local:8080", newDemo.Namespace, newDemo.Namespace)
|
||||
|
||||
secrets["BACKEND_LINK_SECRET"] = backendLinkSecret
|
||||
secrets["DATABASE_URL"] = fmt.Sprintf("postgres://postgres:demo-postgres-pass@%s-postgresql-postgresql:5432/demo_db?sslmode=disable", newDemo.Namespace)
|
||||
secrets["REDIS_URL"] = fmt.Sprintf("redis://:demo-redis-pass@%s-redis-redis:6379/0", newDemo.Namespace)
|
||||
secrets["DATABASE_URL"] = fmt.Sprintf("postgres://postgres:%s@%s-postgresql-postgresql:5432/demo_db?sslmode=disable", pgPassword, newDemo.Namespace)
|
||||
secrets["REDIS_URL"] = fmt.Sprintf("redis://:%s@%s-redis-redis:6379/0", redisPassword, newDemo.Namespace)
|
||||
|
||||
values["host"] = host
|
||||
values["env"] = env
|
||||
@@ -509,7 +552,7 @@ func asStringMap(v interface{}) map[string]string {
|
||||
// un compte admin, par design de l'app). Mot de passe haché en bcrypt
|
||||
// (golang.org/x/crypto/bcrypt), comme le fait l'app elle-même
|
||||
// (bcrypt.CompareHashAndPassword côté LoginAdmin).
|
||||
func (h *HelmProvisioner) createGestionAdmin(ctx context.Context, namespace, username, password string) error {
|
||||
func (h *HelmProvisioner) createGestionAdmin(ctx context.Context, namespace, username, password, pgPassword string) error {
|
||||
if username == "" || password == "" {
|
||||
return fmt.Errorf("username/password admin manquants")
|
||||
}
|
||||
@@ -533,7 +576,7 @@ func (h *HelmProvisioner) createGestionAdmin(ctx context.Context, namespace, use
|
||||
escapedUsername, string(hash),
|
||||
)
|
||||
shellCmd := fmt.Sprintf("PGPASSWORD=%s psql -h localhost -U %s -d %s -v ON_ERROR_STOP=1 -c %s",
|
||||
demoDBPass, demoDBUser, demoDBName, shellQuote(sql))
|
||||
pgPassword, demoDBUser, demoDBName, shellQuote(sql))
|
||||
|
||||
_, stderr, err := h.execInPod(ctx, namespace, pgPod, "postgresql", []string{"sh", "-c", shellCmd}, nil)
|
||||
if err != nil {
|
||||
@@ -550,12 +593,12 @@ func shellQuote(s string) string {
|
||||
return "'" + strings.ReplaceAll(s, "'", `'\''`) + "'"
|
||||
}
|
||||
|
||||
// Identifiants postgres des démos — mêmes valeurs codées en dur que
|
||||
// buildPostgresValues/buildBackendValues (voir ces fonctions).
|
||||
// Identifiants postgres des démos — utilisateur/base fixes (pas des
|
||||
// secrets), le mot de passe est généré par démo (voir Provision et
|
||||
// MigrateToPremiumNamespace, jamais codé en dur ni partagé entre démos).
|
||||
const (
|
||||
demoDBUser = "postgres"
|
||||
demoDBName = "demo_db"
|
||||
demoDBPass = "demo-postgres-pass"
|
||||
)
|
||||
|
||||
// findPod retourne le nom du premier pod du namespace dont le nom contient nameContains.
|
||||
@@ -735,7 +778,10 @@ func parseImage(image string) (repo string, tag string) {
|
||||
// buildBackendValues construit les valeurs pour le chart backend.
|
||||
// backendLinkSecret : partagé avec le chart lbtelegram (voir
|
||||
// buildLBTelegramValues) — vide si le load-balancer Telegram n'est pas activé.
|
||||
func (h *HelmProvisioner) buildBackendValues(d Demo, resources []ExternalResource, cfg ProvisionConfig, backendLinkSecret string) map[string]interface{} {
|
||||
// pgPassword/redisPassword : générés par démo (voir Provision), jamais
|
||||
// codés en dur — placés dans "secrets" (Secret k8s), pas "env" (visible en
|
||||
// clair sur le Deployment sans droit de lecture des Secrets).
|
||||
func (h *HelmProvisioner) buildBackendValues(d Demo, resources []ExternalResource, cfg ProvisionConfig, backendLinkSecret, pgPassword, redisPassword string) map[string]interface{} {
|
||||
// Parser l'image backend pour séparer repository et tag
|
||||
backendRepo, backendTag := parseImage(h.backendImage)
|
||||
|
||||
@@ -751,13 +797,11 @@ func (h *HelmProvisioner) buildBackendValues(d Demo, resources []ExternalResourc
|
||||
env := map[string]string{
|
||||
"DB_HOST": fmt.Sprintf("%s-postgresql-postgresql", d.Namespace),
|
||||
"DB_PORT": "5432",
|
||||
"DB_PASSWORD": "demo-postgres-pass",
|
||||
"DB_USER": "postgres",
|
||||
"DB_NAME": "demo_db",
|
||||
"DB_SSLMODE": "disable",
|
||||
"REDIS_HOST": fmt.Sprintf("%s-redis-redis", d.Namespace),
|
||||
"REDIS_PORT": "6379",
|
||||
"REDIS_PASSWORD": "demo-redis-pass", // Mot de passe Redis
|
||||
"API_PORT": "8080",
|
||||
"NODE_ENV": "production",
|
||||
"STORAGE_DRIVER": storageDriver,
|
||||
@@ -772,6 +816,8 @@ func (h *HelmProvisioner) buildBackendValues(d Demo, resources []ExternalResourc
|
||||
}
|
||||
|
||||
secrets := h.buildSecrets(resources)
|
||||
secrets["DB_PASSWORD"] = pgPassword
|
||||
secrets["REDIS_PASSWORD"] = redisPassword
|
||||
if cfg.TelegramBotToken != "" {
|
||||
secrets["TELEGRAM_BOT_TOKEN"] = cfg.TelegramBotToken
|
||||
// URL publique du webhook Telegram de cette démo (route backend :
|
||||
@@ -857,13 +903,24 @@ func (h *HelmProvisioner) buildIngressRouteValues(d Demo) map[string]interface{}
|
||||
}
|
||||
}
|
||||
|
||||
// buildNetworkPolicyValues construit les valeurs pour le chart
|
||||
// network-policy (isolation réseau stricte d'une démo, voir Provision).
|
||||
// kubeSystemNamespace/blockedEgressCIDRs gardent les défauts du chart
|
||||
// (deploy/chart-gestion/network-policy/values.yaml) ; seul traefikNamespace
|
||||
// est explicité pour rester synchronisé avec sharedTraefikNamespace.
|
||||
func (h *HelmProvisioner) buildNetworkPolicyValues() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"traefikNamespace": sharedTraefikNamespace,
|
||||
}
|
||||
}
|
||||
|
||||
// buildLBTelegramValues construit les valeurs pour le chart lbtelegram
|
||||
// (load-balancer multi-bots Telegram). N'est appelé que si
|
||||
// cfg.LBTelegramEnabled() — au moins un bot renseigné par l'admin. Les
|
||||
// identifiants des bots (username/token) viennent de l'admin ; le reste
|
||||
// (DSN postgres/redis, secrets JWT/webhook/backend-link) est généré ici,
|
||||
// comme indiqué par le commentaire du chart (deploy/chart-gestion/lbtelegram/values.yaml).
|
||||
func (h *HelmProvisioner) buildLBTelegramValues(d Demo, cfg ProvisionConfig, backendLinkSecret string) map[string]interface{} {
|
||||
func (h *HelmProvisioner) buildLBTelegramValues(d Demo, cfg ProvisionConfig, backendLinkSecret, pgPassword, redisPassword string) map[string]interface{} {
|
||||
botCount := 0
|
||||
if cfg.LBBot1Username != "" {
|
||||
botCount++
|
||||
@@ -873,8 +930,8 @@ func (h *HelmProvisioner) buildLBTelegramValues(d Demo, cfg ProvisionConfig, bac
|
||||
}
|
||||
|
||||
backendURL := fmt.Sprintf("http://%s-backend-gestion-backend.%s.svc.cluster.local:8080", d.Namespace, d.Namespace)
|
||||
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)
|
||||
dbURL := fmt.Sprintf("postgres://postgres:%s@%s-postgresql-postgresql:5432/demo_db?sslmode=disable", pgPassword, d.Namespace)
|
||||
redisURL := fmt.Sprintf("redis://:%s@%s-redis-redis:6379/0", redisPassword, d.Namespace)
|
||||
lbRepo, lbTag := parseImage(h.lbtelegramImage)
|
||||
host := fmt.Sprintf("%s.%s", d.Namespace, h.baseDomain)
|
||||
|
||||
@@ -950,11 +1007,12 @@ func (h *HelmProvisioner) buildSecrets(resources []ExternalResource) map[string]
|
||||
}
|
||||
|
||||
// buildPostgresValues construit les valeurs pour le chart postgresql.
|
||||
// Toujours persistant (PVC) : voir Provision.
|
||||
func (h *HelmProvisioner) buildPostgresValues(d Demo) map[string]interface{} {
|
||||
// Toujours persistant (PVC) : voir Provision. password : généré par démo,
|
||||
// jamais partagé (voir Provision/MigrateToPremiumNamespace).
|
||||
func (h *HelmProvisioner) buildPostgresValues(d Demo, password string) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"auth": map[string]interface{}{
|
||||
"password": "demo-postgres-pass", // Mot de passe OBLIGATOIRE (champ correct pour le chart)
|
||||
"password": password,
|
||||
"username": "postgres",
|
||||
"database": "demo_db",
|
||||
},
|
||||
@@ -969,15 +1027,16 @@ func (h *HelmProvisioner) buildPostgresValues(d Demo) map[string]interface{} {
|
||||
}
|
||||
|
||||
// buildRedisValues construit les valeurs pour le chart redis.
|
||||
// Toujours persistant (PVC) : voir Provision.
|
||||
func (h *HelmProvisioner) buildRedisValues(d Demo) map[string]interface{} {
|
||||
// Toujours persistant (PVC) : voir Provision. password : généré par démo,
|
||||
// jamais partagé (voir Provision/MigrateToPremiumNamespace).
|
||||
func (h *HelmProvisioner) buildRedisValues(d Demo, password string) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"service": map[string]interface{}{
|
||||
"type": "ClusterIP",
|
||||
"port": 6379,
|
||||
},
|
||||
"auth": map[string]interface{}{
|
||||
"password": "demo-redis-pass", // Mot de passe simple pour les démos
|
||||
"password": password,
|
||||
},
|
||||
"persistence": map[string]interface{}{
|
||||
"enabled": true,
|
||||
|
||||
Vendored
+1510
File diff suppressed because one or more lines are too long
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Omnex — Plateforme de gestion de commandes & livraison</title>
|
||||
<meta name="description" content="Déployez en un clic une démo complète de la plateforme de gestion de commandes et de livraison." />
|
||||
<script type="module" crossorigin src="/assets/index-CvCTqtVE.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -45,7 +45,10 @@ export function BackofficeLayout() {
|
||||
const roleColor = isAdmin ? 'purple' : isClient ? 'gray' : 'gray'
|
||||
|
||||
return (
|
||||
<Box minH="100vh" bg="chakra-subtle-bg">
|
||||
// overflowX hidden : garde-fou global — si un élément interne déborde
|
||||
// malgré tout (contenu imprévu, régression future), le contenu est
|
||||
// rogné plutôt que d'ouvrir un scroll horizontal sur toute la page.
|
||||
<Box minH="100vh" bg="chakra-subtle-bg" overflowX="hidden">
|
||||
<Flex as="header" px={6} py={3} borderBottomWidth="1px" align="center" gap={6}>
|
||||
<Heading size="sm" as={RouterLink} to="/app">
|
||||
Omnex · {isAdmin ? 'Espace admin' : 'Espace client'}
|
||||
|
||||
@@ -47,7 +47,7 @@ export function DemoCard({
|
||||
<Box borderWidth="1px" borderRadius="lg" overflow="hidden" bg="bg-surface">
|
||||
<Box p={4} cursor="pointer" onClick={onToggle} _active={{ bg: 'chakra-subtle-bg' }}>
|
||||
<HStack justify="space-between" align="start">
|
||||
<HStack spacing={2} minW={0}>
|
||||
<HStack spacing={2} minW={0} flex="1">
|
||||
<Icon
|
||||
as={ChevronIcon}
|
||||
boxSize={3}
|
||||
@@ -56,7 +56,7 @@ export function DemoCard({
|
||||
transform={isOpen ? 'rotate(90deg)' : undefined}
|
||||
transition="transform 0.15s"
|
||||
/>
|
||||
<Text fontFamily="mono" fontSize="sm" noOfLines={1} wordBreak="break-all">
|
||||
<Text fontFamily="mono" fontSize="sm" noOfLines={1} wordBreak="break-all" minW={0} flex="1">
|
||||
{demo.namespace}
|
||||
</Text>
|
||||
</HStack>
|
||||
@@ -74,7 +74,7 @@ export function DemoCard({
|
||||
<Text color="gray.500" flexShrink={0}>
|
||||
URL
|
||||
</Text>
|
||||
<HStack spacing={1} minW={0}>
|
||||
<HStack spacing={1} minW={0} flex="1" justify="flex-end">
|
||||
{demo.status === 'ready' ? (
|
||||
<Link
|
||||
href={demo.url}
|
||||
@@ -82,6 +82,8 @@ export function DemoCard({
|
||||
isExternal
|
||||
noOfLines={1}
|
||||
wordBreak="break-all"
|
||||
minW={0}
|
||||
flex="1"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{demo.url}
|
||||
@@ -111,7 +113,7 @@ export function DemoCard({
|
||||
</Stack>
|
||||
|
||||
{actions && (
|
||||
<HStack mt={3} spacing={2} onClick={(e) => e.stopPropagation()}>
|
||||
<HStack mt={3} spacing={2} flexWrap="wrap" rowGap={2} onClick={(e) => e.stopPropagation()}>
|
||||
{actions}
|
||||
</HStack>
|
||||
)}
|
||||
|
||||
@@ -30,7 +30,11 @@ export function PodStatusPanel({ state }: { state: DemoDetails['state'] }) {
|
||||
: `${downCount} service${downCount > 1 ? 's' : ''} indisponible${downCount > 1 ? 's' : ''}`}
|
||||
</Text>
|
||||
</HStack>
|
||||
<SimpleGrid columns={{ base: 1, sm: 2, lg: 4 }} spacing={3}>
|
||||
{/* 1 colonne jusqu'à lg : à "sm" (480px), 2 colonnes serraient trop les
|
||||
cartes (CPU/mémoire en mono + badge) et forçaient un débordement
|
||||
horizontal — surtout dans le contexte carte mobile, déjà à l'étroit
|
||||
avec son propre padding. */}
|
||||
<SimpleGrid columns={{ base: 1, lg: 4 }} spacing={3}>
|
||||
{PODSTATUS_COMPONENTS.map((c) => (
|
||||
<ComponentCard key={c.key} title={c.label} cs={state[c.key]} />
|
||||
))}
|
||||
@@ -44,9 +48,9 @@ function ComponentCard({ title, cs }: { title: string; cs: ComponentState }) {
|
||||
const memPct = cs.memory_limit_mi > 0 ? Math.min(100, Math.round((cs.memory_mi / cs.memory_limit_mi) * 100)) : 0
|
||||
|
||||
return (
|
||||
<Box p={3} borderWidth="1px" borderRadius="lg" bg="bg-surface">
|
||||
<Box p={3} borderWidth="1px" borderRadius="lg" bg="bg-surface" minW={0}>
|
||||
<HStack justify="space-between" mb={3}>
|
||||
<Text fontSize="sm" fontWeight="semibold">
|
||||
<Text fontSize="sm" fontWeight="semibold" noOfLines={1}>
|
||||
{title}
|
||||
</Text>
|
||||
<HStack spacing={1.5}>
|
||||
|
||||
@@ -53,7 +53,7 @@ const plans: Plan[] = [
|
||||
description: 'Passez en production : stockage persistant, votre démo n\'expire plus.',
|
||||
features: [
|
||||
'Tout ce qui est inclus dans Démo',
|
||||
'Stockage persistant, n\'expire plus',
|
||||
'Votre nom de domaine',
|
||||
'Support prioritaire',
|
||||
],
|
||||
cta: 'Nous contacter',
|
||||
|
||||
Reference in New Issue
Block a user