diff --git a/app/Http/Requests/Api/Client/Servers/Schedules/StoreTaskRequest.php b/app/Http/Requests/Api/Client/Servers/Schedules/StoreTaskRequest.php index a510c1b5f..190d3e54f 100644 --- a/app/Http/Requests/Api/Client/Servers/Schedules/StoreTaskRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Schedules/StoreTaskRequest.php @@ -19,7 +19,7 @@ class StoreTaskRequest extends ViewScheduleRequest public function rules(): array { return [ - 'action' => 'required|in:command,power,backup', + 'action' => 'required|in:command,power,backup,delete_files', 'payload' => 'required_unless:action,backup|string|nullable', 'time_offset' => 'required|numeric|min:0|max:900', 'sequence_id' => 'sometimes|required|numeric|min:1', diff --git a/app/Jobs/Schedule/RunTaskJob.php b/app/Jobs/Schedule/RunTaskJob.php index 0d2b255a6..6a660e5c0 100644 --- a/app/Jobs/Schedule/RunTaskJob.php +++ b/app/Jobs/Schedule/RunTaskJob.php @@ -12,6 +12,7 @@ use Illuminate\Foundation\Bus\DispatchesJobs; use App\Services\Backups\InitiateBackupService; use App\Repositories\Daemon\DaemonPowerRepository; use App\Exceptions\Http\Connection\DaemonConnectionException; +use App\Services\Files\DeleteFilesService; class RunTaskJob extends Job implements ShouldQueue { @@ -34,7 +35,8 @@ class RunTaskJob extends Job implements ShouldQueue */ public function handle( InitiateBackupService $backupService, - DaemonPowerRepository $powerRepository + DaemonPowerRepository $powerRepository, + DeleteFilesService $deleteFilesService ): void { // Do not process a task that is not set to active, unless it's been manually triggered. if (!$this->task->schedule->is_active && !$this->manualRun) { @@ -67,6 +69,9 @@ class RunTaskJob extends Job implements ShouldQueue case Task::ACTION_BACKUP: $backupService->setIgnoredFiles(explode(PHP_EOL, $this->task->payload))->handle($server, null, true); break; + case Task::ACTION_DELETE_FILES: + $deleteFilesService->handle($server, explode(PHP_EOL, $this->task->payload)); + break; default: throw new \InvalidArgumentException('Invalid task action provided: ' . $this->task->action); } diff --git a/app/Models/Task.php b/app/Models/Task.php index 6545ab5f5..254d38ece 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -33,6 +33,7 @@ class Task extends Model public const ACTION_POWER = 'power'; public const ACTION_COMMAND = 'command'; public const ACTION_BACKUP = 'backup'; + public const ACTION_DELETE_FILES = 'delete_files'; /** * The table associated with the model. diff --git a/app/Services/Files/DeleteFilesService.php b/app/Services/Files/DeleteFilesService.php new file mode 100644 index 000000000..2b1be5fda --- /dev/null +++ b/app/Services/Files/DeleteFilesService.php @@ -0,0 +1,41 @@ +daemonFileRepository->setServer($server)->getDirectory($path))->each(function ($item) use ($path, $pattern, $filesToDelete) { + if (Str::is($pattern, $item['name'])) { + $filesToDelete->push($path . '/' . $item['name']); + } + }); + } + + if ($filesToDelete->isNotEmpty()) { + $this->daemonFileRepository->setServer($server)->deleteFiles('/', $filesToDelete->toArray()); + } + } +} diff --git a/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx b/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx index 36db1fff9..ee638e3f9 100644 --- a/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx +++ b/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx @@ -9,6 +9,7 @@ import { faPencilAlt, faToggleOn, faTrashAlt, + faTrash, } from '@fortawesome/free-solid-svg-icons'; import deleteScheduleTask from '@/api/server/schedules/deleteScheduleTask'; import { httpErrorToHuman } from '@/api/http'; @@ -35,6 +36,8 @@ const getActionDetails = (action: string): [string, any] => { return ['Send Power Action', faToggleOn]; case 'backup': return ['Create Backup', faFileArchive]; + case 'delete_files': + return ['Delete Files', faTrash]; default: return ['Unknown Action', faCode]; } @@ -94,6 +97,9 @@ export default ({ schedule, task }: Props) => { {task.action === 'backup' && (

Ignoring files & folders:

)} + {task.action === 'delete_files' && ( +

Files to delete:

+ )}
diff --git a/resources/scripts/components/server/schedules/TaskDetailsModal.tsx b/resources/scripts/components/server/schedules/TaskDetailsModal.tsx index 0af8fb441..4903df931 100644 --- a/resources/scripts/components/server/schedules/TaskDetailsModal.tsx +++ b/resources/scripts/components/server/schedules/TaskDetailsModal.tsx @@ -34,7 +34,7 @@ interface Values { } const schema = object().shape({ - action: string().required().oneOf(['command', 'power', 'backup']), + action: string().required().oneOf(['command', 'power', 'backup', 'delete_files']), payload: string().when('action', { is: (v) => v !== 'backup', then: string().required('A task payload must be provided.'), @@ -131,6 +131,7 @@ const TaskDetailsModal = ({ schedule, task }: Props) => { +
@@ -166,7 +167,7 @@ const TaskDetailsModal = ({ schedule, task }: Props) => { - ) : ( + ) : values.action === 'backup' ? (
{
+ ) : ( +
+ + + + +
)}