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;
}