From fc04e2dace40279bd730513fd6d1bd23d94db85a Mon Sep 17 00:00:00 2001 From: Matteo Gheza Date: Fri, 12 Jan 2024 15:56:24 +0100 Subject: [PATCH] Add admin job list and manual exec to admin --- backend/app/Console/Kernel.php | 12 ++--- .../app/Http/Controllers/AdminController.php | 37 +++++++++++++ ...eOn.php => NotifyUsersManualModeOnJob.php} | 2 +- ...hp => RemoveOldIpAddressesFromLogsJob.php} | 2 +- ...es.php => ResetAvailabilityMinutesJob.php} | 2 +- backend/routes/api.php | 3 ++ .../admin-maintenance.component.html | 28 ++++++++-- .../admin-maintenance.component.scss | 4 ++ .../admin-maintenance.component.ts | 52 +++++++++++++++++++ frontend/src/assets/i18n/en.json | 7 ++- frontend/src/assets/i18n/it.json | 7 ++- 11 files changed, 142 insertions(+), 14 deletions(-) rename backend/app/Jobs/{NotifyUsersManualModeOn.php => NotifyUsersManualModeOnJob.php} (97%) rename backend/app/Jobs/{RemoveOldIpAddressesFromLogs.php => RemoveOldIpAddressesFromLogsJob.php} (90%) rename backend/app/Jobs/{ResetAvailabilityMinutes.php => ResetAvailabilityMinutesJob.php} (96%) diff --git a/backend/app/Console/Kernel.php b/backend/app/Console/Kernel.php index bd3cc4b..d63d823 100644 --- a/backend/app/Console/Kernel.php +++ b/backend/app/Console/Kernel.php @@ -5,9 +5,9 @@ namespace App\Console; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; -use App\Jobs\NotifyUsersManualModeOn; -use App\Jobs\RemoveOldIpAddressesFromLogs; -use App\Jobs\ResetAvailabilityMinutes; +use App\Jobs\NotifyUsersManualModeOnJob; +use App\Jobs\RemoveOldIpAddressesFromLogsJob; +use App\Jobs\ResetAvailabilityMinutesJob; use App\Jobs\UpdateAvailabilityWithSchedulesJob; class Kernel extends ConsoleKernel @@ -17,13 +17,13 @@ class Kernel extends ConsoleKernel */ protected function schedule(Schedule $schedule): void { - $schedule->job(new NotifyUsersManualModeOn) + $schedule->job(new NotifyUsersManualModeOnJob) ->dailyAt('7:00'); //->sentryMonitor(); - $schedule->job(new RemoveOldIpAddressesFromLogs) + $schedule->job(new RemoveOldIpAddressesFromLogsJob) ->dailyAt('0:30'); //->sentryMonitor(); - $schedule->job(new ResetAvailabilityMinutes) + $schedule->job(new ResetAvailabilityMinutesJob) ->monthlyOn(1, '0:00'); //->sentryMonitor(); $schedule->job(new UpdateAvailabilityWithSchedulesJob) diff --git a/backend/app/Http/Controllers/AdminController.php b/backend/app/Http/Controllers/AdminController.php index 983a3b9..80e04eb 100644 --- a/backend/app/Http/Controllers/AdminController.php +++ b/backend/app/Http/Controllers/AdminController.php @@ -94,6 +94,43 @@ class AdminController extends Controller ]); } + public function getJobsList() { + if(!request()->user()->hasPermission("admin-maintenance-read")) abort(401); + + $jobPath = app_path('Jobs'); + $jobs = []; + + $files = scandir($jobPath); + foreach ($files as $file) { + if (pathinfo($file, PATHINFO_EXTENSION) == 'php') { + $jobs[] = pathinfo($file, PATHINFO_FILENAME); + } + } + + return response()->json($jobs); + } + + public function runJob(Request $request) { + if(!request()->user()->hasPermission("admin-maintenance-update")) abort(401); + + $request->validate([ + 'job' => 'required|string' + ]); + + Artisan::call('schedule:test', ['--name' => "App\\Jobs\\".$request->input('job')]); + $output = Artisan::output(); + + if(str_contains($output, 'No matching scheduled command found.')) { + return response()->json([ + 'message' => 'Job not found' + ], 404); + } + + return response()->json([ + 'message' => 'Job ran successfully' + ]); + } + public function getMaintenanceMode() { if(!request()->user()->hasPermission("admin-maintenance-read")) abort(401); diff --git a/backend/app/Jobs/NotifyUsersManualModeOn.php b/backend/app/Jobs/NotifyUsersManualModeOnJob.php similarity index 97% rename from backend/app/Jobs/NotifyUsersManualModeOn.php rename to backend/app/Jobs/NotifyUsersManualModeOnJob.php index fded14b..e395ce8 100644 --- a/backend/app/Jobs/NotifyUsersManualModeOn.php +++ b/backend/app/Jobs/NotifyUsersManualModeOnJob.php @@ -14,7 +14,7 @@ use DefStudio\Telegraph\Keyboard\Button; use DefStudio\Telegraph\Keyboard\Keyboard; use DefStudio\Telegraph\Facades\Telegraph; -class NotifyUsersManualModeOn implements ShouldQueue +class NotifyUsersManualModeOnJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; diff --git a/backend/app/Jobs/RemoveOldIpAddressesFromLogs.php b/backend/app/Jobs/RemoveOldIpAddressesFromLogsJob.php similarity index 90% rename from backend/app/Jobs/RemoveOldIpAddressesFromLogs.php rename to backend/app/Jobs/RemoveOldIpAddressesFromLogsJob.php index d9481aa..345483e 100644 --- a/backend/app/Jobs/RemoveOldIpAddressesFromLogs.php +++ b/backend/app/Jobs/RemoveOldIpAddressesFromLogsJob.php @@ -10,7 +10,7 @@ use Illuminate\Queue\SerializesModels; use App\Models\Log; use Carbon\Carbon; -class RemoveOldIpAddressesFromLogs implements ShouldQueue +class RemoveOldIpAddressesFromLogsJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; diff --git a/backend/app/Jobs/ResetAvailabilityMinutes.php b/backend/app/Jobs/ResetAvailabilityMinutesJob.php similarity index 96% rename from backend/app/Jobs/ResetAvailabilityMinutes.php rename to backend/app/Jobs/ResetAvailabilityMinutesJob.php index 8d360a5..1d9f42b 100644 --- a/backend/app/Jobs/ResetAvailabilityMinutes.php +++ b/backend/app/Jobs/ResetAvailabilityMinutesJob.php @@ -11,7 +11,7 @@ use Illuminate\Queue\SerializesModels; use App\Models\User; use App\Models\AvailabilityMinutesArchive; -class ResetAvailabilityMinutes implements ShouldQueue +class ResetAvailabilityMinutesJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; diff --git a/backend/routes/api.php b/backend/routes/api.php index 0c7eaa0..bf1e9b3 100644 --- a/backend/routes/api.php +++ b/backend/routes/api.php @@ -99,6 +99,9 @@ Route::middleware('auth:sanctum')->group( function () { Route::post('/admin/runMigrations', [AdminController::class, 'runMigrations']); Route::post('/admin/runSeeding', [AdminController::class, 'runSeeding']); + Route::get('/admin/jobs', [AdminController::class, 'getJobsList']); + Route::post('/admin/runJob', [AdminController::class, 'runJob']); + Route::get('/admin/maintenanceMode', [AdminController::class, 'getMaintenanceMode']); Route::post('/admin/maintenanceMode', [AdminController::class, 'updateMaintenanceMode']); diff --git a/frontend/src/app/_routes/admin/maintenance/admin-maintenance.component.html b/frontend/src/app/_routes/admin/maintenance/admin-maintenance.component.html index 1e43959..86f861e 100644 --- a/frontend/src/app/_routes/admin/maintenance/admin-maintenance.component.html +++ b/frontend/src/app/_routes/admin/maintenance/admin-maintenance.component.html @@ -2,7 +2,7 @@
-

Database:

+

{{ 'admin.database'|translate|ftitlecase }}:

@@ -66,8 +66,6 @@ -
-
+
+
+

{{ 'admin.operations'|translate|ftitlecase }}:

+ +
+ + + + + + + + + + + + + +
{{ 'name'|translate|ftitlecase }}{{ 'admin.manual_execution'|translate|ftitlecase }}
{{ job }} + {{ 'admin.run'|translate|ftitlecase }} +
+
+
diff --git a/frontend/src/app/_routes/admin/maintenance/admin-maintenance.component.scss b/frontend/src/app/_routes/admin/maintenance/admin-maintenance.component.scss index 30e7a51..41ff7ed 100644 --- a/frontend/src/app/_routes/admin/maintenance/admin-maintenance.component.scss +++ b/frontend/src/app/_routes/admin/maintenance/admin-maintenance.component.scss @@ -12,6 +12,10 @@ animation: blink 2s linear infinite; } +.pointer { + cursor: pointer; +} + .right-border { border-right: #0000004a 1px solid } \ No newline at end of file diff --git a/frontend/src/app/_routes/admin/maintenance/admin-maintenance.component.ts b/frontend/src/app/_routes/admin/maintenance/admin-maintenance.component.ts index cd8a4d8..58c3a1c 100644 --- a/frontend/src/app/_routes/admin/maintenance/admin-maintenance.component.ts +++ b/frontend/src/app/_routes/admin/maintenance/admin-maintenance.component.ts @@ -12,6 +12,15 @@ export class AdminMaintenanceComponent implements OnInit { public db: any | undefined = undefined; public isTableListCollaped = true; + public jobs: string[] = []; + //Hard-coded list of jobs that should not be run manually + public dangerousJobs: string[] = [ + "NotifyUsersManualModeOnJob" + ]; + public ultraDangerousJobs: string[] = [ + "ResetAvailabilityMinutesJob" + ]; + public isMaintenanceModeActive = false; public telegramBotInfo: any | undefined = undefined; @@ -40,6 +49,19 @@ export class AdminMaintenanceComponent implements OnInit { }); } + getJobs() { + this.api.get('admin/jobs').then((res: any) => { + this.jobs = res; + console.log(this.jobs); + }).catch((err: any) => { + Swal.fire({ + icon: 'error', + title: this.translateService.instant('error_title'), + text: err.message + }); + }); + } + getMaintenanceMode() { this.api.get('admin/maintenanceMode').then((res: any) => { this.isMaintenanceModeActive = res.enabled; @@ -68,6 +90,7 @@ export class AdminMaintenanceComponent implements OnInit { ngOnInit(): void { this.getDB(); + this.getJobs(); this.getMaintenanceMode(); this.getTelegramBotDebugInfo(); } @@ -118,6 +141,35 @@ export class AdminMaintenanceComponent implements OnInit { }); } + runJob(job: string) { + //Require confirmation before proceeding + Swal.fire({ + title: this.translateService.instant('admin.run_confirm_title'), + text: this.translateService.instant('admin.run_confirm_text'), + icon: 'warning', + showCancelButton: true, + confirmButtonText: this.translateService.instant('yes'), + cancelButtonText: this.translateService.instant('no') + }).then((result) => { + if (result.isConfirmed) { + this.api.post('admin/runJob', { job }).then((res: any) => { + Swal.fire({ + icon: 'success', + title: this.translateService.instant('success_title'), + text: this.translateService.instant('admin.run_success') + }); + this.getJobs(); + }).catch((err: any) => { + Swal.fire({ + icon: 'error', + title: this.translateService.instant('error_title'), + text: err.error.message + }); + }); + } + }); + } + updateMaintenanceMode(enabled: boolean) { this.api.post('admin/maintenanceMode', { enabled }).then((res: any) => { this.isMaintenanceModeActive = enabled; diff --git a/frontend/src/assets/i18n/en.json b/frontend/src/assets/i18n/en.json index b3c4818..25a24db 100644 --- a/frontend/src/assets/i18n/en.json +++ b/frontend/src/assets/i18n/en.json @@ -46,7 +46,12 @@ "telegram_webhook_set": "Set Telegram Webhook", "telegram_webhook_set_success": "Telegram Webhook set successfully", "telegram_webhook_unset": "Unset Telegram Webhook", - "telegram_webhook_unset_success": "Telegram Webhook unset successfully" + "telegram_webhook_unset_success": "Telegram Webhook unset successfully", + "manual_execution": "manual execution", + "run": "run", + "run_confirm_title": "Are you sure you want to run this command?", + "run_confirm_text": "This action cannot be undone.", + "run_success": "Command executed successfully" }, "table": { "yes_remove": "Yes, remove", diff --git a/frontend/src/assets/i18n/it.json b/frontend/src/assets/i18n/it.json index d27c453..882383d 100644 --- a/frontend/src/assets/i18n/it.json +++ b/frontend/src/assets/i18n/it.json @@ -46,7 +46,12 @@ "telegram_webhook_set": "Imposta Webhook Telegram", "telegram_webhook_set_success": "Webhook Telegram impostato con successo", "telegram_webhook_unset": "Rimuovi Webhook Telegram", - "telegram_webhook_unset_success": "Webhook Telegram rimosso con successo" + "telegram_webhook_unset_success": "Webhook Telegram rimosso con successo", + "manual_execution": "esecuzione manuale", + "run": "esegui", + "run_confirm_title": "Sei sicuro di voler eseguire questo comando?", + "run_confirm_text": "Questa operazione non potrĂ  essere annullata.", + "run_success": "Comando eseguito con successo" }, "table": { "yes_remove": "Si, rimuovi",