Conversation
Contributor
|
🔄 AI PR Review sedang antri di server...
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
issue #1616
🎯 Deskripsi
Pull request ini mengimplementasikan fitur backup dan restore file asset storage pada menu Pengaturan → Pengaturan Database, sesuai permintaan di issue #1616.
Sebelumnya, fitur backup yang menggunakan
spatie/laravel-backupsudah menghasilkan file ZIP berisi database dump (.sql) dan seluruh file distorage/app/(termasuk foto artikel, dokumen, gambar widget, dll). Namun, fitur restore hanya menerima file.sql— tidak ada mekanisme untuk mengembalikan file asset dari ZIP backup. Akibatnya, saat OpenDK perlu dipindahkan atau diinstal ulang, file asset tidak bisa dipulihkan dari backup yang sudah dibuat, sehingga data lama tidak dapat sepenuhnya berpindah ke instalasi baru.PR ini memperluas fitur restore yang sudah ada agar dapat menerima file
.zip(backup spatie) dan mengembalikan database + file asset sekaligus dalam satu operasi. Selain itu, PR ini juga memperbaiki beberapa bug kritis pada alur backup yang menyebabkan notifikasi sukses palsu.🛠️ Perubahan yang Dilakukan
1.
config/backup.phpPerbaikan konfigurasi — Path relatif & exclude direktori backup:
'relative_path'darinull→base_path()agar entri di dalam ZIP menggunakan path relatif (mis.storage/app/public/artikel/foto.jpg) alih-alih absolute path. Ini membuat backup ZIP portable antar server/OS yang berbeda.storage/app/backup-storagedanstorage/app/backup-tempke daftar'exclude'untuk mencegah backup ZIP menumpuk recursively di dalam backup berikutnya.'exclude' => [ base_path('vendor'), base_path('node_modules'), base_path('storage/debugbar'), base_path('storage/framework'), base_path('storage/logs'), + base_path('storage/app/backup-storage'), + base_path('storage/app/backup-temp'), ], - 'relative_path' => null, + 'relative_path' => base_path(),2.
app/Http/Controllers/Setting/PengaturanDatabaseController.phpFitur — Restore dari file
.zip:restoreBackup()— Memperluas method yang sudah ada untuk menerima kedua format file:$allowedExtensionsdari['sql']→['sql', 'zip']..sql→ alur lama (DB only),.zip→ alur baru (Files + DB)..zip: menggunakan MIME types dariFileUploadService::getAllowedMimes('archive')dan max size 500MB.Storage::path()→Storage::disk('public')->path()(file di-upload ke diskpublic, bukanlocal).Fitur — Method
restoreFromZip()(BARU):Method private baru yang menangani ekstraksi ZIP backup spatie:
ZipArchive::open()— melempar exception jika file rusak.db-dumps/*.sql→ diekstrak ke temp file untuk restore viamysqlcommand.storage/app/) → diekstrak file individual kestorage/app/saat ini.backup-storage/,backup-temp/,framework/,logs/,debugbar/→ di-skip.storage/app/.str_contains($relativePath, '..')→ ditolak.runRestoreDatabase()untuk restore DB dari SQL dump yang ditemukan.finallyblock.Fitur — Method
mapZipEntryToStoragePath()(BARU):Method private baru yang memetakan entri ZIP ke path relatif di dalam
storage/app/:relative_path=null) maupun path relative (backup baru,relative_path=base_path())...).Bug Fix —
createBackup()tidak cek exit code:Artisan::call('backup:run')mengembalikan exit code (int), bukan throw exception saat backup gagal. Controller hanya pakai try/catch sehingga selalu returnsuccess: truemeskipun backup gagal. Sekarang exit code dicek:Bug Fix —
runRestoreDatabase()tidak menemukanmysql.exe:Command
mysqldipanggil sebagai bare command tanpa path lengkap. Di Windows/Laragon,mysql.exetidak ada di PATH sistem. Sekarang menggunakanconfig('database.connections.mysql.dump.dump_binary_path')(sama seperti spatie untukmysqldump):Perbaikan tambahan:
-pdengan--password=—-p+escapeshellarg()rusak di Linux (menghasilkan-p'123456'dengan kutip literal sebagai bagian password).2>&1— redirect stderr ke stdout agar pesan error MySQL tercapture di log.3.
resources/views/setting/pengaturan_database/table-restore.blade.phpFitur — UI restore menerima file
.zip:accept=".sql"→accept=".sql,.zip"..sql(DB only) dan.zip(DB + file asset)..zipakan menimpa file di storage.response.messagedari server (sebelumnya hardcoded).4.
resources/views/setting/pengaturan_database/table-backup.blade.phpBug Fix — JS
runBackup()menampilkan notifikasi sukses palsu:Callback
completedi jQuery AJAX selalu berjalan setelahsuccessmaupunerror, sehingga pesan "Berhasil membuat salinan database" ditampilkan bahkan saat backup gagal, menimpa pesan error.success: function(response) { $('#data-backup-database').DataTable().ajax.reload(); + restoreMessage.html('<p class="text-success">Berhasil membuat salinan database.</p>'); + }, + error: function(xhr, status, error) { + restoreMessage.html('<p class="text-danger">Error: ' + (xhr.responseJSON?.message || 'Gagal melakukan backup.') + '</p>'); + }, + complete: function() { btnBackup.addClass("btn-social"); btnBackup.attr("disabled", false); btnBackup.html('<i class="fa fa-plus"></i>Buat Cadangan Baru Database'); }, - error: function(xhr, status, error) { - restoreMessage.html('<p class="text-danger">Error: ' + xhr.responseJSON.message + '</p>'); - ... - }, - complete: function() { - restoreMessage.html('<p class="text-success">Berhasil membuat salinan database.</p>'); - ... - }5.
.envKonfigurasi — Path binary MySQL:
Menambahkan
DB_MYSQLDUMP_PATHyang diperlukan olehspatie/laravel-backup(untukmysqldump) dan olehrunRestoreDatabase()(untukmysql):DB_PASSWORD=123456 + DB_MYSQLDUMP_PATH=C:\laragon\bin\mysql\mysql-8.0.30-winx64\bin6. Test Files (3 file BARU)
tests/Unit/PengaturanDatabaseTest.php— 10 test casesTest
mapZipEntryToStoragePath()via Reflection — menguji path mapping, path traversal rejection, skip directories, dan cross-platform path normalization:storage/app/public/artikel/foto.jpg) →public/artikel/foto.jpgC:/laragon/.../storage/app/public/artikel/foto.jpg) →public/artikel/foto.jpg/var/www/html/storage/app/...) →public/artikel/foto.jpgstorage/app/public/../../../etc/passwd) →null(rejected)db-dumps/,backup-storage/,backup-temp/,framework/,logs/,debugbar/storage/app/→nullnulltests/Feature/Settings/PengaturanDatabaseBackupTest.php— 2 test casesTest
createBackup()dengan mockingArtisan::call():success: true, HTTP 200success: false, HTTP 500tests/Feature/Settings/PengaturanDatabaseRestoreTest.php— 5 test casesTest
restoreBackup()endpoint:.txt→ 422.exe→ 422.sqlditerima → tidak 422 (lolos validasi ekstensi)✅ Test Cases yang Diimplementasikan
storage/app/backup-storage/.db-dumps/mysql-{dbname}.sql) dan file storage dengan path relatif.backup-storage/danbackup-temp/tidak ikut di-ZIP (tidak menumpuk recursively)..sqlberfungsi seperti sebelumnya (backward compatible)..zipmengembalikan database dan file asset ke lokasi yang benar.backup-storage/,backup-temp/,framework/,logs/,debugbar/tidak diekstrak saat restore...) dalam entri ZIP ditolak.📸 Cara Menjalankan Uji Coba Manual
Pastikan
DB_MYSQLDUMP_PATHdi.envsudah menunjuk ke direktori binary MySQL (mis.C:\laragon\bin\mysql\mysql-8.0.30-winx64\bin).Test Backup:
Verifikasi ZIP dibuat:
Verifikasi struktur path relatif di dalam ZIP:
php -r "$z=new ZipArchive(); $f=glob('storage/app/backup-storage/*.zip'); $z->open($f[0]); for($i=0;$i<min(5,$z->numFiles);$i++){echo $z->getNameIndex($i).PHP_EOL;} $z->close();"Test Restore .sql (backward compat):
.sql— harus berhasil restore database.Test Restore .zip (fitur baru):
.zipbackup dari halaman Restore Database.storage/app/public/(mis. cek folderartikel/).Test notifikasi error backup:
DB_MYSQLDUMP_PATHke path yang salah di.env.php artisan config:clear, lalu klik "Buat Cadangan Baru Database".Jalankan automated tests:
php artisan test --filter=PengaturanDatabase📸 Screenshot atau Video