0.13.14 : fix media uploads, move plugin secrets to env vars
Build release Docker image / Build Docker Images (push) Successful in 7m54s

Tous les uploads échouaient depuis mai 2026 sur « Credential access key
has length 5, should be 32 ».

config/env/production/plugins.ts surchargeait l'upload avec l'ancien
MinIO auto-hébergé (accessKeyId "admin", 5 caractères, endpoint
container.harmonylab.ovh aujourd'hui injoignable). Strapi fusionnant les
surcharges d'environnement en profondeur, la config effective en prod
n'était ni l'une ni l'autre : endpoint R2 hérité de config/plugins.ts,
mais identifiants MinIO, le provider lisant s3Options.credentials en
priorité sur les options à plat.

- surcharge production supprimée : une seule config plugins, les
  différences d'environnement passent par les variables d'environnement
- upload en forme s3Options attendue par @strapi/provider-upload-aws-s3 5.x
  (à plat, le provider n'accepte plus qu'au prix d'une dépréciation)
- secrets R2 et SMTP sortis du fichier vers env(), .env.example à jour
- CSP de production : les médias étaient autorisés depuis
  container.harmonylab.ovh et 192.168.0.211:9000, jamais depuis le domaine
  R2 — l'hôte est désormais dérivé de R2_PUBLIC_URL
- la surcharge production des middlewares réduisait strapi::body à sa forme
  par défaut, perdant includeUnparsed nécessaire au webhook Stripe

⚠️ Déploiement : poser R2_* et SMTP_PASSWORD dans Dokploy AVANT de
déployer. Les clés historiques restent dans l'historique git : rotation
R2 et ZeptoMail à faire.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-26 18:44:58 +02:00
parent ca66bec45b
commit 8e672633dc
6 changed files with 99 additions and 87 deletions
+39 -21
View File
@@ -1,20 +1,34 @@
export default () => ({
/**
* Configuration des plugins — source de vérité unique, tous environnements.
*
* ⚠️ Il n'existe volontairement PAS de `config/env/production/plugins.ts` :
* Strapi fusionne les surcharges d'environnement en profondeur, et cette
* fusion avait produit une configuration hybride (endpoint R2 de ce fichier +
* identifiants MinIO de la surcharge, lus en priorité par le provider via
* `s3Options.credentials`). Tous les uploads échouaient depuis mai 2026 sur
* « Credential access key has length 5, should be 32 ». Toute différence
* entre environnements passe désormais par les variables d'environnement.
*/
export default ({ env }) => ({
email: {
config: {
provider: "nodemailer",
providerOptions: {
host: "smtp.zeptomail.eu",
port: 587,
host: env("SMTP_HOST", "smtp.zeptomail.eu"),
port: env.int("SMTP_PORT", 587),
auth: {
user: "emailapikey",
pass: "yA6KbHsJ4lrywWtTFUc+0pSC94lm/aE/2nzks3i2fpZ1LYXp3qE71RBvd4O4c2CLjdfT5a9UbIkVJoCwvIpbfpczPIBXJpTGTuv4P2uV48xh8ciEYNYjhJivALIWFqVOeBsnDyo4QfEjWA==",
user: env("SMTP_USERNAME", "emailapikey"),
pass: env("SMTP_PASSWORD"),
},
debug: true,
logger: true,
debug: env.bool("SMTP_DEBUG", false),
logger: env.bool("SMTP_DEBUG", false),
},
settings: {
defaultFrom: "ChoralSync <noreply@choralsync.com>",
defaultReplyTo: "contact@choralsync.com",
defaultFrom: env(
"SMTP_DEFAULT_FROM",
"ChoralSync <noreply@choralsync.com>",
),
defaultReplyTo: env("SMTP_DEFAULT_REPLY_TO", "contact@choralsync.com"),
},
},
},
@@ -23,18 +37,22 @@ export default () => ({
config: {
provider: "aws-s3",
providerOptions: {
baseUrl: "https://container.choralsync.com",
credentials: {
accessKeyId: "a6f474b04f010543022788efc626cbfe",
secretAccessKey:
"eeeb631194378edee5145b3ef6314630b152fa186b3dab2fefdf8b48952cde21",
},
endpoint:
"https://4c59d3c67191bdd08e311a5a49c3ac98.r2.cloudflarestorage.com",
forcePathStyle: true,
region: "weur",
params: {
Bucket: "choralsync",
// URL publique servant les médias (domaine personnalisé du bucket).
baseUrl: env("R2_PUBLIC_URL"),
// Le provider 5.x attend les options S3 sous `s3Options` ; à plat,
// elles ne sont acceptées qu'au prix d'un avertissement de dépréciation
// — et une surcharge partielle produit une config incohérente.
s3Options: {
credentials: {
accessKeyId: env("R2_ACCESS_KEY_ID"),
secretAccessKey: env("R2_SECRET_ACCESS_KEY"),
},
endpoint: env("R2_ENDPOINT"),
region: env("R2_REGION", "auto"),
forcePathStyle: true,
params: {
Bucket: env("R2_BUCKET"),
},
},
},
},