dont show site online status for local sites

This commit is contained in:
miloschwartz
2026-04-29 12:35:08 -07:00
parent f03389a9a0
commit a029b107ae
6 changed files with 64 additions and 33 deletions

View File

@@ -152,7 +152,7 @@ export type ResourceWithTargets = {
siteId: number; siteId: number;
siteName: string; siteName: string;
siteNiceId: string; siteNiceId: string;
online: boolean; online?: boolean; // undefined for local sites
}>; }>;
}; };
@@ -383,12 +383,8 @@ export async function listResources(
.select({ resourceId: targets.resourceId }) .select({ resourceId: targets.resourceId })
.from(targets) .from(targets)
.innerJoin(sites, eq(targets.siteId, sites.siteId)) .innerJoin(sites, eq(targets.siteId, sites.siteId))
.where( .where(and(eq(sites.orgId, orgId), eq(sites.siteId, siteId)));
and(eq(sites.orgId, orgId), eq(sites.siteId, siteId)) conditions.push(inArray(resources.resourceId, resourcesWithSite));
);
conditions.push(
inArray(resources.resourceId, resourcesWithSite)
);
} }
const baseQuery = queryResourcesBase().where(and(...conditions)); const baseQuery = queryResourcesBase().where(and(...conditions));
@@ -426,7 +422,8 @@ export async function listResources(
hcEnabled: targetHealthCheck.hcEnabled, hcEnabled: targetHealthCheck.hcEnabled,
siteName: sites.name, siteName: sites.name,
siteNiceId: sites.niceId, siteNiceId: sites.niceId,
siteOnline: sites.online siteOnline: sites.online,
siteType: sites.type
}) })
.from(targets) .from(targets)
.where(inArray(targets.resourceId, resourceIdList)) .where(inArray(targets.resourceId, resourceIdList))
@@ -481,18 +478,19 @@ export async function listResources(
siteId: number; siteId: number;
siteName: string; siteName: string;
siteNiceId: string; siteNiceId: string;
online: boolean; online?: boolean;
} }
>(); >();
for (const t of raw) { for (const t of raw) {
if (typeof t.siteId !== "number" || siteById.has(t.siteId)) { if (typeof t.siteId !== "number" || siteById.has(t.siteId)) {
continue; continue;
} }
const isLocal = t.siteType === "local";
siteById.set(t.siteId, { siteById.set(t.siteId, {
siteId: t.siteId, siteId: t.siteId,
siteName: t.siteName ?? "", siteName: t.siteName ?? "",
siteNiceId: t.siteNiceId ?? "", siteNiceId: t.siteNiceId ?? "",
online: Boolean(t.siteOnline) online: isLocal ? undefined : Boolean(t.siteOnline)
}); });
} }
entry.sites = Array.from(siteById.values()); entry.sites = Array.from(siteById.values());

View File

