Serve a branded maintenance page while automatic updates rebuild Synapsis
Hop-State: A_06FP9GHXYJ7YFGJ8MPGCRRG Hop-Proposal: R_06FP9GGM02EZ1XD9K4DPQ28 Hop-Task: T_06FP9G1KCRT11HM63FWG190 Hop-Attempt: AT_06FP9G1KCTJKQ4EHESDM798
This commit is contained in:
@@ -35,6 +35,8 @@ The installer creates:
|
|||||||
|
|
||||||
Every node checks `origin/main` about once per minute. When a new commit is available, Synapsis fast-forwards the checkout, replaces the single `backups/latest` database snapshot, installs dependencies, runs migrations, builds, and restarts automatically. The repository commit count is shown in the Network Info card; `/api/version` exposes both that number and the full deployed commit hash.
|
Every node checks `origin/main` about once per minute. When a new commit is available, Synapsis fast-forwards the checkout, replaces the single `backups/latest` database snapshot, installs dependencies, runs migrations, builds, and restarts automatically. The repository commit count is shown in the Network Info card; `/api/version` exposes both that number and the full deployed commit hash.
|
||||||
|
|
||||||
|
While an update is being installed, `synapsis-maintenance.service` temporarily serves a branded maintenance page on the node's configured `PORT`. Existing reverse proxies continue receiving an HTTP response instead of showing a gateway error, and browsers automatically reload when Synapsis is ready.
|
||||||
|
|
||||||
For a node installed before automatic updates existed, bootstrap the timer once with:
|
For a node installed before automatic updates existed, bootstrap the timer once with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ runuser -u synapsis -- npm --prefix "$APP_DIR" run db:migrate
|
|||||||
runuser -u synapsis -- npm --prefix "$APP_DIR" run build
|
runuser -u synapsis -- npm --prefix "$APP_DIR" run build
|
||||||
|
|
||||||
install -m 0644 "$APP_DIR/deploy/synapsis.service" /etc/systemd/system/synapsis.service
|
install -m 0644 "$APP_DIR/deploy/synapsis.service" /etc/systemd/system/synapsis.service
|
||||||
|
install -m 0644 "$APP_DIR/deploy/synapsis-maintenance.service" /etc/systemd/system/synapsis-maintenance.service
|
||||||
install -m 0644 "$APP_DIR/deploy/synapsis-update.service" /etc/systemd/system/synapsis-update.service
|
install -m 0644 "$APP_DIR/deploy/synapsis-update.service" /etc/systemd/system/synapsis-update.service
|
||||||
install -m 0644 "$APP_DIR/deploy/synapsis-update.timer" /etc/systemd/system/synapsis-update.timer
|
install -m 0644 "$APP_DIR/deploy/synapsis-update.timer" /etc/systemd/system/synapsis-update.timer
|
||||||
runuser -u synapsis -- git -C "$APP_DIR" rev-parse HEAD | install -m 0644 -o synapsis -g synapsis /dev/stdin "$DATA_DIR/deployed-commit"
|
runuser -u synapsis -- git -C "$APP_DIR" rev-parse HEAD | install -m 0644 -o synapsis -g synapsis /dev/stdin "$DATA_DIR/deployed-commit"
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import http from 'node:http';
|
||||||
|
|
||||||
|
const host = process.env.MAINTENANCE_HOST || '127.0.0.1';
|
||||||
|
const port = Number.parseInt(process.env.PORT || '43821', 10);
|
||||||
|
|
||||||
|
const page = `<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="theme-color" content="#090909">
|
||||||
|
<title>Update in progress</title>
|
||||||
|
<style>
|
||||||
|
:root { color-scheme: dark; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; min-height: 100vh; display: grid; place-items: center; overflow: hidden; background: #090909; color: #f5f5f5; }
|
||||||
|
body::before { content: ""; position: fixed; width: 34rem; height: 34rem; border-radius: 50%; background: rgba(159, 223, 95, .13); filter: blur(110px); transform: translate(-38vw, -30vh); }
|
||||||
|
main { width: min(90vw, 34rem); padding: 3rem; text-align: center; border: 1px solid #292929; border-radius: 1.75rem; background: rgba(18, 18, 18, .92); box-shadow: 0 2rem 7rem rgba(0, 0, 0, .5); }
|
||||||
|
.mark { width: 4.25rem; height: 4.25rem; margin: 0 auto 1.5rem; display: grid; place-items: center; border-radius: 1.25rem; background: #9fdf5f; color: #0b1305; font-size: 2rem; font-weight: 800; box-shadow: 0 0 2.5rem rgba(159, 223, 95, .25); }
|
||||||
|
h1 { margin: 0; font-size: clamp(1.75rem, 6vw, 2.5rem); letter-spacing: -.045em; }
|
||||||
|
p { margin: 1rem auto 0; max-width: 25rem; color: #a6a6a6; font-size: 1.05rem; line-height: 1.6; }
|
||||||
|
.status { margin-top: 2rem; display: inline-flex; align-items: center; gap: .65rem; color: #d7d7d7; font-size: .9rem; }
|
||||||
|
.dot { width: .6rem; height: .6rem; border-radius: 50%; background: #9fdf5f; animation: pulse 1.4s ease-in-out infinite; }
|
||||||
|
@keyframes pulse { 50% { opacity: .3; transform: scale(.75); } }
|
||||||
|
@media (max-width: 520px) { main { padding: 2.25rem 1.5rem; } }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<div class="mark" aria-hidden="true">S</div>
|
||||||
|
<h1>Update in progress</h1>
|
||||||
|
<p>We’re making Synapsis better. We’ll be right back!</p>
|
||||||
|
<div class="status"><span class="dot"></span>Checking for the node…</div>
|
||||||
|
</main>
|
||||||
|
<script>
|
||||||
|
setInterval(async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/health', { cache: 'no-store' });
|
||||||
|
if (response.ok) location.reload();
|
||||||
|
} catch {}
|
||||||
|
}, 5000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>`;
|
||||||
|
|
||||||
|
const server = http.createServer((request, response) => {
|
||||||
|
response.writeHead(503, {
|
||||||
|
'Content-Type': 'text/html; charset=utf-8',
|
||||||
|
'Content-Length': Buffer.byteLength(page),
|
||||||
|
'Cache-Control': 'no-store, max-age=0',
|
||||||
|
'Retry-After': '30',
|
||||||
|
'X-Robots-Tag': 'noindex',
|
||||||
|
});
|
||||||
|
if (request.method === 'HEAD') response.end();
|
||||||
|
else response.end(page);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(port, host, () => {
|
||||||
|
console.log(`Synapsis maintenance page listening on http://${host}:${port}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
const shutdown = () => server.close(() => process.exit(0));
|
||||||
|
process.on('SIGINT', shutdown);
|
||||||
|
process.on('SIGTERM', shutdown);
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Synapsis update maintenance page
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
Conflicts=synapsis.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=synapsis
|
||||||
|
Group=synapsis
|
||||||
|
WorkingDirectory=/opt/synapsis
|
||||||
|
EnvironmentFile=/etc/synapsis.env
|
||||||
|
ExecStart=/usr/bin/env node /opt/synapsis/deploy/maintenance-server.mjs
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=1
|
||||||
|
NoNewPrivileges=true
|
||||||
|
PrivateTmp=true
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=true
|
||||||
|
|
||||||
@@ -12,8 +12,10 @@ fi
|
|||||||
|
|
||||||
systemctl disable --now synapsis-update.timer 2>/dev/null || true
|
systemctl disable --now synapsis-update.timer 2>/dev/null || true
|
||||||
systemctl stop synapsis-update.service 2>/dev/null || true
|
systemctl stop synapsis-update.service 2>/dev/null || true
|
||||||
|
systemctl stop synapsis-maintenance.service 2>/dev/null || true
|
||||||
systemctl disable --now synapsis 2>/dev/null || true
|
systemctl disable --now synapsis 2>/dev/null || true
|
||||||
rm -f /etc/systemd/system/synapsis.service \
|
rm -f /etc/systemd/system/synapsis.service \
|
||||||
|
/etc/systemd/system/synapsis-maintenance.service \
|
||||||
/etc/systemd/system/synapsis-update.service \
|
/etc/systemd/system/synapsis-update.service \
|
||||||
/etc/systemd/system/synapsis-update.timer
|
/etc/systemd/system/synapsis-update.timer
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
|
|||||||
+10
-2
@@ -21,7 +21,7 @@ set +a
|
|||||||
|
|
||||||
install_update_units() {
|
install_update_units() {
|
||||||
units_changed=0
|
units_changed=0
|
||||||
for unit in synapsis.service synapsis-update.service synapsis-update.timer; do
|
for unit in synapsis.service synapsis-maintenance.service synapsis-update.service synapsis-update.timer; do
|
||||||
if ! cmp -s "$APP_DIR/deploy/$unit" "/etc/systemd/system/$unit"; then
|
if ! cmp -s "$APP_DIR/deploy/$unit" "/etc/systemd/system/$unit"; then
|
||||||
install -m 0644 "$APP_DIR/deploy/$unit" "/etc/systemd/system/$unit"
|
install -m 0644 "$APP_DIR/deploy/$unit" "/etc/systemd/system/$unit"
|
||||||
units_changed=1
|
units_changed=1
|
||||||
@@ -66,8 +66,15 @@ if [[ "${SYNAPSIS_APPLY_UPDATE:-0}" != "1" ]]; then
|
|||||||
exec env SYNAPSIS_APPLY_UPDATE=1 APP_DIR="$APP_DIR" DATA_DIR="$DATA_DIR" ENV_FILE="$ENV_FILE" BRANCH="$BRANCH" bash "$APP_DIR/deploy/update.sh"
|
exec env SYNAPSIS_APPLY_UPDATE=1 APP_DIR="$APP_DIR" DATA_DIR="$DATA_DIR" ENV_FILE="$ENV_FILE" BRANCH="$BRANCH" bash "$APP_DIR/deploy/update.sh"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
install_update_units
|
||||||
systemctl stop synapsis
|
systemctl stop synapsis
|
||||||
trap 'systemctl start synapsis' EXIT
|
systemctl start synapsis-maintenance
|
||||||
|
|
||||||
|
restore_service() {
|
||||||
|
systemctl stop synapsis-maintenance 2>/dev/null || true
|
||||||
|
systemctl start synapsis
|
||||||
|
}
|
||||||
|
trap restore_service EXIT
|
||||||
|
|
||||||
if [[ -f "${DATABASE_PATH:-$DATA_DIR/synapsis.db}" ]]; then
|
if [[ -f "${DATABASE_PATH:-$DATA_DIR/synapsis.db}" ]]; then
|
||||||
database_file="${DATABASE_PATH:-$DATA_DIR/synapsis.db}"
|
database_file="${DATABASE_PATH:-$DATA_DIR/synapsis.db}"
|
||||||
@@ -96,6 +103,7 @@ runuser -u synapsis -- npm --prefix "$APP_DIR" run build
|
|||||||
deployed_commit="$(runuser -u synapsis -- git -C "$APP_DIR" rev-parse HEAD)"
|
deployed_commit="$(runuser -u synapsis -- git -C "$APP_DIR" rev-parse HEAD)"
|
||||||
printf '%s\n' "$deployed_commit" | install -m 0644 -o synapsis -g synapsis /dev/stdin "$DEPLOYED_COMMIT_FILE"
|
printf '%s\n' "$deployed_commit" | install -m 0644 -o synapsis -g synapsis /dev/stdin "$DEPLOYED_COMMIT_FILE"
|
||||||
install_update_units
|
install_update_units
|
||||||
|
systemctl stop synapsis-maintenance
|
||||||
systemctl start synapsis
|
systemctl start synapsis
|
||||||
trap - EXIT
|
trap - EXIT
|
||||||
echo "Synapsis updated successfully to ${deployed_commit:0:7}."
|
echo "Synapsis updated successfully to ${deployed_commit:0:7}."
|
||||||
|
|||||||
Reference in New Issue
Block a user