Skip to content

Commit

Permalink
feat: display a maintenance page if a http error 503 occurs (#1111)
Browse files Browse the repository at this point in the history
* display maintenance page only if the error still exists otherwise route to the home page
* requires ICM 7.10.38.16-LTS (WebAdapter version 1.1.4)
  • Loading branch information
SGrueber authored and shauke committed Sep 23, 2022
1 parent a461e35 commit 27d937a
Show file tree
Hide file tree
Showing 12 changed files with 153 additions and 4 deletions.
1 change: 1 addition & 0 deletions server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ export function app() {
UserAgent: '*',
Disallow: [
'/error',
'/maintenance',
'/account',
'/compare',
'/recently',
Expand Down
13 changes: 13 additions & 0 deletions src/app/core/guards/error-status.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { map } from 'rxjs';

import { AppFacade } from 'ish-core/facades/app.facade';

@Injectable({ providedIn: 'root' })
export class ErrorStatusGuard implements CanActivate {
constructor(private appFacade: AppFacade, private router: Router) {}
canActivate() {
return this.appFacade.generalError$.pipe(map(error => (!error ? this.router.parseUrl('/home') : true)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ describe('Http Status Code Service', () => {
describe('on browser', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule.withRoutes([{ path: 'error', children: [] }])],
imports: [
RouterTestingModule.withRoutes([
{ path: 'error', children: [] },
{ path: 'maintenance', children: [] },
]),
],
});
httpStatusCodeService = TestBed.inject(HttpStatusCodeService);
location = TestBed.inject(Location);
Expand Down Expand Up @@ -56,13 +61,25 @@ describe('Http Status Code Service', () => {
verify(resSpy.status(anyNumber())).never();
expect(location.path()).toEqual('/error');
}));

it('should redirect to maintenance page for server 503 errors (Unavailable)', fakeAsync(() => {
httpStatusCodeService.setStatus(503);
tick(500);
verify(resSpy.status(anyNumber())).never();
expect(location.path()).toEqual('/maintenance');
}));
});
});

describe.onSSREnvironment('on server', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule.withRoutes([{ path: 'error', children: [] }])],
imports: [
RouterTestingModule.withRoutes([
{ path: 'error', children: [] },
{ path: 'maintenance', children: [] },
]),
],
providers: [{ provide: RESPONSE, useValue: RES }],
});
httpStatusCodeService = TestBed.inject(HttpStatusCodeService);
Expand Down Expand Up @@ -94,6 +111,13 @@ describe('Http Status Code Service', () => {
verify(resSpy.status(500)).once();
expect(location.path()).toEqual('/error');
}));

it('should redirect to maintenance page for server 503 errors (Unavailable)', fakeAsync(() => {
httpStatusCodeService.setStatus(503);
tick(500);
verify(resSpy.status(503)).once();
expect(location.path()).toEqual('/maintenance');
}));
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@ export class HttpStatusCodeService {
this.response.status(status);
}
if (redirect && status >= 400) {
// 503: server is unavailable
const route = status === 503 ? '/maintenance' : '/error';

if (SSR) {
return this.router.navigateByUrl('/error');
return this.router.navigateByUrl(route);
} else {
return this.router.navigateByUrl('/error', { skipLocationChange: status < 500 });
return this.router.navigateByUrl(route, { skipLocationChange: status < 500 });
}
}
return Promise.resolve(true);
Expand Down
12 changes: 12 additions & 0 deletions src/app/pages/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { AuthGuard } from 'ish-core/guards/auth.guard';
import { ErrorStatusGuard } from 'ish-core/guards/error-status.guard';
import { IdentityProviderInviteGuard } from 'ish-core/guards/identity-provider-invite.guard';
import { IdentityProviderLoginGuard } from 'ish-core/guards/identity-provider-login.guard';
import { IdentityProviderLogoutGuard } from 'ish-core/guards/identity-provider-logout.guard';
Expand Down Expand Up @@ -30,6 +31,17 @@ const routes: Routes = [
},
},
},
{
path: 'maintenance',
loadChildren: () => import('./maintenance/maintenance-page.module').then(m => m.MaintenancePageModule),
canActivate: [ErrorStatusGuard],
data: {
meta: {
title: 'seo.title.maintenance',
robots: 'noindex, nofollow',
},
},
},
{
path: 'account',
loadChildren: () => import('./account/account-page.module').then(m => m.AccountPageModule),
Expand Down
21 changes: 21 additions & 0 deletions src/app/pages/maintenance/maintenance-page.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<div class="errorpage-content col-lg-6 offset-lg-3">
<h1>{{ 'server.maintenance.page.title' | translate }}</h1>
<div class="error-text mb-3">
<div [ishServerHtml]="'server.maintenance.page.text' | translate"></div>
</div>
<div class="text-muted d-flex flex-column flex-sm-row justify-content-sm-between">
<div>{{ 'server.maintenance.page.contact.label' | translate }}</div>
<div>
<fa-icon [icon]="['fas', 'phone']" class="pr-1"></fa-icon>
<a href="tel:{{ 'server.maintenance.page.contact.phone' | translate }}">{{
'server.maintenance.page.contact.phone' | translate
}}</a>
</div>
<div>
<fa-icon [icon]="['fas', 'envelope']" class="pr-1"></fa-icon>
<a href="mailto:{{ 'server.maintenance.page.contact.email' | translate }}">{{
'server.maintenance.page.contact.email' | translate
}}</a>
</div>
</div>
</div>
33 changes: 33 additions & 0 deletions src/app/pages/maintenance/maintenance-page.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { TranslateModule } from '@ngx-translate/core';
import { MockComponent, MockDirective } from 'ng-mocks';

import { ServerHtmlDirective } from 'ish-core/directives/server-html.directive';

import { MaintenancePageComponent } from './maintenance-page.component';

describe('Maintenance Page Component', () => {
let component: MaintenancePageComponent;
let fixture: ComponentFixture<MaintenancePageComponent>;
let element: HTMLElement;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TranslateModule.forRoot()],
declarations: [MaintenancePageComponent, MockComponent(FaIconComponent), MockDirective(ServerHtmlDirective)],
}).compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(MaintenancePageComponent);
component = fixture.componentInstance;
element = fixture.nativeElement;
});

