diff --git a/k8s/lifemonitor-web/templates/configmap.yaml b/k8s/lifemonitor-web/templates/configmap.yaml index 2e83aea4..50ed5aef 100644 --- a/k8s/lifemonitor-web/templates/configmap.yaml +++ b/k8s/lifemonitor-web/templates/configmap.yaml @@ -12,6 +12,8 @@ data: { "apiBaseUrl": "https://{{ .Values.externalServerName }}/api", "appDomain": "{{ .Values.externalServerName }}", - "socketBaseUrl": "https://{{ .Values.externalServerName }}"{{ if .Values.backend.clientId }},{{ end }} - {{ if .Values.backend.clientId }}"clientId": "{{ .Values.backend.clientId }}"{{ end }} + "socketBaseUrl": "https://{{ .Values.externalServerName }}" + {{- if .Values.backend.clientId }}, "clientId": "{{ .Values.backend.clientId }}"{{ end }} + {{- if .Values.maintenanceMode.enabled }}, "maintenanceMode": "true"{{ end }} + {{- if .Values.maintenanceMode.message }}, "maintenanceMessage":"{{.Values.maintenanceMode.message}}"{{ end }} } diff --git a/k8s/lifemonitor-web/templates/deployment.yaml b/k8s/lifemonitor-web/templates/deployment.yaml index 2160716f..2d8ab7dd 100644 --- a/k8s/lifemonitor-web/templates/deployment.yaml +++ b/k8s/lifemonitor-web/templates/deployment.yaml @@ -17,6 +17,8 @@ spec: helm.sh/chart: {{ include "lifemonitor-web.chart" . }} template: metadata: + annotations: + checksum/frontend-config: {{ include (print $.Template.BasePath "/nginx.configmap.yml") . | sha256sum }} labels: app.kubernetes.io/name: {{ include "lifemonitor-web.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} @@ -31,7 +33,7 @@ spec: - name: http containerPort: 4200 protocol: TCP - {{ if .Values.monitoring.enabled }} + {{- if .Values.monitoring.enabled }} - name: metrics containerPort: 9090 protocol: TCP @@ -46,8 +48,10 @@ spec: - name: nginx-config mountPath: /etc/nginx/conf.d/webapp.prod.conf subPath: app.conf - - name: logs-storage + - name: nginx-logs mountPath: /var/log/nginx + - name: nginx-run + mountPath: /var/run/nginx {{ if .Values.extraVolumeMounts }} {{- toYaml .Values.extraVolumeMounts | nindent 12 }} {{- end }} @@ -62,6 +66,24 @@ spec: # port: http resources: {{- toYaml .Values.resources | nindent 12 }} + {{- if .Values.monitoring.enabled }} + - name: promtail + image: grafana/promtail:master + args: + - "-config.file=/etc/promtail/promtail.yaml" # Found in the ConfigMap + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "256Mi" + cpu: "200m" + volumeMounts: + - name: promtail-config + mountPath: /etc/promtail + - name: nginx-logs + mountPath: /var/log/nginx + {{- end }} volumes: - name: frontend-config configMap: @@ -71,8 +93,16 @@ spec: configMap: name: {{ include "lifemonitor-web.fullname" . }}-nginx-config defaultMode: 0644 - - name: logs-storage + - name: nginx-logs emptyDir: {} + - name: nginx-run + emptyDir: {} + {{- if .Values.monitoring.enabled }} + - name: promtail-config + configMap: + name: {{ include "lifemonitor-web.fullname" . }}-promtail-configmap + defaultMode: 0644 + {{- end }} {{ if .Values.extraVolumes }} {{- toYaml .Values.extraVolumes | nindent 8 }} {{- end }} diff --git a/k8s/lifemonitor-web/templates/nginx.configmap.yml b/k8s/lifemonitor-web/templates/nginx.configmap.yml index a1281307..839aa16c 100644 --- a/k8s/lifemonitor-web/templates/nginx.configmap.yml +++ b/k8s/lifemonitor-web/templates/nginx.configmap.yml @@ -47,10 +47,6 @@ data: server { server_name {{ .Values.externalServerName }} {{ include "lifemonitor-web.name" . }}-metrics.default {{ include "lifemonitor-web.name" . }}-metrics.default.svc.cluster.local; - # save logs here - access_log /var/log/nginx/metrics.access.log extended; - error_log /var/log/nginx/metrics.error.log; - proxy_read_timeout 600; proxy_connect_timeout 600; proxy_send_timeout 600; @@ -69,9 +65,12 @@ data: # force HTTP traffic to HTTPS # error_page 497 http://$host:4200$request_uri; - # expose extended metrics + # expose standard nginx metrics location = /metrics { stub_status on; + + access_log off; + log_not_found off; } } {{- end -}} @@ -97,8 +96,46 @@ data: # force HTTP traffic to HTTPS error_page 497 http://$host:4200$request_uri; + # define error pages + error_page 404 /error/404; + error_page 405 /error/405; + error_page 429 /error/429; + error_page 500 /error/500; + error_page 502 /error/502; + + # location for error pages + location ~ ^/error { + # rewrite request uri to point to the api + rewrite /error/(.*) /api/error/$1 break; + + # disable redirects + proxy_redirect off; + + # rewrite headers + proxy_pass_header Server; + proxy_set_header X-Real-IP $http_x_forwarded_for; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Scheme $scheme; + proxy_set_header Host $http_host; + proxy_set_header Cookie $http_cookie; + proxy_set_header X-Requested-With $http_x_requested_with; + + # various proxy settings + proxy_connect_timeout 600; + proxy_read_timeout 600; + proxy_send_timeout 600; + #proxy_intercept_errors on; + + # set uppstream + proxy_pass https://api; + + # log errors using the extended format + access_log /var/log/nginx/http.error.log custom_format; + } + # disable standard nginx metrics location = /nginx-metrics { + # stub_status on; deny all; } @@ -155,7 +192,6 @@ data: proxy_connect_timeout 600; proxy_read_timeout 600; proxy_send_timeout 600; - #proxy_intercept_errors on; } location ~ ^/(account|oauth2|jobs|github|integrations) { @@ -177,7 +213,6 @@ data: proxy_connect_timeout 600; proxy_read_timeout 600; proxy_send_timeout 600; - #proxy_intercept_errors on; } location /socket.io/ { @@ -223,15 +258,15 @@ data: pcre_jit on; # logs - pid /var/run/openresty/nginx.pid; - error_log /var/log/nginx/nginx.error.log crit; + pid /var/log/nginx/nginx.pid; + error_log /var/log/nginx/nginx.error.log warn; events { worker_connections 1024; } http { - + include mime.types; default_type application/octet-stream; @@ -240,27 +275,35 @@ data: # When the use of underscores is disabled, request header fields whose names contain underscores are marked as invalid and become subject to the ignore_invalid_headers directive. # underscores_in_headers off; - server_names_hash_bucket_size 128; - server_names_hash_max_size 512; - + # fix issues with large client headers proxy_headers_hash_max_size 512; proxy_headers_hash_bucket_size 128; - # Define a verbose log format - log_format extended - '[$http_x_forwarded_for] ' - '"$request" $status ($body_bytes_sent bytes) - "$http_referer", "$http_user_agent", "$http_x_request_domain"'; - + # Define the log format + log_format custom_format '$http_x_client_ip - $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent ' + '"$http_referer" "$http_user_agent" - ' + '$http_x_forwarded_for $request_time $request_length - ' + '$connection $connection_requests $pipe - ' + '$upstream_addr $upstream_status $upstream_cache_status ' + '$upstream_response_time $upstream_response_length'; + # Configure Log files - access_log /usr/local/openresty/nginx/logs/access.log extended; - # error_log /usr/local/openresty/nginx/logs/error.log warn; + access_log /var/log/nginx/access.log custom_format; + error_log /var/log/nginx/error.log warn; + + + # See Move default writable paths to a dedicated directory (#119) + # https://github.com/openresty/docker-openresty/issues/119 + client_body_temp_path /var/run/nginx/nginx-client-body; + proxy_temp_path /var/run/nginx/nginx-proxy; + fastcgi_temp_path /var/run/nginx/nginx-fastcgi; + uwsgi_temp_path /var/run/nginx/nginx-uwsgi; + scgi_temp_path /var/run/nginx/nginx-scgi; + - # Extra settings - client_body_temp_path /var/run/openresty/nginx-client-body; - proxy_temp_path /var/run/openresty/nginx-proxy; - fastcgi_temp_path /var/run/openresty/nginx-fastcgi; - uwsgi_temp_path /var/run/openresty/nginx-uwsgi; - scgi_temp_path /var/run/openresty/nginx-scgi; + # Defines a file that will store the process ID of the main process. + server_names_hash_bucket_size 128; sendfile on; #tcp_nopush on; @@ -275,4 +318,3 @@ data: # Don't reveal OpenResty version to clients. # server_tokens off; } - diff --git a/k8s/lifemonitor-web/templates/promtail.configmap.yaml b/k8s/lifemonitor-web/templates/promtail.configmap.yaml new file mode 100644 index 00000000..02ed4e86 --- /dev/null +++ b/k8s/lifemonitor-web/templates/promtail.configmap.yaml @@ -0,0 +1,41 @@ +{{- if .Values.monitoring.loki.enabled }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: "{{ include "lifemonitor-web.fullname" . }}-promtail-configmap" +data: + promtail.yaml: | + server: + http_listen_port: 9080 + grpc_listen_port: 0 + log_level: "debug" + positions: + filename: /tmp/positions.yaml + clients: # Specify target + - url: {{ $.Values.monitoring.loki.url }}/loki/api/v1/push + scrape_configs: + - job_name: "lifemonitor-app-proxy-logger" + static_configs: + - targets: + - localhost + labels: + app: "lifemonitor-webapp" + component: "{{$.Release.Name}}-proxy" + environment: "{{$.Release.Namespace}}" + format: "extended" + level: "INFO" + __path__: /var/log/nginx/access.log + - targets: + - localhost + labels: + app: "lifemonitor-webapp" + component: "{{ $.Release.Name }}-proxy" + environment: "{{ $.Release.Namespace }}" + format: "extended" + level: "ERROR" + __path__: /var/log/nginx/*error.log + pipeline_stages: + - drop: + expression: ".*(DEBUG|health|heartbeat).*" +{{- end }} \ No newline at end of file diff --git a/k8s/lifemonitor-web/values.yaml b/k8s/lifemonitor-web/values.yaml index 098541ad..c8018403 100644 --- a/k8s/lifemonitor-web/values.yaml +++ b/k8s/lifemonitor-web/values.yaml @@ -9,15 +9,20 @@ image: tag: latest pullPolicy: IfNotPresent -nameOverride: '' -fullnameOverride: '' +nameOverride: "" +fullnameOverride: "" -externalServerName: 'localhost' +externalServerName: "localhost" + +# Manage maintenance mode +maintenanceMode: + enabled: false + # message: "The service is currently under maintenance. Please try again later." # Setting for the LifeMonitor Backend API backend: - apiUrl: '' - clientId: '' + apiUrl: "" + clientId: "" service: type: NodePort @@ -29,6 +34,19 @@ monitoring: enabled: false prometheus: namespace: kube-prometheus-stack + loki: + enabled: false + url: http://loki:3100 + exporter: + image: grafana/promtail:main-60ea954 + imagePullPolicy: IfNotPresent + resources: + requests: + memory: 128Mi + cpu: 0.1 + limits: + memory: 256Mi + cpu: 0.2 ingress: enabled: false @@ -39,24 +57,30 @@ ingress: hosts: - host: localhost paths: - - '/' + - "/" # configure TLS for the ingress tls: - secretName: lifemonitor-web-tls hosts: - localhost -resources: {} -# We usually recommend not to specify default resources and to leave this as a conscious -# choice for the user. This also increases chances charts run on environments with little -# resources, such as Minikube. If you do want to specify resources, uncomment the following -# lines, adjust them as necessary, and remove the curly braces after 'resources:'. -# limits: -# cpu: 100m -# memory: 128Mi -# requests: -# cpu: 100m -# memory: 128Mi +resources: + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + requests: + memory: "4096Mi" + cpu: "1000m" + limits: + memory: "7680Mi" + cpu: "1800m" nodeSelector: {} diff --git a/k8s/values.yaml b/k8s/values.yaml index 89fed179..4e117107 100644 --- a/k8s/values.yaml +++ b/k8s/values.yaml @@ -14,6 +14,11 @@ fullnameOverride: '' externalServerName: 'localhost' +# Manage maintenance mode +maintenanceMode: + enabled: false + # message: "The service is currently under maintenance. Please try again later." + # Setting for the LifeMonitor Backend API backend: apiUrl: 'https://api.lifemonitor.eu' diff --git a/ngsw-config.json b/ngsw-config.json index cfcf80ae..4afb4224 100644 --- a/ngsw-config.json +++ b/ngsw-config.json @@ -33,14 +33,14 @@ "name": "api", "urls": [ "/api/**", + "/error/**", "/account/**", "/oauth2/**", "/jobs/**", "/socket.io/**", "/github/**", "/integrations/**", - "/openapi.*", - "/metrics" + "/openapi.*" ], "cacheConfig": { "strategy": "freshness", diff --git a/package-lock.json b/package-lock.json index 516c8b14..1a4b05d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "lifemonitor", - "version": "0.5.5", + "version": "0.5.6", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 0662c5be..2e218ba5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lifemonitor", - "version": "0.5.5", + "version": "0.5.6", "scripts": { "start": "ng build --configuration production && http-server -p 4202 --ssl -C ./certs/lm.crt -K ./certs/lm.key --host lm dist/lifemonitor", "start:dev": "ng serve --port 4200 --ssl --ssl-key ./certs/lm.key --ssl-cert ./certs/lm.crt --host lm", diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 802e25ee..11d5d814 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -16,6 +16,7 @@ import { InputDialogService } from './utils/services/input-dialog.service'; import { DashboardComponent } from './views/dashboard/dashboard.component'; import { SuiteComponent } from './views/suite/suite.component'; import { WorkflowComponent } from './views/workflow/workflow.component'; +import { MaintenanceComponent } from './pages/maintenance/maintenance.component'; const routes: Routes = [ { @@ -28,6 +29,11 @@ const routes: Routes = [ // canActivate: [AuthGuard], // canActivateChild: [AuthGuard], children: [ + { + path: 'maintenance', + component: MaintenanceComponent, + pathMatch: 'full', + }, { path: 'dashboard', component: DashboardComponent, @@ -51,7 +57,7 @@ const routes: Routes = [ { path: 'logout', component: LogoutComponent, - }, + }, // { // path: 'register', // component: RegisterComponent, @@ -83,7 +89,7 @@ export class AppRoutingModule implements OnInit { private router: Router, private inputDialogService: InputDialogService, private toastr: ToastrService - ) {} + ) { } ngOnInit() { this.logger.debug('Initializing app routing module'); @@ -133,7 +139,7 @@ export class AppRoutingModule implements OnInit { cancelText: 'Close', iconClass: 'fas fa-exclamation-triangle', enableCancel: false, - onCancel: () => {}, + onCancel: () => { }, }); } } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index c8eaaaae..f258e621 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,10 +1,12 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { SwUpdate, UpdateAvailableEvent } from '@angular/service-worker'; +import { Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { Logger, LoggerManager } from './utils/logging/index'; import { CacheRefreshStatus } from './utils/services/cache/cache.model'; import { CachedHttpClientService } from './utils/services/cache/cachedhttpclient.service'; +import { AppConfigService } from './utils/services/config.service'; import { InputDialogService } from './utils/services/input-dialog.service'; import { @@ -42,7 +44,9 @@ export class AppComponent implements OnInit, OnDestroy { private inputDialog: InputDialogService, private swUpdate: SwUpdate, private cachedHttpClient: CachedHttpClientService, - private ccService: NgcCookieConsentService + private ccService: NgcCookieConsentService, + private config: AppConfigService, + private router: Router ) { this.refreshStatus$ = this.cachedHttpClient.refreshProgressStatus$; } @@ -51,7 +55,20 @@ export class AppComponent implements OnInit, OnDestroy { return this.swUpdate.available; } + get maintenanceModeEnabled(): boolean { + return this.config.maintenanceMode; + } + ngOnInit() { + + if (this.maintenanceModeEnabled) { + console.log("Current route: " + this.router.url); + if (this.router.url !== '/maintenance' + && !this.router.url.startsWith('/static')) { + return this.router.navigateByUrl('/maintenance'); + } + } + if (this.swUpdate.isEnabled) { this.checkVersionSubscription = this.updateAvailable.subscribe(() => { this.inputDialog.show({ @@ -73,30 +90,30 @@ export class AppComponent implements OnInit, OnDestroy { } // subscribe to cookieconsent observables to react to main events - this.popupOpenSubscription = this.ccService.popupOpen$.subscribe(() => {}); + this.popupOpenSubscription = this.ccService.popupOpen$.subscribe(() => { }); this.popupCloseSubscription = this.ccService.popupClose$.subscribe( - () => {} + () => { } ); this.initializeSubscription = this.ccService.initializing$.subscribe( - (event: NgcInitializingEvent) => {} + (event: NgcInitializingEvent) => { } ); this.statusChangeSubscription = this.ccService.statusChange$.subscribe( - (event: NgcStatusChangeEvent) => {} + (event: NgcStatusChangeEvent) => { } ); this.revokeChoiceSubscription = this.ccService.revokeChoice$.subscribe( - () => {} + () => { } ); this.noCookieLawSubscription = this.ccService.noCookieLaw$.subscribe( - (event: NgcNoCookieLawEvent) => {} + (event: NgcNoCookieLawEvent) => { } ); } - ngAfterViewInit() {} + ngAfterViewInit() { } ngOnDestroy() { if (this.checkVersionSubscription) { diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 89950721..709eb2c5 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -55,6 +55,7 @@ import { BlankComponent } from './views/blank/blank.component'; import { DashboardComponent } from './views/dashboard/dashboard.component'; import { SuiteComponent } from './views/suite/suite.component'; import { WorkflowComponent } from './views/workflow/workflow.component'; +import { MaintenanceComponent } from './pages/maintenance/maintenance.component'; // PrimeNG Modules // import { ChartModule } from 'primeng/chart'; @@ -156,6 +157,7 @@ export function initConfigService( LogoutComponent, ScrollComponent, BaseDataViewComponent, + MaintenanceComponent, ], imports: [ // PrimeNg Modules diff --git a/src/app/pages/main/header/header.component.html b/src/app/pages/main/header/header.component.html index d89bb121..4f189489 100644 --- a/src/app/pages/main/header/header.component.html +++ b/src/app/pages/main/header/header.component.html @@ -35,7 +35,8 @@ /> --> - + Sign In @@ -58,4 +59,4 @@ - + \ No newline at end of file diff --git a/src/app/pages/main/header/header.component.ts b/src/app/pages/main/header/header.component.ts index e47ddaaf..95cb6845 100644 --- a/src/app/pages/main/header/header.component.ts +++ b/src/app/pages/main/header/header.component.ts @@ -10,6 +10,7 @@ import { AppService } from 'src/app/utils/services/app.service'; import { UserDropdownMenuComponent } from './user-dropdown-menu/user-dropdown-menu.component'; import { Router } from '@angular/router'; +import { AppConfigService } from 'src/app/utils/services/config.service'; @Component({ selector: 'app-header', @@ -22,7 +23,9 @@ export class HeaderComponent implements OnInit { public searchForm: FormGroup; - constructor(private router: Router, private appService: AppService) {} + constructor(private router: Router, + private appService: AppService, + private appConfig: AppConfigService) { } ngOnInit() { this.searchForm = new FormGroup({ @@ -30,6 +33,10 @@ export class HeaderComponent implements OnInit { }); } + public get maintenanceMode(): boolean { + return this.appConfig.maintenanceMode; + } + public get isUserLogged(): boolean { return this.appService.isUserLogged(); } diff --git a/src/app/pages/main/main.component.html b/src/app/pages/main/main.component.html index 2e5dcdbc..bec4aa80 100644 --- a/src/app/pages/main/main.component.html +++ b/src/app/pages/main/main.component.html @@ -10,7 +10,7 @@ (mainSidebarHeight)="mainSidebarHeight($event)" >--> -
+
diff --git a/src/app/pages/main/main.component.ts b/src/app/pages/main/main.component.ts index 61a7886d..9ab2d91f 100644 --- a/src/app/pages/main/main.component.ts +++ b/src/app/pages/main/main.component.ts @@ -7,6 +7,7 @@ import { User } from 'src/app/models/user.modes'; import { Logger, LoggerManager } from 'src/app/utils/logging'; import { ApiService } from 'src/app/utils/services/api.service'; import { AppService } from './../../utils/services/app.service'; +import { AppConfigService } from 'src/app/utils/services/config.service'; @Component({ selector: 'app-main', @@ -27,8 +28,9 @@ export class MainComponent implements OnInit { private router: Router, private renderer: Renderer2, private apiService: ApiService, - private appService: AppService - ) {} + private appService: AppService, + private appConfig: AppConfigService + ) { } ngOnInit() { this.renderer.removeClass(document.querySelector('app-root'), 'login-page'); @@ -79,6 +81,10 @@ export class MainComponent implements OnInit { }); } + get maintenanceMode(): boolean { + return this.appConfig.maintenanceMode; + } + mainSidebarHeight(height) { // this.renderer.setStyle( // this.contentWrapper.nativeElement, diff --git a/src/app/pages/maintenance/maintenance.component.html b/src/app/pages/maintenance/maintenance.component.html new file mode 100644 index 00000000..b777c51d --- /dev/null +++ b/src/app/pages/maintenance/maintenance.component.html @@ -0,0 +1,24 @@ +
+

+ We'll be back soon! +

+
+ +
+ + +

+ We're busy updating the + Life-Monitor + service for you.
Please check back soon! +

+ + +

+ {{ maintenanceMessage }} +

+ +
\ No newline at end of file diff --git a/src/app/pages/maintenance/maintenance.component.scss b/src/app/pages/maintenance/maintenance.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/pages/maintenance/maintenance.component.spec.ts b/src/app/pages/maintenance/maintenance.component.spec.ts new file mode 100644 index 00000000..84541e72 --- /dev/null +++ b/src/app/pages/maintenance/maintenance.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MaintenanceComponent } from './maintenance.component'; + +describe('MaintenanceComponent', () => { + let component: MaintenanceComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ MaintenanceComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(MaintenanceComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/maintenance/maintenance.component.ts b/src/app/pages/maintenance/maintenance.component.ts new file mode 100644 index 00000000..a0265751 --- /dev/null +++ b/src/app/pages/maintenance/maintenance.component.ts @@ -0,0 +1,23 @@ +import { Component, OnInit } from '@angular/core'; +import { AppConfigService } from 'src/app/utils/services/config.service'; + +@Component({ + selector: 'app-maintenance', + templateUrl: './maintenance.component.html', + styleUrls: ['./maintenance.component.scss'] +}) +export class MaintenanceComponent implements OnInit { + + constructor(private appConfig: AppConfigService) { } + + ngOnInit(): void { + if (!this.appConfig.maintenanceMode) { + window.location.href = '/'; + } + } + + public get maintenanceMessage(): string { + return this.appConfig.maintenanceMessage; + } + +} diff --git a/src/app/utils/services/config.loader.ts b/src/app/utils/services/config.loader.ts index b1ece77e..6dea8e72 100644 --- a/src/app/utils/services/config.loader.ts +++ b/src/app/utils/services/config.loader.ts @@ -12,7 +12,7 @@ export class AppConfigLoader { // initialize logger private logger: Logger = LoggerManager.create('AppConfigService'); - constructor() {} + constructor() { } public onLoad: Observable = this.subject.asObservable(); @@ -72,6 +72,19 @@ export class AppConfigLoader { return !this.config['production']; } + public get maintenanceMode(): boolean { + try { + return this.config['maintenanceMode']; + } catch (e) { + this.logger.error('Unable to load configuration from server', e); + return false; + } + } + + public get maintenanceMessage(): string { + return this.config['maintenanceMessage']; + } + public get apiBaseUrl(): string { return this.config['apiBaseUrl']; } diff --git a/src/assets/icons/maintenance.png b/src/assets/icons/maintenance.png new file mode 100644 index 00000000..485af25c Binary files /dev/null and b/src/assets/icons/maintenance.png differ