mirror of
https://github.com/fosrl/pangolin.git
synced 2025-08-15 23:17:55 +02:00
add newt version update available to table
This commit is contained in:
parent
4ffdd6f74f
commit
1e5141c27c
4 changed files with 98 additions and 7 deletions
|
@ -1145,5 +1145,7 @@
|
||||||
"settingsErrorUpdate": "Failed to update settings",
|
"settingsErrorUpdate": "Failed to update settings",
|
||||||
"settingsErrorUpdateDescription": "An error occurred while updating settings",
|
"settingsErrorUpdateDescription": "An error occurred while updating settings",
|
||||||
"sidebarCollapse": "Collapse",
|
"sidebarCollapse": "Collapse",
|
||||||
"sidebarExpand": "Expand"
|
"sidebarExpand": "Expand",
|
||||||
|
"newtUpdateAvailable": "Update Available",
|
||||||
|
"newtUpdateAvailableInfo": "A new version of Newt is available. Please update to the latest version for the best experience."
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { db } from "@server/db";
|
import { db, newts } from "@server/db";
|
||||||
import { orgs, roleSites, sites, userSites } from "@server/db";
|
import { orgs, roleSites, sites, userSites } from "@server/db";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
@ -9,6 +9,42 @@ import createHttpError from "http-errors";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
import NodeCache from "node-cache";
|
||||||
|
import semver from "semver";
|
||||||
|
|
||||||
|
const newtVersionCache = new NodeCache({ stdTTL: 3600 }); // 1 hours in seconds
|
||||||
|
|
||||||
|
async function getLatestNewtVersion(): Promise<string | null> {
|
||||||
|
try {
|
||||||
|
const cachedVersion = newtVersionCache.get<string>("latestNewtVersion");
|
||||||
|
if (cachedVersion) {
|
||||||
|
return cachedVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(
|
||||||
|
"https://api.github.com/repos/fosrl/newt/tags"
|
||||||
|
);
|
||||||
|
if (!response.ok) {
|
||||||
|
logger.warn("Failed to fetch latest Newt version from GitHub");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tags = await response.json();
|
||||||
|
if (!Array.isArray(tags) || tags.length === 0) {
|
||||||
|
logger.warn("No tags found for Newt repository");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const latestVersion = tags[0].name;
|
||||||
|
|
||||||
|
newtVersionCache.set("latestNewtVersion", latestVersion);
|
||||||
|
|
||||||
|
return latestVersion;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error fetching latest Newt version:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const listSitesParamsSchema = z
|
const listSitesParamsSchema = z
|
||||||
.object({
|
.object({
|
||||||
|
@ -45,9 +81,11 @@ function querySites(orgId: string, accessibleSiteIds: number[]) {
|
||||||
type: sites.type,
|
type: sites.type,
|
||||||
online: sites.online,
|
online: sites.online,
|
||||||
address: sites.address,
|
address: sites.address,
|
||||||
|
newtVersion: newts.version
|
||||||
})
|
})
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.leftJoin(orgs, eq(sites.orgId, orgs.orgId))
|
.leftJoin(orgs, eq(sites.orgId, orgs.orgId))
|
||||||
|
.leftJoin(newts, eq(newts.siteId, sites.siteId))
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
inArray(sites.siteId, accessibleSiteIds),
|
inArray(sites.siteId, accessibleSiteIds),
|
||||||
|
@ -56,8 +94,12 @@ function querySites(orgId: string, accessibleSiteIds: number[]) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SiteWithUpdateAvailable = Awaited<ReturnType<typeof querySites>>[0] & {
|
||||||
|
newtUpdateAvailable?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export type ListSitesResponse = {
|
export type ListSitesResponse = {
|
||||||
sites: Awaited<ReturnType<typeof querySites>>;
|
sites: SiteWithUpdateAvailable[];
|
||||||
pagination: { total: number; limit: number; offset: number };
|
pagination: { total: number; limit: number; offset: number };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -148,9 +190,36 @@ export async function listSites(
|
||||||
const totalCountResult = await countQuery;
|
const totalCountResult = await countQuery;
|
||||||
const totalCount = totalCountResult[0].count;
|
const totalCount = totalCountResult[0].count;
|
||||||
|
|
||||||
|
const latestNewtVersion = await getLatestNewtVersion();
|
||||||
|
|
||||||
|
const sitesWithUpdates: SiteWithUpdateAvailable[] = sitesList.map(
|
||||||
|
(site) => {
|
||||||
|
const siteWithUpdate: SiteWithUpdateAvailable = { ...site };
|
||||||
|
|
||||||
|
if (
|
||||||
|
site.type === "newt" &&
|
||||||
|
site.newtVersion &&
|
||||||
|
latestNewtVersion
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
siteWithUpdate.newtUpdateAvailable = semver.lt(
|
||||||
|
site.newtVersion,
|
||||||
|
latestNewtVersion
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
siteWithUpdate.newtUpdateAvailable = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
siteWithUpdate.newtUpdateAvailable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return siteWithUpdate;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return response<ListSitesResponse>(res, {
|
return response<ListSitesResponse>(res, {
|
||||||
data: {
|
data: {
|
||||||
sites: sitesList,
|
sites: sitesWithUpdates,
|
||||||
pagination: {
|
pagination: {
|
||||||
total: totalCount,
|
total: totalCount,
|
||||||
limit,
|
limit,
|
||||||
|
|
|
@ -29,6 +29,8 @@ import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||||
import CreateSiteFormModal from "./CreateSiteModal";
|
import CreateSiteFormModal from "./CreateSiteModal";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { parseDataSize } from "@app/lib/dataSize";
|
import { parseDataSize } from "@app/lib/dataSize";
|
||||||
|
import { Badge } from "@app/components/ui/badge";
|
||||||
|
import { InfoPopup } from "@app/components/ui/info-popup";
|
||||||
|
|
||||||
export type SiteRow = {
|
export type SiteRow = {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -38,6 +40,8 @@ export type SiteRow = {
|
||||||
mbOut: string;
|
mbOut: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
type: "newt" | "wireguard";
|
type: "newt" | "wireguard";
|
||||||
|
newtVersion?: string;
|
||||||
|
newtUpdateAvailable?: boolean;
|
||||||
online: boolean;
|
online: boolean;
|
||||||
address?: string;
|
address?: string;
|
||||||
};
|
};
|
||||||
|
@ -238,8 +242,22 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
|
||||||
|
|
||||||
if (originalRow.type === "newt") {
|
if (originalRow.type === "newt") {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-1">
|
||||||
<span>Newt</span>
|
<Badge variant="secondary">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<span>Newt</span>
|
||||||
|
{originalRow.newtVersion && (
|
||||||
|
<span className="text-xs text-gray-500">
|
||||||
|
v{originalRow.newtVersion}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Badge>
|
||||||
|
{originalRow.newtUpdateAvailable && (
|
||||||
|
<InfoPopup
|
||||||
|
info={t("newtUpdateAvailableInfo")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,9 @@ export default async function SitesPage(props: SitesPageProps) {
|
||||||
mbOut: formatSize(site.megabytesOut || 0, site.type),
|
mbOut: formatSize(site.megabytesOut || 0, site.type),
|
||||||
orgId: params.orgId,
|
orgId: params.orgId,
|
||||||
type: site.type as any,
|
type: site.type as any,
|
||||||
online: site.online
|
online: site.online,
|
||||||
|
newtVersion: site.newtVersion || undefined,
|
||||||
|
newtUpdateAvailable: site.newtUpdateAvailable || false,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue