diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php
index 0280cee8..8d762c24 100644
--- a/app/Console/Kernel.php
+++ b/app/Console/Kernel.php
@@ -13,6 +13,7 @@ use Illuminate\Console\Scheduling\Event;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Illuminate\Support\Facades\Schema;
+use Nexus\Database\NexusDB;
class Kernel extends ConsoleKernel
{
@@ -50,8 +51,8 @@ class Kernel extends ConsoleKernel
$schedule->job(new ThirdPartyJob())->everyMinute()->withoutOverlapping();
$schedule->job(new MaintainPluginState())->everyMinute()->withoutOverlapping();
$schedule->job(new UpdateIsSeedBoxFromUserRecordsCache())->everySixHours()->withoutOverlapping();
+ $schedule->job(new CheckCleanup())->everyFifteenMinutes()->withoutOverlapping();
- $this->registerScheduleCleanup($schedule);
}
/**
@@ -66,17 +67,4 @@ class Kernel extends ConsoleKernel
require base_path('routes/console.php');
}
- private function registerScheduleCleanup(Schedule $schedule): void
- {
- if (!file_exists(base_path(".env")) || !Schema::hasTable("settings")) {
- return;
- }
- $interval = get_setting("main.autoclean_interval_one");
- if (!$interval || $interval < 60) {
- $interval = 7200;
- }
- $schedule->job(new CheckCleanup())
- ->cron(sprintf("*/%d * * * *", ceil($interval/60)))
- ->withoutOverlapping();
- }
}
diff --git a/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php b/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php
index 7a7ecdfc..4c8871bb 100644
--- a/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php
+++ b/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php
@@ -15,6 +15,7 @@ use Filament\Facades\Filament;
use Filament\Resources\Pages\Page;
use Filament\Forms;
use Illuminate\Support\HtmlString;
+use Meilisearch\Contracts\Index\Settings;
use Nexus\Database\NexusDB;
class EditSetting extends Page implements Forms\Contracts\HasForms
@@ -124,6 +125,7 @@ class EditSetting extends Page implements Forms\Contracts\HasForms
// Forms\Components\TextInput::make('backup.google_drive_refresh_token')->label(__('label.setting.backup.google_drive_refresh_token')),
// Forms\Components\TextInput::make('backup.google_drive_folder_id')->label(__('label.setting.backup.google_drive_folder_id')),
Forms\Components\TextInput::make('backup.export_path')->label(__('label.setting.backup.export_path'))->helperText(new HtmlString(__('label.setting.backup.export_path_help', ['default_path' => ToolRepository::getBackupExportPathDefault()]))),
+ Forms\Components\TextInput::make('backup.retention_count')->numeric()->label(__('label.setting.backup.retention_count'))->helperText(new HtmlString(__('label.setting.backup.retention_count_help', ['default_count' => ToolRepository::BACKUP_RETENTION_COUNT_DEFAULT]))),
Forms\Components\Radio::make('backup.via_ftp')->options(self::$yesOrNo)->inline(true)->label(__('label.setting.backup.via_ftp'))->helperText(new HtmlString(__('label.setting.backup.via_ftp_help'))),
Forms\Components\Radio::make('backup.via_sftp')->options(self::$yesOrNo)->inline(true)->label(__('label.setting.backup.via_sftp'))->helperText(new HtmlString(__('label.setting.backup.via_sftp_help'))),
])->columns(2);
diff --git a/app/Models/Setting.php b/app/Models/Setting.php
index 36debdba..8e5dcedf 100644
--- a/app/Models/Setting.php
+++ b/app/Models/Setting.php
@@ -253,5 +253,10 @@ class Setting extends NexusModel
return self::get("backup.export_path");
}
+ public static function getBackupRetentionCount(): int
+ {
+ return (int)self::get("backup.retention_count");
+ }
+
}
diff --git a/app/Repositories/ToolRepository.php b/app/Repositories/ToolRepository.php
index 6e251d56..ebf88085 100644
--- a/app/Repositories/ToolRepository.php
+++ b/app/Repositories/ToolRepository.php
@@ -12,6 +12,7 @@ use App\Models\User;
use Carbon\Carbon;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
+use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Nexus\Database\NexusDB;
@@ -27,6 +28,8 @@ class ToolRepository extends BaseRepository
{
const BACKUP_EXCLUDES = ['vendor', 'node_modules', '.git', '.idea', '.settings', '.DS_Store', '.github'];
+ const BACKUP_RETENTION_COUNT_DEFAULT = 10;
+
public function backupWeb($method = null, $transfer = false): array
{
$webRoot = base_path();
@@ -206,6 +209,7 @@ class ToolRepository extends BaseRepository
$transferResult = $this->transfer($backupResult['filename'], $backupResult['result_code'], $setting);
$backupResult['transfer_result'] = $transferResult;
do_log("[BACKUP_ALL_DONE]: " . json_encode($backupResult));
+ $this->cleanupBackupFiles();
return $backupResult;
}
@@ -325,6 +329,28 @@ class ToolRepository extends BaseRepository
}
}
+ private function cleanupBackupFiles(): void
+ {
+ $retentionCount = Setting::getBackupRetentionCount();
+ if ($retentionCount <= 0) {
+ $retentionCount = self::BACKUP_RETENTION_COUNT_DEFAULT;
+ }
+ $path = self::getBackupExportPath();
+ $allFiles = collect(File::allFiles($path));
+ // 按创建时间降序排序
+ $allFiles = $allFiles->sortByDesc(fn (\Symfony\Component\Finder\SplFileInfo $file) => $file->getCTime());
+ $filesToDelete = $allFiles->slice($retentionCount);
+ do_log(sprintf(
+ "retentionCount: %s, path: %s, fileCount: %s, toDeleteCount: %s",
+ $retentionCount, $path, $allFiles->count(), $filesToDelete->count()
+ ));
+ foreach ($filesToDelete as $file) {
+ $realPath = $file->getRealPath();
+ File::delete($realPath);
+ do_log(sprintf("delete backup file: %s", $realPath));
+ }
+ }
+
/**
* @param $to
* @param $subject
diff --git a/resources/lang/en/label.php b/resources/lang/en/label.php
index 2ec774bd..fc4022f3 100644
--- a/resources/lang/en/label.php
+++ b/resources/lang/en/label.php
@@ -68,6 +68,8 @@ return [
'via_sftp_help' => 'Whether to save via FTP. If so, add the configuration information to the .env file, refer to Laravel doc',
'export_path' => 'Export to directory',
'export_path_help' => 'Not set to use the system temporary directory::default_path. you can use third-party specialized tools to transfer offsite saves.' ,
+ 'retention_count' => 'Retention count',
+ 'retention_count_help' => 'Retain only the latest backup records, old ones will be deleted regularly. Default: :default_count',
],
'hr' => [
'tab_header' => 'H&R',
diff --git a/resources/lang/zh_CN/label.php b/resources/lang/zh_CN/label.php
index 76d70355..3cfe0f27 100644
--- a/resources/lang/zh_CN/label.php
+++ b/resources/lang/zh_CN/label.php
@@ -68,6 +68,8 @@ return [
'via_sftp_help' => '是否通过 SFTP 保存。如果通过,把配置信息添加到 .env 文件,参考 Laravel 文档',
'export_path' => '导出到目录',
'export_path_help' => '不设置使用系统临时目录::default_path。可以使用第三方专业工具转移异地保存。',
+ 'retention_count' => '保留数量',
+ 'retention_count_help' => '只保留最新的备份记录,旧的会定时删除。默认::default_count',
],
'hr' => [
'tab_header' => 'H&R',
diff --git a/resources/lang/zh_TW/label.php b/resources/lang/zh_TW/label.php
index 5ac2a0f0..3f11edd1 100644
--- a/resources/lang/zh_TW/label.php
+++ b/resources/lang/zh_TW/label.php
@@ -68,6 +68,8 @@ return [
'via_sftp_help' => '是否通過 SFTP 保存。如果通過,把配置信息添加到 .env 文件,參考 Laravel 文檔',
'export_path' => '導出到目錄',
'export_path_help' => '不設置使用系統臨時目錄::default_path。可以使用第三方專業工具轉移異地保存。',
+ 'retention_count' => '保留數量',
+ 'retention_count_help' => '只保留最新的備份記錄,舊的會定時刪除。默認::default_count',
],
'hr' => [
'tab_header' => 'H&R',