diff --git a/containers/libreportal/frontend/components/dashboard/js/dashboard.js b/containers/libreportal/frontend/components/dashboard/js/dashboard.js
index 141e4b0..41a0586 100755
--- a/containers/libreportal/frontend/components/dashboard/js/dashboard.js
+++ b/containers/libreportal/frontend/components/dashboard/js/dashboard.js
@@ -95,7 +95,14 @@ async function populateDashboardServiceButtons(installedApps) {
// Setup event listeners
function setupEventListeners() {
- setupMobileMenu();
+ // Guarded like system-loader's mobile-menu component: this is a nav nicety,
+ // and an unguarded call meant a missing/reordered script took the dashboard's
+ // app list down with it. Never let optional chrome break the page below it.
+ if (typeof setupMobileMenu === 'function') {
+ setupMobileMenu();
+ } else {
+ console.warn('setupMobileMenu not available — mobile drawer disabled');
+ }
loadInstalledApps();
}
diff --git a/containers/libreportal/frontend/index.html b/containers/libreportal/frontend/index.html
index 478e049..502691e 100755
--- a/containers/libreportal/frontend/index.html
+++ b/containers/libreportal/frontend/index.html
@@ -100,6 +100,12 @@
+
+
diff --git a/scripts/task/crontab_task_processor.sh b/scripts/task/crontab_task_processor.sh
index 26ebcd0..349dd49 100755
--- a/scripts/task/crontab_task_processor.sh
+++ b/scripts/task/crontab_task_processor.sh
@@ -492,6 +492,33 @@ dispatchSpecific() {
# HOUSEKEEPING
# ============================================================================
+# Drop queue entries whose task file no longer exists. queue.json is append-only
+# from the enqueue side, so any task file removed afterwards — by housekeeping, a
+# manual clean-up, a restore — leaves an id behind that the WebUI keeps fetching
+# forever, one 404 per poll per orphan. Nothing else prunes it, so it only ever
+# grows. Cheap: a no-op unless an id has no file.
+cleanupOrphanQueueEntries() {
+ local queue="$TASK_DIR/queue.json"
+ [[ -f "$queue" ]] || return 0
+ command -v jq >/dev/null 2>&1 || return 0
+
+ local ids orphans=() id
+ ids=$(jq -r '.[]?' "$queue" 2>/dev/null) || return 0
+ [[ -z "$ids" ]] && return 0
+ while IFS= read -r id; do
+ [[ -z "$id" ]] && continue
+ [[ -f "$TASK_DIR/${id}.json" ]] || orphans+=("$id")
+ done <<< "$ids"
+ (( ${#orphans[@]} == 0 )) && return 0
+
+ local filtered
+ filtered=$(jq -c --argjson drop "$(printf '%s\n' "${orphans[@]}" | jq -R . | jq -cs .)" \
+ '[ .[] | select(. as $i | $drop | index($i) | not) ]' "$queue" 2>/dev/null) || return 0
+ [[ -n "$filtered" ]] || return 0
+ printf '%s' "$filtered" | runFileWrite "$queue"
+ logInfo "Pruned ${#orphans[@]} orphaned queue entr$( (( ${#orphans[@]} == 1 )) && echo y || echo ies ): ${orphans[*]}"
+}
+
cleanupZeroByteFiles() {
# Use the bash builtin `-s` (file size > 0) instead of forking `stat` per
# file — at 100+ task files the stat fork was a measurable share of the
@@ -596,6 +623,7 @@ mainLoop() {
recoverOrphans
dispatchPending
cleanupZeroByteFiles
+ cleanupOrphanQueueEntries
maybeRegenPoll
fi
done