feat(ui): add EditionLabel and expose IS_ENTERPRISE across apps

- Add EditionLabel component to Common UI to show current edition and info modal
- Show edition label in Login page, Dashboard header and Footer
- Add IS_ENTERPRISE env var to config.example.env and export in Common UI Config
- Propagate IS_ENTERPRISE into docker-compose.base.yml service envs
This commit is contained in:
Nawaz Dhandala 2025-10-31 15:47:12 +00:00
parent f7c05645a9
commit 5e19849ac8
No known key found for this signature in database
GPG key ID: 96C5DCA24769DBCA
7 changed files with 115 additions and 0 deletions

View file

@ -12,6 +12,7 @@ import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchem
import Link from "Common/UI/Components/Link/Link";
import { DASHBOARD_URL } from "Common/UI/Config";
import OneUptimeLogo from "Common/UI/Images/logos/OneUptimeSVG/3-transparent.svg";
import EditionLabel from "Common/UI/Components/EditionLabel/EditionLabel";
import UiAnalytics from "Common/UI/Utils/Analytics";
import LoginUtil from "Common/UI/Utils/Login";
import UserTotpAuth from "Common/Models/DatabaseModels/UserTotpAuth";
@ -192,6 +193,9 @@ const LoginPage: () => JSX.Element = () => {
src={OneUptimeLogo}
alt="OneUptime"
/>
<div className="mt-4 flex justify-center">
<EditionLabel />
</div>
{!showTwoFactorAuth && (
<>
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">

View file

@ -3,6 +3,7 @@ import Logo from "./Logo";
import UserProfile from "./UserProfile";
import Button, { ButtonStyleType } from "Common/UI/Components/Button/Button";
import Header from "Common/UI/Components/Header/Header";
import EditionLabel from "Common/UI/Components/EditionLabel/EditionLabel";
import { DASHBOARD_URL } from "Common/UI/Config";
import Navigation from "Common/UI/Utils/Navigation";
import React, { FunctionComponent, ReactElement } from "react";
@ -27,6 +28,7 @@ const DashboardHeader: FunctionComponent = (): ReactElement => {
}
rightComponents={
<>
<EditionLabel className="mr-3 hidden md:inline-flex" />
<Button
title="Exit Admin"
buttonStyle={ButtonStyleType.NORMAL}

View file

@ -0,0 +1,98 @@
import Modal from "../Modal/Modal";
import React, {
FunctionComponent,
ReactElement,
useState,
} from "react";
import { BILLING_ENABLED, IS_ENTERPRISE } from "../../Config";
export interface ComponentProps {
className?: string | undefined;
}
const ENTERPRISE_URL: string = "https://oneuptime.com/enterprise";
const EditionLabel: FunctionComponent<ComponentProps> = (
props: ComponentProps,
): ReactElement => {
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(false);
if (BILLING_ENABLED) {
return <></>;
}
const editionName: string = IS_ENTERPRISE
? "Enterprise Edition"
: "Community Edition";
const openDialog: () => void = () => {
setIsDialogOpen(true);
};
const closeDialog: () => void = () => {
setIsDialogOpen(false);
};
const handlePrimaryAction: () => void = () => {
if (typeof window !== "undefined") {
window.open(ENTERPRISE_URL, "_blank", "noopener,noreferrer");
}
closeDialog();
};
return (
<>
<button
type="button"
onClick={openDialog}
className={`inline-flex items-center justify-center gap-2 rounded-full border border-indigo-200 bg-indigo-50 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-indigo-700 transition hover:bg-indigo-100 focus:outline-none focus-visible:ring focus-visible:ring-indigo-300 ${
props.className ? props.className : ""
}`}
>
{editionName}
</button>
{isDialogOpen && (
<Modal
title={editionName}
submitButtonText={IS_ENTERPRISE ? "Learn More" : "Talk to Sales"}
closeButtonText="Close"
onClose={closeDialog}
onSubmit={handlePrimaryAction}
>
<div className="space-y-3 text-sm text-gray-600">
{IS_ENTERPRISE ? (
<>
<p>
You are running the Enterprise Edition of OneUptime. This
includes premium capabilities such as enterprise-grade
support, governance controls, and unlimited project scale.
</p>
<p>
Reach out to our team if you need help enabling additional
enterprise features or onboarding new teams.
</p>
</>
) : (
<>
<p>
You are running the Community Edition of OneUptime. Upgrade to
Enterprise to unlock advanced automation, security controls,
and priority support.
</p>
<ul className="list-disc space-y-1 pl-5">
<li>Unlimited monitors, workflows, and integrations.</li>
<li>Role-based access controls and audit trails.</li>
<li>24/7 priority support with guaranteed SLAs.</li>
</ul>
</>
)}
</div>
</Modal>
)}
</>
);
};
export default EditionLabel;

View file

@ -48,6 +48,7 @@ export const HTTP_PROTOCOL: Protocol =
export const HOST: string = env("HOST") || "";
export const BILLING_ENABLED: boolean = env("BILLING_ENABLED") === "true";
export const IS_ENTERPRISE: boolean = env("IS_ENTERPRISE") === "true";
export const BILLING_PUBLIC_KEY: string = env("BILLING_PUBLIC_KEY") || "";
// VAPID Configuration for Push Notifications

View file

@ -7,6 +7,7 @@ import { JSONObject } from "Common/Types/JSON";
import API from "Common/Utils/API";
import Footer from "Common/UI/Components/Footer/Footer";
import ConfirmModal from "Common/UI/Components/Modal/ConfirmModal";
import EditionLabel from "Common/UI/Components/EditionLabel/EditionLabel";
import { HOST, HTTP_PROTOCOL } from "Common/UI/Config";
import React from "react";
@ -66,6 +67,9 @@ const DashboardFooter: () => JSX.Element = () => {
return (
<>
<div className="bg-white px-8 pb-4 flex justify-center">
<EditionLabel />
</div>
<Footer
className="bg-white px-8"
copyright="HackerBay, Inc."

View file

@ -52,6 +52,9 @@ ENVIRONMENT=production
# What image should we pull from docker hub. This only applies when the ENVIRONMENT is production or test
APP_TAG=release
# Change this to true if you are using enterprise edition. Keep it false if you are using community edition.
IS_ENTERPRISE=false
# What is the name of the docker compose project. This is used to prefix the docker containers.
COMPOSE_PROJECT_NAME=oneuptime

View file

@ -14,6 +14,7 @@ x-common-variables: &common-variables
NODE_ENV: ${ENVIRONMENT}
BILLING_ENABLED: ${BILLING_ENABLED}
IS_ENTERPRISE: ${IS_ENTERPRISE}
BILLING_PUBLIC_KEY: ${BILLING_PUBLIC_KEY}
SUBSCRIPTION_PLAN_BASIC: ${SUBSCRIPTION_PLAN_BASIC}
SUBSCRIPTION_PLAN_GROWTH: ${SUBSCRIPTION_PLAN_GROWTH}
@ -444,6 +445,7 @@ services:
PORT: ${ISOLATED_VM_PORT}
ONEUPTIME_SECRET: ${ONEUPTIME_SECRET}
DISABLE_TELEMETRY: ${DISABLE_TELEMETRY_FOR_ISOLATED_VM}
IS_ENTERPRISE: ${IS_ENTERPRISE}
logging:
driver: "local"
options:
@ -533,6 +535,7 @@ services:
DISABLE_COPILOT: ${DISABLE_COPILOT}
OPENAI_API_KEY: ${COPILOT_OPENAI_API_KEY}
LOG_LEVEL: ${LOG_LEVEL}
IS_ENTERPRISE: ${IS_ENTERPRISE}
logging:
driver: "local"
options: