mirror of
https://github.com/fosrl/pangolin.git
synced 2026-09-15 15:19:51 +02:00
Handle the agent and agent version
This commit is contained in:
@@ -153,6 +153,7 @@
|
|||||||
"siteResourcesTargetsOnSite": "Targets on this site",
|
"siteResourcesTargetsOnSite": "Targets on this site",
|
||||||
"siteSetting": "{siteName} Settings",
|
"siteSetting": "{siteName} Settings",
|
||||||
"siteNewtTunnel": "Pangolin Site (Recommended)",
|
"siteNewtTunnel": "Pangolin Site (Recommended)",
|
||||||
|
"pangolinSite": "Pangolin Site",
|
||||||
"siteNewtTunnelDescription": "Easiest way to create an entrypoint into any network. No extra setup.",
|
"siteNewtTunnelDescription": "Easiest way to create an entrypoint into any network. No extra setup.",
|
||||||
"siteWg": "Basic WireGuard",
|
"siteWg": "Basic WireGuard",
|
||||||
"siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.",
|
"siteWgDescription": "Use any WireGuard client to establish a tunnel. Manual NAT setup required.",
|
||||||
|
|||||||
@@ -682,7 +682,7 @@ export const newts = pgTable(
|
|||||||
secretHash: varchar("secretHash").notNull(),
|
secretHash: varchar("secretHash").notNull(),
|
||||||
dateCreated: varchar("dateCreated").notNull(),
|
dateCreated: varchar("dateCreated").notNull(),
|
||||||
version: varchar("version"),
|
version: varchar("version"),
|
||||||
agent: varchar("agent").default("newt"), // either newt or cli
|
agent: varchar("agent"), // either newt or cli
|
||||||
agentVersion: varchar("agentVersion"),
|
agentVersion: varchar("agentVersion"),
|
||||||
siteId: integer("siteId").references(() => sites.siteId, {
|
siteId: integer("siteId").references(() => sites.siteId, {
|
||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
|
|||||||
@@ -703,7 +703,7 @@ export const newts = sqliteTable(
|
|||||||
secretHash: text("secretHash").notNull(),
|
secretHash: text("secretHash").notNull(),
|
||||||
dateCreated: text("dateCreated").notNull(),
|
dateCreated: text("dateCreated").notNull(),
|
||||||
version: text("version"),
|
version: text("version"),
|
||||||
agent: text("agent").default("newt"), // either newt or cli
|
agent: text("agent"), // either newt or cli
|
||||||
agentVersion: text("agentVersion"),
|
agentVersion: text("agentVersion"),
|
||||||
siteId: integer("siteId").references(() => sites.siteId, {
|
siteId: integer("siteId").references(() => sites.siteId, {
|
||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
|
|||||||
@@ -171,7 +171,11 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => {
|
|||||||
logger.error(`Failed to add peer to exit node: ${error}`);
|
logger.error(`Failed to add peer to exit node: ${error}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newtVersion && newtVersion !== newt.version) {
|
if (
|
||||||
|
newtVersion !== newt.version ||
|
||||||
|
agent !== newt.agent ||
|
||||||
|
agentVersion !== newt.agentVersion
|
||||||
|
) {
|
||||||
// update the newt version in the database
|
// update the newt version in the database
|
||||||
await db
|
await db
|
||||||
.update(newts)
|
.update(newts)
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import {
|
|||||||
InfoSections,
|
InfoSections,
|
||||||
InfoSectionTitle
|
InfoSectionTitle
|
||||||
} from "@app/components/InfoSection";
|
} from "@app/components/InfoSection";
|
||||||
import { Badge } from "@app/components/ui/badge";
|
|
||||||
import { Button } from "@app/components/ui/button";
|
import { Button } from "@app/components/ui/button";
|
||||||
import ActionBanner from "@app/components/ActionBanner";
|
import ActionBanner from "@app/components/ActionBanner";
|
||||||
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||||
@@ -41,13 +40,6 @@ 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 "-";
|
||||||
@@ -173,34 +165,6 @@ 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 = Boolean(
|
|
||||||
semver.valid(client.olmVersion) &&
|
|
||||||
semver.lt(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 =
|
||||||
@@ -467,7 +431,7 @@ export default function GeneralPage() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Device Information Section */}
|
{/* Device Information Section */}
|
||||||
{(client.fingerprint || (client.agent && client.olmVersion)) && (
|
{client.fingerprint && (
|
||||||
<SettingsSection>
|
<SettingsSection>
|
||||||
<SettingsSectionHeader>
|
<SettingsSectionHeader>
|
||||||
<SettingsSectionTitle>
|
<SettingsSectionTitle>
|
||||||
@@ -479,33 +443,6 @@ export default function GeneralPage() {
|
|||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
|
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
{client.agent && client.olmVersion && (
|
|
||||||
<div className="mb-6">
|
|
||||||
<InfoSection>
|
|
||||||
<InfoSectionTitle>
|
|
||||||
{t("agent")}
|
|
||||||
</InfoSectionTitle>
|
|
||||||
<InfoSectionContent>
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Badge variant="secondary">
|
|
||||||
{client.agent +
|
|
||||||
" v" +
|
|
||||||
client.olmVersion}
|
|
||||||
</Badge>
|
|
||||||
|
|
||||||
{updateAvailable && (
|
|
||||||
<InfoPopup
|
|
||||||
info={t(
|
|
||||||
"updateAvailableInfo"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</InfoSectionContent>
|
|
||||||
</InfoSection>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{client.fingerprint &&
|
{client.fingerprint &&
|
||||||
(() => {
|
(() => {
|
||||||
const platform = client.fingerprint.platform;
|
const platform = client.fingerprint.platform;
|
||||||
|
|||||||
@@ -11,9 +11,26 @@ import {
|
|||||||
import IdpTypeBadge from "@app/components/IdpTypeBadge";
|
import IdpTypeBadge from "@app/components/IdpTypeBadge";
|
||||||
import { getUserDisplayName } from "@app/lib/getUserDisplayName";
|
import { getUserDisplayName } from "@app/lib/getUserDisplayName";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
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";
|
||||||
|
|
||||||
type ClientInfoCardProps = {};
|
type ClientInfoCardProps = {};
|
||||||
|
|
||||||
|
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"
|
||||||
|
};
|
||||||
|
|
||||||
export default function SiteInfoCard({}: ClientInfoCardProps) {
|
export default function SiteInfoCard({}: ClientInfoCardProps) {
|
||||||
const { client, updateClient } = useClientContext();
|
const { client, updateClient } = useClientContext();
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
@@ -24,14 +41,57 @@ export default function SiteInfoCard({}: ClientInfoCardProps) {
|
|||||||
username: client.userUsername
|
username: client.userUsername
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const data = useQuery(productUpdatesQueries.latestVersion(true));
|
||||||
|
const latestPlatformVersions = data.data?.data;
|
||||||
|
|
||||||
|
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 = Boolean(
|
||||||
|
semver.valid(client.olmVersion) &&
|
||||||
|
semver.lt(client.olmVersion, agentVersion.latestVersion)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const cols =
|
||||||
|
2 +
|
||||||
|
(userDisplayName ? 1 : 0) +
|
||||||
|
(client.agent && client.olmVersion ? 1 : 0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Alert>
|
<Alert>
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
<InfoSections cols={userDisplayName ? 3 : 2}>
|
<InfoSections cols={cols}>
|
||||||
<InfoSection>
|
<InfoSection>
|
||||||
<InfoSectionTitle>{t("name")}</InfoSectionTitle>
|
<InfoSectionTitle>{t("name")}</InfoSectionTitle>
|
||||||
<InfoSectionContent>{client.name}</InfoSectionContent>
|
<InfoSectionContent>{client.name}</InfoSectionContent>
|
||||||
</InfoSection>
|
</InfoSection>
|
||||||
|
{client.agent && client.olmVersion ? (
|
||||||
|
<InfoSection>
|
||||||
|
<InfoSectionTitle>{t("agent")}</InfoSectionTitle>
|
||||||
|
<InfoSectionContent>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span>
|
||||||
|
{client.agent +
|
||||||
|
" v" +
|
||||||
|
client.olmVersion}
|
||||||
|
</span>
|
||||||
|
{updateAvailable && (
|
||||||
|
<InfoPopup
|
||||||
|
info={t("updateAvailableInfo")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</InfoSectionContent>
|
||||||
|
</InfoSection>
|
||||||
|
) : null}
|
||||||
{userDisplayName ? (
|
{userDisplayName ? (
|
||||||
<InfoSection>
|
<InfoSection>
|
||||||
<InfoSectionTitle>{t("user")}</InfoSectionTitle>
|
<InfoSectionTitle>{t("user")}</InfoSectionTitle>
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ export default function SiteInfoCard({}: SiteInfoCardProps) {
|
|||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
if (site.type === "newt") {
|
if (site.type === "newt") {
|
||||||
const isCli = site.agent === "cli";
|
|
||||||
return (
|
return (
|
||||||
<Alert>
|
<Alert>
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
@@ -74,14 +73,16 @@ export default function SiteInfoCard({}: SiteInfoCardProps) {
|
|||||||
{t("connectionType")}
|
{t("connectionType")}
|
||||||
</InfoSectionTitle>
|
</InfoSectionTitle>
|
||||||
<InfoSectionContent>
|
<InfoSectionContent>
|
||||||
{isCli ? "CLI" : "Newt"}
|
{t("pangolinSite")}
|
||||||
</InfoSectionContent>
|
</InfoSectionContent>
|
||||||
</InfoSection>
|
</InfoSection>
|
||||||
<InfoSection>
|
<InfoSection>
|
||||||
<InfoSectionTitle>
|
<InfoSectionTitle>{t("agent")}</InfoSectionTitle>
|
||||||
{t("newtVersion")}
|
|
||||||
</InfoSectionTitle>
|
|
||||||
<InfoSectionContent>
|
<InfoSectionContent>
|
||||||
|
{site.agent == "newt" ? "Newt" : null}
|
||||||
|
{site.agent == "cli"
|
||||||
|
? "Pangolin CLI"
|
||||||
|
: null}{" "}
|
||||||
{site.agentVersion
|
{site.agentVersion
|
||||||
? `v${site.agentVersion}`
|
? `v${site.agentVersion}`
|
||||||
: "-"}
|
: "-"}
|
||||||
|
|||||||
@@ -371,7 +371,7 @@ export default function SitesTable({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "type",
|
accessorKey: "type",
|
||||||
friendlyName: t("type"),
|
friendlyName: t("agent"),
|
||||||
header: () => {
|
header: () => {
|
||||||
return <span className="p-3">{t("type")}</span>;
|
return <span className="p-3">{t("type")}</span>;
|
||||||
},
|
},
|
||||||
@@ -386,13 +386,20 @@ export default function SitesTable({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (originalRow.type === "newt") {
|
if (originalRow.type === "newt") {
|
||||||
const isCli = originalRow.agent === "cli";
|
if (!originalRow.agent) {
|
||||||
|
return <span>-</span>;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center space-x-1">
|
<div className="flex items-center space-x-1">
|
||||||
<Badge variant="secondary">
|
<Badge variant="secondary">
|
||||||
<div className="flex items-center space-x-1">
|
<div className="flex items-center space-x-1">
|
||||||
<span>
|
<span>
|
||||||
{isCli ? "CLI" : "Newt"}
|
{originalRow.agent == "newt"
|
||||||
|
? "Newt"
|
||||||
|
: null}
|
||||||
|
{originalRow.agent == "cli"
|
||||||
|
? "Pangolin CLI"
|
||||||
|
: null}
|
||||||
</span>
|
</span>
|
||||||
{originalRow.agentVersion && (
|
{originalRow.agentVersion && (
|
||||||
<span>
|
<span>
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ export function NewtSiteInstallCommands({
|
|||||||
command: `${runAsRootPrefix}pangolin up site --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}${disableSshFlag}`
|
command: `${runAsRootPrefix}pangolin up site --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}${disableSshFlag}`
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Auto Systemd Service": [
|
"Systemd Service": [
|
||||||
{
|
{
|
||||||
title: t("install"),
|
title: t("install"),
|
||||||
command: `curl -fsSL https://static.pangolin.net/get-cli.sh | bash`
|
command: `curl -fsSL https://static.pangolin.net/get-cli.sh | bash`
|
||||||
@@ -141,7 +141,7 @@ sudo chmod 600 /etc/pangolin/pangolin-site.env`
|
|||||||
title: t("serviceFile"),
|
title: t("serviceFile"),
|
||||||
command: `sudo tee /etc/systemd/system/pangolin-site.service > /dev/null << 'EOF'
|
command: `sudo tee /etc/systemd/system/pangolin-site.service > /dev/null << 'EOF'
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Newt
|
Description=Pangolin Site
|
||||||
Wants=network-online.target
|
Wants=network-online.target
|
||||||
After=network-online.target
|
After=network-online.target
|
||||||
|
|
||||||
@@ -223,9 +223,9 @@ sudo systemctl enable --now pangolin-site`
|
|||||||
docker: {
|
docker: {
|
||||||
"Docker Compose": [
|
"Docker Compose": [
|
||||||
`services:
|
`services:
|
||||||
newt:
|
pangolin-site:
|
||||||
image: fosrl/pangolin-cli
|
image: fosrl/pangolin-cli
|
||||||
container_name: newt
|
container_name: pangolin-site
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
- PANGOLIN_ENDPOINT=${endpoint}
|
- PANGOLIN_ENDPOINT=${endpoint}
|
||||||
@@ -521,7 +521,7 @@ function getPlatformName(platformName: Platform) {
|
|||||||
function getArchitectures(platform: Platform) {
|
function getArchitectures(platform: Platform) {
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
case "linux":
|
case "linux":
|
||||||
return ["Run", "Auto Systemd Service", "Manual Systemd Service"];
|
return ["Run", "Systemd Service", "Manual Systemd Service"];
|
||||||
case "macos":
|
case "macos":
|
||||||
return ["Run", "Service"];
|
return ["Run", "Service"];
|
||||||
case "windows":
|
case "windows":
|
||||||
|
|||||||
Reference in New Issue
Block a user