From ae5faa75fac70a96f8e57374655081bec1a9825b Mon Sep 17 00:00:00 2001 From: Konstantinos Spartalis Date: Tue, 11 Nov 2025 10:35:00 +0200 Subject: [PATCH] backend test --- backend/routes/main.js | 36 ++++++------ backend/routes/version.js | 77 ++++++++++++++++++++++++++ frontend/src/components/SiteFooter.tsx | 32 ++--------- 3 files changed, 100 insertions(+), 45 deletions(-) create mode 100644 backend/routes/version.js diff --git a/backend/routes/main.js b/backend/routes/main.js index 7bc4323d..9b367df3 100644 --- a/backend/routes/main.js +++ b/backend/routes/main.js @@ -14,11 +14,12 @@ import schemaRoutes from "./schema.js"; import settingsRoutes from "./settings.js"; import tokensRoutes from "./tokens.js"; import usersRoutes from "./users.js"; +import versionRoutes from "./version.js"; const router = express.Router({ - caseSensitive: true, - strict: true, - mergeParams: true, + caseSensitive: true, + strict: true, + mergeParams: true, }); /** @@ -26,18 +27,18 @@ const router = express.Router({ * GET /api */ router.get("/", async (_, res /*, next*/) => { - const version = pjson.version.split("-").shift().split("."); - const setup = await isSetup(); + const version = pjson.version.split("-").shift().split("."); + const setup = await isSetup(); - res.status(200).send({ - status: "OK", - setup, - version: { - major: Number.parseInt(version.shift(), 10), - minor: Number.parseInt(version.shift(), 10), - revision: Number.parseInt(version.shift(), 10), - }, - }); + res.status(200).send({ + status: "OK", + setup, + version: { + major: Number.parseInt(version.shift(), 10), + minor: Number.parseInt(version.shift(), 10), + revision: Number.parseInt(version.shift(), 10), + }, + }); }); router.use("/schema", schemaRoutes); @@ -46,6 +47,7 @@ router.use("/users", usersRoutes); router.use("/audit-log", auditLogRoutes); router.use("/reports", reportsRoutes); router.use("/settings", settingsRoutes); +router.use("/version", versionRoutes); router.use("/nginx/proxy-hosts", proxyHostsRoutes); router.use("/nginx/redirection-hosts", redirectionHostsRoutes); router.use("/nginx/dead-hosts", deadHostsRoutes); @@ -59,8 +61,8 @@ router.use("/nginx/certificates", certificatesHostsRoutes); * ALL /api/* */ router.all(/(.+)/, (req, _, next) => { - req.params.page = req.params["0"]; - next(new errs.ItemNotFoundError(req.params.page)); + req.params.page = req.params["0"]; + next(new errs.ItemNotFoundError(req.params.page)); }); -export default router; +export default router; \ No newline at end of file diff --git a/backend/routes/version.js b/backend/routes/version.js new file mode 100644 index 00000000..cd46f12a --- /dev/null +++ b/backend/routes/version.js @@ -0,0 +1,77 @@ +import express from "express"; +import { debug, express as logger } from "../logger.js"; +import pjson from "../package.json" with { type: "json" }; + +const router = express.Router({ + caseSensitive: true, + strict: true, + mergeParams: true, +}); + +/** + * /api/version/check + */ +router + .route("/check") + .options((_, res) => { + res.sendStatus(204); + }) + + /** + * GET /api/version/check + * + * Check for available updates + */ + .get(async (req, res, next) => { + try { + const response = await fetch( + "https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest" + ); + + if (!response.ok) { + throw new Error(`GitHub API returned ${response.status}`); + } + + const data = await response.json(); + const latestVersion = data.tag_name; + + const version = pjson.version.split("-").shift().split("."); + const currentVersion = `v${version[0]}.${version[1]}.${version[2]}`; + + res.status(200).send({ + current: currentVersion, + latest: latestVersion, + updateAvailable: compareVersions(currentVersion, latestVersion), + }); + } catch (error) { + debug(logger, `${req.method.toUpperCase()} ${req.path}: ${error}`); + res.status(200).send({ + current: null, + latest: null, + updateAvailable: false, + }); + } + }); + +/** + * Compare two version strings + * + */ +function compareVersions(current, latest) { + const cleanCurrent = current.replace(/^v/, ""); + const cleanLatest = latest.replace(/^v/, ""); + + const currentParts = cleanCurrent.split(".").map(Number); + const latestParts = cleanLatest.split(".").map(Number); + + for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) { + const curr = currentParts[i] || 0; + const lat = latestParts[i] || 0; + + if (lat > curr) return true; + if (lat < curr) return false; + } + return false; +} + +export default router; \ No newline at end of file diff --git a/frontend/src/components/SiteFooter.tsx b/frontend/src/components/SiteFooter.tsx index 9c94afae..255bb700 100644 --- a/frontend/src/components/SiteFooter.tsx +++ b/frontend/src/components/SiteFooter.tsx @@ -2,23 +2,6 @@ import { useCallback, useEffect, useState } from "react"; import { useHealth } from "src/hooks"; import { T } from "src/locale"; -const compareVersions = (current: string, latest: string): boolean => { - const cleanCurrent = current.replace(/^v/, ""); - const cleanLatest = latest.replace(/^v/, ""); - - const currentParts = cleanCurrent.split(".").map(Number); - const latestParts = cleanLatest.split(".").map(Number); - - for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) { - const curr = currentParts[i] || 0; - const lat = latestParts[i] || 0; - - if (lat > curr) return true; - if (lat < curr) return false; - } - return false; -}; - export function SiteFooter() { const health = useHealth(); const [latestVersion, setLatestVersion] = useState(null); @@ -35,18 +18,11 @@ export function SiteFooter() { useEffect(() => { const checkForUpdates = async () => { try { - const response = await fetch( - "https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest" - ); + const response = await fetch("/api/version/check"); if (response.ok) { const data = await response.json(); - const latest = data.tag_name; - setLatestVersion(latest); - - const currentVersion = getVersion(); - if (currentVersion && compareVersions(currentVersion, latest)) { - setIsNewVersionAvailable(true); - } + setLatestVersion(data.latest); + setIsNewVersionAvailable(data.updateAvailable); } } catch (error) { console.debug("Could not check for updates:", error); @@ -56,7 +32,7 @@ export function SiteFooter() { if (health.data) { checkForUpdates(); } - }, [health.data, getVersion]); + }, [health.data]); return (