Merge branch 'dev' into private-http-ha

This commit is contained in:
Owen
2026-04-13 20:52:47 -07:00
46 changed files with 3017 additions and 2559 deletions

View File

@@ -49,6 +49,7 @@ import { usePaidStatus } from "@/hooks/usePaidStatus";
import { TierFeature, tierMatrix } from "@server/lib/billing/tierMatrix";
import { toUnicode } from "punycode";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useUserContext } from "@app/hooks/useUserContext";
type AvailableOption = {
domainNamespaceId: string;
@@ -97,10 +98,16 @@ export default function DomainPicker({
warnOnProvidedDomain = false
}: DomainPickerProps) {
const { env } = useEnvContext();
const { user } = useUserContext();
const api = createApiClient({ env });
const t = useTranslations();
const { hasSaasSubscription } = usePaidStatus();
const requiresPaywall =
build === "saas" &&
!hasSaasSubscription(tierMatrix[TierFeature.DomainNamespaces]) &&
new Date(user.dateCreated) > new Date("2026-04-13");
const { data = [], isLoading: loadingDomains } = useQuery(
orgQueries.domains({ orgId })
);
@@ -659,6 +666,7 @@ export default function DomainPicker({
})
}
className="mx-2 rounded-md"
disabled={requiresPaywall}
>
<div className="flex items-center justify-center w-8 h-8 rounded-lg bg-primary/10 mr-3">
<Zap className="h-4 w-4 text-primary" />
@@ -699,11 +707,7 @@ export default function DomainPicker({
</div>
</div>
{build === "saas" &&
!hasSaasSubscription(
tierMatrix[TierFeature.DomainNamespaces]
) &&
!hideFreeDomain && (
{requiresPaywall && !hideFreeDomain && (
<Card className="mt-3 border-black-500/30 bg-linear-to-br from-black-500/10 via-background to-background overflow-hidden">
<CardContent className="py-3 px-4">
<div className="flex items-center gap-2.5 text-sm text-muted-foreground">

View File

@@ -54,6 +54,7 @@ export type TargetHealth = {
port: number;
enabled: boolean;
healthStatus: "healthy" | "unhealthy" | "unknown" | null;
siteName: string | null;
};
export type ResourceRow = {
@@ -274,7 +275,9 @@ export default function ProxyResourcesTable({
}
className="h-3 w-3"
/>
{`${target.ip}:${target.port}`}
{target.siteName
? `${target.siteName} (${target.ip}:${target.port})`
: `${target.ip}:${target.port}`}
</div>
<span
className={`capitalize ${
@@ -301,7 +304,9 @@ export default function ProxyResourcesTable({
status="unknown"
className="h-3 w-3"
/>
{`${target.ip}:${target.port}`}
{target.siteName
? `${target.siteName} (${target.ip}:${target.port})`
: `${target.ip}:${target.port}`}
</div>
<span className="text-muted-foreground">
{!target.enabled

View File

@@ -10,6 +10,7 @@ import { Button } from "./ui/button";
import { TicketCheck } from "lucide-react";
import { useTranslations } from "next-intl";
import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext";
import { useUserContext } from "@app/hooks/useUserContext";
import Link from "next/link";
interface SidebarLicenseButtonProps {
@@ -20,8 +21,11 @@ export default function SidebarLicenseButton({
isCollapsed = false
}: SidebarLicenseButtonProps) {
const { licenseStatus, updateLicenseStatus } = useLicenseStatusContext();
const { user } = useUserContext();
const url = "https://docs.pangolin.net/self-host/enterprise-edition";
const url = user?.serverAdmin
? "/admin/license"
: "https://docs.pangolin.net/self-host/enterprise-edition";
const t = useTranslations();

View File

@@ -388,7 +388,7 @@ export default function UserDevicesTable({
},
{
accessorKey: "online",
friendlyName: t("online"),
friendlyName: t("connected"),
header: () => {
return (
<ColumnFilterButton
@@ -410,7 +410,7 @@ export default function UserDevicesTable({
}
searchPlaceholder={t("searchPlaceholder")}
emptyMessage={t("emptySearchOptions")}
label={t("online")}
label={t("connected")}
className="p-3"
/>
);

View File

@@ -237,7 +237,7 @@ function drawInteractiveCountries(
return svg;
}
type WorldJsonCountryData = { properties: { name: string; a3: string } };
type WorldJsonCountryData = d3.ExtendedFeature<d3.GeoGeometryObjects | null, { name: string; a3: string }>;
function parseWorldTopoJsonToGeoJsonFeatures(): Array<WorldJsonCountryData> {
const collection = topojson.feature(