From 52fd6d6d2f52f015e391589c4fa7f8156afca90d Mon Sep 17 00:00:00 2001 From: Matteo Gheza Date: Wed, 10 Jan 2024 17:30:22 +0100 Subject: [PATCH] Add ping route and maintenance mode handling --- backend/routes/api.php | 6 +++++ .../_providers/auth-interceptor.provider.ts | 21 ++++++++++++++---- .../src/app/_routes/list/list.component.html | 16 +++++++------- .../app/_routes/login/login.component.html | 2 +- .../src/app/_routes/login/login.component.ts | 4 +++- .../src/app/_services/api-client.service.ts | 22 +++++++++++++++++++ frontend/src/app/app.component.html | 10 +++++++-- frontend/src/app/app.component.ts | 2 +- frontend/src/assets/i18n/en.json | 1 + frontend/src/assets/i18n/it.json | 1 + 10 files changed, 68 insertions(+), 17 deletions(-) diff --git a/backend/routes/api.php b/backend/routes/api.php index 3e73727..c61de9a 100644 --- a/backend/routes/api.php +++ b/backend/routes/api.php @@ -106,6 +106,12 @@ Route::get('/owner_image', function() { ); }); +Route::get('/ping', function() { + return response()->json([ + 'message' => 'pong' + ]); +}); + Route::post('/cron/execute', function(Request $request) { //Go to app/Console/Kernel.php to view schedules if(config('cron.external_cron_enabled') && $request->header('Cron') == config('cron.execution_code')) { diff --git a/frontend/src/app/_providers/auth-interceptor.provider.ts b/frontend/src/app/_providers/auth-interceptor.provider.ts index 97f09c4..bd3e021 100644 --- a/frontend/src/app/_providers/auth-interceptor.provider.ts +++ b/frontend/src/app/_providers/auth-interceptor.provider.ts @@ -37,7 +37,7 @@ const genericRetryStrategy = ({ // retry after 1s, 2s, etc... return timer(retryAttempt * scalingDuration); }), - finalize(() => console.log('We are done!')) + //finalize(() => console.log('We are done!')) ); }; @@ -86,15 +86,26 @@ export class AuthInterceptor implements HttpInterceptor { } intercept(req: HttpRequest, next: HttpHandler): Observable> { + //If maintenance mode is enabled, return 503 except for ping + if (this.api.maintenanceMode && !req.url.includes("ping")) { + return new Observable>((observer) => { + observer.next(new HttpResponse({ + body: { message: "Maintenance mode" }, + status: 503, + statusText: "Service Unavailable", + url: req.url + })); + observer.complete(); + }); + } return next.handle(this.updateRequest(req)).pipe( retryWhen(genericRetryStrategy({ maxRetryAttempts: 3, scalingDuration: 1, excludedStatusCodes: [304, 400, 404, 419, 500, 503], - excludedUrls: ["login", "logout", "me", "impersonate", "stop_impersonating"] + excludedUrls: ["login", "logout", "me", "impersonate", "stop_impersonating", "ping"] })), catchError(error => { - console.log(error); if (error.status === 304) { //Return current response as successfully return new Observable>((observer) => { @@ -107,6 +118,9 @@ export class AuthInterceptor implements HttpInterceptor { })); observer.complete(); }); + } else if (error.status === 503) { + this.api.maintenanceMode = true; + return throwError(() => error); } else if (error.status === 419) { return new Observable>((observer) => { this.api.get("csrf-cookie").then(() => { @@ -115,7 +129,6 @@ export class AuthInterceptor implements HttpInterceptor { }); } if (error instanceof HttpErrorResponse && !req.url.includes('login') && !req.url.includes('me') && !req.url.includes('logout')) { - console.log("Error: " + error.status); if (error.status === 400) { this.router.navigate(["logout"]); return throwError(() => error); diff --git a/frontend/src/app/_routes/list/list.component.html b/frontend/src/app/_routes/list/list.component.html index 8896c20..e882f72 100644 --- a/frontend/src/app/_routes/list/list.component.html +++ b/frontend/src/app/_routes/list/list.component.html @@ -1,36 +1,36 @@

{{ 'list.your_availability_is'|translate }} {{ available ? ("available"|translate|uppercase) : ("unavailable"|translate|uppercase) }}{{ manual_mode ? "" : " ("+('programmed'|translate)+")" }}

- - + +
- -
-
- -
- + diff --git a/frontend/src/app/_routes/login/login.component.html b/frontend/src/app/_routes/login/login.component.html index 056555f..1199574 100644 --- a/frontend/src/app/_routes/login/login.component.html +++ b/frontend/src/app/_routes/login/login.component.html @@ -10,7 +10,7 @@
- diff --git a/frontend/src/app/_routes/login/login.component.ts b/frontend/src/app/_routes/login/login.component.ts index cbceba6..89858f2 100644 --- a/frontend/src/app/_routes/login/login.component.ts +++ b/frontend/src/app/_routes/login/login.component.ts @@ -2,6 +2,7 @@ import { Component } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { AuthService, LoginResponse } from 'src/app/_services/auth.service'; import { GuardLoaderIconService } from 'src/app/_services/guard-loader-icon.service'; +import { ApiClientService } from 'src/app/_services/api-client.service'; @Component({ selector: 'app-login', @@ -22,7 +23,8 @@ export class LoginComponent { public route: ActivatedRoute, private router: Router, private authService: AuthService, - private guardLoaderIconService: GuardLoaderIconService + private guardLoaderIconService: GuardLoaderIconService, + public api: ApiClientService ) { this.route.params.subscribe((params) => { if (params["redirect"]) { diff --git a/frontend/src/app/_services/api-client.service.ts b/frontend/src/app/_services/api-client.service.ts index e4887b4..5a0c70b 100644 --- a/frontend/src/app/_services/api-client.service.ts +++ b/frontend/src/app/_services/api-client.service.ts @@ -14,6 +14,28 @@ export class ApiClientService { public alertsChanged = new Subject(); public availableUsers: undefined | number = undefined; + private _maintenanceMode = false; + private _maintenanceModeInterval: any = undefined; + public maintenanceModeChanged = new Subject(); + + get maintenanceMode(): boolean { + return this._maintenanceMode; + } + set maintenanceMode(value: boolean) { + if(value && !this._maintenanceMode) { + //Every 5 seconds, check if maintenance mode is still active + this._maintenanceModeInterval = setInterval(() => { + this.get("ping").then(() => { + console.log("Maintenance mode disabled"); + this.maintenanceMode = false; + clearInterval(this._maintenanceModeInterval); + }).catch(() => {}); + }, 10000); + } + this._maintenanceMode = value; + this.maintenanceModeChanged.next(); + } + constructor(private http: HttpClient) { } public apiEndpoint(endpoint: string): string { diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index f2cf18c..743c25d 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -1,4 +1,4 @@ -
+ - +
+ + {{ 'warning'|translate|ftitlecase }}! {{ 'maintenance_mode_warning'|translate }}
+
+
+ + {{ 'warning'|translate|ftitlecase }}! {{ 'alert.warning_body'|translate }}
{{ 'alert.current_alert'|translate }}: {{ alerts[0]["created_at"] | date:'dd/MM/YYYY, HH:mm:ss' }} ({{ 'press_for_more_info'|translate }}) diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 99d492b..2ee5beb 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -28,7 +28,7 @@ export class AppComponent { private locationBackService: LocationBackService, public guardLoaderIconService: GuardLoaderIconService, private router: Router, - private api: ApiClientService, + public api: ApiClientService, private modalService: BsModalService, public guard: AuthorizeGuard ) { diff --git a/frontend/src/assets/i18n/en.json b/frontend/src/assets/i18n/en.json index c71739a..50d9fed 100644 --- a/frontend/src/assets/i18n/en.json +++ b/frontend/src/assets/i18n/en.json @@ -130,6 +130,7 @@ "document_format_not_supported": "Document format not supported", "file_too_big": "File too big" }, + "maintenance_mode_warning": "The application is currently in maintenance mode. Some features may not be available.", "property": "property", "value": "value", "user_agent": "User Agent", diff --git a/frontend/src/assets/i18n/it.json b/frontend/src/assets/i18n/it.json index a60133e..ec44bb7 100644 --- a/frontend/src/assets/i18n/it.json +++ b/frontend/src/assets/i18n/it.json @@ -129,6 +129,7 @@ "image_format_not_supported": "Formato immagine non supportato", "file_too_big": "File troppo grande" }, + "maintenance_mode_warning": "Il gestionale è in manutenzione. Alcune funzionalità potrebbero non essere disponibili.", "property": "proprietà", "value": "valore", "user_agent": "User Agent",