Files
2026-06-17 22:37:47 +05:30

65 lines
2.0 KiB
JavaScript

#!/usr/bin/env node
// Tiny webhook receiver: validates the shared secret, then shells out to rebuild.sh.
// Run on the homeserver (NOT inside docker) on port 4321.
//
// WEBHOOK_PORT=4321 REPO_DIR=/opt/softvowels REBUILD_TOKEN=change-me node scripts/webhook.js
//
// Or via the included systemd unit (see scripts/softvowels-webhook.service).
import { createServer } from 'node:http';
import { spawn } from 'node:child_process';
import { writeFileSync } from 'node:fs';
const PORT = Number(process.env.WEBHOOK_PORT || 4321);
const TOKEN = process.env.REBUILD_TOKEN || process.env.ASTRO_REVALIDATE_TOKEN;
const REPO = process.env.REPO_DIR || '/opt/softvowels';
const SCRIPT = process.env.REBUILD_SCRIPT || `${REPO}/scripts/rebuild.sh`;
const LOG = process.env.LOG_FILE || '/var/log/softvowels-rebuild.log';
if (!TOKEN) {
console.error('REBUILD_TOKEN is required');
process.exit(1);
}
function log(line) {
const ts = new Date().toISOString();
const msg = `[${ts}] ${line}\n`;
try {
writeFileSync(LOG, msg, { flag: 'a' });
} catch {
/* noop */
}
process.stdout.write(msg);
}
function triggerRebuild(collection, op) {
log(`Trigger rebuild for ${collection}.${op}`);
const child = spawn('bash', [SCRIPT], { detached: true, stdio: 'ignore' });
child.unref();
}
const server = createServer((req, res) => {
if (req.method !== 'POST' || req.url === '/') {
res.writeHead(405);
res.end('Method Not Allowed');
return;
}
const url = new URL(req.url, 'http://x');
const secret = url.searchParams.get('secret');
const collection = url.searchParams.get('collection') ?? 'unknown';
const op = url.searchParams.get('op') ?? 'change';
if (secret !== TOKEN) {
log(`Bad secret from ${req.socket.remoteAddress}`);
res.writeHead(401);
res.end('Unauthorized');
return;
}
triggerRebuild(collection, op);
res.writeHead(202, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true, queued: true }));
});
server.listen(PORT, '127.0.0.1', () => {
log(`Webhook listening on http://127.0.0.1:${PORT}/__revalidate`);
});