Save worktree changes
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
const { renderView } = require('../../view');
|
||||
const { hasAnyPermission } = require('../../../rbac');
|
||||
|
||||
const TASKS_PER_PAGE = 10;
|
||||
|
||||
function formatIntervalLabel(intervalMs) {
|
||||
const value = Math.max(1, Number(intervalMs) || 0);
|
||||
if (value % 60000 === 0) {
|
||||
const minutes = Math.max(1, value / 60000);
|
||||
return minutes === 1 ? 'Every minute' : `Every ${minutes} minutes`;
|
||||
}
|
||||
if (value % 1000 === 0) {
|
||||
const seconds = Math.max(1, value / 1000);
|
||||
return seconds === 1 ? 'Every second' : `Every ${seconds} seconds`;
|
||||
}
|
||||
return `${value} ms`;
|
||||
}
|
||||
|
||||
function parsePageNumber(value) {
|
||||
const pageNumber = Math.floor(Number(value) || 1);
|
||||
return Math.max(1, pageNumber);
|
||||
}
|
||||
|
||||
function buildPagination(totalItems, currentPage, pageParam) {
|
||||
const normalizedPageParam = String(pageParam || 'page').trim() || 'page';
|
||||
const totalPages = Math.max(1, Math.ceil(totalItems / TASKS_PER_PAGE));
|
||||
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) {
|
||||
pages.push({
|
||||
number: pageNumber,
|
||||
active: pageNumber === safeCurrentPage,
|
||||
url: `?${normalizedPageParam}=${pageNumber}`
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
currentPage: safeCurrentPage,
|
||||
totalPages: totalPages,
|
||||
totalItems: totalItems,
|
||||
hasMultiplePages: totalPages > 1,
|
||||
startItem: totalItems === 0 ? 0 : startIndex + 1,
|
||||
endItem: endIndex,
|
||||
hasPrevious: safeCurrentPage > 1,
|
||||
hasNext: safeCurrentPage < totalPages,
|
||||
previousUrl: `?${normalizedPageParam}=${safeCurrentPage - 1}`,
|
||||
nextUrl: `?${normalizedPageParam}=${safeCurrentPage + 1}`,
|
||||
pages: pages,
|
||||
pageSize: TASKS_PER_PAGE,
|
||||
pageParam: normalizedPageParam
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = function renderBackgroundTasksPage(data, message, currentUser) {
|
||||
const tasks = (data && data.tasks) || [];
|
||||
const recurringTasks = (data && data.recurringTasks) || [];
|
||||
const summary = (data && data.summary) || { counts: {}, total: 0, activeCount: 0 };
|
||||
const canManage = hasAnyPermission(currentUser, ['background-tasks.allow']);
|
||||
const pagination = buildPagination(tasks.length, parsePageNumber(data && data.page), 'page');
|
||||
const recurringPagination = buildPagination(recurringTasks.length, parsePageNumber(data && data.recurringPage), 'recurringPage');
|
||||
const visibleTasks = tasks.slice((pagination.currentPage - 1) * TASKS_PER_PAGE, pagination.currentPage * TASKS_PER_PAGE);
|
||||
const visibleRecurringTasks = recurringTasks.slice((recurringPagination.currentPage - 1) * TASKS_PER_PAGE, recurringPagination.currentPage * TASKS_PER_PAGE);
|
||||
|
||||
return renderView('settings/background-tasks/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) : '',
|
||||
tasks: visibleTasks,
|
||||
recurringTasks: visibleRecurringTasks.map(function (task) {
|
||||
return Object.assign({}, task, {
|
||||
intervalLabel: formatIntervalLabel(task.intervalMs)
|
||||
});
|
||||
}),
|
||||
summary: summary,
|
||||
pagination: pagination,
|
||||
recurringPagination: recurringPagination,
|
||||
canManage: canManage,
|
||||
canClearFinished: tasks.some(function (task) {
|
||||
return task.status !== 'queued' && task.status !== 'running';
|
||||
})
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,99 @@
|
||||
const { hasAnyPermission } = require('../../../rbac');
|
||||
|
||||
function requireSettingsAccess() {
|
||||
return 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'])) {
|
||||
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;
|
||||
|
||||
if (!pages || !backgroundTaskQueue) {
|
||||
throw new Error('registerAdminSettingsRoutes requires pages and backgroundTaskQueue.');
|
||||
}
|
||||
|
||||
function buildQueueVersion(tasks, recurringTasks, summary) {
|
||||
return [
|
||||
'q:' + Number(summary && summary.counts && summary.counts.queued || 0),
|
||||
'r:' + Number(summary && summary.counts && summary.counts.running || 0),
|
||||
'f:' + Number(summary && summary.counts && summary.counts.failed || 0),
|
||||
'c:' + Number(summary && summary.counts && summary.counts.completed || 0),
|
||||
'a:' + Number(summary && summary.activeCount || 0),
|
||||
't:' + Number(summary && summary.total || 0),
|
||||
'tasks:' + (tasks || []).map(function (task) {
|
||||
return [task.id, task.status, task.startedAt || '', task.finishedAt || '', task.errorMessage || '', task.attempts || 0].join(':');
|
||||
}).join('|'),
|
||||
'recurring:' + (recurringTasks || []).map(function (task) {
|
||||
return [task.key, task.nextRunAt || '', task.lastRunAt || '', task.lastStatus || '', task.lastError || ''].join(':');
|
||||
}).join('|')
|
||||
].join('~');
|
||||
}
|
||||
|
||||
app.get('/settings/background-tasks', requireSettingsAccess(), function (req, res) {
|
||||
const tasks = backgroundTaskQueue.listTasks();
|
||||
const recurringTasks = backgroundTaskQueue.listRecurringTasks();
|
||||
const summary = backgroundTaskQueue.getSummary();
|
||||
const version = buildQueueVersion(tasks, recurringTasks, summary);
|
||||
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) {
|
||||
const tasks = backgroundTaskQueue.listTasks();
|
||||
const recurringTasks = backgroundTaskQueue.listRecurringTasks();
|
||||
const summary = backgroundTaskQueue.getSummary();
|
||||
const version = buildQueueVersion(tasks, recurringTasks, summary);
|
||||
|
||||
res.set('Cache-Control', 'no-store, no-cache, must-revalidate, private');
|
||||
res.json({
|
||||
version: version,
|
||||
summary: summary,
|
||||
taskCount: tasks.length,
|
||||
recurringCount: recurringTasks.length
|
||||
});
|
||||
});
|
||||
|
||||
function requireSettingsManageAccess() {
|
||||
return function (req, res, next) {
|
||||
if (!req.currentUser) {
|
||||
return res.redirect('/login?message=' + encodeURIComponent('Please sign in to continue.'));
|
||||
}
|
||||
|
||||
if (hasAnyPermission(req.currentUser, ['background-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);
|
||||
};
|
||||
}
|
||||
|
||||
app.post('/settings/background-tasks/clear-finished', requireSettingsManageAccess(), function (req, res) {
|
||||
const removedCount = backgroundTaskQueue.clearFinishedTasks();
|
||||
res.redirect('/settings/background-tasks?message=' + encodeURIComponent(removedCount ? 'Cleared ' + removedCount + ' finished task' + (removedCount === 1 ? '' : 's') + '.' : 'No finished tasks to clear.'));
|
||||
});
|
||||
|
||||
app.post('/settings/background-tasks/:id/cancel', requireSettingsManageAccess(), function (req, res) {
|
||||
const canceled = backgroundTaskQueue.cancelTask(Number(req.params.id));
|
||||
res.redirect('/settings/background-tasks?message=' + encodeURIComponent(canceled ? 'Task canceled.' : 'Unable to cancel that task.'));
|
||||
});
|
||||
|
||||
app.post('/settings/background-tasks/:id/retry', requireSettingsManageAccess(), function (req, res) {
|
||||
const retryTask = backgroundTaskQueue.retryTask(Number(req.params.id));
|
||||
res.redirect('/settings/background-tasks?message=' + encodeURIComponent(retryTask ? 'Task requeued.' : 'Unable to retry that task.'));
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
const { renderView } = require('../../../view');
|
||||
|
||||
module.exports = function renderRbacAddPage(message, currentUser, formValues, permissionGroups, messageVariant) {
|
||||
return renderView('settings/rbac/add', {
|
||||
title: 'Create role',
|
||||
active: 'rbac',
|
||||
message: message,
|
||||
messageVariant: messageVariant || 'primary',
|
||||
currentUser: currentUser || null,
|
||||
formValues: formValues || {},
|
||||
permissionGroups: permissionGroups || [],
|
||||
scripts: ['js/rbac-permissions.js']
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
const { renderView } = require('../../../view');
|
||||
|
||||
module.exports = function renderRbacEditPage(role, message, currentUser, permissionGroups, users) {
|
||||
return renderView('settings/rbac/edit', {
|
||||
title: 'Edit role',
|
||||
active: 'rbac',
|
||||
message: message,
|
||||
currentUser: currentUser || null,
|
||||
role: role,
|
||||
permissionGroups: permissionGroups || [],
|
||||
users: users || [],
|
||||
scripts: ['js/rbac-permissions.js']
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
const { renderView } = require('../../../view');
|
||||
|
||||
module.exports = function renderRbacPage(data, message, currentUser) {
|
||||
return renderView('settings/rbac/list', {
|
||||
title: 'Roles and permissions',
|
||||
active: 'rbac',
|
||||
message: message,
|
||||
currentUser: currentUser || null,
|
||||
roles: data.roles || []
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
const { renderView } = require('../../../view');
|
||||
|
||||
module.exports = function renderUsersAddPage(message, currentUser, roles, formValues, messageVariant) {
|
||||
return renderView('settings/users/add', {
|
||||
title: 'Add user',
|
||||
active: 'users',
|
||||
message: message,
|
||||
messageVariant: messageVariant || 'primary',
|
||||
currentUser: currentUser || null,
|
||||
roles: roles || [],
|
||||
formValues: formValues || {}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
const { renderView } = require('../../../view');
|
||||
|
||||
module.exports = function renderUsersEditPage(user, message, currentUser, roles) {
|
||||
return renderView('settings/users/edit', {
|
||||
title: 'Edit user',
|
||||
active: 'users',
|
||||
message: message,
|
||||
currentUser: currentUser || null,
|
||||
user: user,
|
||||
roles: roles || []
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
const { renderView } = require('../../../view');
|
||||
|
||||
module.exports = function renderUsersPage(data, message, currentUser) {
|
||||
return renderView('settings/users/list', {
|
||||
title: 'Users',
|
||||
active: 'users',
|
||||
message: message,
|
||||
currentUser: currentUser || null,
|
||||
users: data.users || []
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user