Piattaforma ITI TECHNOLOGY: monorepo, Medusa, API, sito, area riservata e deploy
Deploy stack su Proxmox / deploy (push) Failing after 3s
Deploy stack su Proxmox / deploy (push) Failing after 3s
Monorepo npm con apps/public (Angular SSR), apps/private (Angular SPA), apps/api (Fastify), packages/shared e packages/styles; Medusa 2.21 in medusa/; infrastruttura Docker (Postgres PostGIS+pgvector, Redis, Caddy, backup) e workflow Gitea Actions per il deploy via SSH a ogni push su master. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018jQyT6dR9czzGwqb7FkG6u
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
#!/bin/sh
|
||||
# Backup giornaliero di produzione: dump dei due database + archivi dei file caricati, con rotazione.
|
||||
# Gira nel container "backup" (profilo tools) di docker-compose.prod.yml:
|
||||
# docker compose -f docker-compose.prod.yml run --rm backup (oppure: npm run backup:prod)
|
||||
# Risultato in BACKUP_DIR (host) → <AAAAMMGGTHHMMSSZ>/
|
||||
# app.dump, medusa.dump pg_dump formato custom (pg_restore), proprietari inclusi
|
||||
# uploads.tar.gz volume uploads dell'API (public/ e private/)
|
||||
# medusa-files.tar.gz file caricati dal dashboard Medusa (immagini prodotto)
|
||||
# SHA256SUMS verificati dal ripristino
|
||||
# Tiene gli ultimi BACKUP_KEEP_DAYS backup (default 14). Redis non serve: cache, sessioni e code.
|
||||
set -eu
|
||||
umask 077
|
||||
|
||||
KEEP="${BACKUP_KEEP_DAYS:-14}"
|
||||
STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
TMP="/backups/.tmp-$STAMP"
|
||||
DEST="/backups/$STAMP"
|
||||
|
||||
log() { echo "[backup $(date -u +%H:%M:%S)] $*"; }
|
||||
|
||||
rm -rf /backups/.tmp-*
|
||||
mkdir -p "$TMP"
|
||||
|
||||
log "dump $APP_DB_NAME"
|
||||
pg_dump --format=custom --dbname="$APP_DB_NAME" --file="$TMP/app.dump"
|
||||
log "dump $MEDUSA_DB_NAME"
|
||||
pg_dump --format=custom --dbname="$MEDUSA_DB_NAME" --file="$TMP/medusa.dump"
|
||||
|
||||
log "archivio uploads"
|
||||
tar -C /data/uploads -czf "$TMP/uploads.tar.gz" .
|
||||
log "archivio medusa-files"
|
||||
tar -C /data/medusa-files -czf "$TMP/medusa-files.tar.gz" .
|
||||
|
||||
(cd "$TMP" && sha256sum app.dump medusa.dump uploads.tar.gz medusa-files.tar.gz > SHA256SUMS)
|
||||
# Rinomina solo a backup completo: una cartella con il timestamp è sempre un backup integro
|
||||
mv "$TMP" "$DEST"
|
||||
log "completato: $DEST ($(du -sh "$DEST" | cut -f1))"
|
||||
|
||||
# Rotazione: i nomi sono ordinabili, si tengono gli ultimi $KEEP
|
||||
OLD="$(ls -1d /backups/[0-9]*T*Z 2>/dev/null | sort | head -n "-$KEEP")"
|
||||
for d in $OLD; do
|
||||
log "rimosso $d"
|
||||
rm -rf "$d"
|
||||
done
|
||||
@@ -0,0 +1,63 @@
|
||||
#!/bin/sh
|
||||
# Ripristino di un backup creato da backup.sh. Sostituisce COMPLETAMENTE i due database e i file caricati.
|
||||
# Prima fermare chi usa i database:
|
||||
# docker compose -f docker-compose.prod.yml stop api medusa public
|
||||
# docker compose -f docker-compose.prod.yml run --rm restore <AAAAMMGGTHHMMSSZ | latest>
|
||||
# docker compose -f docker-compose.prod.yml up -d --wait
|
||||
set -eu
|
||||
|
||||
ID="${1:-}"
|
||||
if [ -z "$ID" ]; then
|
||||
echo "Uso: restore <cartella del backup | latest>. Backup disponibili:" >&2
|
||||
ls -1 /backups | grep -E '^[0-9]+T[0-9]+Z$' >&2 || echo " (nessuno)" >&2
|
||||
exit 2
|
||||
fi
|
||||
if [ "$ID" = "latest" ]; then
|
||||
ID="$(ls -1 /backups | grep -E '^[0-9]+T[0-9]+Z$' | sort | tail -n 1)"
|
||||
[ -n "$ID" ] || { echo "Nessun backup in /backups" >&2; exit 1; }
|
||||
fi
|
||||
SRC="/backups/$ID"
|
||||
[ -d "$SRC" ] || { echo "Backup $SRC inesistente" >&2; exit 1; }
|
||||
|
||||
log() { echo "[restore $(date -u +%H:%M:%S)] $*"; }
|
||||
|
||||
log "verifica checksum di $ID"
|
||||
(cd "$SRC" && sha256sum -c SHA256SUMS)
|
||||
|
||||
# Nessuna connessione degli utenti applicativi: API e Medusa devono essere ferme
|
||||
ACTIVE="$(psql -d postgres -tA -v ON_ERROR_STOP=1 \
|
||||
-c "SELECT count(*) FROM pg_stat_activity WHERE usename IN ('$APP_DB_USER', '$MEDUSA_DB_USER')")"
|
||||
if [ "$ACTIVE" != "0" ]; then
|
||||
echo "Ci sono $ACTIVE connessioni di $APP_DB_USER/$MEDUSA_DB_USER: fermare prima api, medusa e public" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
restore_db() {
|
||||
db="$1"; owner="$2"; dump="$3"
|
||||
log "ricreo il database $db (proprietario $owner)"
|
||||
psql -d postgres -v ON_ERROR_STOP=1 -q \
|
||||
-c "DROP DATABASE IF EXISTS \"$db\" WITH (FORCE)" \
|
||||
-c "CREATE DATABASE \"$db\" OWNER \"$owner\" ENCODING 'UTF8' TEMPLATE template0" \
|
||||
-c "REVOKE ALL ON DATABASE \"$db\" FROM PUBLIC"
|
||||
# Da superuser: ricrea anche le estensioni (postgis, vector, …) e mantiene i proprietari originali
|
||||
log "pg_restore $dump → $db"
|
||||
pg_restore --exit-on-error --dbname="$db" "$SRC/$dump"
|
||||
}
|
||||
|
||||
restore_db "$APP_DB_NAME" "$APP_DB_USER" app.dump
|
||||
restore_db "$MEDUSA_DB_NAME" "$MEDUSA_DB_USER" medusa.dump
|
||||
|
||||
restore_files() {
|
||||
dir="$1"; archive="$2"
|
||||
log "ripristino $dir da $archive"
|
||||
# Svuota il contenuto ma NON le cartelle di primo livello (public/, private/): Caddy monta uploads/public
|
||||
# con un subpath, e una cartella ricreata non sarebbe più quella montata (file serviti come 404)
|
||||
find "$dir" -mindepth 1 -maxdepth 1 ! -type d -delete
|
||||
find "$dir" -mindepth 2 -delete
|
||||
tar -C "$dir" -xzpf "$SRC/$archive"
|
||||
}
|
||||
|
||||
restore_files /data/uploads uploads.tar.gz
|
||||
restore_files /data/medusa-files medusa-files.tar.gz
|
||||
|
||||
log "ripristino di $ID completato: riavviare i servizi (docker compose -f docker-compose.prod.yml up -d --wait)"
|
||||
Reference in New Issue
Block a user