feat: make background polling disablable with on-demand fallback

- Set POLL_INTERVAL=0, off, false, or disabled to disable background polling
- When disabled, data is fetched on-demand when a user opens the dashboard
- On-demand results cached for 30s so other users benefit from fresh data
- A user with a faster refresh rate keeps the cache warm for everyone
- When polling is enabled, behaviour is unchanged (default 5s)
This commit is contained in:
2026-05-15 23:46:51 +01:00
parent f28d94d9a3
commit 85bac5994e
3 changed files with 24 additions and 7 deletions

View File

@@ -54,7 +54,7 @@ const radarrRoutes = require('./routes/radarr');
const embyRoutes = require('./routes/emby');
const dashboardRoutes = require('./routes/dashboard');
const authRoutes = require('./routes/auth');
const { startPoller, POLL_INTERVAL } = require('./utils/poller');
const { startPoller, POLL_INTERVAL, POLLING_ENABLED } = require('./utils/poller');
const app = express();
const PORT = process.env.PORT || 3001;
@@ -80,7 +80,7 @@ app.listen(PORT, () => {
console.log(` sofarr - Your Downloads Dashboard`);
console.log(` Server running on port ${PORT}`);
console.log(` Log level: ${process.env.LOG_LEVEL || 'info'}`);
console.log(` Poll interval: ${POLL_INTERVAL}ms`);
console.log(` Polling: ${POLLING_ENABLED ? POLL_INTERVAL + 'ms' : 'disabled (on-demand)'}`);
console.log(`=================================`);
startPoller();
});

View File

@@ -3,6 +3,7 @@ const router = express.Router();
const { mapTorrentToDownload } = require('../utils/qbittorrent');
const cache = require('../utils/cache');
const { pollAllServices, POLLING_ENABLED } = require('../utils/poller');
const EMBY_URL = process.env.EMBY_URL;
const EMBY_API_KEY = process.env.EMBY_API_KEY;
@@ -105,7 +106,14 @@ router.get('/user-downloads', async (req, res) => {
const showAll = isAdmin && req.query.showAll === 'true';
console.log(`[Dashboard] Serving downloads for user: ${user.name} (${username}), isAdmin: ${isAdmin}, showAll: ${showAll}`);
// Read all data from poller cache
// When polling is disabled, fetch on-demand if cache has expired
// The fetched data is cached (30s TTL) so subsequent requests from any user reuse it
if (!POLLING_ENABLED && !cache.get('poll:sab-queue')) {
console.log(`[Dashboard] Cache expired and polling disabled, fetching on-demand...`);
await pollAllServices();
}
// Read all data from cache
const sabQueueData = cache.get('poll:sab-queue') || { slots: [] };
const sabHistoryData = cache.get('poll:sab-history') || { slots: [] };
const sonarrTagsResults = cache.get('poll:sonarr-tags') || [];

View File

@@ -7,7 +7,11 @@ const {
getRadarrInstances
} = require('./config');
const POLL_INTERVAL = parseInt(process.env.POLL_INTERVAL, 10) || 5000;
const rawPollInterval = (process.env.POLL_INTERVAL || '').toLowerCase();
const POLL_INTERVAL = (rawPollInterval === 'off' || rawPollInterval === 'false' || rawPollInterval === 'disabled')
? 0
: (parseInt(process.env.POLL_INTERVAL, 10) || 5000);
const POLLING_ENABLED = POLL_INTERVAL > 0;
let polling = false;
@@ -125,8 +129,9 @@ async function pollAllServices() {
})
]);
// Aggregate and store in cache (TTL slightly longer than poll interval to avoid gaps)
const cacheTTL = POLL_INTERVAL * 3;
// When polling is active, TTL is 3x interval to avoid gaps between polls
// When polling is disabled (on-demand), use 30s so data refreshes on next request after expiry
const cacheTTL = POLLING_ENABLED ? POLL_INTERVAL * 3 : 30000;
// SABnzbd
const firstSabQueue = sabQueues[0] && sabQueues[0].data && sabQueues[0].data.queue;
@@ -182,6 +187,10 @@ async function pollAllServices() {
let intervalHandle = null;
function startPoller() {
if (!POLLING_ENABLED) {
console.log(`[Poller] Background polling disabled (POLL_INTERVAL=${process.env.POLL_INTERVAL || 'not set'}). Data will be fetched on-demand.`);
return;
}
console.log(`[Poller] Starting background poller (interval: ${POLL_INTERVAL}ms)`);
// Run immediately, then on interval
pollAllServices();
@@ -196,4 +205,4 @@ function stopPoller() {
}
}
module.exports = { startPoller, stopPoller, POLL_INTERVAL };
module.exports = { startPoller, stopPoller, pollAllServices, POLL_INTERVAL, POLLING_ENABLED };