Moves BackupCommand and config/backup.php from the pre-audit WIP that got stashed on 2026-10-21 to master, and registers the command in the Laravel scheduler so it runs at 03:30 Asia/Bahrain nightly. The command handles sqlite (VACUUM INTO after a WAL checkpoint) and mysql (mysqldump | gzip, verified with gzip -t) backends; verifies every snapshot by re-opening it with PDO + PRAGMA integrity_check + a sanity SELECT; ships an off-server copy via Storage::disk() when BACKUP_DISK is set; and prunes artifacts older than BACKUP_RETAIN_DAYS while always keeping at least BACKUP_KEEP_MINIMUM. Scheduler entry mirrors the nas-auto-sync shape — withoutOverlapping + runInBackground so a slow upload doesn't block the next tick. .env knobs: BACKUP_PATH (default: data/backups) BACKUP_RETAIN_DAYS (default: 14) BACKUP_KEEP_MINIMUM (default: 3) BACKUP_DISK (unset — warns on every run until configured) BACKUP_MEDIA_DEST (unset — required when running with --media) BACKUP_MEDIA_DELETE (default: false) Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
75 lines
2.7 KiB
PHP
75 lines
2.7 KiB
PHP
<?php
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Backup
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Settings for `php artisan takeone:backup`.
|
|
|
|
|
| The database and the media library are backed up by DIFFERENT mechanisms on
|
|
| purpose. The database is small and must be captured as a consistent
|
|
| point-in-time snapshot, so it is copied in full on every run. The media
|
|
| library is gigabytes of immutable uploads, so it is mirrored incrementally
|
|
| and only when asked — tarring it nightly would burn hours of CPU and fill
|
|
| the disk with near-identical archives.
|
|
|
|
|
*/
|
|
|
|
return [
|
|
|
|
// Where snapshots are written. Lives under /data, which is git-ignored, so
|
|
// a backup can never be committed by accident.
|
|
'path' => env('BACKUP_PATH', base_path('data/backups')),
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Retention
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Artifacts older than retain_days are pruned, but the newest keep_minimum
|
|
| are ALWAYS kept regardless of age — a box that sat idle for a month must
|
|
| still have something to restore from.
|
|
|
|
|
*/
|
|
'retain_days' => (int) env('BACKUP_RETAIN_DAYS', 14),
|
|
'keep_minimum' => (int) env('BACKUP_KEEP_MINIMUM', 3),
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Off-server copy
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| A filesystems.php disk name. A snapshot sitting on the same disk as the
|
|
| data survives a bad migration but not a dead disk, so set this to a
|
|
| remote disk. The command warns on every run while it is unset.
|
|
|
|
|
*/
|
|
'disk' => env('BACKUP_DISK'),
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Media library
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Uploaded video is the irreplaceable asset here — it cannot be regenerated
|
|
| from anything. It is mirrored with rsync (incremental, resumable) to
|
|
| media_destination, which should be a NAS mount or another machine.
|
|
|
|
|
| Only runs when --media is passed AND a destination is set.
|
|
|
|
|
| media_delete stays false by default: a mirror that deletes is one bad
|
|
| path away from erasing the copy it was meant to protect.
|
|
|
|
|
*/
|
|
'media_paths' => [
|
|
base_path('data/app/users'),
|
|
base_path('data/app/public'),
|
|
],
|
|
|
|
'media_destination' => env('BACKUP_MEDIA_DEST'),
|
|
|
|
'media_delete' => (bool) env('BACKUP_MEDIA_DELETE', false),
|
|
|
|
];
|