From de1c753a42502dbafaa848c9b67d1d132345dffe Mon Sep 17 00:00:00 2001 From: hbrain Date: Sun, 31 May 2026 14:28:05 +0200 Subject: [PATCH] Unify push config resolution for subscribe and send paths --- lib/send_push.php | 92 ++++++++++++++++++++++++++++++++++++++--------- push_config.php | 52 ++++++++++++++++++++++++--- 2 files changed, 123 insertions(+), 21 deletions(-) diff --git a/lib/send_push.php b/lib/send_push.php index 94c3fa5..b6c09fa 100644 --- a/lib/send_push.php +++ b/lib/send_push.php @@ -8,6 +8,66 @@ * Returns true on successful send and cache update, false otherwise. */ +function mvlog_load_push_config(string $mvlog_root): ?array +{ + $candidates = [ + '/etc/mvlog/push.php', + '/etc/mvlog/push.json', + $mvlog_root . '/push.json', + (getenv('HOME') !== false ? getenv('HOME') . '/.config/mvlog/push.json' : null), + ]; + + foreach ($candidates as $candidate) { + if (!$candidate || !is_file($candidate) || !is_readable($candidate)) continue; + + $cfg = null; + if (str_ends_with($candidate, '.php')) { + $loaded = @require $candidate; + if (is_array($loaded)) $cfg = $loaded; + } else { + $decoded = json_decode((string)@file_get_contents($candidate), true); + if (is_array($decoded)) $cfg = $decoded; + } + + if (!is_array($cfg)) continue; + + $public = (string)($cfg['public_key'] ?? $cfg['vapidPublicKey'] ?? ''); + $private = (string)($cfg['private_key'] ?? $cfg['vapidPrivateKey'] ?? ''); + $subject = (string)($cfg['subject'] ?? $cfg['vapidSubject'] ?? ''); + + if ($public !== '' && $private !== '') { + return [ + 'subject' => $subject, + 'public_key' => $public, + 'private_key' => $private, + '_source' => $candidate, + ]; + } + } + + return null; +} + +function mvlog_write_temp_push_json(array $cfg): ?string +{ + $tmp = tempnam(sys_get_temp_dir(), 'mvlog-push-'); + if ($tmp === false) return null; + + $payload = json_encode([ + 'subject' => (string)($cfg['subject'] ?? ''), + 'public_key' => (string)($cfg['public_key'] ?? ''), + 'private_key' => (string)($cfg['private_key'] ?? ''), + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + + if (!is_string($payload) || @file_put_contents($tmp, $payload, LOCK_EX) === false) { + @unlink($tmp); + return null; + } + + @chmod($tmp, 0600); + return $tmp; +} + function try_send_show_notification($job, $jobdir, $mvlog_root = null, $logfile = null) { if ($mvlog_root === null) $mvlog_root = dirname(__DIR__); @@ -15,11 +75,6 @@ function try_send_show_notification($job, $jobdir, $mvlog_root = null, $logfile $subs_file = $mvlog_root . '/cache/push_subscriptions.json'; $cache_file = $mvlog_root . '/cache/push_notifications.json'; - $push_config_candidates = [ - $mvlog_root . '/push.json', - (getenv('HOME') !== false ? getenv('HOME') . '/.config/mvlog/push.json' : null), - '/etc/mvlog/push.json', - ]; // Don't notify for hidden jobs if (is_file($jobdir . '/.mvlog-hidden')) { @@ -54,14 +109,16 @@ function try_send_show_notification($job, $jobdir, $mvlog_root = null, $logfile return false; } - // Determine push config file - $push_config = null; - foreach ($push_config_candidates as $candidate) { - if (!$candidate) continue; - if (is_file($candidate) && is_readable($candidate)) { $push_config = $candidate; break; } + // Resolve push config consistently (same source model as push_config.php) + $cfg = mvlog_load_push_config($mvlog_root); + if (!$cfg) { + error_log("[mvlog] push config not found/invalid, skipping push for $job\n", 3, $logfile); + return false; } - if (!$push_config) { - error_log("[mvlog] push config not found, skipping push for $job\n", 3, $logfile); + + $tmp_push_config = mvlog_write_temp_push_json($cfg); + if (!$tmp_push_config) { + error_log("[mvlog] failed to create temp push config for $job\n", 3, $logfile); return false; } @@ -89,8 +146,8 @@ function try_send_show_notification($job, $jobdir, $mvlog_root = null, $logfile // Call node send_push.js with subscriptions on stdin $send_script = escapeshellarg($mvlog_root . '/send_push.js'); - $push_config_esc = escapeshellarg($push_config); - $payload_esc = escapeshellarg($payload); + $push_config_esc = escapeshellarg($tmp_push_config); + $payload_esc = escapeshellarg((string)$payload); $cmd = "node $send_script $push_config_esc $payload_esc"; $descriptorspec = [ @@ -101,6 +158,7 @@ function try_send_show_notification($job, $jobdir, $mvlog_root = null, $logfile $proc = proc_open($cmd, $descriptorspec, $pipes); if (!is_resource($proc)) { + @unlink($tmp_push_config); error_log("[mvlog] failed to start node send_push process for $job\n", 3, $logfile); return false; } @@ -113,8 +171,10 @@ function try_send_show_notification($job, $jobdir, $mvlog_root = null, $logfile $stderr = stream_get_contents($pipes[2]); fclose($pipes[2]); $rc = proc_close($proc); + @unlink($tmp_push_config); + if ($rc !== 0) { - error_log("[mvlog] send_push failed for $job rc=$rc stdout=" . trim($stdout) . " stderr=" . trim($stderr) . "\n", 3, $logfile); + error_log("[mvlog] send_push failed for $job rc=$rc stdout=" . trim((string)$stdout) . " stderr=" . trim((string)$stderr) . "\n", 3, $logfile); return false; } @@ -137,6 +197,6 @@ function try_send_show_notification($job, $jobdir, $mvlog_root = null, $logfile return false; } - error_log("[mvlog] show-notification sent: $job\n", 3, $logfile); + error_log("[mvlog] show-notification sent: $job (cfg=" . ($cfg['_source'] ?? 'unknown') . ")\n", 3, $logfile); return true; } diff --git a/push_config.php b/push_config.php index e2819e4..59ee0d7 100644 --- a/push_config.php +++ b/push_config.php @@ -1,11 +1,53 @@ $public, + 'private_key' => $private, + 'subject' => $subject, + '_source' => $candidate, + ]; + } + } + + return null; +} + +$cfg = mvlog_load_push_config_for_public_key(__DIR__); +if (!$cfg) { echo json_encode(['enabled' => false]); exit; } -$cfg = require $cfgFile; -$public = (string)($cfg['public_key'] ?? ''); -echo json_encode(['enabled' => $public !== '', 'publicKey' => $public], JSON_UNESCAPED_SLASHES); + +echo json_encode([ + 'enabled' => true, + 'publicKey' => (string)$cfg['public_key'], +], JSON_UNESCAPED_SLASHES);