show newt version on site

This commit is contained in:
miloschwartz
2026-05-01 16:26:45 -07:00
parent f8b85d4b4e
commit 9757c3d8b6
3 changed files with 128 additions and 63 deletions

View File

@@ -763,6 +763,7 @@
"newtEndpoint": "Endpoint", "newtEndpoint": "Endpoint",
"newtId": "ID", "newtId": "ID",
"newtSecretKey": "Secret", "newtSecretKey": "Secret",
"newtVersion": "Version",
"architecture": "Architecture", "architecture": "Architecture",
"sites": "Sites", "sites": "Sites",
"siteWgAnyClients": "Use any WireGuard client to connect. You will have to address internal resources using the peer IP.", "siteWgAnyClients": "Use any WireGuard client to connect. You will have to address internal resources using the peer IP.",

View File

@@ -42,9 +42,12 @@ async function query(siteId?: number, niceId?: string, orgId?: string) {
} }
} }
export type GetSiteResponse = NonNullable< type SiteQueryRow = NonNullable<Awaited<ReturnType<typeof query>>>;
Awaited<ReturnType<typeof query>>
>["sites"] & { newtId: string | null }; export type GetSiteResponse = SiteQueryRow["sites"] & {
newtId: string | null;
newtVersion: string | null;
};
registry.registerPath({ registry.registerPath({
method: "get", method: "get",
@@ -100,7 +103,8 @@ export async function getSite(
const data: GetSiteResponse = { const data: GetSiteResponse = {
...site.sites, ...site.sites,
newtId: site.newt ? site.newt.newtId : null newtId: site.newt ? site.newt.newtId : null,
newtVersion: site.newt?.version ?? null
}; };
return response<GetSiteResponse>(res, { return response<GetSiteResponse>(res, {

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Alert, AlertDescription } from "@/components/ui/alert";
import { useSiteContext } from "@app/hooks/useSiteContext"; import { useSiteContext } from "@app/hooks/useSiteContext";
import { import {
InfoSection, InfoSection,
@@ -9,77 +9,137 @@ import {
InfoSectionTitle InfoSectionTitle
} from "@app/components/InfoSection"; } from "@app/components/InfoSection";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { useEnvContext } from "@app/hooks/useEnvContext";
type SiteInfoCardProps = {}; type SiteInfoCardProps = {};
export default function SiteInfoCard({}: SiteInfoCardProps) { function formatPublicEndpoint(endpoint: string) {
const { site, updateSite } = useSiteContext(); return endpoint.includes(":")
const t = useTranslations(); ? endpoint.substring(0, endpoint.lastIndexOf(":"))
const { env } = useEnvContext(); : endpoint;
}
const getConnectionTypeString = (type: string) => { export default function SiteInfoCard({}: SiteInfoCardProps) {
if (type === "newt") { const { site } = useSiteContext();
return "Newt"; const t = useTranslations();
} else if (type === "wireguard") {
return "WireGuard"; const identifierSection = (
} else if (type === "local") { <InfoSection>
return t("local"); <InfoSectionTitle>{t("identifier")}</InfoSectionTitle>
} else { <InfoSectionContent>{site.niceId}</InfoSectionContent>
return t("unknown"); </InfoSection>
} );
};
const statusSection = (
<InfoSection>
<InfoSectionTitle>{t("status")}</InfoSectionTitle>
<InfoSectionContent>
{site.online ? (
<div className="text-green-500 flex items-center space-x-2">
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
<span>{t("online")}</span>
</div>
) : (
<div className="text-neutral-500 flex items-center space-x-2">
<div className="w-2 h-2 bg-neutral-500 rounded-full"></div>
<span>{t("offline")}</span>
</div>
)}
</InfoSectionContent>
</InfoSection>
);
const endpointSection = site.endpoint ? (
<InfoSection>
<InfoSectionTitle>{t("publicIpEndpoint")}</InfoSectionTitle>
<InfoSectionContent>
{formatPublicEndpoint(site.endpoint)}
</InfoSectionContent>
</InfoSection>
) : null;
if (site.type === "newt") {
return (
<Alert>
<AlertDescription>
<InfoSections cols={site.endpoint ? 5 : 4}>
{identifierSection}
{statusSection}
<InfoSection>
<InfoSectionTitle>
{t("connectionType")}
</InfoSectionTitle>
<InfoSectionContent>Newt</InfoSectionContent>
</InfoSection>
<InfoSection>
<InfoSectionTitle>
{t("newtVersion")}
</InfoSectionTitle>
<InfoSectionContent>
{site.newtVersion
? `v${site.newtVersion}`
: "-"}
</InfoSectionContent>
</InfoSection>
{endpointSection}
</InfoSections>
</AlertDescription>
</Alert>
);
}
if (site.type === "wireguard") {
return (
<Alert>
<AlertDescription>
<InfoSections cols={site.endpoint ? 4 : 3}>
{identifierSection}
{statusSection}
<InfoSection>
<InfoSectionTitle>
{t("connectionType")}
</InfoSectionTitle>
<InfoSectionContent>WireGuard</InfoSectionContent>
</InfoSection>
{endpointSection}
</InfoSections>
</AlertDescription>
</Alert>
);
}
if (site.type === "local") {
return (
<Alert>
<AlertDescription>
<InfoSections cols={site.endpoint ? 3 : 2}>
{identifierSection}
<InfoSection>
<InfoSectionTitle>
{t("connectionType")}
</InfoSectionTitle>
<InfoSectionContent>
{t("local")}
</InfoSectionContent>
</InfoSection>
{endpointSection}
</InfoSections>
</AlertDescription>
</Alert>
);
}
return ( return (
<Alert> <Alert>
<AlertDescription> <AlertDescription>
<InfoSections cols={site.endpoint ? 4 : 3}> <InfoSections cols={site.endpoint ? 3 : 2}>
<InfoSection> {identifierSection}
<InfoSectionTitle>{t("identifier")}</InfoSectionTitle>
<InfoSectionContent>{site.niceId}</InfoSectionContent>
</InfoSection>
{(site.type == "newt" || site.type == "wireguard") && (
<>
<InfoSection>
<InfoSectionTitle>
{t("status")}
</InfoSectionTitle>
<InfoSectionContent>
{site.online ? (
<div className="text-green-500 flex items-center space-x-2">
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
<span>{t("online")}</span>
</div>
) : (
<div className="text-neutral-500 flex items-center space-x-2">
<div className="w-2 h-2 bg-neutral-500 rounded-full"></div>
<span>{t("offline")}</span>
</div>
)}
</InfoSectionContent>
</InfoSection>
</>
)}
<InfoSection> <InfoSection>
<InfoSectionTitle> <InfoSectionTitle>
{t("connectionType")} {t("connectionType")}
</InfoSectionTitle> </InfoSectionTitle>
<InfoSectionContent> <InfoSectionContent>{t("unknown")}</InfoSectionContent>
{getConnectionTypeString(site.type)}
</InfoSectionContent>
</InfoSection> </InfoSection>
{site.endpoint && ( {endpointSection}
<InfoSection>
<InfoSectionTitle>
{t("publicIpEndpoint")}
</InfoSectionTitle>
<InfoSectionContent>
{site.endpoint.includes(":")
? site.endpoint.substring(0, site.endpoint.lastIndexOf(":"))
: site.endpoint}
</InfoSectionContent>
</InfoSection>
)}
</InfoSections> </InfoSections>
</AlertDescription> </AlertDescription>
</Alert> </Alert>