Aller au contenu

La recherche est disponible uniquement en mode production. Essayez de construire puis de prévisualiser votre site pour tester la recherche localement.

Variables d'environnement

Le service lit son environnement au démarrage : fichier .env en local (chargé par dotenvy), ConfigMap et SealedSecret en Kubernetes (core/product-service/deploy/k8s/). Une variable obligatoire absente fait échouer le démarrage. Les secrets ne se commitent jamais en clair.

Nom du service dans les logs. Défaut : product-service.

SERVICE_NAME=product-service

Adresse d’écoute REST. Défaut : 0.0.0.0:8093. Doit écouter sur 0.0.0.0 (pas 127.0.0.1) pour que Traefik, dans son conteneur, l’atteigne via host.docker.internal.

HTTP_ADDR=0.0.0.0:8093

Adresse d’écoute gRPC. Défaut : 0.0.0.0:50051. Non exposé par Traefik.

GRPC_ADDR=0.0.0.0:50051

Origines navigateur autorisées à appeler le service, séparées par des virgules, sans slash final. Jamais * : les cookies de session accompagnent les requêtes. Défaut : vide (aucune origine autorisée).

CORS_ALLOWED_ORIGINS=https://app.clairtyx.com,https://clairtyx.com,http://localhost:5173

Dossier des fichiers de logs. Défaut : ./logs. En Kubernetes : /var/log/product-service.

LOG_DIR=./logs

Niveau de logs (syntaxe tracing). Facultatif ; défaut : info.

RUST_LOG=info,product_service=debug

Connexion PostgreSQL (tables products, prices, outbox, dead-letter, clés d’idempotence). Les migrations sont dans migrations/. Défaut : postgres://product:product@localhost:5432/product. Secret (contient le mot de passe).

DATABASE_URL=postgres://product:motdepasse@localhost:5432/product

Connexion Valkey/Redis du cache des produits provisoires (créés mais pas encore confirmés par le webhook). Défaut : redis://localhost:6379.

REDIS_URL=redis://localhost:6379

Durée de vie d’une entrée provisoire, en secondes. Doit dépasser la latence habituelle Stripe + webhook + Kafka, sinon un produit disparaît de la lecture avant d’être confirmé en base. Défaut : 300. Une valeur non numérique retombe sur 300.

CACHE_TTL_SECONDS=300

Clé d’API Stripe. Obligatoire. Secret. Le service accepte n’importe quelle valeur, il ne vérifie pas le préfixe.

STRIPE_API_KEY=rk_test_51Mexemple

Version d’API Stripe figée pour tous les appels. Défaut : 2026-05-27.dahlia.

STRIPE_API_VERSION=2026-05-27.dahlia

Liste des brokers hôte:port, séparés par des virgules. Défaut : localhost:9092.

KAFKA_BROKERS=localhost:9092

Topic sur lequel le service publie ses événements produit. Défaut : product.events.

KAFKA_TOPIC=product.events

Topic que le service consomme : les webhooks Stripe bruts, publiés par webhook-ingestion-service. Défaut : provider.webhooks.raw.

KAFKA_WEBHOOK_EVENTS_TOPIC=provider.webhooks.raw

Groupe de consommation Kafka. Plusieurs réplicas partagent le même groupe. Défaut : product-service-webhook-events.

KAFKA_WEBHOOK_EVENTS_GROUP=product-service-webhook-events

Bucket des photos produit. Défaut : product-images.

S3_BUCKET=product-images

Point d’accès d’un stockage S3 compatible (Garage, MinIO). Absent : AWS S3 réel. Quand il est défini, le service utilise l’adressage « path-style » et la région garage si AWS_REGION est vide.

S3_ENDPOINT=http://localhost:3900

Identifiants du stockage S3, lus par la chaîne standard du SDK AWS (pas par le code du service). Secrets. Pour Garage, la paire créée avec garage key create.

AWS_ACCESS_KEY_ID=GKexemple
AWS_SECRET_ACCESS_KEY=secretexemple

Région S3. Facultatif ; ne sert qu’avec S3_ENDPOINT (défaut garage).

AWS_REGION=garage

Base publique du CDN qui sert les photos. Stripe n’accepte que des URL : une clé products/tee.png lui est envoyée sous la forme <base>/products/tee.png. Sans slash final. Absente : seules les URL absolues sont acceptées comme images. Si le CDN monte les clés sous un sous-chemin, mettre ce chemin dans la base.

IMAGE_PUBLIC_BASE_URL=https://cdn.clairtyx.com

enforce (défaut) vérifie le JWT et applique AUTH_RULES. off laisse tout passer : développement local uniquement.

AUTH_MODE=enforce

URL du JWKS du backend (clés publiques de signature). Obligatoire en enforce. À pointer directement sur le backend, pas sur https://api.clairtyx.com : Traefik n’y autorise que les réseaux privés (jwks-allowlist) et répond 403 Forbidden à un appel venu de l’adresse publique.

AUTH_JWKS_URL=http://localhost:8091/.well-known/jwks.json

Valeur attendue du champ iss du JWT. Défaut : boutique-backend.

AUTH_ISSUER=boutique-backend

Nom du cookie d’accès, lu quand la requête n’a pas d’en-tête Authorization: Bearer. Défaut : shop_access.

AUTH_COOKIE_NAME=shop_access

