diff --git a/package.json b/package.json index 046f946..00f25df 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,6 @@ "@types/chroma-js": "^3.1.1", "@types/js-cookie": "^3.0.6", "eslint-config-next": "^14.2.28", - "cypress": "^13.13.0", "postcss": "^8", "prettier": "3.0.3", "tailwindcss": "^3" diff --git a/src/modules/peer/PeerSSHInstructions.tsx b/src/modules/peer/PeerSSHInstructions.tsx index e425d0d..8177edf 100644 --- a/src/modules/peer/PeerSSHInstructions.tsx +++ b/src/modules/peer/PeerSSHInstructions.tsx @@ -39,7 +39,7 @@ export const PeerSSHInstructions = ({ icon={} title={"Enable SSH Access"} description={ - "Allow remote SSH access to this machine from other connected network participants. NetBird's embedded SSH server is running on port 44338." + "Allow remote SSH access to this machine from other connected network participants." } color={"netbird"} /> @@ -56,7 +56,7 @@ export const PeerSSHInstructions = ({ {`netbird down # if NetBird is already running`} - {`netbird up --allow-server-ssh`} + {`netbird up --allow-server-ssh --enable-ssh-root`} diff --git a/src/modules/peers/PeerVersionCell.tsx b/src/modules/peers/PeerVersionCell.tsx index bae7edd..9e105bb 100644 --- a/src/modules/peers/PeerVersionCell.tsx +++ b/src/modules/peers/PeerVersionCell.tsx @@ -1,4 +1,3 @@ -import FullTooltip from "@components/FullTooltip"; import InlineLink from "@components/InlineLink"; import { Tooltip, @@ -8,13 +7,14 @@ import { } from "@components/Tooltip"; import MemoizedNetBirdIcon from "@components/ui/MemoizedNetBirdIcon"; import { getOperatingSystem } from "@hooks/useOperatingSystem"; -import { parseVersionString } from "@utils/version"; +import { compareVersions } from "@utils/version"; import { ArrowRightIcon, ArrowUpCircleIcon } from "lucide-react"; import * as React from "react"; import { useMemo } from "react"; import { useApplicationContext } from "@/contexts/ApplicationProvider"; import { OperatingSystem } from "@/interfaces/OperatingSystem"; import { PeerOperatingSystemIcon } from "@/modules/peers/PeerOperatingSystemIcon"; +import FullTooltip from "@components/FullTooltip"; type Props = { version: string; @@ -31,7 +31,8 @@ export default function PeerVersionCell({ version, os, serial }: Props) { operatingSystem === OperatingSystem.ANDROID ) return false; - return parseVersionString(version) < parseVersionString(latestVersion); + if (!latestVersion) return false; + return !compareVersions(version, latestVersion); }, [os, version, latestVersion]); const updateIcon = useMemo(() => { diff --git a/src/modules/remote-access/ssh/useSSH.ts b/src/modules/remote-access/ssh/useSSH.ts index e76922c..dfb7b36 100644 --- a/src/modules/remote-access/ssh/useSSH.ts +++ b/src/modules/remote-access/ssh/useSSH.ts @@ -40,10 +40,16 @@ export const useSSH = (client: any) => { setConfig(config); try { + const requiresJwt = await client.detectSSHServerType( + config.hostname, + config.port, + ); + const ssh = await client.createSSHConnection( config.hostname, config.port, config.username, + requiresJwt ? accessToken : undefined, ); ssh.onclose = () => { @@ -65,7 +71,7 @@ export const useSSH = (client: any) => { return SSHStatus.DISCONNECTED; } }, - [client, status], + [client, status, accessToken], ); const disconnect = useCallback(() => { diff --git a/src/utils/config.ts b/src/utils/config.ts index 4ef0db6..0928f47 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -68,7 +68,7 @@ const loadConfig = (): Config => { googleAnalyticsID: configJson?.googleAnalyticsID || undefined, googleTagManagerID: configJson?.googleTagManagerID || undefined, wasmPath: - configJson.wasmPath || "https://pkgs.netbird.io/wasm/client/v0.59.11", + configJson?.wasmPath || "https://pkgs.netbird.io/wasm/client/v0.60.0", } as Config; }; diff --git a/src/utils/version.test.ts b/src/utils/version.test.ts new file mode 100644 index 0000000..ff173db --- /dev/null +++ b/src/utils/version.test.ts @@ -0,0 +1,34 @@ +import { isNativeSSHSupported } from "./version.js"; + +console.log("=== Testing isNativeSSHSupported ==="); +const sshTestCases = [ + { version: "v0.59.9", shouldSupport: false }, + { version: "v0.59.10", shouldSupport: false }, + { version: "v0.59.11", shouldSupport: false }, + { version: "v0.60.0", shouldSupport: true }, + { version: "v0.60.1", shouldSupport: true }, + { version: "v0.61.0", shouldSupport: true }, + { version: "v1.0.0", shouldSupport: true }, + + // Edge cases + { version: "development", shouldSupport: true, desc: "development build" }, + { version: "0.60.0", shouldSupport: true, desc: "no v prefix" }, + { version: "0.59.11", shouldSupport: false, desc: "no v prefix" }, + { version: "v0.60.0-beta", shouldSupport: true, desc: "with suffix" }, + { version: "v0.60.0-rc1", shouldSupport: true, desc: "with rc suffix" }, + { version: "v0.59.9-beta", shouldSupport: false, desc: "old version with suffix" }, +]; + +let failures = 0; +sshTestCases.forEach(({ version, shouldSupport, desc }) => { + const result = isNativeSSHSupported(version); + const status = result === shouldSupport ? "✓" : "✗"; + if (result !== shouldSupport) failures++; + const label = desc ? `${version.padEnd(15)} (${desc})` : version.padEnd(15); + console.log( + `${status} ${label} → ${result.toString().padStart(5)} (expected: ${shouldSupport})`, + ); +}); + +console.log(`\n${failures} test(s) failed`); +process.exit(failures > 0 ? 1 : 0); diff --git a/src/utils/version.ts b/src/utils/version.ts index 0b43e49..16616e5 100644 --- a/src/utils/version.ts +++ b/src/utils/version.ts @@ -36,9 +36,30 @@ export const getLatestNetbirdRelease = async ( } }; -export const parseVersionString = (version: string | undefined) => { - if (!version) return -1; - return parseInt(version.replace(/\D/g, "")); +/** + * Compare semantic versions. + * Returns true if version >= minVersion. + */ +export const compareVersions = ( + version: string, + minVersion: string, +): boolean => { + const parseVersion = (v: string): number[] => { + return v.replace(/^v/, "").split(".").map(Number); + }; + + const vParts = parseVersion(version); + const minParts = parseVersion(minVersion); + + for (let i = 0; i < Math.max(vParts.length, minParts.length); i++) { + const vPart = vParts[i] || 0; + const minPart = minParts[i] || 0; + + if (vPart > minPart) return true; + if (vPart < minPart) return false; + } + + return true; }; /** @@ -51,16 +72,15 @@ export const isRoutingPeerSupported = (version: string, os: string) => { const operatingSystem = getOperatingSystem(os); if (operatingSystem == OperatingSystem.LINUX) return true; if (version == "development") return true; - const versionNumber = parseVersionString(version); - return versionNumber >= 366; + return compareVersions(version, "0.36.6"); }; /** - * Check if native SSH is supported + * Check if native SSH is supported. + * Supported starting from NetBird v0.60.0+. * @param version */ export const isNativeSSHSupported = (version: string) => { if (version == "development") return true; - const versionNumber = parseVersionString(version); - return versionNumber >= 999999; + return compareVersions(version, "0.60.0"); };