Merge pull request #3277 from fosrl/dev

Fix middleware and suppoter footer
This commit is contained in:
Owen Schwartz
2026-06-14 14:44:33 -07:00
committed by GitHub
7 changed files with 177 additions and 177 deletions

View File

@@ -1294,16 +1294,14 @@ export async function getTraefikConfig(
} }
}; };
// Assets router at higher priority so /_next files load without rewrite // Assets router at higher priority so /_next files load without rewrite.
// Do NOT apply the path-rewrite middleware here — static assets must
// keep their original path; only the host headers are needed.
config_output.http.routers![ config_output.http.routers![
`bg-r${bgResource.resourceId}-assets-router` `bg-r${bgResource.resourceId}-assets-router`
] = { ] = {
entryPoints: [entrypoint], entryPoints: [entrypoint],
middlewares: [ middlewares: [...routerMiddlewares, uiHeadersMiddlewareName],
...routerMiddlewares,
uiRewriteMiddlewareName,
uiHeadersMiddlewareName
],
service: bgUiServiceName, service: bgUiServiceName,
rule: `${hostRule} && (PathPrefix(\`/_next\`) || PathRegexp(\`^/__nextjs*\`) || Path(\`/favicon.ico\`))`, rule: `${hostRule} && (PathPrefix(\`/_next\`) || PathRegexp(\`^/__nextjs*\`) || Path(\`/favicon.ico\`))`,
priority: 101, priority: 101,

View File

@@ -3,7 +3,6 @@ import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
import logger from "@server/logger"; import logger from "@server/logger";
import { response as sendResponse } from "@server/lib/response"; import { response as sendResponse } from "@server/lib/response";
import config from "@server/lib/config";
import { build } from "@server/build"; import { build } from "@server/build";
import { APP_VERSION } from "@server/lib/consts"; import { APP_VERSION } from "@server/lib/consts";
import license from "#dynamic/license/license"; import license from "#dynamic/license/license";
@@ -22,9 +21,6 @@ export async function getServerInfo(
next: NextFunction next: NextFunction
): Promise<any> { ): Promise<any> {
try { try {
const supporterData = config.getSupporterData();
const supporterStatusValid = supporterData?.valid || false;
let enterpriseLicenseValid = false; let enterpriseLicenseValid = false;
let enterpriseLicenseType: string | null = null; let enterpriseLicenseType: string | null = null;
@@ -41,7 +37,7 @@ export async function getServerInfo(
return sendResponse<GetServerInfoResponse>(res, { return sendResponse<GetServerInfoResponse>(res, {
data: { data: {
version: APP_VERSION, version: APP_VERSION,
supporterStatusValid, supporterStatusValid: true,
build, build,
enterpriseLicenseValid, enterpriseLicenseValid,
enterpriseLicenseType enterpriseLicenseType

View File

@@ -42,7 +42,14 @@ import {
SettingsSectionFooter SettingsSectionFooter
} from "@app/components/Settings"; } from "@app/components/Settings";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import { ArrowRight, Check, ExternalLink, Heart, InfoIcon, TicketCheck } from "lucide-react"; import {
ArrowRight,
Check,
ExternalLink,
Heart,
InfoIcon,
TicketCheck
} from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import DismissableBanner from "@app/components/DismissableBanner"; import DismissableBanner from "@app/components/DismissableBanner";
import CopyTextBox from "@app/components/CopyTextBox"; import CopyTextBox from "@app/components/CopyTextBox";
@@ -50,7 +57,7 @@ import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
import { SitePriceCalculator } from "@app/components/SitePriceCalculator"; import { SitePriceCalculator } from "@app/components/SitePriceCalculator";
import { Checkbox } from "@app/components/ui/checkbox"; import { Checkbox } from "@app/components/ui/checkbox";
import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert";
import { useSupporterStatusContext } from "@app/hooks/useSupporterStatusContext"; // import { useSupporterStatusContext } from "@app/hooks/useSupporterStatusContext";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
const ENTERPRISE_DOCS_URL = const ENTERPRISE_DOCS_URL =
@@ -82,7 +89,7 @@ export default function LicensePage() {
const [isActivatingLicense, setIsActivatingLicense] = useState(false); const [isActivatingLicense, setIsActivatingLicense] = useState(false);
const [isDeletingLicense, setIsDeletingLicense] = useState(false); const [isDeletingLicense, setIsDeletingLicense] = useState(false);
const [isRecheckingLicense, setIsRecheckingLicense] = useState(false); const [isRecheckingLicense, setIsRecheckingLicense] = useState(false);
const { supporterStatus } = useSupporterStatusContext(); // const { supporterStatus } = useSupporterStatusContext();
const t = useTranslations(); const t = useTranslations();
@@ -347,9 +354,7 @@ export default function LicensePage() {
storageKey="license-banner-dismissed" storageKey="license-banner-dismissed"
version={1} version={1}
title={t("licenseBannerTitle")} title={t("licenseBannerTitle")}
titleIcon={ titleIcon={<TicketCheck className="w-5 h-5 text-primary" />}
<TicketCheck className="w-5 h-5 text-primary" />
}
description={t("licenseBannerDescription")} description={t("licenseBannerDescription")}
> >
<Link <Link

View File

@@ -68,15 +68,15 @@ export default async function RootLayout({
const env = pullEnv(); const env = pullEnv();
const locale = await getLocale(); const locale = await getLocale();
const supporterData = { // const supporterData = {
visible: true // visible: true
} as any; // } as any;
const res = await priv.get<AxiosResponse<IsSupporterKeyVisibleResponse>>( // const res = await priv.get<AxiosResponse<IsSupporterKeyVisibleResponse>>(
"supporter-key/visible" // "supporter-key/visible"
); // );
supporterData.visible = res.data.data.visible; // supporterData.visible = res.data.data.visible;
supporterData.tier = res.data.data.tier; // supporterData.tier = res.data.data.tier;
let licenseStatus: GetLicenseStatusResponse; let licenseStatus: GetLicenseStatusResponse;
if (build === "enterprise") { if (build === "enterprise") {
@@ -127,20 +127,20 @@ export default async function RootLayout({
<LicenseStatusProvider <LicenseStatusProvider
licenseStatus={licenseStatus} licenseStatus={licenseStatus}
> >
<SupportStatusProvider {/* <SupportStatusProvider
supporterStatus={supporterData} supporterStatus={supporterData}
> > */}
{/* Main content */} {/* Main content */}
<div className="h-full flex flex-col"> <div className="h-full flex flex-col">
<div className="flex-1 overflow-auto"> <div className="flex-1 overflow-auto">
<SplashImage> <SplashImage>
<LicenseViolation />
{children}
</SplashImage>
<LicenseViolation /> <LicenseViolation />
</div> {children}
</SplashImage>
<LicenseViolation />
</div> </div>
</SupportStatusProvider> </div>
{/* </SupportStatusProvider> */}
</LicenseStatusProvider> </LicenseStatusProvider>
<Toaster /> <Toaster />
</TanstackQueryProvider> </TanstackQueryProvider>

View File

@@ -1,24 +1,24 @@
"use client"; "use client";
import { useSupporterStatusContext } from "@app/hooks/useSupporterStatusContext"; // import { useSupporterStatusContext } from "@app/hooks/useSupporterStatusContext";
import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { build } from "@server/build"; import { build } from "@server/build";
export default function AuthPageFooterNotices() { export default function AuthPageFooterNotices() {
const t = useTranslations(); const t = useTranslations();
const { supporterStatus } = useSupporterStatusContext(); // const { supporterStatus } = useSupporterStatusContext();
const { isUnlocked, licenseStatus } = useLicenseStatusContext(); const { isUnlocked, licenseStatus } = useLicenseStatusContext();
return ( return (
<> <>
{supporterStatus?.visible && ( {/* {supporterStatus?.visible && (
<div className="text-center mt-2"> <div className="text-center mt-2">
<span className="text-sm text-muted-foreground opacity-50"> <span className="text-sm text-muted-foreground opacity-50">
{t("noSupportKey")} {t("noSupportKey")}
</span> </span>
</div> </div>
)} )} */}
{build === "enterprise" && !isUnlocked() ? ( {build === "enterprise" && !isUnlocked() ? (
<div className="text-center mt-2"> <div className="text-center mt-2">
<span className="text-sm font-medium text-muted-foreground"> <span className="text-sm font-medium text-muted-foreground">

View File

@@ -9,33 +9,34 @@ export default function SupporterMessage({ tier }: { tier: string }) {
const t = useTranslations(); const t = useTranslations();
return ( return (
<div className="relative flex items-center space-x-2 whitespace-nowrap group"> <></>
<span // <div className="relative flex items-center space-x-2 whitespace-nowrap group">
className="cursor-pointer" // <span
onClick={(e) => { // className="cursor-pointer"
// Get the bounding box of the element // onClick={(e) => {
const rect = ( // // Get the bounding box of the element
e.target as HTMLElement // const rect = (
).getBoundingClientRect(); // e.target as HTMLElement
// ).getBoundingClientRect();
// Trigger confetti centered on the word "Pangolin" // // Trigger confetti centered on the word "Pangolin"
confetti({ // confetti({
particleCount: 100, // particleCount: 100,
spread: 70, // spread: 70,
origin: { // origin: {
x: (rect.left + rect.width / 2) / window.innerWidth, // x: (rect.left + rect.width / 2) / window.innerWidth,
y: rect.top / window.innerHeight // y: rect.top / window.innerHeight
}, // },
colors: ["#FFA500", "#FF4500", "#FFD700"] // colors: ["#FFA500", "#FF4500", "#FFD700"]
}); // });
}} // }}
> // >
Pangolin // Pangolin
</span> // </span>
<Star className="w-3 h-3" /> // <Star className="w-3 h-3" />
<div className="absolute left-1/2 transform -translate-x-1/2 -top-10 hidden group-hover:block text-primary text-sm rounded-md border shadow-md px-4 py-2 pointer-events-none opacity-0 group-hover:opacity-100 transition-opacity"> // <div className="absolute left-1/2 transform -translate-x-1/2 -top-10 hidden group-hover:block text-primary text-sm rounded-md border shadow-md px-4 py-2 pointer-events-none opacity-0 group-hover:opacity-100 transition-opacity">
{t("componentsSupporterMessage", { tier: tier })} // {t("componentsSupporterMessage", { tier: tier })}
</div> // </div>
</div> // </div>
); );
} }

View File

@@ -3,7 +3,7 @@
// THIS IS DEPRECATED AND IS NO LONGER SHOWED TO THE USER WITH THE DISCONTINUATION // THIS IS DEPRECATED AND IS NO LONGER SHOWED TO THE USER WITH THE DISCONTINUATION
// OF THE SUPPORTER PROGRAM. IT MAY BE REMOVED IN A FUTURE UPDATE. // OF THE SUPPORTER PROGRAM. IT MAY BE REMOVED IN A FUTURE UPDATE.
import { useSupporterStatusContext } from "@app/hooks/useSupporterStatusContext"; // import { useSupporterStatusContext } from "@app/hooks/useSupporterStatusContext";
import { useState, useTransition } from "react"; import { useState, useTransition } from "react";
import { import {
Tooltip, Tooltip,
@@ -58,134 +58,134 @@ interface SupporterStatusProps {
export default function SupporterStatus({ export default function SupporterStatus({
isCollapsed = false isCollapsed = false
}: SupporterStatusProps) { }: SupporterStatusProps) {
const { supporterStatus, updateSupporterStatus } = // const { supporterStatus, updateSupporterStatus } =
useSupporterStatusContext(); // useSupporterStatusContext();
const [supportOpen, setSupportOpen] = useState(false); // const [supportOpen, setSupportOpen] = useState(false);
const [keyOpen, setKeyOpen] = useState(false); // const [keyOpen, setKeyOpen] = useState(false);
const [purchaseOptionsOpen, setPurchaseOptionsOpen] = useState(false); // const [purchaseOptionsOpen, setPurchaseOptionsOpen] = useState(false);
const { env } = useEnvContext(); // const { env } = useEnvContext();
const api = createApiClient({ env }); // const api = createApiClient({ env });
const t = useTranslations(); // const t = useTranslations();
const formSchema = z.object({ // const formSchema = z.object({
githubUsername: z.string().nonempty({ // githubUsername: z.string().nonempty({
error: "GitHub username is required" // error: "GitHub username is required"
}), // }),
key: z.string().nonempty({ // key: z.string().nonempty({
error: "Supporter key is required" // error: "Supporter key is required"
}) // })
}); // });
const form = useForm({ // const form = useForm({
resolver: zodResolver(formSchema), // resolver: zodResolver(formSchema),
defaultValues: { // defaultValues: {
githubUsername: "", // githubUsername: "",
key: "" // key: ""
} // }
}); // });
async function hide() { // async function hide() {
await api.post("/supporter-key/hide"); // await api.post("/supporter-key/hide");
updateSupporterStatus({ // updateSupporterStatus({
visible: false // visible: false
}); // });
} // }
async function onSubmit(values: z.infer<typeof formSchema>) { // async function onSubmit(values: z.infer<typeof formSchema>) {
try { // try {
const res = await api.post< // const res = await api.post<
AxiosResponse<ValidateSupporterKeyResponse> // AxiosResponse<ValidateSupporterKeyResponse>
>("/supporter-key/validate", { // >("/supporter-key/validate", {
githubUsername: values.githubUsername, // githubUsername: values.githubUsername,
key: values.key // key: values.key
}); // });
const data = res.data.data; // const data = res.data.data;
if (!data || !data.valid) { // if (!data || !data.valid) {
toast({ // toast({
variant: "destructive", // variant: "destructive",
title: t("supportKeyInvalid"), // title: t("supportKeyInvalid"),
description: t("supportKeyInvalidDescription") // description: t("supportKeyInvalidDescription")
}); // });
return; // return;
} // }
// Trigger the toast // // Trigger the toast
toast({ // toast({
variant: "default", // variant: "default",
title: t("supportKeyValid"), // title: t("supportKeyValid"),
description: t("supportKeyValidDescription") // description: t("supportKeyValidDescription")
}); // });
// Fireworks-style confetti // // Fireworks-style confetti
const duration = 5 * 1000; // 5 seconds // const duration = 5 * 1000; // 5 seconds
const animationEnd = Date.now() + duration; // const animationEnd = Date.now() + duration;
const defaults = { // const defaults = {
startVelocity: 30, // startVelocity: 30,
spread: 360, // spread: 360,
ticks: 60, // ticks: 60,
zIndex: 0, // zIndex: 0,
colors: ["#FFA500", "#FF4500", "#FFD700"] // Orange hues // colors: ["#FFA500", "#FF4500", "#FFD700"] // Orange hues
}; // };
function randomInRange(min: number, max: number) { // function randomInRange(min: number, max: number) {
return Math.random() * (max - min) + min; // return Math.random() * (max - min) + min;
} // }
const interval = setInterval(() => { // const interval = setInterval(() => {
const timeLeft = animationEnd - Date.now(); // const timeLeft = animationEnd - Date.now();
if (timeLeft <= 0) { // if (timeLeft <= 0) {
clearInterval(interval); // clearInterval(interval);
return; // return;
} // }
const particleCount = 50 * (timeLeft / duration); // const particleCount = 50 * (timeLeft / duration);
// Launch confetti from two random horizontal positions // // Launch confetti from two random horizontal positions
confetti({ // confetti({
...defaults, // ...defaults,
particleCount, // particleCount,
origin: { // origin: {
x: randomInRange(0.1, 0.3), // x: randomInRange(0.1, 0.3),
y: Math.random() - 0.2 // y: Math.random() - 0.2
} // }
}); // });
confetti({ // confetti({
...defaults, // ...defaults,
particleCount, // particleCount,
origin: { // origin: {
x: randomInRange(0.7, 0.9), // x: randomInRange(0.7, 0.9),
y: Math.random() - 0.2 // y: Math.random() - 0.2
} // }
}); // });
}, 250); // }, 250);
setPurchaseOptionsOpen(false); // setPurchaseOptionsOpen(false);
setKeyOpen(false); // setKeyOpen(false);
updateSupporterStatus({ // updateSupporterStatus({
visible: false // visible: false
}); // });
} catch (error) { // } catch (error) {
toast({ // toast({
variant: "destructive", // variant: "destructive",
title: t("error"), // title: t("error"),
description: formatAxiosError( // description: formatAxiosError(
error, // error,
t("supportKeyErrorValidationDescription") // t("supportKeyErrorValidationDescription")
) // )
}); // });
return; // return;
} // }
} // }
return ( return (
<> <>
<Credenza {/* <Credenza
open={purchaseOptionsOpen} open={purchaseOptionsOpen}
onOpenChange={(val) => { onOpenChange={(val) => {
setPurchaseOptionsOpen(val); setPurchaseOptionsOpen(val);
@@ -469,7 +469,7 @@ export default function SupporterStatus({
{t("supportKeyBuy")} {t("supportKeyBuy")}
</Button> </Button>
) )
) : null} ) : null} */}
</> </>
); );
} }