diff --git a/app/Http/Controllers/SystemController.php b/app/Http/Controllers/SystemController.php index 2936e650..94e59cde 100644 --- a/app/Http/Controllers/SystemController.php +++ b/app/Http/Controllers/SystemController.php @@ -7,6 +7,7 @@ use App\Notifications\TestEmailSettingNotification; use App\Services\ReleaseRadarService; use Illuminate\Http\Request; use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; @@ -62,7 +63,7 @@ class SystemController extends Controller } /** - * Send a test email. + * Send a test email * * @return \Illuminate\Http\JsonResponse */ @@ -76,4 +77,38 @@ class SystemController extends Controller return response()->json(['message' => 'Ok']); } + + /** + * Clears all app caches and rebuild them + * + * @return \Illuminate\Http\JsonResponse + */ + public function optimize(Request $request) + { + $configCode = Artisan::call('config:cache'); + $routeCode = Artisan::call('route:cache'); + $eventCode = Artisan::call('event:cache'); + $viewCode = Artisan::call('view:cache'); + + return response()->json([ + 'config-exit-code' => $configCode, + 'route-exit-code' => $routeCode, + 'event-exit-code' => $eventCode, + 'view-exit-code' => $viewCode, + ], 200); + } + + /** + * Clears application cache + * + * @return \Illuminate\Http\JsonResponse + */ + public function clear(Request $request) + { + $exitCode = Artisan::call('optimize:clear'); + + return response()->json(['exit-code' => $exitCode], 200); + } + + } diff --git a/resources/js/services/systemService.js b/resources/js/services/systemService.js index a1968542..a1cd1dcc 100644 --- a/resources/js/services/systemService.js +++ b/resources/js/services/systemService.js @@ -8,7 +8,7 @@ export default { * @returns Promise */ getSystemInfos(config = {}) { - return webClient.get('infos', { ...config }) + return webClient.get('system/infos', { ...config }) }, /** @@ -16,7 +16,7 @@ export default { * @returns Promise */ getLastRelease(config = {}) { - return webClient.get('latestRelease', { ...config }) + return webClient.get('system/latestRelease', { ...config }) }, /** @@ -24,7 +24,23 @@ export default { * @returns Promise */ sendTestEmail(config = {}) { - return webClient.post('testEmail', { ...config }) - } + return webClient.post('system/test-email', { ...config }) + }, + + /** + * + * @returns Promise + */ + clearCache(config = {}) { + return webClient.get('system/clear-cache', { ...config }) + }, + + /** + * + * @returns Promise + */ + optimize(config = {}) { + return webClient.get('system/optimize', { ...config }) + }, } \ No newline at end of file diff --git a/resources/js/views/admin/AppSetup.vue b/resources/js/views/admin/AppSetup.vue index 16ca23d3..d5205f3c 100644 --- a/resources/js/views/admin/AppSetup.vue +++ b/resources/js/views/admin/AppSetup.vue @@ -17,6 +17,7 @@ const infos = ref() const listInfos = ref(null) const isSendingTestEmail = ref(false) + const isClearingCache = ref(false) const fieldErrors = ref({ restrictList: null, restrictRule: null, @@ -91,6 +92,20 @@ }) } + /** + * clears app cache + */ + function clearCache() { + isClearingCache.value = true; + + systemService.clearCache().then(response => { + useNotifyStore().success({ type: 'is-success', text: trans('admin.cache_cleared') }) + }) + .finally(() => { + isClearingCache.value = false; + }) + } + onBeforeRouteLeave((to) => { if (! to.name.startsWith('admin.')) { notify.clear() @@ -134,6 +149,7 @@ +
- + +

{{ $t('settings.security') }}

@@ -165,7 +182,25 @@ +

{{ $t('commons.environment') }}

+ +
+ +
+
+

+ +

+
+ +
+
    diff --git a/resources/lang/en/admin.php b/resources/lang/en/admin.php index 17d2d634..9fba60c6 100644 --- a/resources/lang/en/admin.php +++ b/resources/lang/en/admin.php @@ -63,6 +63,9 @@ return [ 'user_role_updated' => 'User role updated', 'pats_succesfully_revoked' => 'User\'s PATs successfully revoked', 'security_devices_succesfully_revoked' => 'User\'s security devices successfully revoked', + 'variables' => 'Variables', + 'cache_cleared' => 'Cache cleared', + 'cache_optimized' => 'Cache optimized', 'check_now' => 'Check now', 'view_on_github' => 'View on Github', 'x_is_available' => ':version is available', @@ -99,6 +102,10 @@ return [ 'label' => 'Email configuration test', 'help' => 'Send a test email to control your instance\'s email configuration. It is important to have a working configuration, otherwise users will not be able to request a reset password.', 'email_will_be_send_to_x' => 'The email will be send to :email', + ], + 'cache_management' => [ + 'label' => 'Cache management', + 'help' => 'Sometimes cache needs to be cleared, for instance after a change to environment variables or an update. You can do it from here.', ] ], diff --git a/resources/lang/en/commons.php b/resources/lang/en/commons.php index 3a802b2d..0e23a4fb 100644 --- a/resources/lang/en/commons.php +++ b/resources/lang/en/commons.php @@ -81,4 +81,5 @@ return [ 'information' => 'Information', 'permissions' => 'Permissions', 'send' => 'Send', + 'optimize' => 'Optimize', ]; diff --git a/routes/web.php b/routes/web.php index 6ad0e304..e6120964 100644 --- a/routes/web.php +++ b/routes/web.php @@ -73,20 +73,21 @@ Route::group(['middleware' => ['behind-auth', 'rejectIfReverseProxy']], function Route::delete('webauthn/credentials/{credential}', [WebAuthnManageController::class, 'delete'])->name('webauthn.credentials.delete'); }); -Route::get('refresh-csrf', function () { - return csrf_token(); -}); - - /** * Routes protected by an authentication guard and restricted to administrators */ Route::group(['middleware' => ['behind-auth', 'admin']], function () { - Route::get('infos', [SystemController::class, 'infos'])->name('system.infos'); - Route::post('testEmail', [SystemController::class, 'testEmail'])->name('system.testEmail'); + Route::get('system/infos', [SystemController::class, 'infos'])->name('system.infos'); + Route::post('system/test-email', [SystemController::class, 'testEmail'])->name('system.testEmail'); }); -Route::get('latestRelease', [SystemController::class, 'latestRelease'])->name('system.latestRelease'); +Route::get('system/optimize', [SystemController::class, 'optimize'])->name('system.optimize'); +Route::get('system/clear-cache', [SystemController::class, 'clear'])->name('system.clear'); +Route::get('system/latestRelease', [SystemController::class, 'latestRelease'])->name('system.latestRelease'); + +Route::get('refresh-csrf', function () { + return csrf_token(); +}); /** * Route for the main landing view diff --git a/tests/Feature/Http/SystemControllerTest.php b/tests/Feature/Http/SystemControllerTest.php index 07ba7ac0..5e455ee2 100644 --- a/tests/Feature/Http/SystemControllerTest.php +++ b/tests/Feature/Http/SystemControllerTest.php @@ -37,7 +37,7 @@ class SystemControllerTest extends FeatureTestCase */ public function test_infos_returns_unauthorized() { - $response = $this->json('GET', '/infos') + $response = $this->json('GET', '/system/infos') ->assertUnauthorized(); } @@ -47,7 +47,7 @@ class SystemControllerTest extends FeatureTestCase public function test_infos_returns_forbidden() { $response = $this->actingAs($this->user, 'api-guard') - ->json('GET', '/infos') + ->json('GET', '/system/infos') ->assertForbidden(); } @@ -57,7 +57,7 @@ class SystemControllerTest extends FeatureTestCase public function test_infos_returns_only_base_collection() { $response = $this->actingAs($this->admin, 'api-guard') - ->json('GET', '/infos') + ->json('GET', '/system/infos') ->assertOk() ->assertJsonStructure([ 'common' => [ @@ -88,7 +88,7 @@ class SystemControllerTest extends FeatureTestCase public function test_infos_returns_proxy_collection_when_signed_in_behind_proxy() { $response = $this->actingAs($this->admin, 'reverse-proxy-guard') - ->json('GET', '/infos') + ->json('GET', '/system/infos') ->assertOk() ->assertJsonStructure([ 'common' => [ @@ -109,7 +109,7 @@ class SystemControllerTest extends FeatureTestCase ->once() ->andReturn('new_release'); - $response = $this->json('GET', '/latestRelease') + $response = $this->json('GET', '/system/latestRelease') ->assertOk() ->assertJson([ 'newRelease' => 'new_release', @@ -124,7 +124,7 @@ class SystemControllerTest extends FeatureTestCase Notification::fake(); $response = $this->actingAs($this->admin, 'web-guard') - ->json('POST', '/testEmail', []); + ->json('POST', '/system/test-email', []); $response->assertStatus(200); @@ -136,7 +136,7 @@ class SystemControllerTest extends FeatureTestCase */ public function test_testEmail_returns_unauthorized() { - $response = $this->json('GET', '/infos') + $response = $this->json('GET', '/system/infos') ->assertUnauthorized(); } @@ -146,7 +146,27 @@ class SystemControllerTest extends FeatureTestCase public function test_testEmail_returns_forbidden() { $response = $this->actingAs($this->user, 'api-guard') - ->json('GET', '/infos') + ->json('GET', '/system/infos') ->assertForbidden(); } + + /** + * @test + */ + public function test_clearCache_returns_success() + { + $response = $this->json('GET', '/system/clear-cache'); + + $response->assertStatus(200); + } + + /** + * @test + */ + public function test_optimize_returns_success() + { + $response = $this->json('GET', '/system/optimize'); + + $response->assertStatus(200); + } }