import { useTranslations } from "next-intl"; import CopyTextBox from "./CopyTextBox"; import { SettingsSection, SettingsSectionBody, SettingsSectionDescription, SettingsSectionHeader, SettingsSectionTitle } from "./Settings"; import { CheckboxWithLabel } from "./ui/checkbox"; import { Button } from "./ui/button"; import { OptionSelect, type OptionSelectOption } from "./OptionSelect"; import { useState } from "react"; import { FaApple, FaCubes, FaDocker, FaHdd, FaLinux, FaWindows } from "react-icons/fa"; import { Download, ExternalLink } from "lucide-react"; import { SiKubernetes, SiNixos } from "react-icons/si"; import { useEnvContext } from "@app/hooks/useEnvContext"; export type CommandItem = | string | { title: string; command: string } | { title: string; link: string }; const PLATFORMS = [ "linux", "macos", "docker", "kubernetes", "advantech", "podman", "nixos", "windows" ] as const; type Platform = (typeof PLATFORMS)[number]; export type NewtSiteInstallCommandsProps = { id: string; secret: string; endpoint: string; }; export function NewtSiteInstallCommands({ id, secret, endpoint }: NewtSiteInstallCommandsProps) { const t = useTranslations(); const { env } = useEnvContext(); const [acceptClients, setAcceptClients] = useState(true); const [allowPangolinSsh, setAllowPangolinSsh] = useState( !env.flags.disableEnterpriseFeatures ); const [platform, setPlatform] = useState("linux"); const [architecture, setArchitecture] = useState( () => getArchitectures(platform)[0] ); const showSiteConfiguration = platform !== "advantech"; const supportsSshOption = platform === "linux" || platform === "nixos"; const acceptClientsFlag = !acceptClients ? " --disable-clients" : ""; const acceptClientsEnv = !acceptClients ? "\n - DISABLE_CLIENTS=true" : ""; const acceptClientsHelmValue = acceptClients ? ` \\ --set newtInstances[0].acceptClients=true` : ""; const disableSshFlag = supportsSshOption && !allowPangolinSsh && !env.flags.disableEnterpriseFeatures ? " --disable-ssh" : ""; const runAsRootPrefix = supportsSshOption && allowPangolinSsh ? "sudo " : ""; const commandList: Record> = { linux: { Run: [ { title: t("install"), command: `curl -fsSL https://static.pangolin.net/get-cli.sh | bash` }, { title: t("run"), command: `${runAsRootPrefix}pangolin up site --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}${disableSshFlag}` } ], "Systemd Service": [ { title: t("install"), command: `curl -fsSL https://static.pangolin.net/get-cli.sh | bash` }, { title: t("run"), command: `sudo pangolin service install site --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}${disableSshFlag}` }, { title: t("check"), command: `sudo pangolin service status site` } ], "Manual Systemd Service": [ { title: t("install"), command: `curl -fsSL https://static.pangolin.net/get-cli.sh | bash` }, { title: t("envFile"), command: `# Create the directory and environment file sudo install -d -m 0755 /etc/pangolin sudo tee /etc/pangolin/pangolin-site.env > /dev/null << 'EOF' SITE_ID=${id} SITE_SECRET=${secret} PANGOLIN_ENDPOINT=${endpoint}${ !acceptClients ? ` DISABLE_CLIENTS=true` : "" }${ !allowPangolinSsh ? ` DISABLE_SSH=true` : "" } EOF 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=Pangolin Site Wants=network-online.target After=network-online.target [Service] Type=simple User=root Group=root EnvironmentFile=/etc/pangolin/pangolin-site.env ExecStart=/home/owen/fossorial/cli/bin/pangolin up site Restart=always RestartSec=2 UMask=0077 PrivateTmp=true [Install] WantedBy=multi-user.target EOF` }, { title: t("enableAndStart"), command: `sudo systemctl daemon-reload sudo systemctl enable --now pangolin-site` } ] }, macos: { Run: [ { title: t("install"), command: `curl -fsSL https://static.pangolin.net/get-cli.sh | bash` }, { title: t("run"), command: `pangolin up site --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}` } ], Service: [ { title: t("install"), command: `curl -fsSL https://static.pangolin.net/get-cli.sh | bash` }, { title: t("run"), command: `sudo pangolin service install site --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}${disableSshFlag}` }, { title: t("check"), command: `sudo pangolin service status site` } ] }, windows: { Run: [ { title: t("install"), link: `https://github.com/fosrl/cli/releases/latest/download/pangolin-cli_windows_installer.msi` }, { title: t("run"), command: `pangolin up site --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}` } ], Service: [ { title: t("install"), link: `https://github.com/fosrl/cli/releases/latest/download/pangolin-cli_windows_installer.msi` }, { title: t("run"), command: `pangolin service install site --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}${disableSshFlag}` }, { title: t("check"), command: `pangolin service status site` } ] }, docker: { "Docker Compose": [ `services: pangolin-site: image: fosrl/pangolin-cli container_name: pangolin-site restart: unless-stopped environment: - PANGOLIN_ENDPOINT=${endpoint} - SITE_ID=${id} - SITE_SECRET=${secret}${acceptClientsEnv}` ], "Docker Run": [ `docker run -dit --network host fosrl/pangolin-cli up site --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}` ] }, kubernetes: { // we are leaving this using newt until we change it to use the cli "Helm Chart": [ `helm repo add fossorial https://charts.fossorial.io`, `helm repo update fossorial`, `kubectl create namespace newt --dry-run=client -o yaml | kubectl apply -f -`, `kubectl create secret generic newt-main-tunnel-auth \\ -n newt \\ --from-literal=PANGOLIN_ENDPOINT="${endpoint}" \\ --from-literal=NEWT_ID="${id}" \\ --from-literal=NEWT_SECRET="${secret}" \\ --dry-run=client -o yaml | kubectl apply -f -`, `helm upgrade --install newt fossorial/newt \\ -n newt \\ --set newtInstances[0].name="main-tunnel" \\ --set newtInstances[0].enabled=true \\ --set-string newtInstances[0].auth.existingSecretName="newt-main-tunnel-auth"${acceptClientsHelmValue}` ] }, advantech: { Documentation: [] }, podman: { "Podman Quadlet": [ `[Unit] Description=Pangolin Site Container [Container] ContainerName=pangolin-site Image=docker.io/fosrl/pangolin-cli Environment=PANGOLIN_ENDPOINT=${endpoint} Environment=SITE_ID=${id} Environment=SITE_SECRET=${secret}${!acceptClients ? "\nEnvironment=DISABLE_CLIENTS=true" : ""} # Secret=pangolin-secret,type=env,target=SITE_SECRET [Service] Restart=always [Install] WantedBy=default.target` ], "Podman Run": [ `podman run -dit docker.io/fosrl/pangolin-cli up site --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}` ] }, nixos: { Flake: [ `${runAsRootPrefix}nix run 'nixpkgs#pangolin-cli' -- up site --id ${id} --secret ${secret} --endpoint ${endpoint}${acceptClientsFlag}${disableSshFlag}` ] } }; const commands = commandList[platform][architecture]; const platformOptions: OptionSelectOption[] = PLATFORMS.map( (os) => ({ value: os, label: getPlatformName(os), icon: getPlatformIcon(os) }) ); return ( {t("siteInstallNewt")} {t("siteInstallNewtDescription")} label={t("operatingSystem")} options={platformOptions} value={platform} onChange={(os) => { setPlatform(os); const architectures = getArchitectures(os); setArchitecture(architectures[0]); }} cols={5} /> label={t("method")} options={getArchitectures(platform).map((arch) => ({ value: arch, label: arch }))} value={architecture} onChange={setArchitecture} cols={5} className="mt-4" /> {showSiteConfiguration && (

{t("siteConfiguration")}

{ const value = checked as boolean; setAcceptClients(value); }} label={t("siteAcceptClientConnections")} />

{t("siteAcceptClientConnectionsDescription")}

{supportsSshOption && !env.flags.disableEnterpriseFeatures && ( <>
{ const value = checked as boolean; setAllowPangolinSsh(value); }} label="Allow Pangolin SSH" />

{t("sitePangolinSshDescription")}

)}
)}

