@oneuptime/common 11.5.12 → 11.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/AIInsight.ts +30 -0
- package/Models/DatabaseModels/EnterpriseLicense.ts +21 -0
- package/Models/DatabaseModels/EnterpriseLicenseInstance.ts +25 -0
- package/Models/DatabaseModels/GlobalConfig.ts +76 -0
- package/Models/DatabaseModels/Project.ts +30 -0
- package/Models/DatabaseModels/TelemetryException.ts +128 -0
- package/Server/API/AIAgentDataAPI.ts +129 -5
- package/Server/API/AIInvestigationAPI.ts +87 -0
- package/Server/API/EnterpriseLicenseAPI.ts +57 -0
- package/Server/API/GlobalConfigAPI.ts +62 -1
- package/Server/EnvironmentConfig.ts +22 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784640000000-AddExceptionTriageAndPrivacyColumns.ts +56 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.ts +38 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Services/AIRunService.ts +8 -0
- package/Server/Services/AnalyticsDatabaseService.ts +48 -45
- package/Server/Services/DatabaseService.ts +66 -1
- package/Server/Services/TelemetryExceptionService.ts +320 -24
- package/Server/Services/WorkflowService.ts +100 -39
- package/Server/Types/Workflow/Components/Schedule.ts +57 -32
- package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +15 -4
- package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +15 -4
- package/Server/Utils/AI/SRE/Insights/InsightScanner.ts +222 -24
- package/Server/Utils/AI/SRE/Insights/InsightTriageRunner.ts +225 -9
- package/Server/Utils/AI/SRE/SubjectCodeFixRun.ts +39 -0
- package/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.ts +114 -0
- package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +59 -0
- package/Server/Utils/Express.ts +13 -0
- package/Server/Utils/Telemetry/ExceptionSanitizer.ts +222 -0
- package/Server/Utils/Telemetry/TelemetryFanInWriter.ts +799 -0
- package/Server/Utils/Telemetry/TelemetryWriterClient.ts +263 -0
- package/Server/Utils/Telemetry/TelemetryWriterServer.ts +240 -0
- package/Server/Utils/Telemetry/TelemetryWriterShedMetrics.ts +93 -0
- package/Tests/Server/Services/DatabaseServiceSanitizeUpdateData.test.ts +232 -0
- package/Tests/Server/Services/TelemetryExceptionCodeFixRun.test.ts +205 -1
- package/Tests/Server/Services/UpdateOneByIdKeepsRowId.test.ts +107 -0
- package/Tests/Server/Services/WorkflowService.test.ts +308 -0
- package/Tests/Server/Utils/AI/Insights/InsightScanner.test.ts +150 -141
- package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +277 -1
- package/Tests/Server/Utils/Attribution.test.ts +203 -0
- package/Tests/Server/Utils/ExceptionSanitizer.test.ts +70 -0
- package/Tests/Server/Utils/InsightTriageClassification.test.ts +57 -0
- package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +183 -0
- package/Tests/Server/Utils/Marketing/GoogleAds.test.ts +340 -0
- package/Tests/Server/Utils/Monitor/Criteria/ExceptionMonitorCriteria.test.ts +87 -0
- package/Tests/Server/Utils/Monitor/Criteria/LogMonitorCriteria.test.ts +165 -0
- package/Tests/Server/Utils/Monitor/Criteria/TraceMonitorCriteria.test.ts +86 -0
- package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +1565 -0
- package/Tests/Server/Utils/Telemetry/TelemetryWriterClient.test.ts +313 -0
- package/Tests/Server/Utils/Telemetry/TelemetryWriterServer.test.ts +270 -0
- package/Tests/Server/Utils/Telemetry/TelemetryWriterShedMetrics.test.ts +127 -0
- package/Tests/Server/Utils/TelemetryImprovementTaskTrigger.test.ts +141 -0
- package/Tests/Types/AI/CodeFixTaskType.test.ts +3 -0
- package/Tests/Types/Monitor/MonitorCriteriaMetricVariables.test.ts +228 -0
- package/Tests/Types/Monitor/MonitorStepDefaultTelemetryConfig.test.ts +169 -0
- package/Tests/Types/Monitor/MonitorStepExceptionMonitor.test.ts +289 -0
- package/Tests/Types/Monitor/MonitorStepLogMonitor.test.ts +231 -0
- package/Tests/Types/Monitor/MonitorStepMetricViewConfigUtil.test.ts +245 -0
- package/Tests/Types/Monitor/MonitorStepNetworkDeviceMonitor.test.ts +155 -0
- package/Tests/Types/Monitor/MonitorStepTelemetrySerialization.test.ts +141 -0
- package/Tests/Types/Monitor/MonitorStepTraceMonitor.test.ts +137 -0
- package/Tests/Types/Workspace/NotificationRules/NotificationRuleCondition.test.ts +311 -0
- package/Tests/UI/Components/ModelTableSelectFromColumns.test.ts +259 -0
- package/Tests/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.test.ts +461 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +2852 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +758 -0
- package/Tests/UI/Utils/NotificationMethodUtil.test.ts +564 -0
- package/Tests/Utils/Array.test.ts +156 -0
- package/Tests/Utils/CronTab.test.ts +199 -0
- package/Tests/Utils/ModelImportExport.test.ts +101 -0
- package/Tests/Utils/Number.test.ts +179 -0
- package/Tests/Utils/VersionUtil.test.ts +348 -0
- package/Tests/jest.setup.ts +1 -0
- package/Types/AI/CodeFixTaskContext.ts +6 -0
- package/Types/AI/CodeFixTaskType.ts +38 -1
- package/Types/AI/ExceptionAIClassification.ts +27 -0
- package/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.ts +6 -0
- package/Types/Log/LogScrubPatternType.ts +7 -0
- package/Types/Monitor/MonitorStep.ts +24 -4
- package/Types/Monitor/MonitorStepMetricViewConfigUtil.ts +105 -0
- package/Types/Trace/TraceScrubPatternType.ts +7 -0
- package/UI/Components/EditionLabel/EditionLabel.tsx +445 -12
- package/UI/Components/ModelTable/BaseModelTable.tsx +15 -46
- package/UI/Components/ModelTable/SelectFromColumns.ts +129 -0
- package/UI/Components/Workflow/ArgumentsForm.tsx +68 -30
- package/UI/Components/Workflow/CronScheduleField.tsx +411 -0
- package/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.ts +176 -0
- package/UI/Utils/NotificationMethodUtil.ts +310 -0
- package/Utils/CronTab.ts +823 -0
- package/Utils/ModelImportExport.ts +21 -6
- package/Utils/VersionUtil.ts +260 -0
- package/build/dist/Models/DatabaseModels/AIInsight.js +31 -0
- package/build/dist/Models/DatabaseModels/AIInsight.js.map +1 -1
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +22 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
- package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js +26 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +81 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +31 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TelemetryException.js +134 -0
- package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
- package/build/dist/Server/API/AIAgentDataAPI.js +97 -8
- package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +55 -0
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/EnterpriseLicenseAPI.js +47 -0
- package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
- package/build/dist/Server/API/GlobalConfigAPI.js +52 -1
- package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +17 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784640000000-AddExceptionTriageAndPrivacyColumns.js +33 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784640000000-AddExceptionTriageAndPrivacyColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AIRunService.js +9 -2
- package/build/dist/Server/Services/AIRunService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +44 -16
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +53 -1
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryExceptionService.js +263 -22
- package/build/dist/Server/Services/TelemetryExceptionService.js.map +1 -1
- package/build/dist/Server/Services/WorkflowService.js +80 -30
- package/build/dist/Server/Services/WorkflowService.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Schedule.js +44 -19
- package/build/dist/Server/Types/Workflow/Components/Schedule.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +12 -4
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +12 -4
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightScanner.js +193 -22
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightScanner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js +178 -9
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js +37 -0
- package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.js +98 -0
- package/build/dist/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.js.map +1 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js +24 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -0
- package/build/dist/Server/Utils/Express.js +12 -0
- package/build/dist/Server/Utils/Express.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js +149 -0
- package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js +496 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterClient.js +165 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterClient.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterServer.js +136 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterServer.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterShedMetrics.js +76 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterShedMetrics.js.map +1 -0
- package/build/dist/Types/AI/CodeFixTaskType.js +38 -1
- package/build/dist/Types/AI/CodeFixTaskType.js.map +1 -1
- package/build/dist/Types/AI/ExceptionAIClassification.js +28 -0
- package/build/dist/Types/AI/ExceptionAIClassification.js.map +1 -0
- package/build/dist/Types/Log/LogScrubPatternType.js +7 -0
- package/build/dist/Types/Log/LogScrubPatternType.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +20 -4
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js +73 -0
- package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js.map +1 -0
- package/build/dist/Types/Trace/TraceScrubPatternType.js +7 -0
- package/build/dist/Types/Trace/TraceScrubPatternType.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +261 -13
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +12 -37
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/SelectFromColumns.js +82 -0
- package/build/dist/UI/Components/ModelTable/SelectFromColumns.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +30 -6
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/CronScheduleField.js +209 -0
- package/build/dist/UI/Components/Workflow/CronScheduleField.js.map +1 -0
- package/build/dist/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.js +129 -0
- package/build/dist/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.js.map +1 -0
- package/build/dist/UI/Utils/NotificationMethodUtil.js +189 -0
- package/build/dist/UI/Utils/NotificationMethodUtil.js.map +1 -0
- package/build/dist/Utils/CronTab.js +609 -0
- package/build/dist/Utils/CronTab.js.map +1 -0
- package/build/dist/Utils/ModelImportExport.js +20 -6
- package/build/dist/Utils/ModelImportExport.js.map +1 -1
- package/build/dist/Utils/VersionUtil.js +193 -0
- package/build/dist/Utils/VersionUtil.js.map +1 -0
- package/package.json +1 -1
|
@@ -28,12 +28,22 @@ import {
|
|
|
28
28
|
} from "../../Config";
|
|
29
29
|
import Alert, { AlertType } from "../Alerts/Alert";
|
|
30
30
|
import EnterpriseLicenseInstanceSummary from "../../../Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary";
|
|
31
|
+
import VersionUtil from "../../../Utils/VersionUtil";
|
|
31
32
|
|
|
32
33
|
export interface ComponentProps {
|
|
33
34
|
className?: string | undefined;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
const ENTERPRISE_URL: string = "https://oneuptime.com/enterprise/demo";
|
|
38
|
+
|
|
39
|
+
/*
|
|
40
|
+
* Linked only for major upgrades. Points at GitHub rather than the docs site
|
|
41
|
+
* on this installation, because the guide for the version you are moving TO is
|
|
42
|
+
* the one you need, and this installation only ships the guide for the version
|
|
43
|
+
* it is already running.
|
|
44
|
+
*/
|
|
45
|
+
const UPGRADE_GUIDE_URL: string =
|
|
46
|
+
"https://github.com/OneUptime/oneuptime/blob/master/App/FeatureSet/Docs/Content/en/installation/upgrading.md";
|
|
37
47
|
const SALES_EMAIL: string = "sales@oneuptime.com";
|
|
38
48
|
const SALES_MAILTO_URL: string = "mailto:sales@oneuptime.com";
|
|
39
49
|
|
|
@@ -85,6 +95,10 @@ const parseLicenseInstances: ParseLicenseInstancesFunction = (
|
|
|
85
95
|
typeof instance["lastReportedAt"] === "string"
|
|
86
96
|
? instance["lastReportedAt"]
|
|
87
97
|
: null,
|
|
98
|
+
version:
|
|
99
|
+
typeof instance["version"] === "string" && instance["version"]
|
|
100
|
+
? instance["version"]
|
|
101
|
+
: null,
|
|
88
102
|
});
|
|
89
103
|
}
|
|
90
104
|
|
|
@@ -127,6 +141,28 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
127
141
|
Array<EnterpriseLicenseInstanceSummary>
|
|
128
142
|
>([]);
|
|
129
143
|
const [thisInstanceId, setThisInstanceId] = useState<string>("");
|
|
144
|
+
/*
|
|
145
|
+
* Version state comes from the server rather than APP_VERSION in the
|
|
146
|
+
* browser bundle: the frontend env var is empty in every build that was
|
|
147
|
+
* not produced by the release pipeline, which would silently render a
|
|
148
|
+
* blank version instead of an honest one.
|
|
149
|
+
*/
|
|
150
|
+
const [currentVersion, setCurrentVersion] = useState<string>("");
|
|
151
|
+
const [latestVersion, setLatestVersion] = useState<string>("");
|
|
152
|
+
const [latestVersionPublishedAt, setLatestVersionPublishedAt] =
|
|
153
|
+
useState<string>("");
|
|
154
|
+
const [latestVersionCheckedAt, setLatestVersionCheckedAt] =
|
|
155
|
+
useState<string>("");
|
|
156
|
+
const [isUpdateAvailable, setIsUpdateAvailable] = useState<boolean>(false);
|
|
157
|
+
const [isUpdateCheckDisabled, setIsUpdateCheckDisabled] =
|
|
158
|
+
useState<boolean>(false);
|
|
159
|
+
/*
|
|
160
|
+
* True when the validated license was issued for evaluation/testing rather
|
|
161
|
+
* than production. Purely informational — it drives the evaluation notice in
|
|
162
|
+
* the modal and a small tag on the pill, and never gates any functionality.
|
|
163
|
+
*/
|
|
164
|
+
const [isEvaluationLicense, setIsEvaluationLicense] =
|
|
165
|
+
useState<boolean>(false);
|
|
130
166
|
/*
|
|
131
167
|
* Validity as computed by the server. The server redacts the license
|
|
132
168
|
* token for signed-out visitors (e.g. on the login page), so the client
|
|
@@ -142,12 +178,13 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
142
178
|
return <></>;
|
|
143
179
|
}
|
|
144
180
|
|
|
181
|
+
/*
|
|
182
|
+
* Runs on Community Edition too. The license half of the response is empty
|
|
183
|
+
* there, but the version and update-check half is not — a community
|
|
184
|
+
* installation still wants to be told a newer OneUptime has been released.
|
|
185
|
+
*/
|
|
145
186
|
const fetchGlobalConfig: () => Promise<void> =
|
|
146
187
|
useCallback(async (): Promise<void> => {
|
|
147
|
-
if (!IS_ENTERPRISE_EDITION) {
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
188
|
setIsConfigLoading(true);
|
|
152
189
|
setConfigError("");
|
|
153
190
|
|
|
@@ -217,6 +254,30 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
217
254
|
? payload["licenseValid"]
|
|
218
255
|
: null,
|
|
219
256
|
);
|
|
257
|
+
setIsEvaluationLicense(payload["isEvaluationLicense"] === true);
|
|
258
|
+
|
|
259
|
+
setCurrentVersion(
|
|
260
|
+
typeof payload["currentVersion"] === "string"
|
|
261
|
+
? payload["currentVersion"]
|
|
262
|
+
: "",
|
|
263
|
+
);
|
|
264
|
+
setLatestVersion(
|
|
265
|
+
typeof payload["latestVersion"] === "string"
|
|
266
|
+
? payload["latestVersion"]
|
|
267
|
+
: "",
|
|
268
|
+
);
|
|
269
|
+
setLatestVersionPublishedAt(
|
|
270
|
+
typeof payload["latestVersionPublishedAt"] === "string"
|
|
271
|
+
? payload["latestVersionPublishedAt"]
|
|
272
|
+
: "",
|
|
273
|
+
);
|
|
274
|
+
setLatestVersionCheckedAt(
|
|
275
|
+
typeof payload["latestVersionCheckedAt"] === "string"
|
|
276
|
+
? payload["latestVersionCheckedAt"]
|
|
277
|
+
: "",
|
|
278
|
+
);
|
|
279
|
+
setIsUpdateAvailable(payload["isUpdateAvailable"] === true);
|
|
280
|
+
setIsUpdateCheckDisabled(payload["isUpdateCheckDisabled"] === true);
|
|
220
281
|
|
|
221
282
|
if (!licenseInputEditedRef.current) {
|
|
222
283
|
setLicenseKeyInput(configModel.enterpriseLicenseKey || "");
|
|
@@ -225,13 +286,30 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
225
286
|
setGlobalConfig(null);
|
|
226
287
|
setLicenseInstances([]);
|
|
227
288
|
setServerLicenseValid(null);
|
|
289
|
+
setIsEvaluationLicense(false);
|
|
290
|
+
setCurrentVersion("");
|
|
291
|
+
setLatestVersion("");
|
|
292
|
+
setLatestVersionPublishedAt("");
|
|
293
|
+
setLatestVersionCheckedAt("");
|
|
294
|
+
setIsUpdateAvailable(false);
|
|
295
|
+
setIsUpdateCheckDisabled(false);
|
|
228
296
|
setConfigError(API.getFriendlyMessage(err));
|
|
229
297
|
} finally {
|
|
230
298
|
setIsConfigLoading(false);
|
|
231
299
|
}
|
|
232
300
|
}, []);
|
|
233
301
|
|
|
302
|
+
/*
|
|
303
|
+
* Only Enterprise Edition needs this on mount — its pill reports license
|
|
304
|
+
* validity and seat pressure, which it cannot know without asking. The
|
|
305
|
+
* community pill is a static label, so it waits until the modal is actually
|
|
306
|
+
* opened rather than adding a request to every page load.
|
|
307
|
+
*/
|
|
234
308
|
useEffect(() => {
|
|
309
|
+
if (!IS_ENTERPRISE_EDITION) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
235
313
|
void fetchGlobalConfig();
|
|
236
314
|
}, [fetchGlobalConfig]);
|
|
237
315
|
|
|
@@ -279,6 +357,105 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
279
357
|
return expiresAt.toLocaleString();
|
|
280
358
|
}, [globalConfig?.enterpriseLicenseExpiresAt]);
|
|
281
359
|
|
|
360
|
+
/*
|
|
361
|
+
* True only for a build that reports a real semantic version. Dev builds and
|
|
362
|
+
* images assembled without APP_VERSION report "unknown", which is worth
|
|
363
|
+
* showing verbatim but must never be compared against a release.
|
|
364
|
+
*/
|
|
365
|
+
const hasComparableVersion: boolean = useMemo(() => {
|
|
366
|
+
return VersionUtil.isValid(currentVersion);
|
|
367
|
+
}, [currentVersion]);
|
|
368
|
+
|
|
369
|
+
const latestVersionPublishedAtText: string | null = useMemo(() => {
|
|
370
|
+
if (!latestVersionPublishedAt) {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const publishedAt: Date = OneUptimeDate.fromString(
|
|
375
|
+
latestVersionPublishedAt,
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
if (Number.isNaN(publishedAt.getTime())) {
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return publishedAt.toLocaleDateString();
|
|
383
|
+
}, [latestVersionPublishedAt]);
|
|
384
|
+
|
|
385
|
+
const latestVersionCheckedAtText: string | null = useMemo(() => {
|
|
386
|
+
if (!latestVersionCheckedAt) {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const checkedAt: Date = OneUptimeDate.fromString(latestVersionCheckedAt);
|
|
391
|
+
|
|
392
|
+
if (Number.isNaN(checkedAt.getTime())) {
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return checkedAt.toLocaleString();
|
|
397
|
+
}, [latestVersionCheckedAt]);
|
|
398
|
+
|
|
399
|
+
/*
|
|
400
|
+
* Only claim an installation is current once a release has actually been
|
|
401
|
+
* fetched to compare it against — an air-gapped install that has never
|
|
402
|
+
* reached GitHub knows nothing, and saying "up to date" there would be a
|
|
403
|
+
* guess dressed up as a fact.
|
|
404
|
+
*/
|
|
405
|
+
const isUpToDate: boolean = useMemo(() => {
|
|
406
|
+
return (
|
|
407
|
+
hasComparableVersion &&
|
|
408
|
+
VersionUtil.isValid(latestVersion) &&
|
|
409
|
+
!isUpdateAvailable
|
|
410
|
+
);
|
|
411
|
+
}, [hasComparableVersion, latestVersion, isUpdateAvailable]);
|
|
412
|
+
|
|
413
|
+
/*
|
|
414
|
+
* Computed client-side rather than served, because the same comparison has
|
|
415
|
+
* to run per-instance in the list below, where the server has no single
|
|
416
|
+
* "current version" to compare against.
|
|
417
|
+
*/
|
|
418
|
+
const isMajorUpgrade: boolean = useMemo(() => {
|
|
419
|
+
return VersionUtil.isMajorUpgrade({
|
|
420
|
+
currentVersion: currentVersion,
|
|
421
|
+
latestVersion: latestVersion,
|
|
422
|
+
});
|
|
423
|
+
}, [currentVersion, latestVersion]);
|
|
424
|
+
|
|
425
|
+
const updateAdvisoryText: string = useMemo(() => {
|
|
426
|
+
const released: string = latestVersionPublishedAtText
|
|
427
|
+
? `Released on ${latestVersionPublishedAtText}. `
|
|
428
|
+
: "";
|
|
429
|
+
|
|
430
|
+
if (isMajorUpgrade) {
|
|
431
|
+
return `${released}Major versions carry breaking changes and have to be applied one at a time, so check the upgrade guide before you start.`;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return `${released}Upgrading picks up the latest fixes and improvements.`;
|
|
435
|
+
}, [isMajorUpgrade, latestVersionPublishedAtText]);
|
|
436
|
+
|
|
437
|
+
/*
|
|
438
|
+
* Null renders no footer at all. Every branch here has to be true of this
|
|
439
|
+
* installation specifically — a line promising a daily check would be a lie
|
|
440
|
+
* on a build that cannot be compared, and a worse one where the check has
|
|
441
|
+
* been turned off.
|
|
442
|
+
*/
|
|
443
|
+
const updateFooterText: string | null = useMemo(() => {
|
|
444
|
+
if (isUpdateCheckDisabled) {
|
|
445
|
+
return "Update checks are turned off on this installation (DISABLE_UPDATE_CHECK), so it is not compared against OneUptime releases.";
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (!hasComparableVersion) {
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (!latestVersionCheckedAtText) {
|
|
453
|
+
return "This installation has not checked for updates yet. It checks OneUptime releases on GitHub once a day.";
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return `Checked for updates on ${latestVersionCheckedAtText}.`;
|
|
457
|
+
}, [isUpdateCheckDisabled, hasComparableVersion, latestVersionCheckedAtText]);
|
|
458
|
+
|
|
282
459
|
const userLimit: number | null = useMemo(() => {
|
|
283
460
|
return typeof globalConfig?.enterpriseLicenseUserLimit === "number"
|
|
284
461
|
? globalConfig.enterpriseLicenseUserLimit
|
|
@@ -619,7 +796,7 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
619
796
|
return "Validate your license key to activate Enterprise Edition.";
|
|
620
797
|
}
|
|
621
798
|
|
|
622
|
-
return "License, seat usage, and the instances covered by this key.";
|
|
799
|
+
return "License, version, seat usage, and the instances covered by this key.";
|
|
623
800
|
}, [isConfigLoading, licenseValid]);
|
|
624
801
|
|
|
625
802
|
/*
|
|
@@ -677,9 +854,7 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
677
854
|
setValidationError("");
|
|
678
855
|
setSuccessMessage("");
|
|
679
856
|
|
|
680
|
-
|
|
681
|
-
void fetchGlobalConfig();
|
|
682
|
-
}
|
|
857
|
+
void fetchGlobalConfig();
|
|
683
858
|
};
|
|
684
859
|
|
|
685
860
|
const closeDialog: () => void = () => {
|
|
@@ -825,9 +1000,34 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
825
1000
|
const showLicenseDetails: boolean =
|
|
826
1001
|
IS_ENTERPRISE_EDITION && !configError && !isConfigLoading && licenseValid;
|
|
827
1002
|
|
|
1003
|
+
/*
|
|
1004
|
+
* The evaluation notice rides along with the license details: it is only
|
|
1005
|
+
* meaningful once there is a valid license to describe, and it sits at the
|
|
1006
|
+
* top of the body as the first thing an evaluating customer sees.
|
|
1007
|
+
*/
|
|
1008
|
+
const showEvaluationNotice: boolean =
|
|
1009
|
+
showLicenseDetails && isEvaluationLicense;
|
|
1010
|
+
|
|
1011
|
+
/*
|
|
1012
|
+
* The pill tag is shown whenever a valid evaluation license is active, so an
|
|
1013
|
+
* admin sees "Evaluation" without opening the modal.
|
|
1014
|
+
*/
|
|
1015
|
+
const showEvaluationTag: boolean =
|
|
1016
|
+
IS_ENTERPRISE_EDITION && licenseValid && isEvaluationLicense;
|
|
1017
|
+
|
|
828
1018
|
const showEnterpriseFeatureList: boolean =
|
|
829
1019
|
IS_ENTERPRISE_EDITION && !configError && !isConfigLoading && !licenseValid;
|
|
830
1020
|
|
|
1021
|
+
/*
|
|
1022
|
+
* Gated on neither edition nor licenseValid: which build you run, and
|
|
1023
|
+
* whether a newer one exists, is a property of the installation rather than
|
|
1024
|
+
* of the license, and is useful precisely when the license needs sorting
|
|
1025
|
+
* out. The server returns currentVersion only to a signed-in user, so an
|
|
1026
|
+
* anonymous visitor on the login page falls out here.
|
|
1027
|
+
*/
|
|
1028
|
+
const showVersionCard: boolean =
|
|
1029
|
+
!configError && !isConfigLoading && Boolean(currentVersion);
|
|
1030
|
+
|
|
831
1031
|
/*
|
|
832
1032
|
* GlobalConfigAPI returns userLimit and currentUserCount to anonymous callers
|
|
833
1033
|
* because the same route serves the signed-out login page, and gates only
|
|
@@ -913,6 +1113,176 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
913
1113
|
? "text-[11px] text-amber-600 group-hover:text-amber-700"
|
|
914
1114
|
: "text-[11px] text-indigo-500 group-hover:text-indigo-600";
|
|
915
1115
|
|
|
1116
|
+
/*
|
|
1117
|
+
* Rendered in both the enterprise and the community branch of the modal:
|
|
1118
|
+
* the version and update state belong to the installation, not the license.
|
|
1119
|
+
*/
|
|
1120
|
+
const versionCardElement: ReactElement | null = showVersionCard ? (
|
|
1121
|
+
<section
|
|
1122
|
+
aria-labelledby="edition-version-heading"
|
|
1123
|
+
className={`overflow-hidden rounded-xl border ${
|
|
1124
|
+
isUpdateAvailable ? "border-amber-200" : "border-gray-200"
|
|
1125
|
+
} bg-white`}
|
|
1126
|
+
>
|
|
1127
|
+
<div className="flex items-center justify-between gap-4 p-5">
|
|
1128
|
+
<div className="min-w-0">
|
|
1129
|
+
<h4
|
|
1130
|
+
id="edition-version-heading"
|
|
1131
|
+
className="text-sm font-semibold text-gray-900"
|
|
1132
|
+
>
|
|
1133
|
+
This installation
|
|
1134
|
+
</h4>
|
|
1135
|
+
<p className="mt-1.5 flex items-baseline gap-2">
|
|
1136
|
+
<span
|
|
1137
|
+
className={`text-2xl font-semibold leading-none tabular-nums ${
|
|
1138
|
+
hasComparableVersion ? "text-gray-900" : "text-gray-400"
|
|
1139
|
+
}`}
|
|
1140
|
+
>
|
|
1141
|
+
{hasComparableVersion ? `v${currentVersion}` : currentVersion}
|
|
1142
|
+
</span>
|
|
1143
|
+
</p>
|
|
1144
|
+
{!hasComparableVersion && (
|
|
1145
|
+
<p className="mt-1.5 text-xs leading-relaxed text-gray-500">
|
|
1146
|
+
This build does not report a version number, so it cannot be
|
|
1147
|
+
compared against the latest OneUptime release.
|
|
1148
|
+
</p>
|
|
1149
|
+
)}
|
|
1150
|
+
</div>
|
|
1151
|
+
{(isUpdateAvailable || isUpToDate) && (
|
|
1152
|
+
<span
|
|
1153
|
+
className={`inline-flex shrink-0 items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-medium ${
|
|
1154
|
+
isUpdateAvailable
|
|
1155
|
+
? "bg-amber-100 text-amber-800"
|
|
1156
|
+
: "bg-emerald-50 text-emerald-700"
|
|
1157
|
+
}`}
|
|
1158
|
+
>
|
|
1159
|
+
{/*
|
|
1160
|
+
* Colour comes from className only — see the note on
|
|
1161
|
+
* the seat advisory icon below.
|
|
1162
|
+
*/}
|
|
1163
|
+
<Icon
|
|
1164
|
+
icon={
|
|
1165
|
+
isUpdateAvailable
|
|
1166
|
+
? IconProp.ArrowCircleUp
|
|
1167
|
+
: IconProp.CheckCircle
|
|
1168
|
+
}
|
|
1169
|
+
className={`h-3.5 w-3.5 shrink-0 ${
|
|
1170
|
+
isUpdateAvailable ? "text-amber-700" : "text-emerald-600"
|
|
1171
|
+
}`}
|
|
1172
|
+
/>
|
|
1173
|
+
{isUpdateAvailable ? "Update available" : "Up to date"}
|
|
1174
|
+
</span>
|
|
1175
|
+
)}
|
|
1176
|
+
</div>
|
|
1177
|
+
|
|
1178
|
+
{isUpdateAvailable && (
|
|
1179
|
+
<div
|
|
1180
|
+
role="status"
|
|
1181
|
+
className="border-t border-amber-200 bg-amber-50 px-5 py-4"
|
|
1182
|
+
>
|
|
1183
|
+
<h5 className="text-sm font-semibold text-amber-900">
|
|
1184
|
+
{isMajorUpgrade
|
|
1185
|
+
? `OneUptime v${latestVersion} is a major upgrade`
|
|
1186
|
+
: `OneUptime v${latestVersion} is available`}
|
|
1187
|
+
</h5>
|
|
1188
|
+
<p className="mt-1 text-xs leading-relaxed text-amber-800">
|
|
1189
|
+
{updateAdvisoryText}
|
|
1190
|
+
</p>
|
|
1191
|
+
{/*
|
|
1192
|
+
* Only a major upgrade gets a call to action. Minor and patch
|
|
1193
|
+
* releases are routine — a button on every one of them trains
|
|
1194
|
+
* administrators to dismiss the banner, and there is nothing to read
|
|
1195
|
+
* before taking them. A major has breaking changes and must be
|
|
1196
|
+
* applied one major at a time, so it earns the interruption.
|
|
1197
|
+
*/}
|
|
1198
|
+
{isMajorUpgrade && (
|
|
1199
|
+
<div className="mt-3">
|
|
1200
|
+
<a
|
|
1201
|
+
href={UPGRADE_GUIDE_URL}
|
|
1202
|
+
target="_blank"
|
|
1203
|
+
rel="noopener noreferrer"
|
|
1204
|
+
/*
|
|
1205
|
+
* Indigo, not amber: the seat advisory directly
|
|
1206
|
+
* below is an amber panel with an indigo primary
|
|
1207
|
+
* button, and two different accent colours for
|
|
1208
|
+
* the same role would read as two conventions.
|
|
1209
|
+
*/
|
|
1210
|
+
className="inline-flex items-center gap-1.5 rounded-md bg-indigo-600 px-3 py-1.5 text-xs font-medium text-white shadow-sm transition hover:bg-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-400 focus-visible:ring-offset-2"
|
|
1211
|
+
>
|
|
1212
|
+
Read the upgrade guide
|
|
1213
|
+
<Icon
|
|
1214
|
+
icon={IconProp.ExternalLink}
|
|
1215
|
+
className="h-3 w-3 shrink-0 text-white"
|
|
1216
|
+
/>
|
|
1217
|
+
</a>
|
|
1218
|
+
</div>
|
|
1219
|
+
)}
|
|
1220
|
+
</div>
|
|
1221
|
+
)}
|
|
1222
|
+
|
|
1223
|
+
{!isUpdateAvailable && updateFooterText && (
|
|
1224
|
+
<div className="border-t border-gray-100 px-5 py-3">
|
|
1225
|
+
<p className="text-xs text-gray-500">{updateFooterText}</p>
|
|
1226
|
+
</div>
|
|
1227
|
+
)}
|
|
1228
|
+
</section>
|
|
1229
|
+
) : null;
|
|
1230
|
+
|
|
1231
|
+
/*
|
|
1232
|
+
* The evaluation notice. Violet rather than amber or red: this is not a
|
|
1233
|
+
* warning about something wrong, it is a statement of what the license is
|
|
1234
|
+
* for. Amber is spoken for by seat pressure and available updates, so a
|
|
1235
|
+
* distinct hue keeps the two from being read as the same kind of message.
|
|
1236
|
+
*/
|
|
1237
|
+
const evaluationNoticeElement: ReactElement | null = showEvaluationNotice ? (
|
|
1238
|
+
<section
|
|
1239
|
+
aria-labelledby="edition-evaluation-heading"
|
|
1240
|
+
className="overflow-hidden rounded-xl border border-violet-200 bg-gradient-to-br from-violet-50 via-white to-white"
|
|
1241
|
+
>
|
|
1242
|
+
<div className="flex items-start gap-3.5 p-5">
|
|
1243
|
+
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-violet-100 ring-4 ring-violet-50">
|
|
1244
|
+
{/*
|
|
1245
|
+
* Colour comes from className only — never add a type prop here, for
|
|
1246
|
+
* the reason spelled out on the seat advisory icon below.
|
|
1247
|
+
*/}
|
|
1248
|
+
<Icon icon={IconProp.Beaker} className="h-5 w-5 text-violet-600" />
|
|
1249
|
+
</span>
|
|
1250
|
+
<div className="min-w-0 flex-1">
|
|
1251
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
1252
|
+
<h4
|
|
1253
|
+
id="edition-evaluation-heading"
|
|
1254
|
+
className="text-sm font-semibold text-violet-900"
|
|
1255
|
+
>
|
|
1256
|
+
Evaluation license
|
|
1257
|
+
</h4>
|
|
1258
|
+
<span className="inline-flex items-center rounded-full bg-violet-100 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-violet-700">
|
|
1259
|
+
Testing only
|
|
1260
|
+
</span>
|
|
1261
|
+
</div>
|
|
1262
|
+
<p className="mt-1.5 text-xs leading-relaxed text-violet-800">
|
|
1263
|
+
This key was issued for evaluation and testing. It is not licensed
|
|
1264
|
+
for production use — reach out whenever you are ready to go live and
|
|
1265
|
+
we will get a production license sorted for you.
|
|
1266
|
+
</p>
|
|
1267
|
+
<div className="mt-3">
|
|
1268
|
+
<a
|
|
1269
|
+
href={`${SALES_MAILTO_URL}?subject=${encodeURIComponent(
|
|
1270
|
+
"Moving our OneUptime evaluation to production",
|
|
1271
|
+
)}`}
|
|
1272
|
+
className="inline-flex items-center gap-1.5 rounded-md bg-violet-600 px-3 py-1.5 text-xs font-medium text-white shadow-sm transition hover:bg-violet-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-violet-400 focus-visible:ring-offset-2"
|
|
1273
|
+
>
|
|
1274
|
+
<Icon
|
|
1275
|
+
icon={IconProp.Email}
|
|
1276
|
+
className="h-3 w-3 shrink-0 text-white"
|
|
1277
|
+
/>
|
|
1278
|
+
Talk to sales about production
|
|
1279
|
+
</a>
|
|
1280
|
+
</div>
|
|
1281
|
+
</div>
|
|
1282
|
+
</div>
|
|
1283
|
+
</section>
|
|
1284
|
+
) : null;
|
|
1285
|
+
|
|
916
1286
|
return (
|
|
917
1287
|
<>
|
|
918
1288
|
<button
|
|
@@ -924,7 +1294,9 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
924
1294
|
* Name) so voice-control users can activate it by the words they see
|
|
925
1295
|
* ("{editionName}" and "{ctaLabel}", e.g. "Learn more").
|
|
926
1296
|
*/
|
|
927
|
-
aria-label={`${editionName}
|
|
1297
|
+
aria-label={`${editionName}${
|
|
1298
|
+
showEvaluationTag ? ", Evaluation" : ""
|
|
1299
|
+
}, ${ctaLabel}`}
|
|
928
1300
|
>
|
|
929
1301
|
{pillTone !== "normal" && (
|
|
930
1302
|
<Icon
|
|
@@ -942,6 +1314,11 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
942
1314
|
className={`h-2 w-2 rounded-full transition group-hover:scale-110 ${indicatorColor}`}
|
|
943
1315
|
></span>
|
|
944
1316
|
<span className="tracking-wide">{editionName}</span>
|
|
1317
|
+
{showEvaluationTag && (
|
|
1318
|
+
<span className="inline-flex items-center rounded-full bg-violet-100 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-violet-700">
|
|
1319
|
+
Evaluation
|
|
1320
|
+
</span>
|
|
1321
|
+
)}
|
|
945
1322
|
<span className={pillCtaTextClassName}>{ctaLabel}</span>
|
|
946
1323
|
</button>
|
|
947
1324
|
|
|
@@ -997,6 +1374,8 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
997
1374
|
</div>
|
|
998
1375
|
)}
|
|
999
1376
|
|
|
1377
|
+
{evaluationNoticeElement}
|
|
1378
|
+
|
|
1000
1379
|
{showLicenseDetails && (
|
|
1001
1380
|
<dl className="grid grid-cols-1 gap-px overflow-hidden rounded-xl border border-gray-200 bg-gray-200 sm:grid-cols-2">
|
|
1002
1381
|
<div className="min-w-0 bg-white px-4 py-3">
|
|
@@ -1021,6 +1400,8 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
1021
1400
|
</dl>
|
|
1022
1401
|
)}
|
|
1023
1402
|
|
|
1403
|
+
{versionCardElement}
|
|
1404
|
+
|
|
1024
1405
|
{showLicenseDetails && (
|
|
1025
1406
|
<section
|
|
1026
1407
|
aria-labelledby="edition-seats-heading"
|
|
@@ -1240,7 +1621,7 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
1240
1621
|
<p className="mt-1 text-xs leading-relaxed text-gray-500">
|
|
1241
1622
|
Use the same license key on every instance you deploy
|
|
1242
1623
|
(staging, production, and so on). Each instance reports
|
|
1243
|
-
its usage
|
|
1624
|
+
its usage and the version it runs once a day.
|
|
1244
1625
|
</p>
|
|
1245
1626
|
{licenseInstances.length > 1 && (
|
|
1246
1627
|
<p className="mt-1.5 text-xs leading-relaxed text-gray-500">
|
|
@@ -1257,15 +1638,47 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
1257
1638
|
Boolean(thisInstanceId) &&
|
|
1258
1639
|
instance.instanceId === thisInstanceId;
|
|
1259
1640
|
|
|
1641
|
+
/*
|
|
1642
|
+
* The list is a snapshot of what each instance last
|
|
1643
|
+
* reported, refreshed at most once a day. For the
|
|
1644
|
+
* instance serving this page the server just told us
|
|
1645
|
+
* what it is running right now, so prefer that —
|
|
1646
|
+
* otherwise this row contradicts the "This
|
|
1647
|
+
* installation" card above it for up to a day after
|
|
1648
|
+
* an upgrade. hasComparableVersion keeps a dev build
|
|
1649
|
+
* from rendering "vunknown".
|
|
1650
|
+
*/
|
|
1651
|
+
const displayVersion: string | null =
|
|
1652
|
+
isThisInstance && hasComparableVersion
|
|
1653
|
+
? currentVersion
|
|
1654
|
+
: instance.version;
|
|
1655
|
+
|
|
1656
|
+
/*
|
|
1657
|
+
* Per-instance, so an admin can see which of their
|
|
1658
|
+
* deployments are lagging rather than only the one
|
|
1659
|
+
* they happen to be signed in to.
|
|
1660
|
+
*/
|
|
1661
|
+
const isInstanceOutdated: boolean =
|
|
1662
|
+
VersionUtil.isUpdateAvailable({
|
|
1663
|
+
currentVersion: displayVersion,
|
|
1664
|
+
latestVersion: latestVersion,
|
|
1665
|
+
});
|
|
1666
|
+
|
|
1260
1667
|
return (
|
|
1261
1668
|
<li
|
|
1262
1669
|
key={instance.instanceId || index}
|
|
1263
1670
|
className="flex items-center justify-between gap-4 bg-white px-3 py-2.5 transition-colors hover:bg-gray-50"
|
|
1264
1671
|
>
|
|
1265
1672
|
<div className="min-w-0 flex-1">
|
|
1266
|
-
|
|
1673
|
+
{/*
|
|
1674
|
+
* Wraps rather than squeezing: both pills are
|
|
1675
|
+
* shrink-0, so without this a long hostname on
|
|
1676
|
+
* a narrow modal absorbs all the overflow and
|
|
1677
|
+
* truncates to a few characters.
|
|
1678
|
+
*/}
|
|
1679
|
+
<div className="flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1">
|
|
1267
1680
|
<span
|
|
1268
|
-
className="truncate text-sm font-medium text-gray-900"
|
|
1681
|
+
className="min-w-0 max-w-full truncate text-sm font-medium text-gray-900"
|
|
1269
1682
|
title={instance.host || "Unknown host"}
|
|
1270
1683
|
>
|
|
1271
1684
|
{instance.host || "Unknown host"}
|
|
@@ -1275,11 +1688,30 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
1275
1688
|
This instance
|
|
1276
1689
|
</span>
|
|
1277
1690
|
)}
|
|
1691
|
+
{displayVersion && (
|
|
1692
|
+
<span
|
|
1693
|
+
className={`shrink-0 rounded-full px-1.5 py-0.5 text-[10px] font-medium tabular-nums ${
|
|
1694
|
+
isInstanceOutdated
|
|
1695
|
+
? "bg-amber-50 text-amber-800"
|
|
1696
|
+
: "bg-gray-100 text-gray-600"
|
|
1697
|
+
}`}
|
|
1698
|
+
title={
|
|
1699
|
+
isInstanceOutdated
|
|
1700
|
+
? `Running v${displayVersion}. OneUptime v${latestVersion} is available.`
|
|
1701
|
+
: `Running v${displayVersion}.`
|
|
1702
|
+
}
|
|
1703
|
+
>
|
|
1704
|
+
v{displayVersion}
|
|
1705
|
+
</span>
|
|
1706
|
+
)}
|
|
1278
1707
|
</div>
|
|
1279
1708
|
<p className="mt-0.5 truncate text-xs text-gray-500">
|
|
1280
1709
|
{formatInstanceReportedAt(
|
|
1281
1710
|
instance.lastReportedAt,
|
|
1282
1711
|
)}
|
|
1712
|
+
{isInstanceOutdated
|
|
1713
|
+
? " Update available."
|
|
1714
|
+
: ""}
|
|
1283
1715
|
</p>
|
|
1284
1716
|
</div>
|
|
1285
1717
|
<div className="flex shrink-0 items-baseline justify-end gap-1">
|
|
@@ -1424,6 +1856,7 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
1424
1856
|
</>
|
|
1425
1857
|
) : (
|
|
1426
1858
|
<>
|
|
1859
|
+
{versionCardElement}
|
|
1427
1860
|
<p>
|
|
1428
1861
|
You are running the Community Edition of OneUptime. Here is a
|
|
1429
1862
|
quick comparison to help you decide if Enterprise is the right
|