diff --git a/frontend/src/component/App.tsx b/frontend/src/component/App.tsx index 4b5d47d979..5f23afba35 100644 --- a/frontend/src/component/App.tsx +++ b/frontend/src/component/App.tsx @@ -23,6 +23,7 @@ import { LicenseBanner } from './banners/internalBanners/LicenseBanner'; import { Demo } from './demo/Demo'; import { LoginRedirect } from './common/LoginRedirect/LoginRedirect'; import { SecurityBanner } from './banners/internalBanners/SecurityBanner'; +import { MonthsOldVersionBanner } from './banners/internalBanners/MonthsOldVersionBanner'; const StyledContainer = styled('div')(() => ({ '& ul': { @@ -67,6 +68,7 @@ export const App = () => { /> + diff --git a/frontend/src/component/banners/internalBanners/MonthsOldVersionBanner.tsx b/frontend/src/component/banners/internalBanners/MonthsOldVersionBanner.tsx new file mode 100644 index 0000000000..d311cb9086 --- /dev/null +++ b/frontend/src/component/banners/internalBanners/MonthsOldVersionBanner.tsx @@ -0,0 +1,33 @@ +import { Banner } from '../Banner/Banner'; +import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; +import { parseValidDate } from 'component/common/util'; +import { differenceInMonths } from 'date-fns'; + +export const MonthsOldVersionBanner = () => { + const { + uiConfig: { versionInfo }, + } = useUiConfig(); + + if (!versionInfo?.buildDate) return null; + + const buildDate = parseValidDate(versionInfo.buildDate); + + if (!buildDate) return null; + + const monthsOld = differenceInMonths(new Date(), new Date(buildDate)); + + const isOldBuild = monthsOld >= 3; + + if (!isOldBuild) return null; + + return ( + + ); +}; diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index 585b691926..7a885e32f7 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -102,6 +102,7 @@ export interface IVersionInfo { isLatest: boolean; latest: Partial; current: IVersion; + buildDate?: string; } export interface IVersion { diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap index 96e349b91e..d7b451441c 100644 --- a/src/lib/__snapshots__/create-config.test.ts.snap +++ b/src/lib/__snapshots__/create-config.test.ts.snap @@ -23,6 +23,7 @@ exports[`should create default config 1`] = ` "initialAdminUser": undefined, "type": "open-source", }, + "buildDate": undefined, "clientFeatureCaching": { "enabled": true, "maxAge": 3600000, diff --git a/src/lib/create-config.ts b/src/lib/create-config.ts index ecd1d4eb69..89743f784b 100644 --- a/src/lib/create-config.ts +++ b/src/lib/create-config.ts @@ -795,6 +795,7 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig { dailyMetricsStorageDays, openAIAPIKey, userInactivityThresholdInDays, + buildDate: process.env.BUILD_DATE, }; } diff --git a/src/lib/openapi/spec/version-schema.ts b/src/lib/openapi/spec/version-schema.ts index a4695769c2..285f96e5d4 100644 --- a/src/lib/openapi/spec/version-schema.ts +++ b/src/lib/openapi/spec/version-schema.ts @@ -56,6 +56,14 @@ export const versionSchema = { description: 'The instance identifier of the Unleash instance', example: '0d652a82-43db-4144-8e02-864b0b030710', }, + buildDate: { + description: + 'The date and time of when this Unleash instance version was built', + type: 'string', + format: 'date-time', + nullable: true, + example: '2023-06-30T11:41:00.123Z', + }, }, components: {}, } as const; diff --git a/src/lib/services/version-service.ts b/src/lib/services/version-service.ts index 798e9406a2..5bb0206340 100644 --- a/src/lib/services/version-service.ts +++ b/src/lib/services/version-service.ts @@ -15,6 +15,7 @@ export interface IVersionHolder { latest: Partial; isLatest: boolean; instanceId: string; + buildDate?: string; } export interface IVersionResponse { @@ -72,6 +73,8 @@ export default class VersionService { private timer: NodeJS.Timeout; + private readonly buildDate?: string; + constructor( { settingStore }: Pick, { @@ -79,9 +82,14 @@ export default class VersionService { versionCheck, enterpriseVersion, telemetry, + buildDate, }: Pick< IUnleashConfig, - 'getLogger' | 'versionCheck' | 'enterpriseVersion' | 'telemetry' + | 'getLogger' + | 'versionCheck' + | 'enterpriseVersion' + | 'telemetry' + | 'buildDate' >, ) { this.logger = getLogger('lib/services/version-service.js'); @@ -94,6 +102,7 @@ export default class VersionService { this.telemetryEnabled = telemetry; this.versionCheckUrl = versionCheck.url; this.isLatest = true; + this.buildDate = buildDate; } private async readInstanceId(): Promise { @@ -164,6 +173,7 @@ export default class VersionService { latest: this.latest || {}, isLatest: this.isLatest, instanceId: instanceId || 'unresolved-instance-id', + buildDate: this.buildDate, }; } } diff --git a/src/lib/types/option.ts b/src/lib/types/option.ts index 28471ca62f..fffe00539d 100644 --- a/src/lib/types/option.ts +++ b/src/lib/types/option.ts @@ -286,4 +286,5 @@ export interface IUnleashConfig { feedbackUriPath?: string; openAIAPIKey?: string; userInactivityThresholdInDays: number; + buildDate?: string; }