feat(attachments): R2 toggle, storage metrics, and demo R2 config

Add an ATTACHMENTS_ENABLED switch (default on when R2 is bound) via a
central getAttachmentBucket helper, surface R2 + estimated KV usage
against the free tier on the status page and /api/stats (refreshed by the
hourly cron), let setup.sh create and wire the R2 bucket, and bind the
demo bucket so the deployed demo has attachments.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Julien Herr
2026-05-23 17:33:50 +02:00
parent 7226e718f7
commit f150d40c45
19 changed files with 387 additions and 23 deletions
+46
View File
@@ -134,6 +134,34 @@ if [ -z "$domain" ]; then
fi
echo "✅ Domain: $domain"
ENABLE_R2=false
R2_BUCKET=""
R2_PREVIEW_BUCKET=""
read -r -p "Enable email attachments stored in R2? [y/N]: " enable_r2
if [[ "$enable_r2" =~ ^[Yy]$ ]]; then
R2_BUCKET="${WORKER_NAME}-attachments"
R2_PREVIEW_BUCKET="${R2_BUCKET}-preview"
echo "🪣 Creating R2 buckets..."
set +e
R2_OUT="$(npx wrangler r2 bucket create "$R2_BUCKET" 2>&1)"
R2_STATUS=$?
R2_PREVIEW_OUT="$(npx wrangler r2 bucket create "$R2_PREVIEW_BUCKET" 2>&1)"
R2_PREVIEW_STATUS=$?
set -e
# An existing bucket is fine; only treat real failures as blocking.
echo "$R2_OUT" | grep -qi "already exists" && R2_STATUS=0
echo "$R2_PREVIEW_OUT" | grep -qi "already exists" && R2_PREVIEW_STATUS=0
if [ "$R2_STATUS" -eq 0 ] && [ "$R2_PREVIEW_STATUS" -eq 0 ]; then
ENABLE_R2=true
echo " ✅ R2 bucket: $R2_BUCKET"
echo " ✅ R2 preview bucket: $R2_PREVIEW_BUCKET"
else
echo " ⚠️ Could not create R2 buckets (is R2 enabled on your account?)."
echo " Attachments will stay disabled — see README → 'Email attachments (R2)'."
echo "$R2_OUT"
fi
fi
escape_sed_replacement() {
printf '%s' "$1" | sed -e 's/[\/&]/\\&/g'
}
@@ -158,6 +186,24 @@ else
sed -i "s/REPLACE_WITH_COMPATIBILITY_DATE/$COMPATIBILITY_DATE_ESCAPED/g" wrangler.toml
fi
if [ "$ENABLE_R2" = true ]; then
echo "🔗 Enabling R2 attachment binding in wrangler.toml..."
node - "wrangler.toml" "$R2_BUCKET" "$R2_PREVIEW_BUCKET" <<'NODE'
const fs = require("node:fs");
const [file, bucket, previewBucket] = process.argv.slice(2);
let txt = fs.readFileSync(file, "utf8");
txt = txt.split("REPLACE_WITH_YOUR_PREVIEW_BUCKET_NAME").join(previewBucket);
txt = txt.split("REPLACE_WITH_YOUR_BUCKET_NAME").join(bucket);
// Uncomment the commented r2_buckets blocks (global + [env.production]).
txt = txt.replace(
/# r2_buckets = \[\n#(\s+\{ binding = "ATTACHMENT_BUCKET".*\})\n# \]/g,
'r2_buckets = [\n$1\n]',
);
fs.writeFileSync(file, txt);
NODE
echo " ✅ ATTACHMENT_BUCKET bound to $R2_BUCKET"
fi
echo "✅ wrangler.toml has been created and configured successfully!"
echo ""
echo "✅ Setup complete! Next steps:"