queue-med/scripts/backup-db.sh
Hermes 81c6bccf8a security: Phase 1 hardening - rate limit, helmet, CORS, JWT, session persistence
- express-rate-limit: 100/15min global, 5/15min on auth.login + auth.register,
  3/hour reserved for password-reset endpoints; trust proxy enabled.
- helmet: enabled with contentSecurityPolicy + crossOriginEmbedderPolicy off
  to keep Vite dev and the SPA bundle working.
- CORS: explicit allowlist (https://attente.cosmolan.fr in prod, localhost in
  dev), credentials true, restricted methods/headers; same allowlist applied
  to socket.io.
- JWT_SECRET: must be set and >= 32 chars; assertAuthEnv() called from the
  server bootstrap so the process refuses to start without one. The insecure
  "changeme-in-production" fallback in docker-compose.yml is removed.
- qm_auth cookie: maxAge reduced from 30d to 7d, JWT expiry matches.
- WhatsApp sessions: path now driven by WHATSAPP_SESSION_DIR and defaults to
  /app/data/whatsapp-sessions; docker-compose.yml mounts a named app_data
  volume so credentials survive container restarts.
- scripts/backup-db.sh: timestamped, gzipped mysqldump into /app/data/backups
  with rotation (keeps last 7); Dockerfile installs mysql-client and bundles
  the script.
- .env.example refreshed with documented placeholders for every required var
  (DATABASE_URL, JWT_SECRET, WHATSAPP_SESSION_DIR, MYSQL_*, BACKUP_*).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 13:06:51 +00:00

69 lines
2.5 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/sh
# QueueMed — MySQL backup helper.
#
# Dumps the configured MySQL database into /app/data/backups (or $BACKUP_DIR)
# with a timestamped filename, then prunes everything but the 7 most recent
# backups. Designed to run inside the `app` container — schedule from the host
# with `docker compose exec app /app/scripts/backup-db.sh` (cron, systemd, …).
#
# Required environment variables (already wired through docker-compose.yml):
# MYSQL_HOST hostname of the MySQL service (default: db)
# MYSQL_DATABASE database name to dump (default: queuemed)
# MYSQL_USER MySQL user (default: queuemed)
# MYSQL_PASSWORD MySQL password (required)
# Optional:
# BACKUP_DIR override backup destination (default: /app/data/backups)
# BACKUP_KEEP number of backups to retain (default: 7)
set -eu
MYSQL_HOST="${MYSQL_HOST:-db}"
MYSQL_DATABASE="${MYSQL_DATABASE:-queuemed}"
MYSQL_USER="${MYSQL_USER:-queuemed}"
BACKUP_DIR="${BACKUP_DIR:-/app/data/backups}"
BACKUP_KEEP="${BACKUP_KEEP:-7}"
if [ -z "${MYSQL_PASSWORD:-}" ]; then
echo "[backup-db] MYSQL_PASSWORD is not set, aborting." >&2
exit 1
fi
mkdir -p "$BACKUP_DIR"
TIMESTAMP="$(date -u +%Y%m%dT%H%M%SZ)"
OUT_FILE="$BACKUP_DIR/${MYSQL_DATABASE}-${TIMESTAMP}.sql.gz"
TMP_FILE="${OUT_FILE}.partial"
echo "[backup-db] dumping ${MYSQL_DATABASE} from ${MYSQL_HOST} -> ${OUT_FILE}"
# --single-transaction keeps the dump consistent without locking InnoDB tables.
# --quick streams rows row-by-row to keep memory bounded for large tables.
mysqldump \
--host="$MYSQL_HOST" \
--user="$MYSQL_USER" \
--password="$MYSQL_PASSWORD" \
--single-transaction \
--quick \
--routines \
--triggers \
--no-tablespaces \
--default-character-set=utf8mb4 \
"$MYSQL_DATABASE" | gzip -9 > "$TMP_FILE"
mv "$TMP_FILE" "$OUT_FILE"
echo "[backup-db] backup written: $OUT_FILE ($(wc -c < "$OUT_FILE") bytes)"
# ── Rotate: keep only the last $BACKUP_KEEP backups ─────────────────────────
# `ls -1t` sorts by mtime descending; everything after the first $BACKUP_KEEP
# entries is removed. Filenames are constrained to our prefix to avoid eating
# unrelated files that might share the directory.
cd "$BACKUP_DIR"
ls -1t "${MYSQL_DATABASE}"-*.sql.gz 2>/dev/null \
| awk -v keep="$BACKUP_KEEP" 'NR > keep' \
| while IFS= read -r old; do
echo "[backup-db] pruning old backup: $old"
rm -f -- "$old"
done
echo "[backup-db] done."