mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 23:40:13 +01:00
552 lines
16 KiB
JavaScript
552 lines
16 KiB
JavaScript
import { Template } from 'meteor/templating';
|
|
import { ReactiveVar } from 'meteor/reactive-var';
|
|
import { Meteor } from 'meteor/meteor';
|
|
import { TAPi18n } from '/imports/i18n';
|
|
|
|
// Reactive variables for cron settings
|
|
const migrationProgress = new ReactiveVar(0);
|
|
const migrationStatus = new ReactiveVar('');
|
|
const migrationCurrentStep = new ReactiveVar('');
|
|
const migrationSteps = new ReactiveVar([]);
|
|
const isMigrating = new ReactiveVar(false);
|
|
const cronJobs = new ReactiveVar([]);
|
|
|
|
Template.cronSettings.onCreated(function() {
|
|
this.loading = new ReactiveVar(true);
|
|
this.showMigrations = new ReactiveVar(true);
|
|
this.showBoardOperations = new ReactiveVar(false);
|
|
this.showJobs = new ReactiveVar(false);
|
|
this.showAddJob = new ReactiveVar(false);
|
|
|
|
// Board operations pagination
|
|
this.currentPage = new ReactiveVar(1);
|
|
this.pageSize = new ReactiveVar(20);
|
|
this.searchTerm = new ReactiveVar('');
|
|
this.boardOperations = new ReactiveVar([]);
|
|
this.operationStats = new ReactiveVar({});
|
|
this.pagination = new ReactiveVar({});
|
|
this.queueStats = new ReactiveVar({});
|
|
this.systemResources = new ReactiveVar({});
|
|
this.boardMigrationStats = new ReactiveVar({});
|
|
|
|
// Load initial data
|
|
loadCronData(this);
|
|
});
|
|
|
|
Template.cronSettings.helpers({
|
|
loading() {
|
|
const instance = Template.instance();
|
|
return instance && instance.loading ? instance.loading.get() : true;
|
|
},
|
|
|
|
showMigrations() {
|
|
const instance = Template.instance();
|
|
return instance && instance.showMigrations ? instance.showMigrations.get() : true;
|
|
},
|
|
|
|
showBoardOperations() {
|
|
const instance = Template.instance();
|
|
return instance && instance.showBoardOperations ? instance.showBoardOperations.get() : false;
|
|
},
|
|
|
|
showJobs() {
|
|
const instance = Template.instance();
|
|
return instance && instance.showJobs ? instance.showJobs.get() : false;
|
|
},
|
|
|
|
showAddJob() {
|
|
const instance = Template.instance();
|
|
return instance && instance.showAddJob ? instance.showAddJob.get() : false;
|
|
},
|
|
|
|
migrationProgress() {
|
|
return migrationProgress.get();
|
|
},
|
|
|
|
migrationStatus() {
|
|
return migrationStatus.get();
|
|
},
|
|
|
|
migrationCurrentStep() {
|
|
return migrationCurrentStep.get();
|
|
},
|
|
|
|
migrationSteps() {
|
|
const steps = migrationSteps.get();
|
|
const currentStep = migrationCurrentStep.get();
|
|
|
|
return steps.map(step => ({
|
|
...step,
|
|
isCurrentStep: step.name === currentStep
|
|
}));
|
|
},
|
|
|
|
cronJobs() {
|
|
return cronJobs.get();
|
|
},
|
|
|
|
formatDate(date) {
|
|
if (!date) return '-';
|
|
return new Date(date).toLocaleString();
|
|
},
|
|
|
|
boardOperations() {
|
|
const instance = Template.instance();
|
|
return instance && instance.boardOperations ? instance.boardOperations.get() : [];
|
|
},
|
|
|
|
operationStats() {
|
|
const instance = Template.instance();
|
|
return instance && instance.operationStats ? instance.operationStats.get() : {};
|
|
},
|
|
|
|
pagination() {
|
|
const instance = Template.instance();
|
|
return instance && instance.pagination ? instance.pagination.get() : {};
|
|
},
|
|
|
|
queueStats() {
|
|
const instance = Template.instance();
|
|
return instance && instance.queueStats ? instance.queueStats.get() : {};
|
|
},
|
|
|
|
systemResources() {
|
|
const instance = Template.instance();
|
|
return instance && instance.systemResources ? instance.systemResources.get() : {};
|
|
},
|
|
|
|
boardMigrationStats() {
|
|
const instance = Template.instance();
|
|
return instance && instance.boardMigrationStats ? instance.boardMigrationStats.get() : {};
|
|
},
|
|
|
|
formatDateTime(date) {
|
|
if (!date) return '-';
|
|
return new Date(date).toLocaleString();
|
|
},
|
|
|
|
formatDuration(startTime, endTime) {
|
|
if (!startTime) return '-';
|
|
const start = new Date(startTime);
|
|
const end = endTime ? new Date(endTime) : new Date();
|
|
const diffMs = end - start;
|
|
const diffMins = Math.floor(diffMs / 60000);
|
|
const diffSecs = Math.floor((diffMs % 60000) / 1000);
|
|
|
|
if (diffMins > 0) {
|
|
return `${diffMins}m ${diffSecs}s`;
|
|
} else {
|
|
return `${diffSecs}s`;
|
|
}
|
|
}
|
|
});
|
|
|
|
Template.cronSettings.switchMenu = function(event, targetID) {
|
|
const instance = Template.instance();
|
|
|
|
// Reset all tabs
|
|
instance.showMigrations.set(false);
|
|
instance.showBoardOperations.set(false);
|
|
instance.showJobs.set(false);
|
|
instance.showAddJob.set(false);
|
|
|
|
// Set the selected tab
|
|
if (targetID === 'cron-migrations') {
|
|
instance.showMigrations.set(true);
|
|
} else if (targetID === 'cron-board-operations') {
|
|
instance.showBoardOperations.set(true);
|
|
} else if (targetID === 'cron-jobs') {
|
|
instance.showJobs.set(true);
|
|
} else if (targetID === 'cron-add') {
|
|
instance.showAddJob.set(true);
|
|
}
|
|
};
|
|
|
|
Template.cronSettings.events({
|
|
'click .js-cron-migrations'(event) {
|
|
event.preventDefault();
|
|
const instance = Template.instance();
|
|
instance.showMigrations.set(true);
|
|
instance.showJobs.set(false);
|
|
instance.showAddJob.set(false);
|
|
},
|
|
|
|
'click .js-cron-board-operations'(event) {
|
|
event.preventDefault();
|
|
const instance = Template.instance();
|
|
instance.showMigrations.set(false);
|
|
instance.showBoardOperations.set(true);
|
|
instance.showJobs.set(false);
|
|
instance.showAddJob.set(false);
|
|
loadBoardOperations(instance);
|
|
},
|
|
|
|
'click .js-cron-jobs'(event) {
|
|
event.preventDefault();
|
|
const instance = Template.instance();
|
|
instance.showMigrations.set(false);
|
|
instance.showBoardOperations.set(false);
|
|
instance.showJobs.set(true);
|
|
instance.showAddJob.set(false);
|
|
loadCronJobs(instance);
|
|
},
|
|
|
|
'click .js-cron-add'(event) {
|
|
event.preventDefault();
|
|
const instance = Template.instance();
|
|
instance.showMigrations.set(false);
|
|
instance.showJobs.set(false);
|
|
instance.showAddJob.set(true);
|
|
},
|
|
|
|
'click .js-start-all-migrations'(event) {
|
|
event.preventDefault();
|
|
Meteor.call('cron.startAllMigrations', (error, result) => {
|
|
if (error) {
|
|
console.error('Failed to start migrations:', error);
|
|
alert('Failed to start migrations: ' + error.message);
|
|
} else {
|
|
// Migrations started successfully
|
|
pollMigrationProgress(Template.instance());
|
|
}
|
|
});
|
|
},
|
|
|
|
'click .js-pause-all-migrations'(event) {
|
|
event.preventDefault();
|
|
// Pause all migration cron jobs
|
|
const jobs = cronJobs.get();
|
|
jobs.forEach(job => {
|
|
if (job.name.startsWith('migration_')) {
|
|
Meteor.call('cron.pauseJob', job.name);
|
|
}
|
|
});
|
|
},
|
|
|
|
'click .js-stop-all-migrations'(event) {
|
|
event.preventDefault();
|
|
// Stop all migration cron jobs
|
|
const jobs = cronJobs.get();
|
|
jobs.forEach(job => {
|
|
if (job.name.startsWith('migration_')) {
|
|
Meteor.call('cron.stopJob', job.name);
|
|
}
|
|
});
|
|
},
|
|
|
|
'click .js-refresh-jobs'(event) {
|
|
event.preventDefault();
|
|
loadCronJobs(Template.instance());
|
|
},
|
|
|
|
'click .js-start-job'(event) {
|
|
event.preventDefault();
|
|
const jobName = $(event.currentTarget).data('job');
|
|
Meteor.call('cron.startJob', jobName, (error, result) => {
|
|
if (error) {
|
|
console.error('Failed to start job:', error);
|
|
alert('Failed to start job: ' + error.message);
|
|
} else {
|
|
console.log('Job started successfully');
|
|
loadCronJobs(Template.instance());
|
|
}
|
|
});
|
|
},
|
|
|
|
'click .js-pause-job'(event) {
|
|
event.preventDefault();
|
|
const jobName = $(event.currentTarget).data('job');
|
|
Meteor.call('cron.pauseJob', jobName, (error, result) => {
|
|
if (error) {
|
|
console.error('Failed to pause job:', error);
|
|
alert('Failed to pause job: ' + error.message);
|
|
} else {
|
|
console.log('Job paused successfully');
|
|
loadCronJobs(Template.instance());
|
|
}
|
|
});
|
|
},
|
|
|
|
'click .js-stop-job'(event) {
|
|
event.preventDefault();
|
|
const jobName = $(event.currentTarget).data('job');
|
|
Meteor.call('cron.stopJob', jobName, (error, result) => {
|
|
if (error) {
|
|
console.error('Failed to stop job:', error);
|
|
alert('Failed to stop job: ' + error.message);
|
|
} else {
|
|
console.log('Job stopped successfully');
|
|
loadCronJobs(Template.instance());
|
|
}
|
|
});
|
|
},
|
|
|
|
'click .js-remove-job'(event) {
|
|
event.preventDefault();
|
|
const jobName = $(event.currentTarget).data('job');
|
|
if (confirm('Are you sure you want to remove this job?')) {
|
|
Meteor.call('cron.removeJob', jobName, (error, result) => {
|
|
if (error) {
|
|
console.error('Failed to remove job:', error);
|
|
alert('Failed to remove job: ' + error.message);
|
|
} else {
|
|
console.log('Job removed successfully');
|
|
loadCronJobs(Template.instance());
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
'submit .js-add-cron-job-form'(event) {
|
|
event.preventDefault();
|
|
const form = event.currentTarget;
|
|
const formData = new FormData(form);
|
|
|
|
const jobData = {
|
|
name: formData.get('name'),
|
|
description: formData.get('description'),
|
|
schedule: formData.get('schedule'),
|
|
weight: parseInt(formData.get('weight'))
|
|
};
|
|
|
|
Meteor.call('cron.addJob', jobData, (error, result) => {
|
|
if (error) {
|
|
console.error('Failed to add job:', error);
|
|
alert('Failed to add job: ' + error.message);
|
|
} else {
|
|
console.log('Job added successfully');
|
|
form.reset();
|
|
Template.instance().showJobs.set(true);
|
|
Template.instance().showAddJob.set(false);
|
|
loadCronJobs(Template.instance());
|
|
}
|
|
});
|
|
},
|
|
|
|
'click .js-cancel-add-job'(event) {
|
|
event.preventDefault();
|
|
const instance = Template.instance();
|
|
instance.showJobs.set(true);
|
|
instance.showAddJob.set(false);
|
|
},
|
|
|
|
'click .js-refresh-board-operations'(event) {
|
|
event.preventDefault();
|
|
loadBoardOperations(Template.instance());
|
|
},
|
|
|
|
'click .js-start-test-operation'(event) {
|
|
event.preventDefault();
|
|
const testBoardId = 'test-board-' + Date.now();
|
|
const operationData = {
|
|
sourceBoardId: 'source-board',
|
|
targetBoardId: 'target-board',
|
|
copyOptions: { includeCards: true, includeAttachments: true }
|
|
};
|
|
|
|
Meteor.call('cron.startBoardOperation', testBoardId, 'copy_board', operationData, (error, result) => {
|
|
if (error) {
|
|
console.error('Failed to start test operation:', error);
|
|
alert('Failed to start test operation: ' + error.message);
|
|
} else {
|
|
console.log('Test operation started:', result);
|
|
loadBoardOperations(Template.instance());
|
|
}
|
|
});
|
|
},
|
|
|
|
'input .js-search-board-operations'(event) {
|
|
const searchTerm = $(event.currentTarget).val();
|
|
const instance = Template.instance();
|
|
instance.searchTerm.set(searchTerm);
|
|
instance.currentPage.set(1);
|
|
loadBoardOperations(instance);
|
|
},
|
|
|
|
'click .js-prev-page'(event) {
|
|
event.preventDefault();
|
|
const instance = Template.instance();
|
|
const currentPage = instance.currentPage.get();
|
|
if (currentPage > 1) {
|
|
instance.currentPage.set(currentPage - 1);
|
|
loadBoardOperations(instance);
|
|
}
|
|
},
|
|
|
|
'click .js-next-page'(event) {
|
|
event.preventDefault();
|
|
const instance = Template.instance();
|
|
const currentPage = instance.currentPage.get();
|
|
const pagination = instance.pagination.get();
|
|
if (currentPage < pagination.totalPages) {
|
|
instance.currentPage.set(currentPage + 1);
|
|
loadBoardOperations(instance);
|
|
}
|
|
},
|
|
|
|
'click .js-pause-operation'(event) {
|
|
event.preventDefault();
|
|
const operationId = $(event.currentTarget).data('operation');
|
|
// Implementation for pausing operation
|
|
console.log('Pause operation:', operationId);
|
|
},
|
|
|
|
'click .js-resume-operation'(event) {
|
|
event.preventDefault();
|
|
const operationId = $(event.currentTarget).data('operation');
|
|
// Implementation for resuming operation
|
|
console.log('Resume operation:', operationId);
|
|
},
|
|
|
|
'click .js-stop-operation'(event) {
|
|
event.preventDefault();
|
|
const operationId = $(event.currentTarget).data('operation');
|
|
if (confirm('Are you sure you want to stop this operation?')) {
|
|
// Implementation for stopping operation
|
|
console.log('Stop operation:', operationId);
|
|
}
|
|
},
|
|
|
|
'click .js-view-details'(event) {
|
|
event.preventDefault();
|
|
const operationId = $(event.currentTarget).data('operation');
|
|
// Implementation for viewing operation details
|
|
console.log('View details for operation:', operationId);
|
|
},
|
|
|
|
'click .js-force-board-scan'(event) {
|
|
event.preventDefault();
|
|
Meteor.call('cron.forceBoardMigrationScan', (error, result) => {
|
|
if (error) {
|
|
console.error('Failed to force board scan:', error);
|
|
alert('Failed to force board scan: ' + error.message);
|
|
} else {
|
|
// Board scan started successfully
|
|
// Refresh the data
|
|
loadBoardOperations(Template.instance());
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// Helper functions for cron settings
|
|
function loadCronData(instance) {
|
|
instance.loading.set(true);
|
|
|
|
// Load migration progress
|
|
Meteor.call('cron.getMigrationProgress', (error, result) => {
|
|
if (result) {
|
|
migrationProgress.set(result.progress);
|
|
migrationStatus.set(result.status);
|
|
migrationCurrentStep.set(result.currentStep);
|
|
migrationSteps.set(result.steps);
|
|
isMigrating.set(result.isMigrating);
|
|
}
|
|
});
|
|
|
|
// Load cron jobs
|
|
loadCronJobs(instance);
|
|
|
|
instance.loading.set(false);
|
|
}
|
|
|
|
function loadCronJobs(instance) {
|
|
Meteor.call('cron.getJobs', (error, result) => {
|
|
if (result) {
|
|
cronJobs.set(result);
|
|
}
|
|
});
|
|
}
|
|
|
|
function loadBoardOperations(instance) {
|
|
const page = instance.currentPage.get();
|
|
const limit = instance.pageSize.get();
|
|
const searchTerm = instance.searchTerm.get();
|
|
|
|
Meteor.call('cron.getAllBoardOperations', page, limit, searchTerm, (error, result) => {
|
|
if (result) {
|
|
instance.boardOperations.set(result.operations);
|
|
instance.pagination.set({
|
|
total: result.total,
|
|
page: result.page,
|
|
limit: result.limit,
|
|
totalPages: result.totalPages,
|
|
start: ((result.page - 1) * result.limit) + 1,
|
|
end: Math.min(result.page * result.limit, result.total),
|
|
hasPrev: result.page > 1,
|
|
hasNext: result.page < result.totalPages
|
|
});
|
|
}
|
|
});
|
|
|
|
// Load operation stats
|
|
Meteor.call('cron.getBoardOperationStats', (error, result) => {
|
|
if (result) {
|
|
instance.operationStats.set(result);
|
|
}
|
|
});
|
|
|
|
// Load queue stats
|
|
Meteor.call('cron.getQueueStats', (error, result) => {
|
|
if (result) {
|
|
instance.queueStats.set(result);
|
|
}
|
|
});
|
|
|
|
// Load system resources
|
|
Meteor.call('cron.getSystemResources', (error, result) => {
|
|
if (result) {
|
|
instance.systemResources.set(result);
|
|
}
|
|
});
|
|
|
|
// Load board migration stats
|
|
Meteor.call('cron.getBoardMigrationStats', (error, result) => {
|
|
if (result) {
|
|
instance.boardMigrationStats.set(result);
|
|
}
|
|
});
|
|
}
|
|
|
|
function pollMigrationProgress(instance) {
|
|
const pollInterval = setInterval(() => {
|
|
Meteor.call('cron.getMigrationProgress', (error, result) => {
|
|
if (result) {
|
|
migrationProgress.set(result.progress);
|
|
migrationStatus.set(result.status);
|
|
migrationCurrentStep.set(result.currentStep);
|
|
migrationSteps.set(result.steps);
|
|
isMigrating.set(result.isMigrating);
|
|
|
|
// Stop polling if migration is complete
|
|
if (!result.isMigrating && result.progress === 100) {
|
|
clearInterval(pollInterval);
|
|
}
|
|
}
|
|
});
|
|
}, 1000);
|
|
}
|
|
|
|
// Template helpers for cronSettings
|
|
Template.cronSettings.helpers({
|
|
loading() {
|
|
const instance = Template.instance();
|
|
return instance.loading && instance.loading.get();
|
|
},
|
|
showMigrations() {
|
|
const instance = Template.instance();
|
|
return instance.showMigrations && instance.showMigrations.get();
|
|
},
|
|
showBoardOperations() {
|
|
const instance = Template.instance();
|
|
return instance.showBoardOperations && instance.showBoardOperations.get();
|
|
},
|
|
showJobs() {
|
|
const instance = Template.instance();
|
|
return instance.showJobs && instance.showJobs.get();
|
|
},
|
|
showAddJob() {
|
|
const instance = Template.instance();
|
|
return instance.showAddJob && instance.showAddJob.get();
|
|
},
|
|
});
|