it('should be created', () => {
expect(component).toBeTruthy();
expect(element).toBeTruthy();
expect(() => fixture.detectChanges()).not.toThrow();
});
});
8 changes: 8 additions & 0 deletions src/app/pages/maintenance/maintenance-page.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';

@Component({
selector: 'ish-maintenance-page',
templateUrl: './maintenance-page.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MaintenancePageComponent {}
16 changes: 16 additions & 0 deletions src/app/pages/maintenance/maintenance-page.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { SharedModule } from 'ish-shared/shared.module';

import { MaintenancePageComponent } from './maintenance-page.component';

const maintenancePageRoutes: Routes = [
{ path: '', component: MaintenancePageComponent, data: { wrapperClass: 'errorpage', headerType: 'error' } },
];

@NgModule({
imports: [RouterModule.forChild(maintenancePageRoutes), SharedModule],
declarations: [MaintenancePageComponent],
})
export class MaintenancePageModule {}
6 changes: 6 additions & 0 deletions src/assets/i18n/de_DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -1043,7 +1043,13 @@
"seo.title.checkout": "Kasse",
"seo.title.error": "Fehler",
"seo.title.home": "inTRONICS Startseite",
"seo.title.maintenance": "Wartung",
"seo.title.search": "Suchergebnis für '{{0}}'",
"server.maintenance.page.contact.email": "info@test.intershop.de",
"server.maintenance.page.contact.label": "Sie können uns kontaktieren:",
"server.maintenance.page.contact.phone": "1-800-xxx-xxxx",
"server.maintenance.page.text": "<h3>Es tut uns leid ...</h3><h4>Unsere Webseite ist auf Grund von Wartungsarbeiten kurzzeitig nicht verfügbar. Bitte kommen Sie später wieder.</h4>",
"server.maintenance.page.title": "Wir sind bald zurück",
"servererror.mailServer.error": "Ihre E-Mail konnte leider nicht versandt werden. Wir bitten um Entschuldigung. Bitte versuchen Sie es später erneut.",
"servererror.page.text": "<h3>Es tut uns leid ...</h3><h4>Gehen Sie zurück zur <a href=\"route://home\" target=\"_self\">Startseite</a> und versuchen Sie es erneut.</h4>",
"servererror.page.title": "Etwas ist schiefgegangen.",
Expand Down
6 changes: 6 additions & 0 deletions src/assets/i18n/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -1043,7 +1043,13 @@
"seo.title.checkout": "Checkout",
"seo.title.error": "Error",
"seo.title.home": "inTRONICS Home",
"seo.title.maintenance": "Maintenance",
"seo.title.search": "Search Result for '{{0}}'",
"server.maintenance.page.contact.email": "info@test.intershop.de",
"server.maintenance.page.contact.label": "You can contact us:",
"server.maintenance.page.contact.phone": "1-800-xxx-xxxx",
"server.maintenance.page.text": "<h3>We are sorry ...</h3><h4>Our website is briefly unavailable for a scheduled maintenance. Please come back again later.</h4>",
"server.maintenance.page.title": "We will be back soon",
"servererror.mailServer.error": "Your mail couldn't be sent. We apologize for the inconvenience. Please try again later.",
"servererror.page.text": "<h3>We are sorry ...</h3><h4>Please go back to the <a href=\"route://home\" target=\"_self\">Home Page</a> and try again.</h4>",
"servererror.page.title": "Something Went Wrong",
Expand Down
6 changes: 6 additions & 0 deletions src/assets/i18n/fr_FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -1043,7 +1043,13 @@
"seo.title.checkout": "Passer la commande",
"seo.title.error": "Erreur",
"seo.title.home": "Page d’accueil InTRONICS",
"seo.title.maintenance": "Maintenance",
"seo.title.search": "Résultat de recherche pour '{{0}}'.",
"server.maintenance.page.contact.email": "info@test.intershop.de",
"server.maintenance.page.contact.label": "Vous pouvez nou contacter:",
"server.maintenance.page.contact.phone": "1-800-xxx-xxxx",
"server.maintenance.page.text": "<h3>Nous sommes désolés ...</h3>",
"server.maintenance.page.title": "Arrêt de maintenance",
"servererror.mailServer.error": "Votre courriel n’a pas pu être envoyé. Nous nous excusons pour les inconvénients. Veuillez réessayer plus tard.",
"servererror.page.text": "<h3>Nous sommes désolés ...</h3><h4>Retournez à la <a href=\"route://home\" target=\"_self\">Page d’accueil,</a> puis réessayez.</h4>",
"servererror.page.title": "Quelque chose s’est mal passé",
Expand Down

0 comments on commit 27d937a

Please sign in to comment.