mirror of
https://github.com/wekan/wekan.git
synced 2026-11-08 03:22:32 -05:00
The scan counts what points at nothing: cards on a missing or another board's list or swimlane, ghost and duplicate board members, boards with no active admin, custom field values and boards that no longer exist, checklist items without a checklist, and subtasks or linked cards whose card is gone. It runs nightly when the CPU is low, claimed so that only one pod scans or repairs. A repair that only moves data reuses the upgrade steps; one that deletes asks twice, and the server refuses it without that confirmation. Every repair edits with $pull or a guarded update, so a concurrent edit is not overwritten, and a member dedupe keeps the admin entry. Legacy scalar boardId and string boardIds of custom fields are understood. Translations done with low confidence: ay, csb, lld, kok, wa-RR, ve-CC, bi, tpi. Thanks to Raymond-Voogt and xet7 ! Fixes #6818.
45 lines
1.6 KiB
JavaScript
45 lines
1.6 KiB
JavaScript
import { Meteor } from 'meteor/meteor';
|
|
import { check, Match } from 'meteor/check';
|
|
import { ReactiveCache } from '/imports/reactiveCache';
|
|
import { startDataHealthScan, repairDataHealthCheck } from '/server/lib/dataHealthScan';
|
|
|
|
const { CHECKS } = require('/models/lib/dataHealth');
|
|
|
|
// Admin Panel → Problems → Summary: Start (Data health) and the Repair button behind each
|
|
// "Data health: ..." problem (models/lib/dataHealth.js says which checks have a
|
|
// repair and which ask for confirmation first). A repair that deletes data
|
|
// ('confirm') runs only when the call says confirmed === true - the page sends
|
|
// that on the second click - so a bare call, or an old page, cannot delete.
|
|
|
|
async function requireAdmin() {
|
|
const user = await ReactiveCache.getCurrentUser();
|
|
if (!user || !user.isAdmin) {
|
|
throw new Meteor.Error('not-authorized', 'Admin only');
|
|
}
|
|
return user;
|
|
}
|
|
|
|
Meteor.methods({
|
|
async dataHealthScanNow() {
|
|
await requireAdmin();
|
|
startDataHealthScan();
|
|
return { started: true };
|
|
},
|
|
|
|
async repairDataHealth(checkId, confirmed) {
|
|
check(checkId, String);
|
|
check(confirmed, Match.Optional(Boolean));
|
|
await requireAdmin();
|
|
const kind = (CHECKS.find(c => c.id === checkId) || {}).repair;
|
|
if (kind === 'confirm' && confirmed !== true) {
|
|
throw new Meteor.Error('data-health-confirm', 'This repair deletes data: confirm it first.');
|
|
}
|
|
this.unblock();
|
|
try {
|
|
return await repairDataHealthCheck(checkId, { confirmed: confirmed === true });
|
|
} catch (error) {
|
|
throw new Meteor.Error('data-health-repair', (error && error.message) || String(error));
|
|
}
|
|
},
|
|
});
|