Switch admin/API flows to article IDs with legacy job fallback
This commit is contained in:
parent
3cc2828bc0
commit
efcf87d00c
5 changed files with 205 additions and 67 deletions
|
|
@ -7,17 +7,57 @@ mvlog_require_login();
|
|||
$MVLOG_ROOT = dirname(__DIR__);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
$job = $_POST['job'] ?? '';
|
||||
$id = strtolower(trim((string)($_POST['id'] ?? '')));
|
||||
$job = trim((string)($_POST['job'] ?? ''));
|
||||
$max_frames = isset($_POST['max_frames']) ? intval($_POST['max_frames']) : 16;
|
||||
|
||||
if (!preg_match('/^[0-9]{8}[-_][A-Za-z0-9._-]+$/', $job)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['status'=>'error','message'=>'invalid job']);
|
||||
exit;
|
||||
function mvlog_article_id_is_valid(string $id): bool {
|
||||
return (bool)preg_match('/^[0-9]{14}[a-f0-9]{16}$/', $id);
|
||||
}
|
||||
|
||||
$jobdir = $MVLOG_ROOT . '/in-dir/' . $job;
|
||||
if (!is_dir($jobdir)) {
|
||||
function mvlog_legacy_job_is_valid(string $job): bool {
|
||||
return (bool)preg_match('/^[0-9]{8}[-_][A-Za-z0-9._-]+$/', $job);
|
||||
}
|
||||
|
||||
function mvlog_read_article_id(string $jobdir): string {
|
||||
$path = $jobdir . '/.mvlog-id';
|
||||
if (!is_file($path)) return '';
|
||||
$value = strtolower(trim((string)@file_get_contents($path)));
|
||||
return mvlog_article_id_is_valid($value) ? $value : '';
|
||||
}
|
||||
|
||||
function mvlog_find_jobdir_by_id(string $mvlogRoot, string $articleId): ?string {
|
||||
if (!mvlog_article_id_is_valid($articleId)) return null;
|
||||
|
||||
$cacheFile = $mvlogRoot . '/cache/articles-index.json';
|
||||
if (is_file($cacheFile)) {
|
||||
$cache = json_decode((string)@file_get_contents($cacheFile), true);
|
||||
if (is_array($cache) && is_array($cache['by_id'] ?? null)) {
|
||||
$name = (string)($cache['by_id'][$articleId] ?? '');
|
||||
if ($name !== '') {
|
||||
$candidate = $mvlogRoot . '/in-dir/' . basename($name);
|
||||
if (is_dir($candidate)) return $candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (glob($mvlogRoot . '/in-dir/*', GLOB_ONLYDIR) ?: [] as $dir) {
|
||||
if (mvlog_read_article_id($dir) === $articleId) return $dir;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$jobdir = null;
|
||||
if ($id !== '' && mvlog_article_id_is_valid($id)) {
|
||||
$jobdir = mvlog_find_jobdir_by_id($MVLOG_ROOT, $id);
|
||||
}
|
||||
if ($jobdir === null && $job !== '' && mvlog_legacy_job_is_valid($job)) {
|
||||
$candidate = $MVLOG_ROOT . '/in-dir/' . basename($job);
|
||||
if (is_dir($candidate)) $jobdir = $candidate;
|
||||
}
|
||||
|
||||
if ($jobdir === null || !is_dir($jobdir)) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['status'=>'error','message'=>'job not found']);
|
||||
exit;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* send_push.php
|
||||
* Helper function to send "Show enabled" web-push notifications from the webserver.
|
||||
*
|
||||
* Function: try_send_show_notification(string $job, string $jobdir, string $mvlog_root = null, string $logfile = null): bool
|
||||
* Function: try_send_show_notification(string $articleRefOrJob, string $jobdir, string $mvlog_root = null, string $logfile = null): bool
|
||||
*
|
||||
* Returns true on successful send and cache update, false otherwise.
|
||||
*/
|
||||
|
|
@ -73,7 +73,20 @@ function mvlog_nice_title(string $s): string
|
|||
return trim(ucwords(str_replace(['_', '-'], ' ', $s)));
|
||||
}
|
||||
|
||||
function try_send_show_notification($job, $jobdir, $mvlog_root = null, $logfile = null)
|
||||
function mvlog_article_id_is_valid(string $id): bool
|
||||
{
|
||||
return (bool)preg_match('/^[0-9]{14}[a-f0-9]{16}$/', $id);
|
||||
}
|
||||
|
||||
function mvlog_read_article_id(string $jobdir): string
|
||||
{
|
||||
$path = rtrim($jobdir, '/') . '/.mvlog-id';
|
||||
if (!is_file($path)) return '';
|
||||
$value = strtolower(trim((string)@file_get_contents($path)));
|
||||
return mvlog_article_id_is_valid($value) ? $value : '';
|
||||
}
|
||||
|
||||
function try_send_show_notification($articleRefOrJob, $jobdir, $mvlog_root = null, $logfile = null)
|
||||
{
|
||||
if ($mvlog_root === null) $mvlog_root = dirname(__DIR__);
|
||||
if ($logfile === null) $logfile = '/var/log/mvlog_notify.log';
|
||||
|
|
@ -81,6 +94,11 @@ 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';
|
||||
|
||||
$job = basename((string)$jobdir);
|
||||
$articleRef = strtolower(trim((string)$articleRefOrJob));
|
||||
$articleId = mvlog_article_id_is_valid($articleRef) ? $articleRef : mvlog_read_article_id((string)$jobdir);
|
||||
$identity = $articleId !== '' ? $articleId : $job;
|
||||
|
||||
// Don't notify for hidden jobs
|
||||
if (is_file($jobdir . '/.mvlog-hidden')) {
|
||||
error_log("[mvlog] job $job hidden, skipping push\n", 3, $logfile);
|
||||
|
|
@ -89,17 +107,17 @@ function try_send_show_notification($job, $jobdir, $mvlog_root = null, $logfile
|
|||
|
||||
// Gather subscriptions
|
||||
if (!is_file($subs_file)) {
|
||||
error_log("[mvlog] subscriptions file missing, skipping push for $job\n", 3, $logfile);
|
||||
error_log("[mvlog] subscriptions file missing, skipping push for $identity\n", 3, $logfile);
|
||||
return false;
|
||||
}
|
||||
$subs_raw = @file_get_contents($subs_file);
|
||||
if (!is_string($subs_raw) || trim($subs_raw) === '') {
|
||||
error_log("[mvlog] subscriptions empty, skipping push for $job\n", 3, $logfile);
|
||||
error_log("[mvlog] subscriptions empty, skipping push for $identity\n", 3, $logfile);
|
||||
return false;
|
||||
}
|
||||
$subs = json_decode($subs_raw, true);
|
||||
if (!is_array($subs) || count($subs) === 0) {
|
||||
error_log("[mvlog] subscriptions invalid/empty, skipping push for $job\n", 3, $logfile);
|
||||
error_log("[mvlog] subscriptions invalid/empty, skipping push for $identity\n", 3, $logfile);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -109,21 +127,22 @@ function try_send_show_notification($job, $jobdir, $mvlog_root = null, $logfile
|
|||
$cache = json_decode((string)@file_get_contents($cache_file), true) ?: [];
|
||||
}
|
||||
$shown = isset($cache['shown']) && is_array($cache['shown']) ? $cache['shown'] : [];
|
||||
if (isset($shown[$job])) {
|
||||
error_log("[mvlog] push already sent for $job, skipping\n", 3, $logfile);
|
||||
// Transition support: honor both new (ID) and legacy (job name) keys.
|
||||
if (isset($shown[$identity]) || ($identity !== $job && isset($shown[$job]))) {
|
||||
error_log("[mvlog] push already sent for $identity (job=$job), skipping\n", 3, $logfile);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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);
|
||||
error_log("[mvlog] push config not found/invalid, skipping push for $identity\n", 3, $logfile);
|
||||
return false;
|
||||
}
|
||||
|
||||
$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);
|
||||
error_log("[mvlog] failed to create temp push config for $identity\n", 3, $logfile);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -135,7 +154,7 @@ function try_send_show_notification($job, $jobdir, $mvlog_root = null, $logfile
|
|||
|
||||
$state_file = $jobdir . '/.movmaker-state.json';
|
||||
if (is_file($state_file)) {
|
||||
$state = json_decode(@file_get_contents($state_file), true) ?: [];
|
||||
$state = json_decode((string)@file_get_contents($state_file), true) ?: [];
|
||||
if (!empty($state['output']) && is_string($state['output'])) {
|
||||
$out = pathinfo($state['output'], PATHINFO_FILENAME);
|
||||
if ($out) {
|
||||
|
|
@ -158,7 +177,7 @@ function try_send_show_notification($job, $jobdir, $mvlog_root = null, $logfile
|
|||
'title' => $articleTitle,
|
||||
'body' => 'New MVLog article is now visible',
|
||||
'url' => $filterUrl,
|
||||
'tag' => "mvlog-show-{$job}",
|
||||
'tag' => "mvlog-show-{$identity}",
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
|
||||
// Call node send_push.js with subscriptions on stdin
|
||||
|
|
@ -176,7 +195,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);
|
||||
error_log("[mvlog] failed to start node send_push process for $identity\n", 3, $logfile);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -191,29 +210,33 @@ function try_send_show_notification($job, $jobdir, $mvlog_root = null, $logfile
|
|||
@unlink($tmp_push_config);
|
||||
|
||||
if ($rc !== 0) {
|
||||
error_log("[mvlog] send_push failed for $job rc=$rc stdout=" . trim((string)$stdout) . " stderr=" . trim((string)$stderr) . "\n", 3, $logfile);
|
||||
error_log("[mvlog] send_push failed for $identity rc=$rc stdout=" . trim((string)$stdout) . " stderr=" . trim((string)$stderr) . "\n", 3, $logfile);
|
||||
return false;
|
||||
}
|
||||
|
||||
// On success, record in cache['shown']
|
||||
if (!is_array($cache)) $cache = [];
|
||||
if (!isset($cache['shown']) || !is_array($cache['shown'])) $cache['shown'] = [];
|
||||
$cache['shown'][$job] = ['sent_at' => date(DATE_ATOM)];
|
||||
$cache['shown'][$identity] = [
|
||||
'sent_at' => date(DATE_ATOM),
|
||||
'job' => $job,
|
||||
'article_id' => $articleId,
|
||||
];
|
||||
|
||||
// Atomic write: write to tmp then rename
|
||||
$tmp = $cache_file . '.tmp.' . bin2hex(random_bytes(6));
|
||||
$json = json_encode($cache, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n";
|
||||
if (@file_put_contents($tmp, $json, LOCK_EX) === false) {
|
||||
error_log("[mvlog] failed to write cache temp file for $job\n", 3, $logfile);
|
||||
error_log("[mvlog] failed to write cache temp file for $identity\n", 3, $logfile);
|
||||
return false;
|
||||
}
|
||||
@chmod($tmp, 0664);
|
||||
if (!@rename($tmp, $cache_file)) {
|
||||
@unlink($tmp);
|
||||
error_log("[mvlog] failed to atomically write cache file for $job\n", 3, $logfile);
|
||||
error_log("[mvlog] failed to atomically write cache file for $identity\n", 3, $logfile);
|
||||
return false;
|
||||
}
|
||||
|
||||
error_log("[mvlog] show-notification sent: $job (cfg=" . ($cfg['_source'] ?? 'unknown') . ")\n", 3, $logfile);
|
||||
error_log("[mvlog] show-notification sent: identity=$identity job=$job (cfg=" . ($cfg['_source'] ?? 'unknown') . ")\n", 3, $logfile);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue