From b5bf1d6dd10fdde3e76f67babe87df0893ec70b1 Mon Sep 17 00:00:00 2001 From: Nawaz Dhandala Date: Thu, 26 Mar 2026 16:38:06 +0000 Subject: [PATCH] feat: Introduce DashboardCNameRecord for custom domain handling in dashboards --- .../src/Pages/Dashboards/View/CustomDomains.tsx | 14 +++++++------- Common/Server/API/DashboardDomainAPI.ts | 6 +++--- Common/Server/EnvironmentConfig.ts | 4 ++++ Common/Server/Services/DashboardDomainService.ts | 12 ++++++------ Common/UI/Config.ts | 3 +++ HelmChart/Public/oneuptime/templates/_helpers.tpl | 2 ++ HelmChart/Public/oneuptime/values.schema.json | 9 +++++++++ HelmChart/Public/oneuptime/values.yaml | 8 ++++++++ config.example.env | 10 ++++++++-- docker-compose.base.yml | 1 + 10 files changed, 51 insertions(+), 18 deletions(-) diff --git a/App/FeatureSet/Dashboard/src/Pages/Dashboards/View/CustomDomains.tsx b/App/FeatureSet/Dashboard/src/Pages/Dashboards/View/CustomDomains.tsx index e8138db36b..11c45dff7a 100644 --- a/App/FeatureSet/Dashboard/src/Pages/Dashboards/View/CustomDomains.tsx +++ b/App/FeatureSet/Dashboard/src/Pages/Dashboards/View/CustomDomains.tsx @@ -12,7 +12,7 @@ import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchem import ConfirmModal from "Common/UI/Components/Modal/ConfirmModal"; import ModelTable from "Common/UI/Components/ModelTable/ModelTable"; import FieldType from "Common/UI/Components/Types/FieldType"; -import { APP_API_URL, StatusPageCNameRecord } from "Common/UI/Config"; +import { APP_API_URL, DashboardCNameRecord } from "Common/UI/Config"; import API from "Common/UI/Utils/API/API"; import ModelAPI from "Common/UI/Utils/ModelAPI/ModelAPI"; import Navigation from "Common/UI/Utils/Navigation"; @@ -69,7 +69,7 @@ const DashboardCustomDomains: FunctionComponent = ( isEditable={true} cardProps={{ title: "Custom Domains", - description: `Important: Please add a CNAME record pointing to ${StatusPageCNameRecord} for these domains for this to work.`, + description: `Important: Please add a CNAME record pointing to ${DashboardCNameRecord} for these domains for this to work.`, }} refreshToggle={refreshToggle} onBeforeCreate={( @@ -325,7 +325,7 @@ const DashboardCustomDomains: FunctionComponent = ( Please add CNAME record to your domain. Details of @@ -344,7 +344,7 @@ const DashboardCustomDomains: FunctionComponent = (
Content: - {StatusPageCNameRecord} + {DashboardCNameRecord}

@@ -360,10 +360,10 @@ const DashboardCustomDomains: FunctionComponent = ( installation. Please contact your server admin to enable this feature. To enable this feature, if you are using Docker compose, the - STATUS_PAGE_CNAME_RECORD environment variable + DASHBOARD_CNAME_RECORD environment variable must be set when starting the OneUptime cluster. If you are using Helm and Kubernetes then set - statusPage.cnameRecord in the values.yaml file. + dashboard.cnameRecord in the values.yaml file.
) @@ -418,7 +418,7 @@ const DashboardCustomDomains: FunctionComponent = ( Please click on the button below to order SSL for this domain. We will use LetsEncrypt to order a certificate. diff --git a/Common/Server/API/DashboardDomainAPI.ts b/Common/Server/API/DashboardDomainAPI.ts index 739a6191e9..48cc4f867b 100644 --- a/Common/Server/API/DashboardDomainAPI.ts +++ b/Common/Server/API/DashboardDomainAPI.ts @@ -1,4 +1,4 @@ -import { StatusPageCNameRecord } from "../EnvironmentConfig"; +import { DashboardCNameRecord } from "../EnvironmentConfig"; import UserMiddleware from "../Middleware/UserAuthorization"; import DashboardDomainService, { Service as DashboardDomainServiceType, @@ -35,7 +35,7 @@ export default class DashboardDomainAPI extends BaseAPI< next: NextFunction, ) => { try { - if (!StatusPageCNameRecord) { + if (!DashboardCNameRecord) { return Response.sendErrorResponse( req, res, @@ -131,7 +131,7 @@ export default class DashboardDomainAPI extends BaseAPI< next: NextFunction, ) => { try { - if (!StatusPageCNameRecord) { + if (!DashboardCNameRecord) { return Response.sendErrorResponse( req, res, diff --git a/Common/Server/EnvironmentConfig.ts b/Common/Server/EnvironmentConfig.ts index 76c4947eb2..c1a6a3808b 100644 --- a/Common/Server/EnvironmentConfig.ts +++ b/Common/Server/EnvironmentConfig.ts @@ -35,6 +35,7 @@ const FRONTEND_ENV_ALLOW_LIST: Array = [ "VAPID_SUBJECT", "VERSION", "STATUS_PAGE_CNAME_RECORD", + "DASHBOARD_CNAME_RECORD", "ANALYTICS_KEY", "ANALYTICS_HOST", "GIT_SHA", @@ -252,6 +253,9 @@ export const ClickhouseHost: Hostname = Hostname.fromString( export const StatusPageCNameRecord: string = process.env["STATUS_PAGE_CNAME_RECORD"] || ""; +export const DashboardCNameRecord: string = + process.env["DASHBOARD_CNAME_RECORD"] || ""; + export const ClickhousePort: Port = new Port( process.env["CLICKHOUSE_PORT"] || "8123", ); diff --git a/Common/Server/Services/DashboardDomainService.ts b/Common/Server/Services/DashboardDomainService.ts index f13d91f7b1..65db0089c0 100644 --- a/Common/Server/Services/DashboardDomainService.ts +++ b/Common/Server/Services/DashboardDomainService.ts @@ -19,7 +19,7 @@ import DashboardDomain from "../../Models/DatabaseModels/DashboardDomain"; import AcmeCertificateService from "./AcmeCertificateService"; import Telemetry, { Span } from "../Utils/Telemetry"; import CaptureSpan from "../Utils/Telemetry/CaptureSpan"; -import { StatusPageCNameRecord } from "../EnvironmentConfig"; +import { DashboardCNameRecord } from "../EnvironmentConfig"; import Domain from "../Types/Domain"; export class Service extends DatabaseService { @@ -367,7 +367,7 @@ export class Service extends DatabaseService { } try { - if (StatusPageCNameRecord) { + if (DashboardCNameRecord) { const cnameRecords: Array = await Domain.getCnameRecords({ domain: fullDomain, }); @@ -379,7 +379,7 @@ export class Service extends DatabaseService { if (!cnameRecord) { logger.debug( - `No CNAME record found for ${fullDomain}. Expected record: ${StatusPageCNameRecord}`, + `No CNAME record found for ${fullDomain}. Expected record: ${DashboardCNameRecord}`, ); await this.updateCnameStatusForDashboardDomain({ domain: fullDomain, @@ -391,10 +391,10 @@ export class Service extends DatabaseService { if ( cnameRecord && cnameRecord.trim().toLocaleLowerCase() === - StatusPageCNameRecord.trim().toLocaleLowerCase() + DashboardCNameRecord.trim().toLocaleLowerCase() ) { logger.debug( - `CNAME record for ${fullDomain} matches the expected record: ${StatusPageCNameRecord}`, + `CNAME record for ${fullDomain} matches the expected record: ${DashboardCNameRecord}`, ); await this.updateCnameStatusForDashboardDomain({ @@ -406,7 +406,7 @@ export class Service extends DatabaseService { } logger.debug( - `CNAME record for ${fullDomain} is ${cnameRecord} and it does not match the expected record: ${StatusPageCNameRecord}`, + `CNAME record for ${fullDomain} is ${cnameRecord} and it does not match the expected record: ${DashboardCNameRecord}`, ); } } catch (err) { diff --git a/Common/UI/Config.ts b/Common/UI/Config.ts index 89d944e20f..6d3483286c 100644 --- a/Common/UI/Config.ts +++ b/Common/UI/Config.ts @@ -200,6 +200,9 @@ export const SubscriptionPlans: Array = export const StatusPageCNameRecord: string = env("STATUS_PAGE_CNAME_RECORD") || ""; +export const DashboardCNameRecord: string = + env("DASHBOARD_CNAME_RECORD") || ""; + export const AnalyticsKey: string = env("ANALYTICS_KEY") || ""; export const AnalyticsHost: string = env("ANALYTICS_HOST"); diff --git a/HelmChart/Public/oneuptime/templates/_helpers.tpl b/HelmChart/Public/oneuptime/templates/_helpers.tpl index 19a488fe5a..dfc77cffa7 100644 --- a/HelmChart/Public/oneuptime/templates/_helpers.tpl +++ b/HelmChart/Public/oneuptime/templates/_helpers.tpl @@ -72,6 +72,8 @@ Usage: value: {{ ternary "true" "false" $provisionSSL | quote }} - name: STATUS_PAGE_CNAME_RECORD value: {{ $.Values.statusPage.cnameRecord }} +- name: DASHBOARD_CNAME_RECORD + value: {{ $.Values.dashboard.cnameRecord | default "" }} - name: ALLOWED_ACTIVE_MONITOR_COUNT_IN_FREE_PLAN value: {{ $.Values.billing.allowedActiveMonitorCountInFreePlan | quote }} - name: LOG_LEVEL diff --git a/HelmChart/Public/oneuptime/values.schema.json b/HelmChart/Public/oneuptime/values.schema.json index bae8b8e5f2..b926442558 100644 --- a/HelmChart/Public/oneuptime/values.schema.json +++ b/HelmChart/Public/oneuptime/values.schema.json @@ -784,6 +784,15 @@ }, "additionalProperties": false }, + "dashboard": { + "type": "object", + "properties": { + "cnameRecord": { + "type": ["string", "null"] + } + }, + "additionalProperties": false + }, "probes": { "type": "object", "patternProperties": { diff --git a/HelmChart/Public/oneuptime/values.yaml b/HelmChart/Public/oneuptime/values.yaml index e10a600590..f59332aae9 100644 --- a/HelmChart/Public/oneuptime/values.yaml +++ b/HelmChart/Public/oneuptime/values.yaml @@ -324,6 +324,14 @@ alerts: statusPage: cnameRecord: +# If you would like to attach public dashboards to custom domains use this setting. +# Works the same way as statusPage.cnameRecord but for dashboards. +# For example, if you want dashboard.yourcompany.com to show a public dashboard: +# 1. Set the dashboard.cnameRecord to "oneuptime.yourcompany.com" +# 2. Create CNAME record in your DNS provider with the name "dashboard.yourcompany.com" and value "oneuptime.yourcompany.com" +dashboard: + cnameRecord: + probes: one: name: "Probe" diff --git a/config.example.env b/config.example.env index 341630a842..77867d7053 100644 --- a/config.example.env +++ b/config.example.env @@ -36,14 +36,20 @@ GLOBAL_PROBE_2_KEY=probe-2-please-change-this-to-random-value STATUS_PAGE_HTTPS_PORT=443 -# If you would like to attach status pages or public dashboards to custom domains use this setting. +# If you would like to attach status pages to custom domains use this setting. # For example, lets say you would like the status page to be hosted on status.yourcompany.com, then # 1. Create a A record in your DNS provider with the name "oneuptime.yourcompany.com" and value to Public IP of the server oneuptime is deployed on. # 2. Set the STATUS_PAGE_CNAME_RECORD to "oneuptime.yourcompany.com" # 3. Create CNAME record in your DNS provider with the name "status.yourcompany.com" and value "oneuptime.yourcompany.com" -# This same CNAME is used for both status page and public dashboard custom domains. STATUS_PAGE_CNAME_RECORD=oneuptime.yourcompany.com +# If you would like to attach public dashboards to custom domains use this setting. +# Works the same way as STATUS_PAGE_CNAME_RECORD but for dashboards. +# For example, if you want dashboard.yourcompany.com to show a public dashboard: +# 1. Set the DASHBOARD_CNAME_RECORD to "oneuptime.yourcompany.com" +# 2. Create CNAME record in your DNS provider with the name "dashboard.yourcompany.com" and value "oneuptime.yourcompany.com" +DASHBOARD_CNAME_RECORD=oneuptime.yourcompany.com + # --------------------------------------------- # # You can safely ignore anything below this line. Keep them as default to make things work. diff --git a/docker-compose.base.yml b/docker-compose.base.yml index 192cb84808..b79592da5e 100644 --- a/docker-compose.base.yml +++ b/docker-compose.base.yml @@ -6,6 +6,7 @@ x-common-variables: &common-variables HTTP_PROTOCOL: ${HTTP_PROTOCOL} STATUS_PAGE_CNAME_RECORD: ${STATUS_PAGE_CNAME_RECORD} + DASHBOARD_CNAME_RECORD: ${DASHBOARD_CNAME_RECORD} LOG_LEVEL: ${LOG_LEVEL}