fix: harden gmail watcher restart
This commit is contained in:
parent
aae54b4af2
commit
efd10c49db
@ -22,8 +22,32 @@ const log = createSubsystemLogger("gmail-watcher");
|
|||||||
|
|
||||||
let watcherProcess: ChildProcess | null = null;
|
let watcherProcess: ChildProcess | null = null;
|
||||||
let renewInterval: ReturnType<typeof setInterval> | null = null;
|
let renewInterval: ReturnType<typeof setInterval> | null = null;
|
||||||
|
let restartTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
let shuttingDown = false;
|
let shuttingDown = false;
|
||||||
let currentConfig: GmailHookRuntimeConfig | null = null;
|
let currentConfig: GmailHookRuntimeConfig | null = null;
|
||||||
|
const redactedValue = "<redacted>";
|
||||||
|
const redactedFlags = new Set(["--token", "--hook-token", "--hook-url"]);
|
||||||
|
|
||||||
|
function redactArgs(args: string[]): string[] {
|
||||||
|
const redacted = [...args];
|
||||||
|
for (let i = 0; i < redacted.length; i += 1) {
|
||||||
|
if (redactedFlags.has(redacted[i] ?? "") && redacted[i + 1]) {
|
||||||
|
redacted[i + 1] = redactedValue;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return redacted;
|
||||||
|
}
|
||||||
|
|
||||||
|
function scheduleRestart(reason: string) {
|
||||||
|
if (shuttingDown || !currentConfig || restartTimer) return;
|
||||||
|
log.warn(`${reason}; restarting in 5s`);
|
||||||
|
restartTimer = setTimeout(() => {
|
||||||
|
restartTimer = null;
|
||||||
|
if (shuttingDown || !currentConfig) return;
|
||||||
|
watcherProcess = spawnGogServe(currentConfig);
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if gog binary is available
|
* Check if gog binary is available
|
||||||
@ -59,7 +83,11 @@ async function startGmailWatch(
|
|||||||
*/
|
*/
|
||||||
function spawnGogServe(cfg: GmailHookRuntimeConfig): ChildProcess {
|
function spawnGogServe(cfg: GmailHookRuntimeConfig): ChildProcess {
|
||||||
const args = buildGogWatchServeArgs(cfg);
|
const args = buildGogWatchServeArgs(cfg);
|
||||||
log.info(`starting gog ${args.join(" ")}`);
|
if (restartTimer) {
|
||||||
|
clearTimeout(restartTimer);
|
||||||
|
restartTimer = null;
|
||||||
|
}
|
||||||
|
log.info(`starting gog ${redactArgs(args).join(" ")}`);
|
||||||
|
|
||||||
const child = spawn("gog", args, {
|
const child = spawn("gog", args, {
|
||||||
stdio: ["ignore", "pipe", "pipe"],
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
@ -78,16 +106,18 @@ function spawnGogServe(cfg: GmailHookRuntimeConfig): ChildProcess {
|
|||||||
|
|
||||||
child.on("error", (err) => {
|
child.on("error", (err) => {
|
||||||
log.error(`gog process error: ${String(err)}`);
|
log.error(`gog process error: ${String(err)}`);
|
||||||
|
if (watcherProcess === child) {
|
||||||
|
watcherProcess = null;
|
||||||
|
}
|
||||||
|
scheduleRestart("gog process error");
|
||||||
});
|
});
|
||||||
|
|
||||||
child.on("exit", (code, signal) => {
|
child.on("exit", (code, signal) => {
|
||||||
if (shuttingDown) return;
|
if (shuttingDown) return;
|
||||||
log.warn(`gog exited (code=${code}, signal=${signal}); restarting in 5s`);
|
if (watcherProcess === child) {
|
||||||
watcherProcess = null;
|
watcherProcess = null;
|
||||||
setTimeout(() => {
|
}
|
||||||
if (shuttingDown || !currentConfig) return;
|
scheduleRestart(`gog exited (code=${code}, signal=${signal})`);
|
||||||
watcherProcess = spawnGogServe(currentConfig);
|
|
||||||
}, 5000);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return child;
|
return child;
|
||||||
@ -127,6 +157,23 @@ export async function startGmailWatcher(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const runtimeConfig = resolved.value;
|
const runtimeConfig = resolved.value;
|
||||||
|
if (isGmailWatcherRunning()) {
|
||||||
|
log.info("gmail watcher already running; skipping start");
|
||||||
|
return { started: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renewInterval) {
|
||||||
|
clearInterval(renewInterval);
|
||||||
|
renewInterval = null;
|
||||||
|
}
|
||||||
|
if (restartTimer) {
|
||||||
|
clearTimeout(restartTimer);
|
||||||
|
restartTimer = null;
|
||||||
|
}
|
||||||
|
if (watcherProcess) {
|
||||||
|
watcherProcess = null;
|
||||||
|
}
|
||||||
|
|
||||||
currentConfig = runtimeConfig;
|
currentConfig = runtimeConfig;
|
||||||
|
|
||||||
// Set up Tailscale endpoint if needed
|
// Set up Tailscale endpoint if needed
|
||||||
@ -176,6 +223,11 @@ export async function startGmailWatcher(
|
|||||||
export async function stopGmailWatcher(): Promise<void> {
|
export async function stopGmailWatcher(): Promise<void> {
|
||||||
shuttingDown = true;
|
shuttingDown = true;
|
||||||
|
|
||||||
|
if (restartTimer) {
|
||||||
|
clearTimeout(restartTimer);
|
||||||
|
restartTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (renewInterval) {
|
if (renewInterval) {
|
||||||
clearInterval(renewInterval);
|
clearInterval(renewInterval);
|
||||||
renewInterval = null;
|
renewInterval = null;
|
||||||
@ -211,5 +263,9 @@ export async function stopGmailWatcher(): Promise<void> {
|
|||||||
* Check if the Gmail watcher is running.
|
* Check if the Gmail watcher is running.
|
||||||
*/
|
*/
|
||||||
export function isGmailWatcherRunning(): boolean {
|
export function isGmailWatcherRunning(): boolean {
|
||||||
return watcherProcess !== null && !shuttingDown;
|
return (
|
||||||
|
watcherProcess !== null &&
|
||||||
|
!shuttingDown &&
|
||||||
|
watcherProcess.exitCode === null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user