Durée pendant laquelle les clés récupérées sont gardées, en secondes. Défaut : 300.

AUTH_JWKS_TTL_SECONDS=300

Politique d’accès, une règle par ligne (ou séparées par ;). Obligatoire en enforce : vide, le service refuse de démarrer.

MÉTHODES CHEMIN = EXIGENCE
  • MÉTHODES : * ou une liste (PATCH,DELETE). HEAD compte comme GET.
  • CHEMIN : le motif de route tel qu’enregistré (/v1/products/:id, /v1/products/images/*key), pas l’URL reçue. Comparaison exacte : DELETE /v1/products (suppression en lot) et DELETE /v1/products/:id sont deux routes distinctes.
  • EXIGENCE :
    • public : aucun jeton ;
    • authenticated : n’importe quel jeton valide ;
    • a|b|… : au moins un des éléments. Un élément nu est une permission (claim perms du JWT) ; role:<code> est un rôle (claim roles).
  • Les lignes qui commencent par # sont des commentaires.
  • La première règle qui correspond l’emporte.
  • Une route sans règle répond 403 et écrit un avertissement dans les logs.
  • Jeton absent, invalide ou expiré : 401. Jeton valide sans le droit : 403.

Permission ou rôle ? Préférer les permissions. admin et editor ont tous deux products.write et prices.write : une règle sur la permission les accepte sans que le service connaisse les rôles. editor n’est pas inclus dans admin : role:editor seul refuse un compte admin, et un rôle renommé côté backend casse la règle.

Permission admin editor customer
products.write ✓ ✓
prices.write ✓ ✓
coupons.write, promo_codes.write, discounts.manage ✓ ✓
customers.read, customers.manage, members.read, members.manage ✓

Source de la matrice : backend/src/domains/authz/permissions.rs.

AUTH_RULES='
* /healthz = public
* /readyz = public
GET /metrics = public
GET /v1/products = public
GET /v1/products/:id = public
GET /v1/products/images/*key = public
GET /v1/products/schema = authenticated
POST /v1/products = products.write
DELETE /v1/products = products.write
PATCH /v1/products/:id = products.write
DELETE /v1/products/:id = products.write
POST /v1/products/images = products.write
PATCH /v1/products/:id/images = products.write
POST /v1/products/:id/prices = prices.write
PATCH /v1/prices/:id = prices.write
DELETE /v1/prices/:id = prices.write
'

Pour réserver la suppression aux administrateurs, remplacer products.write par role:admin sur les deux routes DELETE.

Jeton Bearer exigé par GET /metrics (comparé en temps constant). Secret. Sans lui, l’endpoint refuse toutes les requêtes, sauf si METRICS_ALLOW_OPEN=true. Note : /metrics peut être public dans AUTH_RULES et rester protégé par ce jeton.

METRICS_TOKEN=un-jeton-long-et-aleatoire

true ouvre /metrics sans jeton. Défaut : false. À réserver à un réseau fermé.

METRICS_ALLOW_OPEN=false

Développement local, avec Garage, Redpanda, PostgreSQL et Valkey lancés sur l’hôte. Les valeurs exemple sont à remplacer.

# --- Service ---
SERVICE_NAME=product-service
HTTP_ADDR=0.0.0.0:8093
GRPC_ADDR=0.0.0.0:50051
CORS_ALLOWED_ORIGINS=http://localhost:5173,http://localhost:5174
LOG_DIR=./logs
RUST_LOG=info
# --- Données ---
DATABASE_URL=postgres://product:motdepasse@localhost:5432/product
REDIS_URL=redis://localhost:6379
CACHE_TTL_SECONDS=300
# --- Stripe ---
STRIPE_API_KEY=rk_test_51Mexemple
STRIPE_API_VERSION=2026-05-27.dahlia
# --- Kafka / Redpanda ---
KAFKA_BROKERS=localhost:9092
KAFKA_TOPIC=product.events
KAFKA_WEBHOOK_EVENTS_TOPIC=provider.webhooks.raw
KAFKA_WEBHOOK_EVENTS_GROUP=product-service-webhook-events
# --- Images ---
S3_BUCKET=product-images
S3_ENDPOINT=http://localhost:3900
AWS_ACCESS_KEY_ID=GKexemple
AWS_SECRET_ACCESS_KEY=secretexemple
IMAGE_PUBLIC_BASE_URL=https://cdn.clairtyx.com
# --- Authentification ---
AUTH_MODE=enforce
AUTH_JWKS_URL=http://localhost:8091/.well-known/jwks.json
AUTH_ISSUER=boutique-backend
AUTH_COOKIE_NAME=shop_access
AUTH_JWKS_TTL_SECONDS=300
AUTH_RULES='
* /healthz = public
* /readyz = public
GET /metrics = public
GET /v1/products = public
GET /v1/products/:id = public
GET /v1/products/images/*key = public
GET /v1/products/schema = authenticated
POST /v1/products = products.write
DELETE /v1/products = products.write
PATCH /v1/products/:id = products.write
DELETE /v1/products/:id = products.write
POST /v1/products/images = products.write
PATCH /v1/products/:id/images = products.write
POST /v1/products/:id/prices = prices.write
PATCH /v1/prices/:id = prices.write
DELETE /v1/prices/:id = prices.write
'
# --- Métriques ---
METRICS_TOKEN=un-jeton-long-et-aleatoire
METRICS_ALLOW_OPEN=false