cronSettings.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. import { Template } from 'meteor/templating';
  2. import { ReactiveVar } from 'meteor/reactive-var';
  3. import { Meteor } from 'meteor/meteor';
  4. import { TAPi18n } from '/imports/i18n';
  5. // Reactive variables for cron settings
  6. const migrationProgress = new ReactiveVar(0);
  7. const migrationStatus = new ReactiveVar('');
  8. const migrationCurrentStep = new ReactiveVar('');
  9. const migrationSteps = new ReactiveVar([]);
  10. const isMigrating = new ReactiveVar(false);
  11. const cronJobs = new ReactiveVar([]);
  12. Template.cronSettings.onCreated(function() {
  13. this.loading = new ReactiveVar(true);
  14. this.showMigrations = new ReactiveVar(true);
  15. this.showBoardOperations = new ReactiveVar(false);
  16. this.showJobs = new ReactiveVar(false);
  17. this.showAddJob = new ReactiveVar(false);
  18. // Board operations pagination
  19. this.currentPage = new ReactiveVar(1);
  20. this.pageSize = new ReactiveVar(20);
  21. this.searchTerm = new ReactiveVar('');
  22. this.boardOperations = new ReactiveVar([]);
  23. this.operationStats = new ReactiveVar({});
  24. this.pagination = new ReactiveVar({});
  25. // Load initial data
  26. this.loadCronData();
  27. });
  28. Template.cronSettings.helpers({
  29. loading() {
  30. return Template.instance().loading.get();
  31. },
  32. showMigrations() {
  33. return Template.instance().showMigrations.get();
  34. },
  35. showBoardOperations() {
  36. return Template.instance().showBoardOperations.get();
  37. },
  38. showJobs() {
  39. return Template.instance().showJobs.get();
  40. },
  41. showAddJob() {
  42. return Template.instance().showAddJob.get();
  43. },
  44. migrationProgress() {
  45. return migrationProgress.get();
  46. },
  47. migrationStatus() {
  48. return migrationStatus.get();
  49. },
  50. migrationCurrentStep() {
  51. return migrationCurrentStep.get();
  52. },
  53. migrationSteps() {
  54. const steps = migrationSteps.get();
  55. const currentStep = migrationCurrentStep.get();
  56. return steps.map(step => ({
  57. ...step,
  58. isCurrentStep: step.name === currentStep
  59. }));
  60. },
  61. cronJobs() {
  62. return cronJobs.get();
  63. },
  64. formatDate(date) {
  65. if (!date) return '-';
  66. return new Date(date).toLocaleString();
  67. },
  68. boardOperations() {
  69. return Template.instance().boardOperations.get();
  70. },
  71. operationStats() {
  72. return Template.instance().operationStats.get();
  73. },
  74. pagination() {
  75. return Template.instance().pagination.get();
  76. },
  77. formatDateTime(date) {
  78. if (!date) return '-';
  79. return new Date(date).toLocaleString();
  80. },
  81. formatDuration(startTime, endTime) {
  82. if (!startTime) return '-';
  83. const start = new Date(startTime);
  84. const end = endTime ? new Date(endTime) : new Date();
  85. const diffMs = end - start;
  86. const diffMins = Math.floor(diffMs / 60000);
  87. const diffSecs = Math.floor((diffMs % 60000) / 1000);
  88. if (diffMins > 0) {
  89. return `${diffMins}m ${diffSecs}s`;
  90. } else {
  91. return `${diffSecs}s`;
  92. }
  93. }
  94. });
  95. Template.cronSettings.events({
  96. 'click .js-cron-migrations'(event) {
  97. event.preventDefault();
  98. const instance = Template.instance();
  99. instance.showMigrations.set(true);
  100. instance.showJobs.set(false);
  101. instance.showAddJob.set(false);
  102. },
  103. 'click .js-cron-board-operations'(event) {
  104. event.preventDefault();
  105. const instance = Template.instance();
  106. instance.showMigrations.set(false);
  107. instance.showBoardOperations.set(true);
  108. instance.showJobs.set(false);
  109. instance.showAddJob.set(false);
  110. instance.loadBoardOperations();
  111. },
  112. 'click .js-cron-jobs'(event) {
  113. event.preventDefault();
  114. const instance = Template.instance();
  115. instance.showMigrations.set(false);
  116. instance.showBoardOperations.set(false);
  117. instance.showJobs.set(true);
  118. instance.showAddJob.set(false);
  119. instance.loadCronJobs();
  120. },
  121. 'click .js-cron-add'(event) {
  122. event.preventDefault();
  123. const instance = Template.instance();
  124. instance.showMigrations.set(false);
  125. instance.showJobs.set(false);
  126. instance.showAddJob.set(true);
  127. },
  128. 'click .js-start-all-migrations'(event) {
  129. event.preventDefault();
  130. Meteor.call('cron.startAllMigrations', (error, result) => {
  131. if (error) {
  132. console.error('Failed to start migrations:', error);
  133. alert('Failed to start migrations: ' + error.message);
  134. } else {
  135. console.log('Migrations started successfully');
  136. Template.instance().pollMigrationProgress();
  137. }
  138. });
  139. },
  140. 'click .js-pause-all-migrations'(event) {
  141. event.preventDefault();
  142. // Pause all migration cron jobs
  143. const jobs = cronJobs.get();
  144. jobs.forEach(job => {
  145. if (job.name.startsWith('migration_')) {
  146. Meteor.call('cron.pauseJob', job.name);
  147. }
  148. });
  149. },
  150. 'click .js-stop-all-migrations'(event) {
  151. event.preventDefault();
  152. // Stop all migration cron jobs
  153. const jobs = cronJobs.get();
  154. jobs.forEach(job => {
  155. if (job.name.startsWith('migration_')) {
  156. Meteor.call('cron.stopJob', job.name);
  157. }
  158. });
  159. },
  160. 'click .js-refresh-jobs'(event) {
  161. event.preventDefault();
  162. Template.instance().loadCronJobs();
  163. },
  164. 'click .js-start-job'(event) {
  165. event.preventDefault();
  166. const jobName = $(event.currentTarget).data('job');
  167. Meteor.call('cron.startJob', jobName, (error, result) => {
  168. if (error) {
  169. console.error('Failed to start job:', error);
  170. alert('Failed to start job: ' + error.message);
  171. } else {
  172. console.log('Job started successfully');
  173. Template.instance().loadCronJobs();
  174. }
  175. });
  176. },
  177. 'click .js-pause-job'(event) {
  178. event.preventDefault();
  179. const jobName = $(event.currentTarget).data('job');
  180. Meteor.call('cron.pauseJob', jobName, (error, result) => {
  181. if (error) {
  182. console.error('Failed to pause job:', error);
  183. alert('Failed to pause job: ' + error.message);
  184. } else {
  185. console.log('Job paused successfully');
  186. Template.instance().loadCronJobs();
  187. }
  188. });
  189. },
  190. 'click .js-stop-job'(event) {
  191. event.preventDefault();
  192. const jobName = $(event.currentTarget).data('job');
  193. Meteor.call('cron.stopJob', jobName, (error, result) => {
  194. if (error) {
  195. console.error('Failed to stop job:', error);
  196. alert('Failed to stop job: ' + error.message);
  197. } else {
  198. console.log('Job stopped successfully');
  199. Template.instance().loadCronJobs();
  200. }
  201. });
  202. },
  203. 'click .js-remove-job'(event) {
  204. event.preventDefault();
  205. const jobName = $(event.currentTarget).data('job');
  206. if (confirm('Are you sure you want to remove this job?')) {
  207. Meteor.call('cron.removeJob', jobName, (error, result) => {
  208. if (error) {
  209. console.error('Failed to remove job:', error);
  210. alert('Failed to remove job: ' + error.message);
  211. } else {
  212. console.log('Job removed successfully');
  213. Template.instance().loadCronJobs();
  214. }
  215. });
  216. }
  217. },
  218. 'submit .js-add-cron-job-form'(event) {
  219. event.preventDefault();
  220. const form = event.currentTarget;
  221. const formData = new FormData(form);
  222. const jobData = {
  223. name: formData.get('name'),
  224. description: formData.get('description'),
  225. schedule: formData.get('schedule'),
  226. weight: parseInt(formData.get('weight'))
  227. };
  228. Meteor.call('cron.addJob', jobData, (error, result) => {
  229. if (error) {
  230. console.error('Failed to add job:', error);
  231. alert('Failed to add job: ' + error.message);
  232. } else {
  233. console.log('Job added successfully');
  234. form.reset();
  235. Template.instance().showJobs.set(true);
  236. Template.instance().showAddJob.set(false);
  237. Template.instance().loadCronJobs();
  238. }
  239. });
  240. },
  241. 'click .js-cancel-add-job'(event) {
  242. event.preventDefault();
  243. const instance = Template.instance();
  244. instance.showJobs.set(true);
  245. instance.showAddJob.set(false);
  246. },
  247. 'click .js-refresh-board-operations'(event) {
  248. event.preventDefault();
  249. Template.instance().loadBoardOperations();
  250. },
  251. 'click .js-start-test-operation'(event) {
  252. event.preventDefault();
  253. const testBoardId = 'test-board-' + Date.now();
  254. const operationData = {
  255. sourceBoardId: 'source-board',
  256. targetBoardId: 'target-board',
  257. copyOptions: { includeCards: true, includeAttachments: true }
  258. };
  259. Meteor.call('cron.startBoardOperation', testBoardId, 'copy_board', operationData, (error, result) => {
  260. if (error) {
  261. console.error('Failed to start test operation:', error);
  262. alert('Failed to start test operation: ' + error.message);
  263. } else {
  264. console.log('Test operation started:', result);
  265. Template.instance().loadBoardOperations();
  266. }
  267. });
  268. },
  269. 'input .js-search-board-operations'(event) {
  270. const searchTerm = $(event.currentTarget).val();
  271. const instance = Template.instance();
  272. instance.searchTerm.set(searchTerm);
  273. instance.currentPage.set(1);
  274. instance.loadBoardOperations();
  275. },
  276. 'click .js-prev-page'(event) {
  277. event.preventDefault();
  278. const instance = Template.instance();
  279. const currentPage = instance.currentPage.get();
  280. if (currentPage > 1) {
  281. instance.currentPage.set(currentPage - 1);
  282. instance.loadBoardOperations();
  283. }
  284. },
  285. 'click .js-next-page'(event) {
  286. event.preventDefault();
  287. const instance = Template.instance();
  288. const currentPage = instance.currentPage.get();
  289. const pagination = instance.pagination.get();
  290. if (currentPage < pagination.totalPages) {
  291. instance.currentPage.set(currentPage + 1);
  292. instance.loadBoardOperations();
  293. }
  294. },
  295. 'click .js-pause-operation'(event) {
  296. event.preventDefault();
  297. const operationId = $(event.currentTarget).data('operation');
  298. // Implementation for pausing operation
  299. console.log('Pause operation:', operationId);
  300. },
  301. 'click .js-resume-operation'(event) {
  302. event.preventDefault();
  303. const operationId = $(event.currentTarget).data('operation');
  304. // Implementation for resuming operation
  305. console.log('Resume operation:', operationId);
  306. },
  307. 'click .js-stop-operation'(event) {
  308. event.preventDefault();
  309. const operationId = $(event.currentTarget).data('operation');
  310. if (confirm('Are you sure you want to stop this operation?')) {
  311. // Implementation for stopping operation
  312. console.log('Stop operation:', operationId);
  313. }
  314. },
  315. 'click .js-view-details'(event) {
  316. event.preventDefault();
  317. const operationId = $(event.currentTarget).data('operation');
  318. // Implementation for viewing operation details
  319. console.log('View details for operation:', operationId);
  320. }
  321. });
  322. Template.cronSettings.prototype.loadCronData = function() {
  323. this.loading.set(true);
  324. // Load migration progress
  325. Meteor.call('cron.getMigrationProgress', (error, result) => {
  326. if (result) {
  327. migrationProgress.set(result.progress);
  328. migrationStatus.set(result.status);
  329. migrationCurrentStep.set(result.currentStep);
  330. migrationSteps.set(result.steps);
  331. isMigrating.set(result.isMigrating);
  332. }
  333. });
  334. // Load cron jobs
  335. this.loadCronJobs();
  336. this.loading.set(false);
  337. };
  338. Template.cronSettings.prototype.loadCronJobs = function() {
  339. Meteor.call('cron.getJobs', (error, result) => {
  340. if (result) {
  341. cronJobs.set(result);
  342. }
  343. });
  344. };
  345. Template.cronSettings.prototype.loadBoardOperations = function() {
  346. const instance = this;
  347. const page = instance.currentPage.get();
  348. const limit = instance.pageSize.get();
  349. const searchTerm = instance.searchTerm.get();
  350. Meteor.call('cron.getAllBoardOperations', page, limit, searchTerm, (error, result) => {
  351. if (result) {
  352. instance.boardOperations.set(result.operations);
  353. instance.pagination.set({
  354. total: result.total,
  355. page: result.page,
  356. limit: result.limit,
  357. totalPages: result.totalPages,
  358. start: ((result.page - 1) * result.limit) + 1,
  359. end: Math.min(result.page * result.limit, result.total),
  360. hasPrev: result.page > 1,
  361. hasNext: result.page < result.totalPages
  362. });
  363. }
  364. });
  365. // Load operation stats
  366. Meteor.call('cron.getBoardOperationStats', (error, result) => {
  367. if (result) {
  368. instance.operationStats.set(result);
  369. }
  370. });
  371. };
  372. Template.cronSettings.prototype.pollMigrationProgress = function() {
  373. const pollInterval = setInterval(() => {
  374. Meteor.call('cron.getMigrationProgress', (error, result) => {
  375. if (result) {
  376. migrationProgress.set(result.progress);
  377. migrationStatus.set(result.status);
  378. migrationCurrentStep.set(result.currentStep);
  379. migrationSteps.set(result.steps);
  380. isMigrating.set(result.isMigrating);
  381. // Stop polling if migration is complete
  382. if (!result.isMigrating && result.progress === 100) {
  383. clearInterval(pollInterval);
  384. }
  385. }
  386. });
  387. }, 1000);
  388. };