Release v1.5.10

This commit is contained in:
2026-07-26 12:32:01 +01:00
parent b5fbf79af7
commit 07ec17ad91
24 changed files with 1113 additions and 352 deletions
@@ -1,7 +1,10 @@
const { renderView } = require('../../view');
const { hasAnyPermission } = require('../../../rbac');
const { buildPaginationPages } = require('../../lib/pagination');
const TASKS_PER_PAGE = 10;
const QUEUE_PAGE_URL = '/settings/background-tasks';
const SCHEDULED_PAGE_URL = '/settings/background-tasks/scheduled';
function formatIntervalLabel(intervalMs) {
const value = Math.max(1, Number(intervalMs) || 0);
@@ -55,16 +58,7 @@ function buildPagination(totalItems, currentPage, pageParam, queryState) {
const safeCurrentPage = Math.min(Math.max(1, currentPage), totalPages);
const startIndex = (safeCurrentPage - 1) * TASKS_PER_PAGE;
const endIndex = Math.min(totalItems, startIndex + TASKS_PER_PAGE);
const pages = [];
for (let pageNumber = 1; pageNumber <= totalPages; pageNumber += 1) {
const nextQuery = Object.assign({}, queryState, { [normalizedPageParam]: pageNumber });
pages.push({
number: pageNumber,
active: pageNumber === safeCurrentPage,
url: buildQueryString(nextQuery)
});
}
const pages = buildPaginationPages(totalPages, safeCurrentPage, normalizedPageParam, queryState, 7);
const previousQuery = Object.assign({}, queryState, { [normalizedPageParam]: safeCurrentPage - 1 });
const nextQuery = Object.assign({}, queryState, { [normalizedPageParam]: safeCurrentPage + 1 });
@@ -137,7 +131,7 @@ function buildTaskSourceFilterUrl(queryState, task, sourceFilter) {
return buildQueryString(nextQuery);
}
module.exports = function renderBackgroundTasksPage(data, message, currentUser) {
function buildQueuePageViewModel(data, message, currentUser) {
const tasks = (data && data.tasks) || [];
const recurringTasks = (data && data.recurringTasks) || [];
const summary = (data && data.summary) || { counts: {}, total: 0, activeCount: 0 };
@@ -199,19 +193,15 @@ module.exports = function renderBackgroundTasksPage(data, message, currentUser)
});
const hasActiveFilters = Boolean(queryState.sourceType || queryState.sourceId);
return renderView('settings/background-tasks/index', {
return renderView('settings/tasks-background/index', {
title: 'Background tasks',
active: 'background-tasks',
message: message,
currentUser: currentUser || null,
scripts: ['js/settings/background-tasks.js'],
stateVersion: data && data.stateVersion ? String(data.stateVersion) : '',
backgroundTasksMenuOpen: true,
tasks: visibleTasksWithSources,
recurringTasks: visibleRecurringTasks.map(function (task) {
return Object.assign({}, task, {
intervalLabel: formatIntervalLabel(task.intervalMs)
});
}),
summary: summary,
statusCards: statusCards,
activeSourceLabel: queryState.sourceType && queryState.sourceId ? `${queryState.sourceType} #${queryState.sourceId}` : '',
@@ -224,4 +214,38 @@ module.exports = function renderBackgroundTasksPage(data, message, currentUser)
return task.status !== 'queued' && task.status !== 'running';
})
});
}
function buildScheduledPageViewModel(data, message, currentUser) {
const recurringTasks = (data && data.recurringTasks) || [];
const summary = (data && data.summary) || { counts: {}, total: 0, activeCount: 0, scheduledCount: 0 };
const canManage = hasAnyPermission(currentUser, ['scheduled-tasks.allow']);
return renderView('settings/tasks-scheduled/index', {
title: 'Scheduled tasks',
active: 'background-tasks-scheduled',
message: message,
currentUser: currentUser || null,
scripts: ['js/settings/background-tasks.js'],
stateVersion: data && data.stateVersion ? String(data.stateVersion) : '',
backgroundTasksMenuOpen: true,
recurringTasks: recurringTasks.map(function (task) {
return Object.assign({}, task, {
intervalLabel: formatIntervalLabel(task.intervalMs)
});
}),
summary: summary,
canManage: canManage,
scheduledCount: Number(summary.scheduledCount || recurringTasks.length || 0),
recurringPagination: buildPagination(recurringTasks.length, parsePageNumber(data && data.recurringPage), 'recurringPage', { recurringPage: parsePageNumber(data && data.recurringPage) })
});
}
module.exports = {
renderBackgroundTasksPage: function (data, message, currentUser) {
return buildQueuePageViewModel(data, message, currentUser);
},
renderBackgroundTasksScheduledPage: function (data, message, currentUser) {
return buildScheduledPageViewModel(data, message, currentUser);
}
};
+57 -1
View File
@@ -17,6 +17,23 @@ function requireSettingsAccess() {
};
}
function requireScheduledSettingsAccess() {
return function (req, res, next) {
if (!req.currentUser) {
return res.redirect('/login?message=' + encodeURIComponent('Please sign in to continue.'));
}
if (hasAnyPermission(req.currentUser, ['scheduled-tasks.read', 'scheduled-tasks.allow'])) {
return next();
}
const error = new Error('You do not have permission to access this area.');
error.statusCode = 403;
error.expose = true;
next(error);
};
}
module.exports = function registerAdminSettingsRoutes(app, deps) {
const pages = deps.pages;
const backgroundTaskQueue = deps.backgroundTaskQueue;
@@ -50,7 +67,28 @@ module.exports = function registerAdminSettingsRoutes(app, deps) {
res.send(pages.renderBackgroundTasksPage({ tasks: tasks, recurringTasks: recurringTasks, summary: summary, stateVersion: version, page: req.query.page, recurringPage: req.query.recurringPage }, req.query.message ? String(req.query.message) : '', req.currentUser));
});
app.get('/settings/background-tasks/state', requireSettingsAccess(), function (req, res) {
app.get('/settings/background-tasks/scheduled', requireScheduledSettingsAccess(), function (req, res) {
const tasks = backgroundTaskQueue.listTasks();
const recurringTasks = backgroundTaskQueue.listRecurringTasks();
const summary = backgroundTaskQueue.getSummary();
const version = buildQueueVersion(tasks, recurringTasks, summary);
res.send(pages.renderBackgroundTasksScheduledPage({ tasks: tasks, recurringTasks: recurringTasks, summary: summary, stateVersion: version, page: req.query.page, recurringPage: req.query.recurringPage }, req.query.message ? String(req.query.message) : '', req.currentUser));
});
app.get('/settings/background-tasks/state', function (req, res, next) {
if (!req.currentUser) {
return res.redirect('/login?message=' + encodeURIComponent('Please sign in to continue.'));
}
if (!hasAnyPermission(req.currentUser, ['background-tasks.read', 'background-tasks.allow', 'scheduled-tasks.read', 'scheduled-tasks.allow'])) {
const error = new Error('You do not have permission to access this area.');
error.statusCode = 403;
error.expose = true;
return next(error);
}
return next();
}, function (req, res) {
const tasks = backgroundTaskQueue.listTasks();
const recurringTasks = backgroundTaskQueue.listRecurringTasks();
const summary = backgroundTaskQueue.getSummary();
@@ -96,4 +134,22 @@ module.exports = function registerAdminSettingsRoutes(app, deps) {
const retryTask = backgroundTaskQueue.retryTask(Number(req.params.id));
res.redirect('/settings/background-tasks?message=' + encodeURIComponent(retryTask ? 'Task requeued.' : 'Unable to retry that task.'));
});
app.post('/settings/background-tasks/recurring/:key/run', function (req, res, next) {
if (!req.currentUser) {
return res.redirect('/login?message=' + encodeURIComponent('Please sign in to continue.'));
}
if (!hasAnyPermission(req.currentUser, ['scheduled-tasks.allow'])) {
const error = new Error('You do not have permission to access this area.');
error.statusCode = 403;
error.expose = true;
return next(error);
}
return next();
}, function (req, res) {
const ran = backgroundTaskQueue.runRecurringTask(String(req.params.key || '').trim());
res.redirect('/settings/background-tasks/scheduled?message=' + encodeURIComponent(ran ? 'Scheduled refresh run queued.' : 'Unable to run that scheduled refresh.'));
});
};