Add standalone backup script and admin update-check banner
ops/backup.sh snapshots Redis (BGSAVE, dynamic RDB path lookup) and ./config for standalone deployments, with retention. A background service polls GitHub releases every 24h and surfaces an admin-only banner in the UI when a newer version is published.
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
'use strict';
|
||||
|
||||
// Periodic "is a newer release available" check against GitHub releases.
|
||||
// Nothing auto-updates -- this only surfaces a notice (an admin-only banner,
|
||||
// see routes/update_check.js + views/top.ejs) so operators know to
|
||||
// `git pull` + rebuild on their own schedule. State lives in memory only
|
||||
// (single-process app); a restart just re-checks on the next interval.
|
||||
|
||||
const { buildVersion } = require('./build_info');
|
||||
|
||||
const REPO = 'theta42/proxy';
|
||||
const API_URL = `https://api.github.com/repos/${REPO}/releases/latest`;
|
||||
|
||||
let state = {
|
||||
currentVersion: buildVersion,
|
||||
latestVersion: null,
|
||||
updateAvailable: false,
|
||||
releaseUrl: null,
|
||||
checkedAt: null,
|
||||
error: null,
|
||||
};
|
||||
|
||||
// Basic semver compare (major.minor.patch, ignoring any -prerelease/+build
|
||||
// suffix) -- good enough for comparing release tags like "v1.2.0" against
|
||||
// package.json's "1.1.0". Returns true if `a` is strictly newer than `b`.
|
||||
function isNewer(a, b) {
|
||||
const pa = a.replace(/^v/i, '').split('.').map(n => parseInt(n, 10) || 0);
|
||||
const pb = b.replace(/^v/i, '').split('.').map(n => parseInt(n, 10) || 0);
|
||||
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
||||
const na = pa[i] || 0, nb = pb[i] || 0;
|
||||
if (na !== nb) return na > nb;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async function checkNow() {
|
||||
try {
|
||||
const res = await fetch(API_URL, {
|
||||
headers: { 'Accept': 'application/vnd.github+json', 'User-Agent': 'theta42-proxy-update-check' },
|
||||
});
|
||||
if (!res.ok) throw new Error(`GitHub API returned ${res.status}`);
|
||||
const data = await res.json();
|
||||
const latestVersion = String(data.tag_name || '').replace(/^v/i, '');
|
||||
|
||||
state = {
|
||||
currentVersion: buildVersion,
|
||||
latestVersion: latestVersion || null,
|
||||
updateAvailable: latestVersion ? isNewer(latestVersion, buildVersion) : false,
|
||||
releaseUrl: data.html_url || `https://github.com/${REPO}/releases/latest`,
|
||||
checkedAt: Date.now(),
|
||||
error: null,
|
||||
};
|
||||
} catch (error) {
|
||||
// Network hiccup, rate limit, no releases published yet, etc. -- keep
|
||||
// the previous state and just note the failure; never throw, this
|
||||
// runs unattended on a timer.
|
||||
state = { ...state, checkedAt: Date.now(), error: error.message };
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
function getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
module.exports = { checkNow, getState, isNewer, REPO };
|
||||
Reference in New Issue
Block a user