{t("commands")}

{platform === "kubernetes" && (

{t.rich("siteInstallKubernetesDocsDescription", { docsLink: (chunks) => ( {chunks} ) })}

)} {platform === "advantech" && (

{t.rich("siteInstallAdvantechDocsDescription", { docsLink: (chunks) => ( {chunks} ) })}

)}
{commands.map((item, index) => { const isLink = typeof item !== "string" && "link" in item; const commandText = typeof item === "string" ? item : isLink ? undefined : item.command; const linkHref = isLink ? (item as { link: string }).link : undefined; const title = typeof item === "string" ? undefined : item.title; const key = `${title ?? ""}::${commandText ?? linkHref}`; return (
{title && (

{title}

)} {isLink ? ( ) : ( )}
); })}
); } function getPlatformIcon(platformName: Platform) { switch (platformName) { case "windows": return ; case "linux": return ; case "macos": return ; case "docker": return ; case "kubernetes": return ; case "advantech": return ; case "podman": return ; case "nixos": return ; default: return ; } } function getPlatformName(platformName: Platform) { switch (platformName) { case "windows": return "Windows"; case "linux": return "Linux"; case "macos": return "macOS"; case "docker": return "Docker"; case "kubernetes": return "Kubernetes"; case "advantech": return "Advantech"; case "podman": return "Podman"; case "nixos": return "NixOS"; default: return "Linux"; } } function getArchitectures(platform: Platform) { switch (platform) { case "linux": return ["Run", "Systemd Service", "Manual Systemd Service"]; case "macos": return ["Run", "Service"]; case "windows": return ["Run", "Service"]; case "docker": return ["Docker Compose", "Docker Run"]; case "kubernetes": return ["Helm Chart"]; case "advantech": return ["Documentation"]; case "podman": return ["Podman Quadlet", "Podman Run"]; case "nixos": return ["Flake"]; default: return ["Run"]; } }