From 29fda690fd8567aefada421b75d6cee96d15a99a Mon Sep 17 00:00:00 2001 From: novysan87 Date: Tue, 14 Apr 2026 15:28:21 +0000 Subject: [PATCH] feat: add server-side backup scripts and restore guide --- README.md | 31 ++++- docs/BACKUP_RESTORE.md | 195 +++++++++++++++++++++++++++ scripts/backup-database-kesiswaan.sh | 65 +++++++++ scripts/backup-dokumen-kesiswaan.sh | 36 +++++ scripts/backup-full-kesiswaan.sh | 53 ++++++++ 5 files changed, 373 insertions(+), 7 deletions(-) create mode 100644 docs/BACKUP_RESTORE.md create mode 100755 scripts/backup-database-kesiswaan.sh create mode 100755 scripts/backup-dokumen-kesiswaan.sh create mode 100755 scripts/backup-full-kesiswaan.sh diff --git a/README.md b/README.md index 1549d1b..911aee7 100644 --- a/README.md +++ b/README.md @@ -163,21 +163,38 @@ npm run dev ## Backup -Project ini menyediakan script backup manual: +Project ini menyediakan dua lapis mekanisme backup: +### 1. Script lama (manual dasar) ```bash ./backup.sh ``` -Yang dibackup: -- database MySQL +### 2. Script backup operasional yang direkomendasikan +Gunakan script di folder `scripts/` untuk backup rutin via shell/cron: + +```bash +./scripts/backup-database-kesiswaan.sh +./scripts/backup-dokumen-kesiswaan.sh +./scripts/backup-full-kesiswaan.sh +``` + +Yang diamankan: +- database MySQL/MariaDB - `storage/app/public` -- file `.env` +- file `.env` (opsional pada full backup) Lokasi backup default: -- `storage/app/backups-manual/database` -- `storage/app/backups-manual/storage` -- `storage/app/backups-manual/env` +- `/var/backups/database-kesiswaan/database` +- `/var/backups/database-kesiswaan/files` +- `/var/backups/database-kesiswaan/env` +- `/var/backups/database-kesiswaan/manifests` + +Panduan restore lengkap tersedia di: + +```bash +docs/BACKUP_RESTORE.md +``` --- diff --git a/docs/BACKUP_RESTORE.md b/docs/BACKUP_RESTORE.md new file mode 100644 index 0000000..6c55b2c --- /dev/null +++ b/docs/BACKUP_RESTORE.md @@ -0,0 +1,195 @@ +# BACKUP_RESTORE.md - Database Kesiswaan + +Panduan backup dan pemulihan data untuk project `database-kesiswaan`. + +## Tujuan + +Menyediakan mekanisme backup **tanpa UI aplikasi**, agar admin/server operator bisa menjalankan backup rutin langsung dari shell/cron. + +Yang diamankan: +- database MySQL/MariaDB `database_kesiswaan` +- file dokumen di `storage/app/public` +- file `.env` (opsional, tapi sangat disarankan) + +--- + +## Struktur Script + +Semua script ada di folder: + +```bash +/var/www/database-kesiswaan/scripts +``` + +### 1. Backup database saja +```bash +./scripts/backup-database-kesiswaan.sh +``` +Output: +- file `.sql.gz` +- default lokasi: `/var/backups/database-kesiswaan/database` + +### 2. Backup dokumen saja +```bash +./scripts/backup-dokumen-kesiswaan.sh +``` +Output: +- file `.tar.gz` +- default lokasi: `/var/backups/database-kesiswaan/files` + +### 3. Backup penuh (direkomendasikan) +```bash +./scripts/backup-full-kesiswaan.sh +``` +Output: +- backup database +- backup dokumen +- salinan `.env` +- manifest backup + +--- + +## Lokasi Backup Default + +```bash +/var/backups/database-kesiswaan/ +├── database/ +├── files/ +├── env/ +└── manifests/ +``` + +--- + +## Retensi Default + +Script menggunakan retensi default: + +```bash +RETENTION_DAYS=14 +``` + +Artinya backup lebih lama dari 14 hari akan dibersihkan otomatis. + +Kalau ingin ubah retensi saat menjalankan script: + +```bash +RETENTION_DAYS=30 ./scripts/backup-full-kesiswaan.sh +``` + +--- + +## Menjalankan Manual + +### Backup penuh manual +```bash +cd /var/www/database-kesiswaan +chmod +x scripts/*.sh +./scripts/backup-full-kesiswaan.sh +``` + +### Backup hanya database +```bash +cd /var/www/database-kesiswaan +./scripts/backup-database-kesiswaan.sh +``` + +### Backup hanya dokumen +```bash +cd /var/www/database-kesiswaan +./scripts/backup-dokumen-kesiswaan.sh +``` + +--- + +## Contoh Cron Rutin + +### Backup penuh setiap hari jam 01:00 +```cron +0 1 * * * cd /var/www/database-kesiswaan && /bin/bash ./scripts/backup-full-kesiswaan.sh >> /var/log/database-kesiswaan-backup.log 2>&1 +``` + +### Backup database saja setiap 6 jam +```cron +0 */6 * * * cd /var/www/database-kesiswaan && /bin/bash ./scripts/backup-database-kesiswaan.sh >> /var/log/database-kesiswaan-db-backup.log 2>&1 +``` + +### Backup dokumen setiap hari jam 01:30 +```cron +30 1 * * * cd /var/www/database-kesiswaan && /bin/bash ./scripts/backup-dokumen-kesiswaan.sh >> /var/log/database-kesiswaan-files-backup.log 2>&1 +``` + +--- + +## Restore Database + +Contoh restore dump database: + +```bash +gunzip -c /var/backups/database-kesiswaan/database/database_kesiswaan-YYYY-MM-DD-HHMMSS.sql.gz | mysql -u kesiswaan -p database_kesiswaan +``` + +> Pastikan database target sudah ada dan kredensial benar. + +--- + +## Restore Dokumen + +Contoh restore dokumen: + +```bash +mkdir -p /var/www/database-kesiswaan/storage/app/public +tar -xzf /var/backups/database-kesiswaan/files/storage-public-YYYY-MM-DD-HHMMSS.tar.gz -C /var/www/database-kesiswaan/storage/app/public +``` + +Lalu pastikan storage link tetap ada: + +```bash +cd /var/www/database-kesiswaan +php artisan storage:link +php artisan optimize:clear +``` + +--- + +## Restore `.env` + +Kalau perlu mengembalikan `.env`: + +```bash +cp /var/backups/database-kesiswaan/env/env-YYYY-MM-DD-HHMMSS.backup /var/www/database-kesiswaan/.env +chmod 600 /var/www/database-kesiswaan/.env +``` + +--- + +## Checklist Setelah Restore + +- [ ] database berhasil di-restore +- [ ] dokumen berhasil di-restore +- [ ] `.env` benar +- [ ] `php artisan optimize:clear` dijalankan +- [ ] `php artisan storage:link` valid +- [ ] halaman login bisa dibuka +- [ ] data siswa muncul +- [ ] preview/download dokumen berjalan +- [ ] portal publik berjalan normal + +--- + +## Rekomendasi Operasional + +1. Gunakan **backup penuh harian**. +2. Simpan backup di disk/partisi yang cukup longgar. +3. Kalau memungkinkan, sinkronkan backup ke lokasi kedua (server lain / external storage). +4. Uji restore minimal sesekali, jangan hanya percaya backup berhasil dibuat. +5. Lindungi folder backup karena berisi data sensitif dan kemungkinan salinan `.env`. + +--- + +## Catatan Keamanan + +- Folder backup bisa mengandung data sensitif. +- Jangan expose `/var/backups/database-kesiswaan` ke web. +- Jangan commit backup ke GitHub. +- Jangan berikan izin baca sembarangan ke file backup `.env`. diff --git a/scripts/backup-database-kesiswaan.sh b/scripts/backup-database-kesiswaan.sh new file mode 100755 index 0000000..8a06076 --- /dev/null +++ b/scripts/backup-database-kesiswaan.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +set -euo pipefail + +APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +ENV_FILE="${ENV_FILE:-$APP_DIR/.env}" +BACKUP_ROOT="${BACKUP_ROOT:-/var/backups/database-kesiswaan}" +TIMESTAMP="${TIMESTAMP:-$(date '+%Y-%m-%d-%H%M%S')}" +RETENTION_DAYS="${RETENTION_DAYS:-14}" + +abort() { + printf '[ERROR] %s\n' "$1" >&2 + exit 1 +} + +log() { + printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" +} + +require_bin() { + command -v "$1" >/dev/null 2>&1 || abort "Binary '$1' tidak ditemukan." +} + +read_env() { + local key="$1" + grep -E "^${key}=" "$ENV_FILE" | tail -n 1 | cut -d '=' -f2- | sed 's/^"//; s/"$//' +} + +[[ -f "$ENV_FILE" ]] || abort "File .env tidak ditemukan di $ENV_FILE" +require_bin mysqldump +require_bin gzip + +DB_CONNECTION="$(read_env DB_CONNECTION)" +DB_HOST="$(read_env DB_HOST)" +DB_PORT="$(read_env DB_PORT)" +DB_DATABASE="$(read_env DB_DATABASE)" +DB_USERNAME="$(read_env DB_USERNAME)" +DB_PASSWORD="$(read_env DB_PASSWORD)" + +[[ "$DB_CONNECTION" == "mysql" ]] || abort "Script ini saat ini hanya mendukung DB_CONNECTION=mysql" +[[ -n "$DB_DATABASE" ]] || abort "DB_DATABASE kosong" +[[ -n "$DB_USERNAME" ]] || abort "DB_USERNAME kosong" + +DB_HOST="${DB_HOST:-127.0.0.1}" +DB_PORT="${DB_PORT:-3306}" + +TARGET_DIR="$BACKUP_ROOT/database" +mkdir -p "$TARGET_DIR" +TARGET_FILE="$TARGET_DIR/${DB_DATABASE}-${TIMESTAMP}.sql.gz" + +log "Memulai backup database '$DB_DATABASE'" +MYSQL_PWD="$DB_PASSWORD" mysqldump \ + --host="$DB_HOST" \ + --port="$DB_PORT" \ + --user="$DB_USERNAME" \ + --single-transaction \ + --quick \ + --skip-lock-tables \ + --routines \ + --triggers \ + "$DB_DATABASE" | gzip -c > "$TARGET_FILE" + +find "$TARGET_DIR" -type f -name '*.sql.gz' -mtime +"$RETENTION_DAYS" -delete || true + +log "Backup database selesai: $TARGET_FILE" +printf '%s\n' "$TARGET_FILE" diff --git a/scripts/backup-dokumen-kesiswaan.sh b/scripts/backup-dokumen-kesiswaan.sh new file mode 100755 index 0000000..f125ba3 --- /dev/null +++ b/scripts/backup-dokumen-kesiswaan.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -euo pipefail + +APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +BACKUP_ROOT="${BACKUP_ROOT:-/var/backups/database-kesiswaan}" +TIMESTAMP="${TIMESTAMP:-$(date '+%Y-%m-%d-%H%M%S')}" +RETENTION_DAYS="${RETENTION_DAYS:-14}" +SOURCE_DIR="${SOURCE_DIR:-$APP_DIR/storage/app/public}" + +abort() { + printf '[ERROR] %s\n' "$1" >&2 + exit 1 +} + +log() { + printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" +} + +require_bin() { + command -v "$1" >/dev/null 2>&1 || abort "Binary '$1' tidak ditemukan." +} + +[[ -d "$SOURCE_DIR" ]] || abort "Folder sumber dokumen tidak ditemukan di $SOURCE_DIR" +require_bin tar + +TARGET_DIR="$BACKUP_ROOT/files" +mkdir -p "$TARGET_DIR" +TARGET_FILE="$TARGET_DIR/storage-public-${TIMESTAMP}.tar.gz" + +log "Memulai backup dokumen dari '$SOURCE_DIR'" +tar -czf "$TARGET_FILE" -C "$SOURCE_DIR" . + +find "$TARGET_DIR" -type f -name '*.tar.gz' -mtime +"$RETENTION_DAYS" -delete || true + +log "Backup dokumen selesai: $TARGET_FILE" +printf '%s\n' "$TARGET_FILE" diff --git a/scripts/backup-full-kesiswaan.sh b/scripts/backup-full-kesiswaan.sh new file mode 100755 index 0000000..586e4fe --- /dev/null +++ b/scripts/backup-full-kesiswaan.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +set -euo pipefail + +APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +BACKUP_ROOT="${BACKUP_ROOT:-/var/backups/database-kesiswaan}" +ENV_FILE="${ENV_FILE:-$APP_DIR/.env}" +TIMESTAMP="${TIMESTAMP:-$(date '+%Y-%m-%d-%H%M%S')}" +RETENTION_DAYS="${RETENTION_DAYS:-14}" +KEEP_ENV_BACKUP="${KEEP_ENV_BACKUP:-true}" + +abort() { + printf '[ERROR] %s\n' "$1" >&2 + exit 1 +} + +log() { + printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" +} + +[[ -f "$ENV_FILE" ]] || abort "File .env tidak ditemukan di $ENV_FILE" + +mkdir -p "$BACKUP_ROOT/manifests" "$BACKUP_ROOT/env" + +DB_BACKUP="$(BACKUP_ROOT="$BACKUP_ROOT" ENV_FILE="$ENV_FILE" TIMESTAMP="$TIMESTAMP" RETENTION_DAYS="$RETENTION_DAYS" "$APP_DIR/scripts/backup-database-kesiswaan.sh")" +FILES_BACKUP="$(BACKUP_ROOT="$BACKUP_ROOT" TIMESTAMP="$TIMESTAMP" RETENTION_DAYS="$RETENTION_DAYS" "$APP_DIR/scripts/backup-dokumen-kesiswaan.sh")" +ENV_BACKUP="" + +if [[ "$KEEP_ENV_BACKUP" == "true" ]]; then + ENV_BACKUP="$BACKUP_ROOT/env/env-${TIMESTAMP}.backup" + cp "$ENV_FILE" "$ENV_BACKUP" + chmod 600 "$ENV_BACKUP" || true + find "$BACKUP_ROOT/env" -type f -name '*.backup' -mtime +"$RETENTION_DAYS" -delete || true +fi + +MANIFEST_FILE="$BACKUP_ROOT/manifests/backup-${TIMESTAMP}.txt" +{ + echo "timestamp=$TIMESTAMP" + echo "database_backup=$DB_BACKUP" + echo "files_backup=$FILES_BACKUP" + echo "env_backup=${ENV_BACKUP:-not_saved}" + echo "host=$(hostname)" + echo "app_dir=$APP_DIR" +} > "$MANIFEST_FILE" + +find "$BACKUP_ROOT/manifests" -type f -name 'backup-*.txt' -mtime +"$RETENTION_DAYS" -delete || true + +log "Backup penuh selesai" +printf 'Database: %s\n' "$DB_BACKUP" +printf 'Dokumen : %s\n' "$FILES_BACKUP" +if [[ -n "$ENV_BACKUP" ]]; then + printf '.env : %s\n' "$ENV_BACKUP" +fi +printf 'Manifest: %s\n' "$MANIFEST_FILE"