@@ -31,7 +31,9 @@ let staleNewtVersion: string | null = null;
async function getLatestNewtVersion(): Promise<string | null> { async function getLatestNewtVersion(): Promise<string | null> {
try { try {
const cachedVersion = await cache.get<string>("cache:latestNewtVersion"); const cachedVersion = await cache.get<string>(
"cache:latestNewtVersion"
);
if (cachedVersion) { if (cachedVersion) {
return cachedVersion; return cachedVersion;
} }
@@ -226,7 +228,10 @@ function querySitesBase() {
); );
} }
type SiteWithUpdateAvailable = Awaited<ReturnType<typeof querySitesBase>>[0] & { type SiteRowBase = Awaited<ReturnType<typeof querySitesBase>>[0];
type SiteWithUpdateAvailable = Omit<SiteRowBase, "online"> & {
online?: SiteRowBase["online"]; // undefined for local sites
newtUpdateAvailable?: boolean; newtUpdateAvailable?: boolean;
}; };
@@ -338,7 +343,9 @@ export async function listSites(
// we need to add `as` so that drizzle filters the result as a subquery // we need to add `as` so that drizzle filters the result as a subquery
const countQuery = db.$count( const countQuery = db.$count(
querySitesBase().where(and(...conditions)).as("filtered_sites") querySitesBase()
.where(and(...conditions))
.as("filtered_sites")
); );
const siteListQuery = baseQuery const siteListQuery = baseQuery
@@ -397,9 +404,13 @@ export async function listSites(
); );
} }
const sitesPayload = sitesWithUpdates.map((site) =>
site.type === "local" ? { ...site, online: undefined } : site
);
return response<ListSitesResponse>(res, { return response<ListSitesResponse>(res, {
data: { data: {
sites: sitesWithUpdates, sites: sitesPayload,
pagination: { pagination: {
total: totalCount, total: totalCount,
pageSize, pageSize,

View File

@@ -16,10 +16,10 @@ export type ResourceSiteRow = {
siteId: number; siteId: number;
siteName: string; siteName: string;
siteNiceId: string; siteNiceId: string;
online: boolean; online?: boolean | null;
}; };
type AggregateSitesStatus = "allOnline" | "partial" | "allOffline"; type AggregateSitesStatus = "allOnline" | "partial" | "allOffline" | "unknown";
function aggregateSitesStatus( function aggregateSitesStatus(
resourceSites: ResourceSiteRow[] resourceSites: ResourceSiteRow[]
@@ -27,8 +27,17 @@ function aggregateSitesStatus(
if (resourceSites.length === 0) { if (resourceSites.length === 0) {
return "allOffline"; return "allOffline";
} }
const onlineCount = resourceSites.filter((rs) => rs.online).length;
if (onlineCount === resourceSites.length) return "allOnline"; const knownStatuses = resourceSites
.map((rs) => rs.online)
.filter((status): status is boolean => typeof status === "boolean");
if (knownStatuses.length === 0) {
return "unknown";
}
const onlineCount = knownStatuses.filter(Boolean).length;
if (onlineCount === knownStatuses.length) return "allOnline";
if (onlineCount > 0) return "partial"; if (onlineCount > 0) return "partial";
return "allOffline"; return "allOffline";
} }
@@ -40,8 +49,10 @@ function aggregateStatusDotClass(status: AggregateSitesStatus): string {
case "partial": case "partial":
return "bg-yellow-500"; return "bg-yellow-500";
case "allOffline": case "allOffline":
default:
return "bg-neutral-500"; return "bg-neutral-500";
case "unknown":
default:
return "bg-transparent";
} }
} }
@@ -84,6 +95,7 @@ export function ResourceSitesStatusCell({
<DropdownMenuContent align="start" className="min-w-56"> <DropdownMenuContent align="start" className="min-w-56">
{resourceSites.map((site) => { {resourceSites.map((site) => {
const isOnline = site.online; const isOnline = site.online;
const hasKnownStatus = typeof isOnline === "boolean";
return ( return (
<DropdownMenuItem key={site.siteId} asChild> <DropdownMenuItem key={site.siteId} asChild>
<Link <Link
@@ -94,7 +106,9 @@ export function ResourceSitesStatusCell({
<div <div
className={cn( className={cn(
"h-2 w-2 shrink-0 rounded-full", "h-2 w-2 shrink-0 rounded-full",
isOnline !hasKnownStatus
? "bg-transparent"
: isOnline
? "bg-green-500" ? "bg-green-500"
: "bg-neutral-500" : "bg-neutral-500"
)} )}
@@ -106,12 +120,16 @@ export function ResourceSitesStatusCell({
<span <span
className={cn( className={cn(
"shrink-0 capitalize", "shrink-0 capitalize",
isOnline hasKnownStatus && isOnline
? "text-green-600" ? "text-green-600"
: "text-muted-foreground" : "text-muted-foreground"
)} )}
> >
{isOnline ? t("online") : t("offline")} {!hasKnownStatus
? t("resourcesTableUnknown")
: isOnline
? t("online")
: t("offline")}
</span> </span>
</Link> </Link>
</DropdownMenuItem> </DropdownMenuItem>

View File

@@ -60,7 +60,7 @@ export type SiteRow = {
type: "newt" | "wireguard" | "local"; type: "newt" | "wireguard" | "local";
newtVersion?: string; newtVersion?: string;
newtUpdateAvailable?: boolean; newtUpdateAvailable?: boolean;
online: boolean; online?: boolean | null;
address?: string; address?: string;
exitNodeName?: string; exitNodeName?: string;
exitNodeEndpoint?: string; exitNodeEndpoint?: string;

View File

@@ -111,11 +111,13 @@ export function MultiSitesSelector({
<span className="min-w-0 flex-1 truncate"> <span className="min-w-0 flex-1 truncate">
{site.name} {site.name}
</span> </span>
<SiteOnlineStatus {site.online != null && (
type={site.type} <SiteOnlineStatus
online={site.online} type={site.type}
t={t} online={site.online}
/> t={t}
/>
)}
</div> </div>
</CommandItem> </CommandItem>
))} ))}

View File

@@ -124,11 +124,13 @@ export function SitesSelector({
<span className="min-w-0 flex-1 truncate"> <span className="min-w-0 flex-1 truncate">
{site.name} {site.name}
</span> </span>
<SiteOnlineStatus {site.online != null && (
type={site.type} <SiteOnlineStatus
online={site.online} type={site.type}
t={t} online={site.online}
/> t={t}
/>
)}
</div> </div>
</CommandItem> </CommandItem>
))} ))}