Refactor code for improved readability and consistency

- Simplified arrow function syntax in MasterPassword.tsx and DashboardAPI.ts
- Consolidated logger.debug statements in PublicDashboard.ts and DashboardDomainAPI.ts
- Reformatted multi-line statements for better clarity in various files
- Updated migration files for consistent naming conventions and formatting
- Enhanced code structure in DashboardDomainService.ts and MonitorTelemetryMonitor.ts
- Incremented version number to 10.0.40
This commit is contained in:
Nawaz Dhandala
2026-03-26 16:41:11 +00:00
parent b5bf1d6dd1
commit d9c7259356
48 changed files with 474 additions and 420 deletions

View File

@@ -2075,10 +2075,7 @@ const BaseAPIFeatureSet: FeatureSet = {
new StatusPageDomainAPI().getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new DashboardAPI().getRouter(),
);
app.use(`/${APP_NAME.toLocaleLowerCase()}`, new DashboardAPI().getRouter());
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,

View File

@@ -22,16 +22,30 @@ const BlankCanvasElement: FunctionComponent<ComponentProps> = (
if (!props.isEditMode && props.dashboardViewConfig.components.length === 0) {
return (
<div className="mx-3 mt-4 rounded-lg border border-dashed border-gray-200 bg-gray-50/50 text-center py-20 px-10">
<div className="mx-auto w-14 h-14 rounded-full bg-white border border-gray-200 flex items-center justify-center mb-4"
<div
className="mx-auto w-14 h-14 rounded-full bg-white border border-gray-200 flex items-center justify-center mb-4"
style={{ boxShadow: "0 1px 3px 0 rgba(0, 0, 0, 0.04)" }}
>
<svg className="w-6 h-6 text-gray-400" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 6A2.25 2.25 0 0 1 6 3.75h2.25A2.25 2.25 0 0 1 10.5 6v2.25a2.25 2.25 0 0 1-2.25 2.25H6a2.25 2.25 0 0 1-2.25-2.25V6ZM3.75 15.75A2.25 2.25 0 0 1 6 13.5h2.25a2.25 2.25 0 0 1 2.25 2.25V18a2.25 2.25 0 0 1-2.25 2.25H6A2.25 2.25 0 0 1 3.75 18v-2.25ZM13.5 6a2.25 2.25 0 0 1 2.25-2.25H18A2.25 2.25 0 0 1 20.25 6v2.25A2.25 2.25 0 0 1 18 10.5h-2.25a2.25 2.25 0 0 1-2.25-2.25V6ZM13.5 15.75a2.25 2.25 0 0 1 2.25-2.25H18a2.25 2.25 0 0 1 2.25 2.25V18A2.25 2.25 0 0 1 18 20.25h-2.25a2.25 2.25 0 0 1-2.25-2.25v-2.25Z" />
<svg
className="w-6 h-6 text-gray-400"
fill="none"
viewBox="0 0 24 24"
strokeWidth="1.5"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M3.75 6A2.25 2.25 0 0 1 6 3.75h2.25A2.25 2.25 0 0 1 10.5 6v2.25a2.25 2.25 0 0 1-2.25 2.25H6a2.25 2.25 0 0 1-2.25-2.25V6ZM3.75 15.75A2.25 2.25 0 0 1 6 13.5h2.25a2.25 2.25 0 0 1 2.25 2.25V18a2.25 2.25 0 0 1-2.25 2.25H6A2.25 2.25 0 0 1 3.75 18v-2.25ZM13.5 6a2.25 2.25 0 0 1 2.25-2.25H18A2.25 2.25 0 0 1 20.25 6v2.25A2.25 2.25 0 0 1 18 10.5h-2.25a2.25 2.25 0 0 1-2.25-2.25V6ZM13.5 15.75a2.25 2.25 0 0 1 2.25-2.25H18a2.25 2.25 0 0 1 2.25 2.25V18A2.25 2.25 0 0 1 18 20.25h-2.25a2.25 2.25 0 0 1-2.25-2.25v-2.25Z"
/>
</svg>
</div>
<h3 className="text-sm font-semibold text-gray-700 mb-1">No widgets yet</h3>
<h3 className="text-sm font-semibold text-gray-700 mb-1">
No widgets yet
</h3>
<p className="text-sm text-gray-400 max-w-sm mx-auto">
Click <strong className="text-gray-500">Edit</strong> to start adding charts, values, gauges, and more to this dashboard.
Click <strong className="text-gray-500">Edit</strong> to start adding
charts, values, gauges, and more to this dashboard.
</p>
</div>
);

View File

@@ -88,7 +88,8 @@ const ComponentSettingsSideOver: FunctionComponent<ComponentProps> = (
{component.componentType} Widget
</span>
<span className="text-xs text-gray-400">
{component.widthInDashboardUnits} x {component.heightInDashboardUnits} units
{component.widthInDashboardUnits} x{" "}
{component.heightInDashboardUnits} units
</span>
</div>

View File

@@ -77,7 +77,8 @@ const DashboardBaseComponentElement: FunctionComponent<ComponentProps> = (
}
if (props.isSelected && props.isEditMode) {
className += " !border-blue-400 ring-2 ring-blue-50 shadow-lg shadow-blue-100/50";
className +=
" !border-blue-400 ring-2 ring-blue-50 shadow-lg shadow-blue-100/50";
}
if (!props.isEditMode) {
@@ -386,7 +387,8 @@ const DashboardBaseComponentElement: FunctionComponent<ComponentProps> = (
widthOfComponent +
(SpaceBetweenUnitsInPx - 2) * (widthOfComponent - 1)
}px`,
boxShadow: "0 1px 3px 0 rgba(0, 0, 0, 0.04), 0 1px 2px -1px rgba(0, 0, 0, 0.03)",
boxShadow:
"0 1px 3px 0 rgba(0, 0, 0, 0.04), 0 1px 2px -1px rgba(0, 0, 0, 0.03)",
}}
key={component.componentId?.toString() || Math.random().toString()}
ref={dashboardComponentRef}

View File

@@ -171,9 +171,7 @@ const DashboardChartComponentElement: FunctionComponent<ComponentProps> = (
<Icon icon={IconProp.ChartBar} />
</div>
</div>
<p className="text-xs text-gray-400 text-center max-w-48">
{error}
</p>
<p className="text-xs text-gray-400 text-center max-w-48">{error}</p>
</div>
);
}

View File

@@ -21,8 +21,9 @@ const DashboardGaugeComponentElement: FunctionComponent<ComponentProps> = (
const [metricResults, setMetricResults] = React.useState<
Array<AggregatedResult>
>([]);
const [aggregationType, setAggregationType] =
React.useState<AggregationType>(AggregationType.Avg);
const [aggregationType, setAggregationType] = React.useState<AggregationType>(
AggregationType.Avg,
);
const [error, setError] = React.useState<string | null>(null);
const [isLoading, setIsLoading] = React.useState<boolean>(true);
@@ -132,8 +133,18 @@ const DashboardGaugeComponentElement: FunctionComponent<ComponentProps> = (
return (
<div className="flex flex-col items-center justify-center w-full h-full gap-1.5">
<div className="w-10 h-10 rounded-full bg-gray-50 flex items-center justify-center">
<svg className="w-5 h-5 text-gray-300" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="M10.5 6a7.5 7.5 0 1 0 7.5 7.5h-7.5V6Z" />
<svg
className="w-5 h-5 text-gray-300"
fill="none"
viewBox="0 0 24 24"
strokeWidth="1.5"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M10.5 6a7.5 7.5 0 1 0 7.5 7.5h-7.5V6Z"
/>
</svg>
</div>
<p className="text-xs text-gray-400 text-center max-w-40">{error}</p>
@@ -152,8 +163,18 @@ const DashboardGaugeComponentElement: FunctionComponent<ComponentProps> = (
return (
<div className="flex flex-col items-center justify-center w-full h-full gap-1.5">
<div className="w-10 h-10 rounded-full bg-emerald-50 flex items-center justify-center">
<svg className="w-5 h-5 text-emerald-300" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="M10.5 6a7.5 7.5 0 1 0 7.5 7.5h-7.5V6Z" />
<svg
className="w-5 h-5 text-emerald-300"
fill="none"
viewBox="0 0 24 24"
strokeWidth="1.5"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M10.5 6a7.5 7.5 0 1 0 7.5 7.5h-7.5V6Z"
/>
</svg>
</div>
<p className="text-xs font-medium text-gray-500">
@@ -211,10 +232,7 @@ const DashboardGaugeComponentElement: FunctionComponent<ComponentProps> = (
// Determine color based on thresholds
let gaugeColor: string = "#10b981"; // green
if (
criticalThreshold !== undefined &&
aggregatedValue >= criticalThreshold
) {
if (criticalThreshold !== undefined && aggregatedValue >= criticalThreshold) {
gaugeColor = "#ef4444"; // red
} else if (
warningThreshold !== undefined &&
@@ -322,7 +340,9 @@ const DashboardGaugeComponentElement: FunctionComponent<ComponentProps> = (
<stop offset="50%" stopColor={gaugeColor} stopOpacity="0.85" />
<stop offset="100%" stopColor={gaugeColor} stopOpacity="1" />
</linearGradient>
<filter id={`gauge-glow-${props.componentId?.toString() || "default"}`}>
<filter
id={`gauge-glow-${props.componentId?.toString() || "default"}`}
>
<feGaussianBlur stdDeviation="3" result="blur" />
<feMerge>
<feMergeNode in="blur" />
@@ -350,21 +370,19 @@ const DashboardGaugeComponentElement: FunctionComponent<ComponentProps> = (
/>
)}
{/* Threshold markers */}
{thresholdMarkers.map(
(marker: ThresholdMarker, index: number) => {
return (
<circle
key={index}
cx={marker.x}
cy={marker.y}
r={3}
fill={marker.color}
stroke="white"
strokeWidth={1.5}
/>
);
},
)}
{thresholdMarkers.map((marker: ThresholdMarker, index: number) => {
return (
<circle
key={index}
cx={marker.x}
cy={marker.y}
r={3}
fill={marker.color}
stroke="white"
strokeWidth={1.5}
/>
);
})}
{/* Needle tip dot at current position */}
{percentage > 0 && (
<circle
@@ -407,10 +425,16 @@ const DashboardGaugeComponentElement: FunctionComponent<ComponentProps> = (
className="flex justify-between w-full px-2 mt-0.5"
style={{ maxWidth: `${gaugeSize + 10}px` }}
>
<span className="text-gray-300 tabular-nums" style={{ fontSize: "10px" }}>
<span
className="text-gray-300 tabular-nums"
style={{ fontSize: "10px" }}
>
{minValue}
</span>
<span className="text-gray-300 tabular-nums" style={{ fontSize: "10px" }}>
<span
className="text-gray-300 tabular-nums"
style={{ fontSize: "10px" }}
>
{maxValue}
</span>
</div>

View File

@@ -14,7 +14,10 @@ import InBetween from "Common/Types/BaseDatabase/InBetween";
import SortOrder from "Common/Types/BaseDatabase/SortOrder";
import OneUptimeDate from "Common/Types/Date";
import Query from "Common/Types/BaseDatabase/Query";
import { queryStringToFilter, LogFilter } from "Common/Types/Log/LogQueryToFilter";
import {
queryStringToFilter,
LogFilter,
} from "Common/Types/Log/LogQueryToFilter";
export interface ComponentProps extends DashboardBaseComponentProps {
component: DashboardLogStreamComponent;
@@ -31,13 +34,21 @@ const getSeverityColor: (severity: string) => SeverityColor = (
): SeverityColor => {
const lower: string = severity.toLowerCase();
if (lower === "fatal") {
return { dot: "bg-purple-500", text: "text-purple-700", bg: "bg-purple-50" };
return {
dot: "bg-purple-500",
text: "text-purple-700",
bg: "bg-purple-50",
};
}
if (lower === "error") {
return { dot: "bg-red-500", text: "text-red-700", bg: "bg-red-50" };
}
if (lower === "warning") {
return { dot: "bg-yellow-500", text: "text-yellow-700", bg: "bg-yellow-50" };
return {
dot: "bg-yellow-500",
text: "text-yellow-700",
bg: "bg-yellow-50",
};
}
if (lower === "information") {
return { dot: "bg-blue-500", text: "text-blue-700", bg: "bg-blue-50" };
@@ -115,26 +126,25 @@ const DashboardLogStreamComponentElement: FunctionComponent<ComponentProps> = (
}
}
const listResult: ListResult<Log> =
await AnalyticsModelAPI.getList<Log>({
modelType: Log,
query: query,
limit: maxRows,
skip: 0,
select: {
time: true,
severityText: true,
body: true,
serviceId: true,
traceId: true,
spanId: true,
attributes: true,
},
sort: {
time: SortOrder.Descending,
},
requestOptions: {},
});
const listResult: ListResult<Log> = await AnalyticsModelAPI.getList<Log>({
modelType: Log,
query: query,
limit: maxRows,
skip: 0,
select: {
time: true,
severityText: true,
body: true,
serviceId: true,
traceId: true,
spanId: true,
attributes: true,
},
sort: {
time: SortOrder.Descending,
},
requestOptions: {},
});
setLogs(listResult.data);
setError("");

View File

@@ -124,7 +124,11 @@ const DashboardTableComponentElement: FunctionComponent<ComponentProps> = (
</div>
{Array.from({ length: 5 }).map((_: unknown, i: number) => {
return (
<div key={i} className="flex gap-4" style={{ opacity: 1 - i * 0.15 }}>
<div
key={i}
className="flex gap-4"
style={{ opacity: 1 - i * 0.15 }}
>
<div className="h-3 w-28 bg-gray-50 rounded"></div>
<div className="h-3 w-14 bg-gray-50 rounded ml-auto"></div>
</div>
@@ -185,20 +189,27 @@ const DashboardTableComponentElement: FunctionComponent<ComponentProps> = (
<table className="w-full text-sm text-left">
<thead className="text-xs text-gray-400 uppercase bg-gray-50/80 sticky top-0 border-b border-gray-100">
<tr>
<th className="px-4 py-2.5 font-medium tracking-wider" style={{ width: "45%" }}>
<th
className="px-4 py-2.5 font-medium tracking-wider"
style={{ width: "45%" }}
>
Timestamp
</th>
<th className="px-4 py-2.5 font-medium tracking-wider text-right" style={{ width: "25%" }}>
<th
className="px-4 py-2.5 font-medium tracking-wider text-right"
style={{ width: "25%" }}
>
Value
</th>
<th className="px-4 py-2.5 font-medium tracking-wider" style={{ width: "30%" }}>
</th>
<th
className="px-4 py-2.5 font-medium tracking-wider"
style={{ width: "30%" }}
></th>
</tr>
</thead>
<tbody className="divide-y divide-gray-50">
{displayData.map((item: AggregatedModel, index: number) => {
const roundedValue: number =
Math.round(item.value * 100) / 100;
const roundedValue: number = Math.round(item.value * 100) / 100;
const barWidth: number =
maxDataValue > 0
? (Math.abs(roundedValue) / maxDataValue) * 100

View File

@@ -19,7 +19,10 @@ const DashboardTextComponentElement: FunctionComponent<ComponentProps> = (
}
const textClassName: string = `flex items-center justify-center h-full text-gray-800 leading-snug ${props.component.arguments.isBold ? "font-semibold" : "font-normal"} ${props.component.arguments.isItalic ? "italic" : ""} ${props.component.arguments.isUnderline ? "underline decoration-gray-300 underline-offset-4" : ""}`;
const textHeightInxPx: number = Math.min(props.dashboardComponentHeightInPx * 0.35, 64);
const textHeightInxPx: number = Math.min(
props.dashboardComponentHeightInPx * 0.35,
64,
);
return (
<div className="h-full px-2">

View File

@@ -302,10 +302,7 @@ const DashboardValueComponentElement: FunctionComponent<ComponentProps> = (
const criticalThreshold: number | undefined =
props.component.arguments.criticalThreshold;
if (
criticalThreshold !== undefined &&
aggregatedValue >= criticalThreshold
) {
if (criticalThreshold !== undefined && aggregatedValue >= criticalThreshold) {
valueColorClass = "text-red-600";
bgStyle = {
background:
@@ -393,8 +390,7 @@ const DashboardValueComponentElement: FunctionComponent<ComponentProps> = (
<span
className="text-gray-400 font-normal"
style={{
fontSize:
valueHeightInPx > 0 ? `${valueHeightInPx * 0.3}px` : "",
fontSize: valueHeightInPx > 0 ? `${valueHeightInPx * 0.3}px` : "",
}}
>
{unit ? ` ${unit}` : ""}
@@ -407,7 +403,9 @@ const DashboardValueComponentElement: FunctionComponent<ComponentProps> = (
className={`flex items-center gap-0.5 mt-0.5 ${
trendDirection === "up" ? "text-emerald-500" : "text-red-500"
}`}
style={{ fontSize: `${Math.max(Math.min(titleHeightInPx, 12), 10)}px` }}
style={{
fontSize: `${Math.max(Math.min(titleHeightInPx, 12), 10)}px`,
}}
>
<span>{trendDirection === "up" ? "\u2191" : "\u2193"}</span>
<span className="font-medium tabular-nums">

View File

@@ -32,7 +32,9 @@ export interface ComponentProps {
onAutoRefreshIntervalChange: (interval: AutoRefreshInterval) => void;
isRefreshing?: boolean | undefined;
variables?: Array<DashboardVariable> | undefined;
onVariableValueChange?: ((variableId: string, value: string) => void) | undefined;
onVariableValueChange?:
| ((variableId: string, value: string) => void)
| undefined;
canResetZoom?: boolean | undefined;
onResetZoom?: (() => void) | undefined;
}
@@ -46,17 +48,18 @@ const DashboardToolbar: FunctionComponent<ComponentProps> = (
const isSaving: boolean = props.isSaving;
const hasComponents: boolean = !!(
const hasComponents: boolean = Boolean(
props.dashboardViewConfig &&
props.dashboardViewConfig.components &&
props.dashboardViewConfig.components.length > 0
props.dashboardViewConfig.components &&
props.dashboardViewConfig.components.length > 0,
);
return (
<div
className="mx-3 mt-3 mb-2 rounded-lg bg-white border border-gray-200"
style={{
boxShadow: "0 1px 3px 0 rgba(0, 0, 0, 0.05), 0 1px 2px -1px rgba(0, 0, 0, 0.04)",
boxShadow:
"0 1px 3px 0 rgba(0, 0, 0, 0.05), 0 1px 2px -1px rgba(0, 0, 0, 0.04)",
}}
>
{/* Accent top bar */}
@@ -82,7 +85,8 @@ const DashboardToolbar: FunctionComponent<ComponentProps> = (
)}
{hasComponents && !isEditMode && (
<span className="text-xs text-gray-400 tabular-nums">
{props.dashboardViewConfig.components.length} widget{props.dashboardViewConfig.components.length !== 1 ? "s" : ""}
{props.dashboardViewConfig.components.length} widget
{props.dashboardViewConfig.components.length !== 1 ? "s" : ""}
</span>
)}
{/* Refreshing indicator */}

View File

@@ -88,7 +88,11 @@ const CriteriaFilterElement: FunctionComponent<ComponentProps> = (
// Auto-select MetricValue for metric-only monitor types (Kubernetes, Metrics)
useEffect(() => {
if (isMetricOnly && criteriaFilter && criteriaFilter.checkOn !== CheckOn.MetricValue) {
if (
isMetricOnly &&
criteriaFilter &&
criteriaFilter.checkOn !== CheckOn.MetricValue
) {
props.onChange?.({
...criteriaFilter,
checkOn: CheckOn.MetricValue,
@@ -194,24 +198,26 @@ const CriteriaFilterElement: FunctionComponent<ComponentProps> = (
<div className="rounded-md p-2 bg-gray-50 my-5 border-gray-200 border-solid border-2">
{/* Hide Filter Type dropdown for metric-only monitors since MetricValue is the only option */}
{!isMetricOnly && (
<div className="">
<FieldLabelElement title="Filter Type" />
<Dropdown
value={checkOnOptions.find((i: DropdownOption) => {
return i.value === criteriaFilter?.checkOn;
})}
options={checkOnOptions}
onChange={(value: DropdownValue | Array<DropdownValue> | null) => {
props.onChange?.({
checkOn: value?.toString() as CheckOn,
filterType: undefined,
value: undefined,
evaluateOverTime: false,
evaluateOverTimeOptions: undefined,
});
}}
/>
</div>
<div className="">
<FieldLabelElement title="Filter Type" />
<Dropdown
value={checkOnOptions.find((i: DropdownOption) => {
return i.value === criteriaFilter?.checkOn;
})}
options={checkOnOptions}
onChange={(
value: DropdownValue | Array<DropdownValue> | null,
) => {
props.onChange?.({
checkOn: value?.toString() as CheckOn,
filterType: undefined,
value: undefined,
evaluateOverTime: false,
evaluateOverTimeOptions: undefined,
});
}}
/>
</div>
)}
{criteriaFilter?.checkOn &&
@@ -239,7 +245,11 @@ const CriteriaFilterElement: FunctionComponent<ComponentProps> = (
<div className="mt-1">
<FieldLabelElement
title={isMetricOnly ? "Metric" : "Select Metric Variable"}
description={isMetricOnly ? "Which metric query should this alert rule check?" : undefined}
description={
isMetricOnly
? "Which metric query should this alert rule check?"
: undefined
}
/>
<Dropdown
value={selectedMetricVariableOption}
@@ -264,7 +274,11 @@ const CriteriaFilterElement: FunctionComponent<ComponentProps> = (
<div className="mt-1">
<FieldLabelElement
title={isMetricOnly ? "Aggregation" : "Select Aggregation"}
description={isMetricOnly ? "How to combine multiple data points (e.g. Average, Max, Min)." : undefined}
description={
isMetricOnly
? "How to combine multiple data points (e.g. Average, Max, Min)."
: undefined
}
/>
<Dropdown
value={metricAggregationValue}
@@ -385,7 +399,9 @@ const CriteriaFilterElement: FunctionComponent<ComponentProps> = (
<div className="mt-1">
<FieldLabelElement
title={isMetricOnly ? "Condition" : "Filter Condition"}
description={isMetricOnly ? "When should this alert trigger?" : undefined}
description={
isMetricOnly ? "When should this alert trigger?" : undefined
}
/>
<Dropdown
value={filterConditionValue}
@@ -415,7 +431,9 @@ const CriteriaFilterElement: FunctionComponent<ComponentProps> = (
<div className="mt-1">
<FieldLabelElement
title={isMetricOnly ? "Threshold" : "Value"}
description={isMetricOnly ? "The value to compare against." : undefined}
description={
isMetricOnly ? "The value to compare against." : undefined
}
/>
<Input
placeholder={valuePlaceholder}

View File

@@ -221,8 +221,10 @@ const KubernetesMonitorStepForm: FunctionComponent<ComponentProps> = (
const clusterIdentifier: string =
monitorStepKubernetesMonitor.clusterIdentifier;
// Get a dummy monitor step from the template to extract the kubernetes config
// Build even without a cluster so the metricViewConfig is populated for the METRIC dropdown
/*
* Get a dummy monitor step from the template to extract the kubernetes config
* Build even without a cluster so the metricViewConfig is populated for the METRIC dropdown
*/
const dummyStep: MonitorStep = template.getMonitorStep({
clusterIdentifier: clusterIdentifier || "",
onlineMonitorStatusId: ObjectID.generate(),

View File

@@ -248,12 +248,14 @@ const MonitorCriteriaInstanceElement: FunctionComponent<ComponentProps> = (
{/* Filters Section - Collapsible */}
<CollapsibleSection
title={
props.monitorType === MonitorType.Kubernetes || props.monitorType === MonitorType.Metrics
props.monitorType === MonitorType.Kubernetes ||
props.monitorType === MonitorType.Metrics
? "Alert Rules"
: "Filters"
}
description={
props.monitorType === MonitorType.Kubernetes || props.monitorType === MonitorType.Metrics
props.monitorType === MonitorType.Kubernetes ||
props.monitorType === MonitorType.Metrics
? "Define when this alert should trigger based on metric values."
: "Add criteria for different monitor properties."
}
@@ -266,12 +268,14 @@ const MonitorCriteriaInstanceElement: FunctionComponent<ComponentProps> = (
<div className="mb-3">
<FieldLabelElement
title={
props.monitorType === MonitorType.Kubernetes || props.monitorType === MonitorType.Metrics
props.monitorType === MonitorType.Kubernetes ||
props.monitorType === MonitorType.Metrics
? "Match Condition"
: "Filter Condition"
}
description={
props.monitorType === MonitorType.Kubernetes || props.monitorType === MonitorType.Metrics
props.monitorType === MonitorType.Kubernetes ||
props.monitorType === MonitorType.Metrics
? "Should all rules match, or just any one of them?"
: "Select All if you want all the criteria to be met. Select any if you like any criteria to be met."
}

View File

@@ -494,7 +494,7 @@ const WorkspaceSummaryTable: FunctionComponent<ComponentProps> = (
},
title: "What to Include",
description:
"Choose which sections appear in the summary. Select \"All\" to include everything, or pick specific sections.",
'Choose which sections appear in the summary. Select "All" to include everything, or pick specific sections.',
fieldType: FormFieldSchemaType.CustomComponent,
required: false,
stepId: "content",
@@ -503,8 +503,9 @@ const WorkspaceSummaryTable: FunctionComponent<ComponentProps> = (
elementProps: CustomElementProps,
): ReactElement => {
const currentItems: Array<WorkspaceNotificationSummaryItem> =
(value.summaryItems as Array<WorkspaceNotificationSummaryItem>) ||
[WorkspaceNotificationSummaryItem.All];
(value.summaryItems as Array<WorkspaceNotificationSummaryItem>) || [
WorkspaceNotificationSummaryItem.All,
];
const isAllSelected: boolean = currentItems.includes(
WorkspaceNotificationSummaryItem.All,
@@ -542,9 +543,7 @@ const WorkspaceSummaryTable: FunctionComponent<ComponentProps> = (
key={item}
title={item}
disabled={isAllSelected}
value={
isAllSelected || currentItems.includes(item)
}
value={isAllSelected || currentItems.includes(item)}
onChange={(checked: boolean) => {
if (elementProps.onChange) {
let newItems: Array<WorkspaceNotificationSummaryItem> =

View File

@@ -42,15 +42,13 @@ const DashboardCustomDomains: FunctionComponent<PageComponentProps> = (
const [selectedDashboardDomain, setSelectedDashboardDomain] =
useState<DashboardDomain | null>(null);
const [verifyCnameLoading, setVerifyCnameLoading] =
useState<boolean>(false);
const [verifyCnameLoading, setVerifyCnameLoading] = useState<boolean>(false);
const [orderSslLoading, setOrderSslLoading] = useState<boolean>(false);
const [error, setError] = useState<string>("");
const [showOrderSSLModal, setShowOrderSSLModal] =
useState<boolean>(false);
const [showOrderSSLModal, setShowOrderSSLModal] = useState<boolean>(false);
return (
<Fragment>
@@ -72,9 +70,7 @@ const DashboardCustomDomains: FunctionComponent<PageComponentProps> = (
description: `Important: Please add a CNAME record pointing to ${DashboardCNameRecord} for these domains for this to work.`,
}}
refreshToggle={refreshToggle}
onBeforeCreate={(
item: DashboardDomain,
): Promise<DashboardDomain> => {
onBeforeCreate={(item: DashboardDomain): Promise<DashboardDomain> => {
if (!props.currentProject || !props.currentProject._id) {
throw new BadDataException("Project ID cannot be null");
}
@@ -272,9 +268,7 @@ const DashboardCustomDomains: FunctionComponent<PageComponentProps> = (
if (!item.isCnameVerified) {
return (
<span>
<span className="font-semibold">
Action Required:
</span>{" "}
<span className="font-semibold">Action Required:</span>{" "}
Please add your CNAME record.
</span>
);
@@ -283,8 +277,8 @@ const DashboardCustomDomains: FunctionComponent<PageComponentProps> = (
if (item.isCustomCertificate) {
return (
<span>
No action is required. Please allow 30 minutes for
the certificate to be provisioned.
No action is required. Please allow 30 minutes for the
certificate to be provisioned.
</span>
);
}
@@ -292,9 +286,7 @@ const DashboardCustomDomains: FunctionComponent<PageComponentProps> = (
if (!item.isSslOrdered) {
return (
<span>
<span className="font-semibold">
Action Required:
</span>{" "}
<span className="font-semibold">Action Required:</span>{" "}
Please order SSL certificate.
</span>
);
@@ -304,16 +296,16 @@ const DashboardCustomDomains: FunctionComponent<PageComponentProps> = (
return (
<span>
No action is required. This SSL certificate will be
provisioned in 1 hour. If this does not happen.
Please contact support.
provisioned in 1 hour. If this does not happen. Please
contact support.
</span>
);
}
return (
<span>
Certificate Provisioned. We will automatically renew
this certificate. No action required.{" "}
Certificate Provisioned. We will automatically renew this
certificate. No action required.{" "}
</span>
);
},
@@ -328,8 +320,8 @@ const DashboardCustomDomains: FunctionComponent<PageComponentProps> = (
DashboardCNameRecord ? (
<div>
<span>
Please add CNAME record to your domain. Details of
the CNAME records are:
Please add CNAME record to your domain. Details of the CNAME
records are:
</span>
<br />
<br />
@@ -356,14 +348,13 @@ const DashboardCustomDomains: FunctionComponent<PageComponentProps> = (
) : (
<div>
<span>
Custom Domains not enabled for this OneUptime
installation. Please contact your server admin to
enable this feature. To enable this feature, if you
are using Docker compose, the
<b>DASHBOARD_CNAME_RECORD</b> environment variable
must be set when starting the OneUptime cluster. If
you are using Helm and Kubernetes then set
dashboard.cnameRecord in the values.yaml file.
Custom Domains not enabled for this OneUptime installation.
Please contact your server admin to enable this feature. To
enable this feature, if you are using Docker compose, the
<b>DASHBOARD_CNAME_RECORD</b> environment variable must be
set when starting the OneUptime cluster. If you are using
Helm and Kubernetes then set dashboard.cnameRecord in the
values.yaml file.
</span>
</div>
)
@@ -381,13 +372,9 @@ const DashboardCustomDomains: FunctionComponent<PageComponentProps> = (
setVerifyCnameLoading(true);
setError("");
const response:
| HTTPResponse<JSONObject>
| HTTPErrorResponse =
const response: HTTPResponse<JSONObject> | HTTPErrorResponse =
await API.get<JSONObject>({
url: URL.fromString(
APP_API_URL.toString(),
).addRoute(
url: URL.fromString(APP_API_URL.toString()).addRoute(
`/${
new DashboardDomain().crudApiPath
}/verify-cname/${selectedDashboardDomain?.id?.toString()}`,
@@ -401,9 +388,7 @@ const DashboardCustomDomains: FunctionComponent<PageComponentProps> = (
}
setShowCnameModal(false);
setRefreshToggle(
OneUptimeDate.getCurrentDate().toString(),
);
setRefreshToggle(OneUptimeDate.getCurrentDate().toString());
setSelectedDashboardDomain(null);
} catch (err) {
setError(API.getFriendlyMessage(err));
@@ -420,18 +405,16 @@ const DashboardCustomDomains: FunctionComponent<PageComponentProps> = (
description={
DashboardCNameRecord ? (
<div>
Please click on the button below to order SSL for this
domain. We will use LetsEncrypt to order a certificate.
This process is secure and completely free. The
certificate takes 3 hours to provision after its been
ordered.
Please click on the button below to order SSL for this domain.
We will use LetsEncrypt to order a certificate. This process
is secure and completely free. The certificate takes 3 hours
to provision after its been ordered.
</div>
) : (
<div>
<span>
Custom Domains not enabled for this OneUptime
installation. Please contact your server admin to
enable this feature.
Custom Domains not enabled for this OneUptime installation.
Please contact your server admin to enable this feature.
</span>
</div>
)
@@ -449,13 +432,9 @@ const DashboardCustomDomains: FunctionComponent<PageComponentProps> = (
setOrderSslLoading(true);
setError("");
const response:
| HTTPResponse<JSONObject>
| HTTPErrorResponse =
const response: HTTPResponse<JSONObject> | HTTPErrorResponse =
await API.get<JSONObject>({
url: URL.fromString(
APP_API_URL.toString(),
).addRoute(
url: URL.fromString(APP_API_URL.toString()).addRoute(
`/${
new DashboardDomain().crudApiPath
}/order-ssl/${selectedDashboardDomain?.id?.toString()}`,
@@ -469,9 +448,7 @@ const DashboardCustomDomains: FunctionComponent<PageComponentProps> = (
}
setShowOrderSSLModal(false);
setRefreshToggle(
OneUptimeDate.getCurrentDate().toString(),
);
setRefreshToggle(OneUptimeDate.getCurrentDate().toString());
setSelectedDashboardDomain(null);
} catch (err) {
setError(API.getFriendlyMessage(err));

View File

@@ -59,9 +59,7 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
link={{
title: "Authentication",
to: RouteUtil.populateRouteParams(
RouteMap[
PageMap.DASHBOARD_VIEW_AUTHENTICATION_SETTINGS
] as Route,
RouteMap[PageMap.DASHBOARD_VIEW_AUTHENTICATION_SETTINGS] as Route,
{ modelId: props.modelId },
),
}}

View File

@@ -242,7 +242,8 @@ const StatusPageDelete: FunctionComponent<PageComponentProps> = (
title: "Show Status History Chart",
fieldType: FormFieldSchemaType.Toggle,
required: false,
description: "Show resource status history chart. The number of days is configured in Status Page Settings.",
description:
"Show resource status history chart. The number of days is configured in Status Page Settings.",
defaultValue: true,
stepId: "advanced",
},

View File

@@ -278,7 +278,9 @@ const Delete: FunctionComponent<PageComponentProps> = (): ReactElement => {
boxShadow: "0 1px 2px 0 rgba(0, 0, 0, 0.03)",
}}
>
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem" }}>
<div
style={{ display: "flex", alignItems: "center", gap: "0.75rem" }}
>
<div
style={{
display: "flex",

View File

@@ -450,8 +450,10 @@ const registerCustomDomainFallback: () => void = (): void => {
return next();
}
// Check if this custom domain belongs to a PublicDashboard.
// If so, serve the PublicDashboard SPA instead of StatusPage.
/*
* Check if this custom domain belongs to a PublicDashboard.
* If so, serve the PublicDashboard SPA instead of StatusPage.
*/
const requestHostname: string = getRequestHostname(req);
if (requestHostname && (await isDashboardDomain(requestHostname))) {

View File

@@ -163,8 +163,10 @@ router.post(
MarkdownContentType.Email,
);
// Send response immediately so the request doesn't timeout.
// Emails are sent in the background.
/*
* Send response immediately so the request doesn't timeout.
* Emails are sent in the background.
*/
Response.sendJsonObjectResponse(req, res, {
message:
"Broadcast email job has been started. Emails will be sent in the background.",

View File

@@ -4,7 +4,6 @@ import RouteParams from "./Utils/RouteParams";
import PublicDashboardUtil from "./Utils/PublicDashboard";
import { PUBLIC_DASHBOARD_API_URL } from "./Utils/Config";
import API from "./Utils/API";
import Route from "Common/Types/API/Route";
import URL from "Common/Types/API/URL";
import { JSONObject } from "Common/Types/JSON";
import ObjectID from "Common/Types/ObjectID";
@@ -42,13 +41,12 @@ const MasterPassword: React.LazyExoticComponent<
});
});
const NotFoundPage: React.LazyExoticComponent<
AllPagesModule["NotFoundPage"]
> = lazy(() => {
return import("./Pages/AllPages").then((m: AllPagesModule) => {
return { default: m.NotFoundPage };
const NotFoundPage: React.LazyExoticComponent<AllPagesModule["NotFoundPage"]> =
lazy(() => {
return import("./Pages/AllPages").then((m: AllPagesModule) => {
return { default: m.NotFoundPage };
});
});
});
const ForbiddenPage: React.LazyExoticComponent<
AllPagesModule["ForbiddenPage"]
@@ -123,8 +121,7 @@ const App: () => JSX.Element = () => {
});
if (response.data) {
const name: string =
(response.data["name"] as string) || "Dashboard";
const name: string = (response.data["name"] as string) || "Dashboard";
setDashboardName(name);
document.title = name;

View File

@@ -1,5 +1,7 @@
// Re-export the DashboardCanvas from the Dashboard FeatureSet
// The PublicDashboard app reuses the same canvas rendering logic
/*
* Re-export the DashboardCanvas from the Dashboard FeatureSet
* The PublicDashboard app reuses the same canvas rendering logic
*/
export {
default,
type ComponentProps,

View File

@@ -33,7 +33,6 @@ import MoreMenuItem from "Common/UI/Components/MoreMenu/MoreMenuItem";
import IconProp from "Common/Types/Icon/IconProp";
import Button, { ButtonStyleType } from "Common/UI/Components/Button/Button";
import DashboardVariableSelector from "./DashboardVariableSelector";
import DashboardBaseComponent from "Common/Types/Dashboard/DashboardComponents/DashboardBaseComponent";
import NavBar from "Common/UI/Components/Navbar/NavBar";
import NavBarItem from "Common/UI/Components/Navbar/NavBarItem";
import PageMap from "../../Utils/PageMap";
@@ -97,10 +96,10 @@ const DashboardViewPage: FunctionComponent<ComponentProps> = (
const [error, setError] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(true);
const hasComponents: boolean = !!(
const hasComponents: boolean = Boolean(
dashboardViewConfig &&
dashboardViewConfig.components &&
dashboardViewConfig.components.length > 0
dashboardViewConfig.components &&
dashboardViewConfig.components.length > 0,
);
const fetchDashboardViewConfig: PromiseVoidFunction =
@@ -159,7 +158,9 @@ const DashboardViewPage: FunctionComponent<ComponentProps> = (
// Auto-refresh
const triggerRefresh: () => void = useCallback(() => {
setIsRefreshing(true);
setRefreshTick((prev: number) => prev + 1);
setRefreshTick((prev: number) => {
return prev + 1;
});
setTimeout(() => {
setIsRefreshing(false);
}, 500);
@@ -224,9 +225,7 @@ const DashboardViewPage: FunctionComponent<ComponentProps> = (
</h1>
</div>
<NavBar
className="bg-white flex text-center justify-between py-2 mt-5 rounded-lg shadow px-5"
>
<NavBar className="bg-white flex text-center justify-between py-2 mt-5 rounded-lg shadow px-5">
<NavBarItem
id="overview-nav-bar-item"
title="Overview"
@@ -248,8 +247,7 @@ const DashboardViewPage: FunctionComponent<ComponentProps> = (
<div
className="h-0.5 rounded-t-lg"
style={{
background:
"linear-gradient(90deg, #6366f1 0%, #8b5cf6 100%)",
background: "linear-gradient(90deg, #6366f1 0%, #8b5cf6 100%)",
}}
></div>
<div className="flex items-center justify-between px-5 py-3">
@@ -341,10 +339,7 @@ const DashboardViewPage: FunctionComponent<ComponentProps> = (
<RangeStartAndEndDateView
dashboardStartAndEndDate={startAndEndDate}
onChange={(newRange: RangeStartAndEndDateTime) => {
setTimeRangeStack([
...timeRangeStack,
startAndEndDate,
]);
setTimeRangeStack([...timeRangeStack, startAndEndDate]);
setStartAndEndDate(newRange);
}}
/>
@@ -360,14 +355,12 @@ const DashboardViewPage: FunctionComponent<ComponentProps> = (
value: string,
) => {
setDashboardVariables(
dashboardVariables.map(
(v: DashboardVariable) => {
if (v.id === variableId) {
return { ...v, currentValue: value };
}
return v;
},
),
dashboardVariables.map((v: DashboardVariable) => {
if (v.id === variableId) {
return { ...v, currentValue: value };
}
return v;
}),
);
}}
/>
@@ -390,9 +383,7 @@ const DashboardViewPage: FunctionComponent<ComponentProps> = (
}}
dashboardTotalWidth={dashboardTotalWidth}
startAndEndDate={startAndEndDate}
onStartAndEndDateChange={(
newRange: RangeStartAndEndDateTime,
) => {
onStartAndEndDateChange={(newRange: RangeStartAndEndDateTime) => {
setTimeRangeStack([...timeRangeStack, startAndEndDate]);
setStartAndEndDate(newRange);
}}

View File

@@ -150,10 +150,7 @@ const MasterPasswordPage: FunctionComponent<ComponentProps> = (
maxPrimaryButtonWidth={true}
isLoading={isSubmitting}
error={formError || undefined}
onSubmit={(
values: JSONObject,
onSubmitSuccessful?: () => void,
) => {
onSubmit={(values: JSONObject, onSubmitSuccessful?: () => void) => {
void handleFormSubmit(values, onSubmitSuccessful);
}}
footer={<></>}

View File

@@ -40,9 +40,7 @@ export const getPublicDashboardData: (
req.hostname?.toString() || req.headers["host"]?.toString() || "";
if (host) {
dashboardIdOrDomain = host;
logger.debug(
`Found domain in request headers: ${dashboardIdOrDomain}`,
);
logger.debug(`Found domain in request headers: ${dashboardIdOrDomain}`);
}
}

View File

@@ -6,8 +6,7 @@ import BaseAPI from "Common/UI/Utils/API/API";
export default class API extends BaseAPI {
public static override getDefaultHeaders(): Headers {
const dashboardId: ObjectID | null =
PublicDashboardUtil.getDashboardId();
const dashboardId: ObjectID | null = PublicDashboardUtil.getDashboardId();
if (!dashboardId) {
return {};

View File

@@ -23,7 +23,8 @@ export default class PublicDashboardUtil {
}
public static setRequiresMasterPassword(value: boolean): void {
const storageKey: string = PublicDashboardUtil.getRequiresMasterPasswordStorageKey();
const storageKey: string =
PublicDashboardUtil.getRequiresMasterPasswordStorageKey();
LocalStorage.setItem(storageKey, value);
if (!value) {
@@ -32,7 +33,8 @@ export default class PublicDashboardUtil {
}
public static requiresMasterPassword(): boolean {
const storageKey: string = PublicDashboardUtil.getRequiresMasterPasswordStorageKey();
const storageKey: string =
PublicDashboardUtil.getRequiresMasterPasswordStorageKey();
return Boolean(LocalStorage.getItem(storageKey));
}

View File

@@ -34,10 +34,7 @@ export class RouteUtil {
const id: ObjectID = LocalStorage.getItem("dashboardId") as ObjectID;
if (id) {
route = tempRoute.addRouteParam(
RouteParams.DashboardId,
id.toString(),
);
route = tempRoute.addRouteParam(RouteParams.DashboardId, id.toString());
}
return tempRoute;

View File

@@ -126,8 +126,7 @@ const Overview: FunctionComponent<PageComponentProps> = (
scheduledMaintenanceStateTimelines,
setScheduledMaintenanceStateTimelines,
] = useState<Array<ScheduledMaintenanceStateTimeline>>([]);
const uptimeHistoryDays: number =
statusPage?.showUptimeHistoryInDays || 90;
const uptimeHistoryDays: number = statusPage?.showUptimeHistoryInDays || 90;
const startDate: Date = OneUptimeDate.getSomeDaysAgo(uptimeHistoryDays);
const endDate: Date = OneUptimeDate.getCurrentDate();
const [currentStatus, setCurrentStatus] = useState<MonitorStatus | null>(

View File

@@ -224,8 +224,7 @@ export default class DashboardDomain extends BaseModel {
type: TableColumnType.Entity,
modelType: Dashboard,
title: "Dashboard",
description:
"Relation to Dashboard Resource in which this object belongs",
description: "Relation to Dashboard Resource in which this object belongs",
})
@ManyToOne(
() => {

View File

@@ -34,11 +34,7 @@ export default class DashboardAPI extends BaseAPI<
.getCrudApiPath()
?.toString()}/seo/:dashboardIdOrDomain`,
UserMiddleware.getUserMiddleware,
async (
req: ExpressRequest,
res: ExpressResponse,
next: NextFunction,
) => {
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
try {
const dashboardIdOrDomain: string = req.params[
"dashboardIdOrDomain"
@@ -46,10 +42,7 @@ export default class DashboardAPI extends BaseAPI<
let dashboardId: ObjectID | null = null;
if (
dashboardIdOrDomain &&
dashboardIdOrDomain.includes(".")
) {
if (dashboardIdOrDomain && dashboardIdOrDomain.includes(".")) {
// This is a domain - resolve to dashboard ID
const dashboardDomain: DashboardDomain | null =
await DashboardDomainService.findOneBy({
@@ -114,8 +107,7 @@ export default class DashboardAPI extends BaseAPI<
_id: dashboard._id?.toString() || "",
title: dashboard.name || "Dashboard",
description:
dashboard.description ||
"View dashboard metrics and insights.",
dashboard.description || "View dashboard metrics and insights.",
});
} catch (err) {
next(err);
@@ -125,20 +117,12 @@ export default class DashboardAPI extends BaseAPI<
// Domain resolution endpoint
this.router.post(
`${new this.entityType()
.getCrudApiPath()
?.toString()}/domain`,
`${new this.entityType().getCrudApiPath()?.toString()}/domain`,
UserMiddleware.getUserMiddleware,
async (
req: ExpressRequest,
res: ExpressResponse,
next: NextFunction,
) => {
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
try {
if (!req.body["domain"]) {
throw new BadDataException(
"domain is required in request body",
);
throw new BadDataException("domain is required in request body");
}
const domain: string = req.body["domain"] as string;
@@ -160,9 +144,7 @@ export default class DashboardAPI extends BaseAPI<
});
if (!dashboardDomain) {
throw new BadDataException(
"No dashboard found with this domain",
);
throw new BadDataException("No dashboard found with this domain");
}
const objectId: ObjectID = dashboardDomain.dashboardId!;
@@ -182,11 +164,7 @@ export default class DashboardAPI extends BaseAPI<
.getCrudApiPath()
?.toString()}/metadata/:dashboardId`,
UserMiddleware.getUserMiddleware,
async (
req: ExpressRequest,
res: ExpressResponse,
next: NextFunction,
) => {
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
try {
const dashboardId: ObjectID = new ObjectID(
req.params["dashboardId"] as string,
@@ -214,11 +192,9 @@ export default class DashboardAPI extends BaseAPI<
return Response.sendJsonObjectResponse(req, res, {
_id: dashboard._id?.toString() || "",
name: dashboard.name || "Dashboard",
description:
dashboard.description || "",
description: dashboard.description || "",
isPublicDashboard: dashboard.isPublicDashboard || false,
enableMasterPassword:
dashboard.enableMasterPassword || false,
enableMasterPassword: dashboard.enableMasterPassword || false,
});
} catch (err) {
next(err);
@@ -231,11 +207,7 @@ export default class DashboardAPI extends BaseAPI<
.getCrudApiPath()
?.toString()}/master-password/:dashboardId`,
UserMiddleware.getUserMiddleware,
async (
req: ExpressRequest,
res: ExpressResponse,
next: NextFunction,
) => {
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
try {
if (!req.params["dashboardId"]) {
throw new BadDataException("Dashboard ID not found");
@@ -277,10 +249,7 @@ export default class DashboardAPI extends BaseAPI<
);
}
if (
!dashboard.enableMasterPassword ||
!dashboard.masterPassword
) {
if (!dashboard.enableMasterPassword || !dashboard.masterPassword) {
throw new BadDataException(
"Master password has not been configured for this dashboard.",
);

View File

@@ -29,11 +29,7 @@ export default class DashboardDomainAPI extends BaseAPI<
this.router.get(
`${new this.entityType().getCrudApiPath()?.toString()}/verify-cname/:id`,
UserMiddleware.getUserMiddleware,
async (
req: ExpressRequest,
res: ExpressResponse,
next: NextFunction,
) => {
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
try {
if (!DashboardCNameRecord) {
return Response.sendErrorResponse(
@@ -101,8 +97,9 @@ export default class DashboardDomainAPI extends BaseAPI<
);
}
const isValid: boolean =
await DashboardDomainService.isCnameValid(domain.fullDomain!);
const isValid: boolean = await DashboardDomainService.isCnameValid(
domain.fullDomain!,
);
if (!isValid) {
return Response.sendErrorResponse(
@@ -125,11 +122,7 @@ export default class DashboardDomainAPI extends BaseAPI<
this.router.get(
`${new this.entityType().getCrudApiPath()?.toString()}/order-ssl/:id`,
UserMiddleware.getUserMiddleware,
async (
req: ExpressRequest,
res: ExpressResponse,
next: NextFunction,
) => {
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
try {
if (!DashboardCNameRecord) {
return Response.sendErrorResponse(
@@ -230,9 +223,7 @@ export default class DashboardDomainAPI extends BaseAPI<
await DashboardDomainService.orderCert(domain);
logger.debug(
"SSL Provisioned for domain - " + domain.fullDomain,
);
logger.debug("SSL Provisioned for domain - " + domain.fullDomain);
return Response.sendEmptySuccessResponse(req, res);
} catch (e) {

View File

@@ -1,42 +1,97 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class MigrationName1774524742177 implements MigrationInterface {
name = 'MigrationName1774524742177'
name = "MigrationName1774524742177";
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE "DashboardDomain" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "version" integer NOT NULL, "projectId" uuid NOT NULL, "domainId" uuid NOT NULL, "dashboardId" uuid NOT NULL, "subdomain" character varying(100) NOT NULL, "fullDomain" character varying(100) NOT NULL, "createdByUserId" uuid, "cnameVerificationToken" character varying(100) NOT NULL, "isCnameVerified" boolean NOT NULL DEFAULT false, "isSslOrdered" boolean NOT NULL DEFAULT false, "isSslProvisioned" boolean NOT NULL DEFAULT false, "deletedByUserId" uuid, "customCertificate" text, "customCertificateKey" text, "isCustomCertificate" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_3897ff3212d5d8ddbdeca684bf6" PRIMARY KEY ("_id"))`);
await queryRunner.query(`CREATE INDEX "IDX_8c0e357d0490d45c89ee673005" ON "DashboardDomain" ("projectId") `);
await queryRunner.query(`CREATE INDEX "IDX_0f58973f28172817bf9c1b34e7" ON "DashboardDomain" ("domainId") `);
await queryRunner.query(`CREATE INDEX "IDX_601f68ad16b421ede8b06b3f40" ON "DashboardDomain" ("dashboardId") `);
await queryRunner.query(`ALTER TABLE "Dashboard" ADD "isPublicDashboard" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "Dashboard" ADD "enableMasterPassword" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "Dashboard" ADD "masterPassword" character varying(64)`);
await queryRunner.query(`ALTER TABLE "Dashboard" ADD "ipWhitelist" text`);
await queryRunner.query(`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type":"Recurring","value":{"intervalType":"Day","intervalCount":{"_type":"PositiveNumber","value":1}}}'`);
await queryRunner.query(`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type":"RestrictionTimes","value":{"restictionType":"None","dayRestrictionTimes":null,"weeklyRestrictionTimes":[]}}'`);
await queryRunner.query(`ALTER TABLE "DashboardDomain" ADD CONSTRAINT "FK_8c0e357d0490d45c89ee673005c" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "DashboardDomain" ADD CONSTRAINT "FK_0f58973f28172817bf9c1b34e73" FOREIGN KEY ("domainId") REFERENCES "Domain"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "DashboardDomain" ADD CONSTRAINT "FK_601f68ad16b421ede8b06b3f40c" FOREIGN KEY ("dashboardId") REFERENCES "Dashboard"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "DashboardDomain" ADD CONSTRAINT "FK_de80950ba9f0d034f5c47940b3c" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "DashboardDomain" ADD CONSTRAINT "FK_de0c87b9c94b5dfeb21f1ce106f" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "DashboardDomain" DROP CONSTRAINT "FK_de0c87b9c94b5dfeb21f1ce106f"`);
await queryRunner.query(`ALTER TABLE "DashboardDomain" DROP CONSTRAINT "FK_de80950ba9f0d034f5c47940b3c"`);
await queryRunner.query(`ALTER TABLE "DashboardDomain" DROP CONSTRAINT "FK_601f68ad16b421ede8b06b3f40c"`);
await queryRunner.query(`ALTER TABLE "DashboardDomain" DROP CONSTRAINT "FK_0f58973f28172817bf9c1b34e73"`);
await queryRunner.query(`ALTER TABLE "DashboardDomain" DROP CONSTRAINT "FK_8c0e357d0490d45c89ee673005c"`);
await queryRunner.query(`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type": "RestrictionTimes", "value": {"restictionType": "None", "dayRestrictionTimes": null, "weeklyRestrictionTimes": []}}'`);
await queryRunner.query(`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type": "Recurring", "value": {"intervalType": "Day", "intervalCount": {"_type": "PositiveNumber", "value": 1}}}'`);
await queryRunner.query(`ALTER TABLE "Dashboard" DROP COLUMN "ipWhitelist"`);
await queryRunner.query(`ALTER TABLE "Dashboard" DROP COLUMN "masterPassword"`);
await queryRunner.query(`ALTER TABLE "Dashboard" DROP COLUMN "enableMasterPassword"`);
await queryRunner.query(`ALTER TABLE "Dashboard" DROP COLUMN "isPublicDashboard"`);
await queryRunner.query(`DROP INDEX "public"."IDX_601f68ad16b421ede8b06b3f40"`);
await queryRunner.query(`DROP INDEX "public"."IDX_0f58973f28172817bf9c1b34e7"`);
await queryRunner.query(`DROP INDEX "public"."IDX_8c0e357d0490d45c89ee673005"`);
await queryRunner.query(`DROP TABLE "DashboardDomain"`);
}
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "DashboardDomain" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "version" integer NOT NULL, "projectId" uuid NOT NULL, "domainId" uuid NOT NULL, "dashboardId" uuid NOT NULL, "subdomain" character varying(100) NOT NULL, "fullDomain" character varying(100) NOT NULL, "createdByUserId" uuid, "cnameVerificationToken" character varying(100) NOT NULL, "isCnameVerified" boolean NOT NULL DEFAULT false, "isSslOrdered" boolean NOT NULL DEFAULT false, "isSslProvisioned" boolean NOT NULL DEFAULT false, "deletedByUserId" uuid, "customCertificate" text, "customCertificateKey" text, "isCustomCertificate" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_3897ff3212d5d8ddbdeca684bf6" PRIMARY KEY ("_id"))`,
);
await queryRunner.query(
`CREATE INDEX "IDX_8c0e357d0490d45c89ee673005" ON "DashboardDomain" ("projectId") `,
);
await queryRunner.query(
`CREATE INDEX "IDX_0f58973f28172817bf9c1b34e7" ON "DashboardDomain" ("domainId") `,
);
await queryRunner.query(
`CREATE INDEX "IDX_601f68ad16b421ede8b06b3f40" ON "DashboardDomain" ("dashboardId") `,
);
await queryRunner.query(
`ALTER TABLE "Dashboard" ADD "isPublicDashboard" boolean NOT NULL DEFAULT false`,
);
await queryRunner.query(
`ALTER TABLE "Dashboard" ADD "enableMasterPassword" boolean NOT NULL DEFAULT false`,
);
await queryRunner.query(
`ALTER TABLE "Dashboard" ADD "masterPassword" character varying(64)`,
);
await queryRunner.query(`ALTER TABLE "Dashboard" ADD "ipWhitelist" text`);
await queryRunner.query(
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type":"Recurring","value":{"intervalType":"Day","intervalCount":{"_type":"PositiveNumber","value":1}}}'`,
);
await queryRunner.query(
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type":"RestrictionTimes","value":{"restictionType":"None","dayRestrictionTimes":null,"weeklyRestrictionTimes":[]}}'`,
);
await queryRunner.query(
`ALTER TABLE "DashboardDomain" ADD CONSTRAINT "FK_8c0e357d0490d45c89ee673005c" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "DashboardDomain" ADD CONSTRAINT "FK_0f58973f28172817bf9c1b34e73" FOREIGN KEY ("domainId") REFERENCES "Domain"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "DashboardDomain" ADD CONSTRAINT "FK_601f68ad16b421ede8b06b3f40c" FOREIGN KEY ("dashboardId") REFERENCES "Dashboard"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "DashboardDomain" ADD CONSTRAINT "FK_de80950ba9f0d034f5c47940b3c" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "DashboardDomain" ADD CONSTRAINT "FK_de0c87b9c94b5dfeb21f1ce106f" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "DashboardDomain" DROP CONSTRAINT "FK_de0c87b9c94b5dfeb21f1ce106f"`,
);
await queryRunner.query(
`ALTER TABLE "DashboardDomain" DROP CONSTRAINT "FK_de80950ba9f0d034f5c47940b3c"`,
);
await queryRunner.query(
`ALTER TABLE "DashboardDomain" DROP CONSTRAINT "FK_601f68ad16b421ede8b06b3f40c"`,
);
await queryRunner.query(
`ALTER TABLE "DashboardDomain" DROP CONSTRAINT "FK_0f58973f28172817bf9c1b34e73"`,
);
await queryRunner.query(
`ALTER TABLE "DashboardDomain" DROP CONSTRAINT "FK_8c0e357d0490d45c89ee673005c"`,
);
await queryRunner.query(
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type": "RestrictionTimes", "value": {"restictionType": "None", "dayRestrictionTimes": null, "weeklyRestrictionTimes": []}}'`,
);
await queryRunner.query(
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type": "Recurring", "value": {"intervalType": "Day", "intervalCount": {"_type": "PositiveNumber", "value": 1}}}'`,
);
await queryRunner.query(
`ALTER TABLE "Dashboard" DROP COLUMN "ipWhitelist"`,
);
await queryRunner.query(
`ALTER TABLE "Dashboard" DROP COLUMN "masterPassword"`,
);
await queryRunner.query(
`ALTER TABLE "Dashboard" DROP COLUMN "enableMasterPassword"`,
);
await queryRunner.query(
`ALTER TABLE "Dashboard" DROP COLUMN "isPublicDashboard"`,
);
await queryRunner.query(
`DROP INDEX "public"."IDX_601f68ad16b421ede8b06b3f40"`,
);
await queryRunner.query(
`DROP INDEX "public"."IDX_0f58973f28172817bf9c1b34e7"`,
);
await queryRunner.query(
`DROP INDEX "public"."IDX_8c0e357d0490d45c89ee673005"`,
);
await queryRunner.query(`DROP TABLE "DashboardDomain"`);
}
}

View File

@@ -1,14 +1,17 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class MigrationName1774524742178 implements MigrationInterface {
name = 'MigrationName1774524742178'
name = "MigrationName1774524742178";
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "StatusPage" ADD "showUptimeHistoryInDays" integer NOT NULL DEFAULT 90`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "StatusPage" DROP COLUMN "showUptimeHistoryInDays"`);
}
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "StatusPage" ADD "showUptimeHistoryInDays" integer NOT NULL DEFAULT 90`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "StatusPage" DROP COLUMN "showUptimeHistoryInDays"`,
);
}
}

View File

@@ -547,5 +547,5 @@ export default [
MigrationName1774355321449,
MigrationName1774357353502,
MigrationName1774524742177,
MigrationName1774524742178
MigrationName1774524742178,
];

View File

@@ -34,9 +34,7 @@ export class Service extends DatabaseService<DashboardDomain> {
const domain: DomainModel | null = await DomainService.findOneBy({
query: {
_id:
createBy.data.domainId?.toString() ||
createBy.data.domain?._id ||
"",
createBy.data.domainId?.toString() || createBy.data.domain?._id || "",
},
select: { domain: true, isVerified: true },
props: {
@@ -175,9 +173,7 @@ export class Service extends DatabaseService<DashboardDomain> {
},
});
logger.debug(
"SSL ordered for domain: " + dashboardDomain.fullDomain,
);
logger.debug("SSL ordered for domain: " + dashboardDomain.fullDomain);
await this.updateOneById({
id: dashboardDomain.id!,
@@ -308,9 +304,7 @@ export class Service extends DatabaseService<DashboardDomain> {
const token: string = dashboardDomain.cnameVerificationToken!;
logger.debug(
"Checking for CNAME " + fullDomain + " with token " + token,
);
logger.debug("Checking for CNAME " + fullDomain + " with token " + token);
try {
const result: HTTPErrorResponse | HTTPResponse<JSONObject> =
@@ -643,9 +637,7 @@ export class Service extends DatabaseService<DashboardDomain> {
try {
await this.orderCert(domain);
} catch (err) {
logger.error(
"Cannot order cert for domain: " + domain.fullDomain,
);
logger.error("Cannot order cert for domain: " + domain.fullDomain);
logger.error(err);
}
}

View File

@@ -1448,7 +1448,7 @@ export class Service extends DatabaseService<WorkspaceNotificationSummary> {
for (const [, td] of tlMap) {
// For ack: if not explicitly acknowledged but resolved, use resolve time as ack time
const eventTime: Date | undefined =
kind === "ack" ? (td.ackAt || td.resolvedAt) : td.resolvedAt;
kind === "ack" ? td.ackAt || td.resolvedAt : td.resolvedAt;
if (eventTime && td.declaredAt) {
total += OneUptimeDate.getMinutesBetweenTwoDates(
td.declaredAt,

View File

@@ -682,7 +682,9 @@ ${contextBlock}
// Cluster context
const clusterDetails: Array<string> = [];
clusterDetails.push(`- Cluster: ${breakdown.clusterName}`);
clusterDetails.push(`- Metric: ${breakdown.metricFriendlyName} (\`${breakdown.metricName}\`)`);
clusterDetails.push(
`- Metric: ${breakdown.metricFriendlyName} (\`${breakdown.metricName}\`)`,
);
if (breakdown.attributes["k8s.namespace.name"]) {
clusterDetails.push(
@@ -695,23 +697,15 @@ ${contextBlock}
);
// Affected resources
if (
breakdown.affectedResources &&
breakdown.affectedResources.length > 0
) {
if (breakdown.affectedResources && breakdown.affectedResources.length > 0) {
const resourceLines: Array<string> = [];
// Sort by metric value descending (worst first)
const sortedResources: Array<KubernetesAffectedResource> = [
...breakdown.affectedResources,
].sort(
(
a: KubernetesAffectedResource,
b: KubernetesAffectedResource,
) => {
return b.metricValue - a.metricValue;
},
);
].sort((a: KubernetesAffectedResource, b: KubernetesAffectedResource) => {
return b.metricValue - a.metricValue;
});
// Show top 10 affected resources
const resourcesToShow: Array<KubernetesAffectedResource> =
@@ -798,9 +792,7 @@ ${contextBlock}
metricName === "k8s.pod.phase" &&
breakdown.attributes["k8s.pod.phase"] === "Pending"
) {
lines.push(
`Pods are stuck in Pending phase and unable to be scheduled.`,
);
lines.push(`Pods are stuck in Pending phase and unable to be scheduled.`);
lines.push(
`Common causes: insufficient CPU/memory resources on nodes, node affinity/taint restrictions preventing scheduling, PersistentVolumeClaim pending, or resource quota exceeded.`,
);
@@ -811,7 +803,7 @@ ${contextBlock}
}
} else if (
metricName === "k8s.node.condition_ready" ||
metricName.includes("node") && metricName.includes("condition")
(metricName.includes("node") && metricName.includes("condition"))
) {
lines.push(`One or more nodes have transitioned to a NotReady state.`);
if (topResource.nodeName) {
@@ -827,7 +819,7 @@ ${contextBlock}
);
} else if (
metricName === "k8s.node.cpu.utilization" ||
metricName.includes("cpu") && metricName.includes("utilization")
(metricName.includes("cpu") && metricName.includes("utilization"))
) {
lines.push(`Node CPU utilization has exceeded the configured threshold.`);
if (topResource.nodeName) {
@@ -843,7 +835,7 @@ ${contextBlock}
);
} else if (
metricName === "k8s.node.memory.usage" ||
metricName.includes("memory") && metricName.includes("usage")
(metricName.includes("memory") && metricName.includes("usage"))
) {
lines.push(
`Node memory utilization has exceeded the configured threshold.`,
@@ -879,7 +871,7 @@ ${contextBlock}
);
} else if (
metricName === "k8s.job.failed_pods" ||
metricName.includes("job") && metricName.includes("fail")
(metricName.includes("job") && metricName.includes("fail"))
) {
lines.push(`Kubernetes Job has failed pods.`);
if (topResource.workloadName) {
@@ -916,9 +908,7 @@ ${contextBlock}
metricName === "k8s.daemonset.misscheduled_nodes" ||
metricName.includes("daemonset")
) {
lines.push(
`DaemonSet has misscheduled or unavailable nodes.`,
);
lines.push(`DaemonSet has misscheduled or unavailable nodes.`);
if (topResource.workloadName) {
lines.push(
`DaemonSet \`${topResource.workloadName}\` has **${topResource.metricValue}** misscheduled node(s).`,

View File

@@ -1023,8 +1023,10 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
logger.debug("Sending message to Microsoft Teams with data:");
logger.debug(data);
// Teams adaptive cards have a ~28KB payload limit.
// Split message blocks into chunks of 40 to avoid hitting the limit.
/*
* Teams adaptive cards have a ~28KB payload limit.
* Split message blocks into chunks of 40 to avoid hitting the limit.
*/
const maxBlocksPerCard: number = 40;
const allMessageBlocks: Array<WorkspaceMessageBlock> =
data.workspaceMessagePayload.messageBlocks;

View File

@@ -1140,11 +1140,7 @@ export default class SlackUtil extends WorkspaceBase {
blocks: blocks,
});
} else {
for (
let i: number = 0;
i < blocks.length;
i += maxBlocksPerMessage
) {
for (let i: number = 0; i < blocks.length; i += maxBlocksPerMessage) {
const chunk: Array<JSONObject> = blocks.slice(
i,
i + maxBlocksPerMessage,

View File

@@ -95,7 +95,11 @@ const ComponentSettingsModal: FunctionComponent<ComponentProps> = (
>
<Icon
icon={IconProp.Label}
style={{ color: "#64748b", width: "0.875rem", height: "0.875rem" }}
style={{
color: "#64748b",
width: "0.875rem",
height: "0.875rem",
}}
/>
<span
style={{
@@ -199,7 +203,11 @@ const ComponentSettingsModal: FunctionComponent<ComponentProps> = (
>
<Icon
icon={IconProp.Settings}
style={{ color: "#64748b", width: "0.875rem", height: "0.875rem" }}
style={{
color: "#64748b",
width: "0.875rem",
height: "0.875rem",
}}
/>
<span
style={{
@@ -247,7 +255,11 @@ const ComponentSettingsModal: FunctionComponent<ComponentProps> = (
>
<Icon
icon={IconProp.Link}
style={{ color: "#64748b", width: "0.875rem", height: "0.875rem" }}
style={{
color: "#64748b",
width: "0.875rem",
height: "0.875rem",
}}
/>
<span
style={{
@@ -291,7 +303,11 @@ const ComponentSettingsModal: FunctionComponent<ComponentProps> = (
>
<Icon
icon={IconProp.ArrowCircleRight}
style={{ color: "#64748b", width: "0.875rem", height: "0.875rem" }}
style={{
color: "#64748b",
width: "0.875rem",
height: "0.875rem",
}}
/>
<span
style={{

View File

@@ -165,10 +165,7 @@ const ComponentsModal: FunctionComponent<ComponentProps> = (
{/* Component cards grid */}
<div className="grid grid-cols-1 gap-2">
{categoryComponents.map(
(
componentMetadata: ComponentMetadata,
j: number,
) => {
(componentMetadata: ComponentMetadata, j: number) => {
const isSelected: boolean =
selectedComponentMetadata !== null &&
selectedComponentMetadata.id ===
@@ -178,9 +175,7 @@ const ComponentsModal: FunctionComponent<ComponentProps> = (
<div
key={j}
onClick={() => {
setSelectedComponentMetadata(
componentMetadata,
);
setSelectedComponentMetadata(componentMetadata);
}}
className="cursor-pointer transition-all duration-150"
style={{
@@ -219,9 +214,7 @@ const ComponentsModal: FunctionComponent<ComponentProps> = (
<Icon
icon={componentMetadata.iconProp}
style={{
color: isSelected
? "#ffffff"
: "#64748b",
color: isSelected ? "#ffffff" : "#64748b",
width: "1rem",
height: "1rem",
}}
@@ -234,9 +227,7 @@ const ComponentsModal: FunctionComponent<ComponentProps> = (
style={{
fontSize: "0.8125rem",
fontWeight: 600,
color: isSelected
? "#4338ca"
: "#1e293b",
color: isSelected ? "#4338ca" : "#1e293b",
margin: 0,
lineHeight: "1.25rem",
}}
@@ -246,9 +237,7 @@ const ComponentsModal: FunctionComponent<ComponentProps> = (
<p
style={{
fontSize: "0.75rem",
color: isSelected
? "#6366f1"
: "#94a3b8",
color: isSelected ? "#6366f1" : "#94a3b8",
margin: 0,
marginTop: "2px",
lineHeight: "1rem",

View File

@@ -200,8 +200,7 @@ export const SubscriptionPlans: Array<SubscriptionPlan> =
export const StatusPageCNameRecord: string =
env("STATUS_PAGE_CNAME_RECORD") || "";
export const DashboardCNameRecord: string =
env("DASHBOARD_CNAME_RECORD") || "";
export const DashboardCNameRecord: string = env("DASHBOARD_CNAME_RECORD") || "";
export const AnalyticsKey: string = env("ANALYTICS_KEY") || "";
export const AnalyticsHost: string = env("ANALYTICS_HOST");

View File

@@ -80,8 +80,7 @@ export default class DashboardChartComponentUtil extends DashboardBaseComponentU
componentArguments.push({
name: "Additional Queries",
description:
"Add multiple metric queries to overlay on the same chart",
description: "Add multiple metric queries to overlay on the same chart",
required: false,
type: ComponentInputType.MetricsQueryConfigs,
id: "metricQueryConfigs",

View File

@@ -76,8 +76,7 @@ export default class DashboardGaugeComponentUtil extends DashboardBaseComponentU
componentArguments.push({
name: "Warning Threshold",
description:
"Values above this threshold will be shown in yellow",
description: "Values above this threshold will be shown in yellow",
required: false,
type: ComponentInputType.Number,
id: "warningThreshold",
@@ -86,8 +85,7 @@ export default class DashboardGaugeComponentUtil extends DashboardBaseComponentU
componentArguments.push({
name: "Critical Threshold",
description:
"Values above this threshold will be shown in red",
description: "Values above this threshold will be shown in red",
required: false,
type: ComponentInputType.Number,
id: "criticalThreshold",

View File

@@ -1 +1 @@
10.0.39
10.0.40

View File

@@ -488,7 +488,8 @@ const monitorKubernetes: MonitorKubernetesFunction = async (data: {
if (resourceFilters.workloadName && resourceFilters.workloadType) {
const workloadType: string = resourceFilters.workloadType.toLowerCase();
attributes[`resource.k8s.${workloadType}.name`] = resourceFilters.workloadName;
attributes[`resource.k8s.${workloadType}.name`] =
resourceFilters.workloadName;
}
}
@@ -569,10 +570,14 @@ const monitorKubernetes: MonitorKubernetesFunction = async (data: {
if (metricAttrs["resource.k8s.deployment.name"]) {
workloadType = "Deployment";
workloadName = metricAttrs["resource.k8s.deployment.name"] as string;
workloadName = metricAttrs[
"resource.k8s.deployment.name"
] as string;
} else if (metricAttrs["resource.k8s.statefulset.name"]) {
workloadType = "StatefulSet";
workloadName = metricAttrs["resource.k8s.statefulset.name"] as string;
workloadName = metricAttrs[
"resource.k8s.statefulset.name"
] as string;
} else if (metricAttrs["resource.k8s.daemonset.name"]) {
workloadType = "DaemonSet";
workloadName = metricAttrs["resource.k8s.daemonset.name"] as string;
@@ -584,7 +589,9 @@ const monitorKubernetes: MonitorKubernetesFunction = async (data: {
workloadName = metricAttrs["resource.k8s.cronjob.name"] as string;
} else if (metricAttrs["resource.k8s.replicaset.name"]) {
workloadType = "ReplicaSet";
workloadName = metricAttrs["resource.k8s.replicaset.name"] as string;
workloadName = metricAttrs[
"resource.k8s.replicaset.name"
] as string;
}
// Build unique key for deduplication