mirror of
https://github.com/fosrl/pangolin.git
synced 2026-06-24 00:02:04 +00:00
💄 Show updates available in the frontend, on sites & user devices
This commit is contained in:
@@ -2957,6 +2957,7 @@
|
|||||||
"orgOrDomainIdMissing": "Organization or Domain ID is missing",
|
"orgOrDomainIdMissing": "Organization or Domain ID is missing",
|
||||||
"loadingDNSRecords": "Loading DNS records...",
|
"loadingDNSRecords": "Loading DNS records...",
|
||||||
"olmUpdateAvailableInfo": "An updated version of Olm is available. Please update to the latest version for the best experience.",
|
"olmUpdateAvailableInfo": "An updated version of Olm is available. Please update to the latest version for the best experience.",
|
||||||
|
"updateAvailableInfo": "An updated version is available. Please update to the latest version for the best experience.",
|
||||||
"client": "Client",
|
"client": "Client",
|
||||||
"proxyProtocol": "Proxy Protocol Settings",
|
"proxyProtocol": "Proxy Protocol Settings",
|
||||||
"proxyProtocolDescription": "Configure Proxy Protocol to preserve client IP addresses for TCP services.",
|
"proxyProtocolDescription": "Configure Proxy Protocol to preserve client IP addresses for TCP services.",
|
||||||
|
|||||||
@@ -41,6 +41,13 @@ import { useParams } from "next/navigation";
|
|||||||
import { FaApple, FaWindows, FaLinux } from "react-icons/fa";
|
import { FaApple, FaWindows, FaLinux } from "react-icons/fa";
|
||||||
import { SiAndroid } from "react-icons/si";
|
import { SiAndroid } from "react-icons/si";
|
||||||
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||||
|
import {
|
||||||
|
productUpdatesQueries,
|
||||||
|
type LatestVersionResponse
|
||||||
|
} from "@app/lib/queries";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import semver from "semver";
|
||||||
|
import { InfoPopup } from "@app/components/ui/info-popup";
|
||||||
|
|
||||||
function formatTimestamp(timestamp: number | null | undefined): string {
|
function formatTimestamp(timestamp: number | null | undefined): string {
|
||||||
if (!timestamp) return "-";
|
if (!timestamp) return "-";
|
||||||
@@ -166,6 +173,34 @@ export default function GeneralPage() {
|
|||||||
}>(null);
|
}>(null);
|
||||||
const [isCheckingCache, setIsCheckingCache] = useState(false);
|
const [isCheckingCache, setIsCheckingCache] = useState(false);
|
||||||
const [isRebuildingCache, setIsRebuildingCache] = useState(false);
|
const [isRebuildingCache, setIsRebuildingCache] = useState(false);
|
||||||
|
const data = useQuery(productUpdatesQueries.latestVersion(true));
|
||||||
|
const latestPlatformVersions = data.data?.data;
|
||||||
|
|
||||||
|
const agentVersionMap: Record<string, string> = {
|
||||||
|
"Pangolin Windows": "windows",
|
||||||
|
"Pangolin Android": "android",
|
||||||
|
"Pangolin iOS": "ios",
|
||||||
|
"Pangolin iPadOS": "ios",
|
||||||
|
"Pangolin macOS": "mac",
|
||||||
|
"Pangolin CLI": "cli",
|
||||||
|
"Olm CLI": "olm"
|
||||||
|
};
|
||||||
|
|
||||||
|
let updateAvailable = false;
|
||||||
|
if (client.agent && client.olmVersion && latestPlatformVersions) {
|
||||||
|
const agent = agentVersionMap[
|
||||||
|
client.agent
|
||||||
|
] as keyof LatestVersionResponse;
|
||||||
|
|
||||||
|
if (agent in latestPlatformVersions) {
|
||||||
|
const agentVersion = latestPlatformVersions[agent];
|
||||||
|
|
||||||
|
updateAvailable = semver.lte(
|
||||||
|
client.olmVersion,
|
||||||
|
agentVersion.latestVersion
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// get "imp" from local storage to determine if we should show the verify button (imp = "1" means show)
|
// get "imp" from local storage to determine if we should show the verify button (imp = "1" means show)
|
||||||
const showVerifyButton =
|
const showVerifyButton =
|
||||||
@@ -451,11 +486,21 @@ export default function GeneralPage() {
|
|||||||
{t("agent")}
|
{t("agent")}
|
||||||
</InfoSectionTitle>
|
</InfoSectionTitle>
|
||||||
<InfoSectionContent>
|
<InfoSectionContent>
|
||||||
<Badge variant="secondary">
|
<div className="flex items-center">
|
||||||
{client.agent +
|
<Badge variant="secondary">
|
||||||
" v" +
|
{client.agent +
|
||||||
client.olmVersion}
|
" v" +
|
||||||
</Badge>
|
client.olmVersion}
|
||||||
|
</Badge>
|
||||||
|
|
||||||
|
{updateAvailable && (
|
||||||
|
<InfoPopup
|
||||||
|
info={t(
|
||||||
|
"updateAvailableInfo"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</InfoSectionContent>
|
</InfoSectionContent>
|
||||||
</InfoSection>
|
</InfoSection>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
|||||||
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||||
import { LabelColumnFilterButton } from "./LabelColumnFilterButton";
|
import { LabelColumnFilterButton } from "./LabelColumnFilterButton";
|
||||||
import { LabelsTableCell } from "./LabelsTableCell";
|
import { LabelsTableCell } from "./LabelsTableCell";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { productUpdatesQueries } from "@app/lib/queries";
|
||||||
|
import semver from "semver";
|
||||||
|
|
||||||
export type SiteRow = {
|
export type SiteRow = {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -113,12 +116,11 @@ export default function SitesTable({
|
|||||||
const api = createApiClient(useEnvContext());
|
const api = createApiClient(useEnvContext());
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
|
||||||
// useEffect(() => {
|
const { data: latestVersions } = useQuery(
|
||||||
// const interval = setInterval(() => {
|
productUpdatesQueries.latestVersion(true)
|
||||||
// router.refresh();
|
);
|
||||||
// }, 30_000);
|
|
||||||
// return () => clearInterval(interval);
|
const latestNewtVersion = latestVersions?.data?.newt?.latestVersion;
|
||||||
// }, []);
|
|
||||||
|
|
||||||
const booleanSearchFilterSchema = z
|
const booleanSearchFilterSchema = z
|
||||||
.enum(["true", "false"])
|
.enum(["true", "false"])
|
||||||
@@ -333,6 +335,11 @@ export default function SitesTable({
|
|||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const originalRow = row.original;
|
const originalRow = row.original;
|
||||||
|
|
||||||
|
let updateAvailable =
|
||||||
|
latestNewtVersion &&
|
||||||
|
originalRow.newtVersion &&
|
||||||
|
semver.lt(originalRow.newtVersion, latestNewtVersion);
|
||||||
|
|
||||||
if (originalRow.type === "newt") {
|
if (originalRow.type === "newt") {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center space-x-1">
|
<div className="flex items-center space-x-1">
|
||||||
@@ -346,7 +353,7 @@ export default function SitesTable({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Badge>
|
</Badge>
|
||||||
{originalRow.newtUpdateAvailable && (
|
{updateAvailable && (
|
||||||
<InfoPopup
|
<InfoPopup
|
||||||
info={t("newtUpdateAvailableInfo")}
|
info={t("newtUpdateAvailableInfo")}
|
||||||
/>
|
/>
|
||||||
@@ -561,7 +568,7 @@ export default function SitesTable({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return cols;
|
return cols;
|
||||||
}, [isLabelFeatureEnabled, orgId, t, searchParams]);
|
}, [isLabelFeatureEnabled, orgId, t, searchParams, latestNewtVersion]);
|
||||||
|
|
||||||
function toggleSort(column: string) {
|
function toggleSort(column: string) {
|
||||||
const newSearch = getNextSortOrder(column, searchParams);
|
const newSearch = getNextSortOrder(column, searchParams);
|
||||||
|
|||||||
@@ -38,6 +38,12 @@ import { ColumnFilterButton } from "./ColumnFilterButton";
|
|||||||
import IdpTypeBadge from "./IdpTypeBadge";
|
import IdpTypeBadge from "./IdpTypeBadge";
|
||||||
import { Badge } from "./ui/badge";
|
import { Badge } from "./ui/badge";
|
||||||
import { ControlledDataTable } from "./ui/controlled-data-table";
|
import { ControlledDataTable } from "./ui/controlled-data-table";
|
||||||
|
import {
|
||||||
|
productUpdatesQueries,
|
||||||
|
type LatestVersionResponse
|
||||||
|
} from "@app/lib/queries";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import semver from "semver";
|
||||||
|
|
||||||
export type ClientRow = {
|
export type ClientRow = {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -100,6 +106,9 @@ export default function UserDevicesTable({
|
|||||||
searchParams
|
searchParams
|
||||||
} = useNavigationContext();
|
} = useNavigationContext();
|
||||||
const [isRefreshing, startTransition] = useTransition();
|
const [isRefreshing, startTransition] = useTransition();
|
||||||
|
const data = useQuery(productUpdatesQueries.latestVersion(true));
|
||||||
|
|
||||||
|
const latestPlatformVersions = data.data?.data;
|
||||||
|
|
||||||
const defaultUserColumnVisibility = {
|
const defaultUserColumnVisibility = {
|
||||||
subnet: false,
|
subnet: false,
|
||||||
@@ -555,6 +564,37 @@ export default function UserDevicesTable({
|
|||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const originalRow = row.original;
|
const originalRow = row.original;
|
||||||
|
|
||||||
|
const agentVersionMap: Record<string, string> = {
|
||||||
|
"Pangolin Windows": "windows",
|
||||||
|
"Pangolin Android": "android",
|
||||||
|
"Pangolin iOS": "ios",
|
||||||
|
"Pangolin iPadOS": "ios",
|
||||||
|
"Pangolin macOS": "mac",
|
||||||
|
"Pangolin CLI": "cli",
|
||||||
|
"Olm CLI": "olm"
|
||||||
|
};
|
||||||
|
|
||||||
|
let updateAvailable = false;
|
||||||
|
|
||||||
|
if (
|
||||||
|
originalRow.olmVersion &&
|
||||||
|
originalRow.agent &&
|
||||||
|
latestPlatformVersions
|
||||||
|
) {
|
||||||
|
const agent = agentVersionMap[
|
||||||
|
originalRow.agent
|
||||||
|
] as keyof LatestVersionResponse;
|
||||||
|
|
||||||
|
if (agent in latestPlatformVersions) {
|
||||||
|
const agentVersion = latestPlatformVersions[agent];
|
||||||
|
|
||||||
|
updateAvailable = semver.lt(
|
||||||
|
originalRow.olmVersion,
|
||||||
|
agentVersion.latestVersion
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center space-x-1">
|
<div className="flex items-center space-x-1">
|
||||||
{originalRow.agent && originalRow.olmVersion ? (
|
{originalRow.agent && originalRow.olmVersion ? (
|
||||||
@@ -567,9 +607,9 @@ export default function UserDevicesTable({
|
|||||||
"-"
|
"-"
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/*originalRow.olmUpdateAvailable && (
|
{updateAvailable && (
|
||||||
<InfoPopup info={t("olmUpdateAvailableInfo")} />
|
<InfoPopup info={t("updateAvailableInfo")} />
|
||||||
)*/}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -714,7 +754,7 @@ export default function UserDevicesTable({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return allOptions;
|
return allOptions;
|
||||||
}, [t]);
|
}, [t, latestPlatformVersions]);
|
||||||
|
|
||||||
function handleFilterChange(
|
function handleFilterChange(
|
||||||
column: string,
|
column: string,
|
||||||
|
|||||||
Reference in New Issue
Block a user