From 5acb95e5a708b1738642e999926a737ed069053b Mon Sep 17 00:00:00 2001 From: nsemets Date: Tue, 6 Oct 2026 18:17:03 +0300 Subject: [PATCH] feat(maintenance): added maintenance to config and new text with link --- src/app/app.component.html | 4 +++ src/app/app.component.scss | 2 +- .../services/maintenance-mode.service.spec.ts | 27 +++++++++++++++++++ .../core/services/maintenance-mode.service.ts | 15 ++++++++++- src/app/shared/models/environment.model.ts | 1 + src/assets/config/template.json | 3 ++- src/assets/i18n/en.json | 1 + 7 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/app/app.component.html b/src/app/app.component.html index 3e72ea9b0..c642376d8 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -6,5 +6,9 @@

{{ 'maintenance.title' | translate }}

{{ 'maintenance.message' | translate }}

+

+ {{ 'maintenance.details' | translate }} + https://status.cos.io/ +

} diff --git a/src/app/app.component.scss b/src/app/app.component.scss index 62deb123b..da0b60fd8 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -1,6 +1,6 @@ .maintenance-overlay { position: fixed; inset: 0; - z-index: 2000; + z-index: 3100; background: var(--white); } diff --git a/src/app/core/services/maintenance-mode.service.spec.ts b/src/app/core/services/maintenance-mode.service.spec.ts index 39c988442..7c82286ec 100644 --- a/src/app/core/services/maintenance-mode.service.spec.ts +++ b/src/app/core/services/maintenance-mode.service.spec.ts @@ -3,6 +3,7 @@ import { TestBed } from '@angular/core/testing'; import { BYPASS_ERROR_INTERCEPTOR } from '@core/interceptors/error-interceptor.tokens'; import { MaintenanceResponse } from '@core/models/maintenance-response.model'; +import { ENVIRONMENT } from '@core/provider/environment.provider'; import { provideOSFCore, provideOSFHttp } from '@testing/osf.testing.provider'; @@ -116,4 +117,30 @@ describe('MaintenanceModeService', () => { vi.advanceTimersByTime(pollIntervalMs); httpMock.expectNone(apiUrl); }); + + describe('when maintenance is forced by config', () => { + beforeEach(() => { + TestBed.inject(ENVIRONMENT).maintenance = true; + }); + + it('should activate on checkOnce without requesting /v2/', () => { + service.checkOnce(); + httpMock.expectNone(apiUrl); + expect(service.isActive()).toBe(true); + }); + + it('should not start polling on activate', () => { + vi.useFakeTimers(); + service.activate(); + vi.advanceTimersByTime(pollIntervalMs); + httpMock.expectNone(apiUrl); + expect(service.isActive()).toBe(true); + }); + + it('should stay active on deactivate', () => { + service.checkOnce(); + service.deactivate(); + expect(service.isActive()).toBe(true); + }); + }); }); diff --git a/src/app/core/services/maintenance-mode.service.ts b/src/app/core/services/maintenance-mode.service.ts index 6d89c27f1..bb92be21c 100644 --- a/src/app/core/services/maintenance-mode.service.ts +++ b/src/app/core/services/maintenance-mode.service.ts @@ -24,11 +24,20 @@ export class MaintenanceModeService implements OnDestroy { readonly isActive = this._isActive.asReadonly(); + private get isForcedByConfig(): boolean { + return this.environment.maintenance === true; + } + /** * Check for maintenance mode upon application startup. * If the application is in maintenance mode, activate the service and start polling for when maintenance mode ends. */ checkOnce(): void { + if (this.isForcedByConfig) { + this._isActive.set(true); + return; + } + this.checkMaintenanceStatus().subscribe((status) => { if (status === MaintenanceStatus.Active) { this.activate(); @@ -38,13 +47,17 @@ export class MaintenanceModeService implements OnDestroy { activate(): void { this._isActive.set(true); - if (this.pollingSubscription) { + if (this.isForcedByConfig || this.pollingSubscription) { return; } this.startPolling(); } deactivate(): void { + if (this.isForcedByConfig) { + return; + } + this._isActive.set(false); this.stopPolling(); } diff --git a/src/app/shared/models/environment.model.ts b/src/app/shared/models/environment.model.ts index 184fe4ce4..fcc33126d 100644 --- a/src/app/shared/models/environment.model.ts +++ b/src/app/shared/models/environment.model.ts @@ -65,4 +65,5 @@ export interface EnvironmentModel { */ googleFilePickerAppId: number; throttleToken: string; + maintenance?: boolean; } diff --git a/src/assets/config/template.json b/src/assets/config/template.json index 826cb39c4..f74a71179 100644 --- a/src/assets/config/template.json +++ b/src/assets/config/template.json @@ -27,5 +27,6 @@ "newRelicLoaderConfigTrustKey": "", "newRelicLoaderConfigAgentID": "", "newRelicLoaderConfigLicenseKey": "", - "newRelicLoaderConfigApplicationID": "" + "newRelicLoaderConfigApplicationID": "", + "maintenance": false } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 113afb819..26437b779 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -2901,6 +2901,7 @@ } }, "maintenance": { + "details": "For more details, please visit", "message": "Please come back later.", "title": "The OSF is currently down for scheduled maintenance." },