feat: add server-side version update check

Logs current version on startup and checks GitHub for newer releases.
Only runs in production, output stays in server logs (no client exposure).
This commit is contained in:
Matthieu MALVACHE
2026-02-28 14:59:11 +01:00
committed by Matthieu MALVACHE
parent c65a6454c5
commit a3fe9deaa6
6 changed files with 50 additions and 1 deletions
+1
View File
@@ -131,6 +131,7 @@ Stalwart is a mail server written in Rust with native JMAP support, not IMAP/SMT
- Runtime environment variables (no rebuild needed)
- Health check endpoint
- Structured logging (text/JSON)
- Update check on startup (server logs only, no client exposure)
## Tech stack
+1
View File
@@ -233,6 +233,7 @@ This document tracks the development status and planned features for JMAP Webmai
- [x] Pre-built Docker image on [Docker Hub](https://hub.docker.com/r/rootfr/jmap-webmail) and [GHCR](https://ghcr.io/root-fr/jmap-webmail) with multi-arch support (amd64/arm64)
- [x] GitHub Actions CI/CD for automated image publishing on releases
- [x] CVE remediation: remove npm from production image, upgrade Alpine packages
- [x] Server-side update check (logs newer version availability on startup)
## Planned Features
+1
View File
@@ -0,0 +1 @@
1.1.0
+43
View File
@@ -0,0 +1,43 @@
import { readFileSync } from "fs";
const VERSION_CHECK_URL =
"https://raw.githubusercontent.com/root-fr/jmap-webmail/main/VERSION";
const SEMVER_RE = /^\d+\.\d+\.\d+$/;
function compareVersions(current: string, remote: string): number {
const a = current.split(".").map(Number);
const b = remote.split(".").map(Number);
for (let i = 0; i < 3; i++) {
if ((b[i] ?? 0) > (a[i] ?? 0)) return 1;
if ((b[i] ?? 0) < (a[i] ?? 0)) return -1;
}
return 0;
}
export async function register() {
const pkg = JSON.parse(
readFileSync(`${process.cwd()}/package.json`, "utf-8")
);
const current: string = pkg.version ?? "0.0.0";
console.info(`JMAP Webmail v${current}`);
if (process.env.NODE_ENV !== "production") return;
try {
const res = await fetch(VERSION_CHECK_URL, {
cache: "no-store",
signal: AbortSignal.timeout(5000),
});
if (!res.ok) return;
const remote = (await res.text()).trim();
if (!SEMVER_RE.test(remote)) return;
if (compareVersions(current, remote) > 0) {
console.info(
`Update available: v${remote} — https://github.com/root-fr/jmap-webmail`
);
}
} catch {
// Network unavailable — not critical
}
}
+3
View File
@@ -3,6 +3,9 @@ import createNextIntlPlugin from "next-intl/plugin";
const nextConfig: NextConfig = {
output: "standalone",
turbopack: {
root: import.meta.dirname,
},
};
const withNextIntl = createNextIntlPlugin();
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "jmap-webmail",
"version": "1.0.0",
"version": "1.1.0",
"description": "A modern JMAP webmail client built for Stalwart Mail Server",
"author": "Matthieu MALVACHE <matthieu@root.cloud>",
"license": "MIT",