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