feat(updates): auto-apply and self-restart on new dashboard versions

Adds POST /api/updates/apply (pull, rebuild, restart) plus an "Update now"
button and hourly auto-check in the UI, for checkouts that are safely
fast-forwardable. Reintroduces self-restart (previously removed in edc25ca
for cross-environment reliability concerns) per explicit user request.
This commit is contained in:
2026-08-18 16:04:27 +07:00
parent f053051a5d
commit 8e49d2b300
15 changed files with 502 additions and 19 deletions
+39 -3
View File
@@ -1,11 +1,15 @@
/**
* @file HTTP routes for dashboard upstream-update detection. The dashboard never
* restarts itself — users copy the printed command and run it in their terminal.
* @file HTTP routes for dashboard upstream-update detection, plus the one
* self-applying path: POST /apply fast-forwards, rebuilds, and restarts the
* process on the user's explicit click (see `server/lib/update-check.js`'s
* `applyUpdate` and `server/lib/self-restart.js`). Everywhere else, the
* dashboard only ever prints a command for the user to run themselves.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
const { Router } = require("express");
const { getUpdatesStatus } = require("../lib/update-check");
const { getUpdatesStatus, applyUpdate } = require("../lib/update-check");
const { scheduleRestart } = require("../lib/self-restart");
const router = Router();
@@ -37,4 +41,36 @@ router.post("/check", async (_req, res) => {
}
});
router.post("/apply", async (_req, res) => {
try {
const result = await applyUpdate();
if (!result.applied) {
res.status(409).json({
...result,
error: {
code: "UPDATE_NOT_APPLICABLE",
message:
result.reason === "up_to_date"
? "Already up to date."
: "Current branch can't be fast-forwarded automatically.",
},
});
return;
}
res.json(result);
try {
const { broadcast } = require("../websocket");
broadcast("update_status", { ...result, update_available: false });
} catch {
// WS not initialized (e.g. in isolated tests) — safe to ignore.
}
// After the response above, restart onto the freshly-built code.
setImmediate(() => scheduleRestart());
} catch (err) {
res.status(500).json({
error: { code: "UPDATE_APPLY_FAILED", message: err.message || String(err) },
});
}
});
module.exports = router;