mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 15:30:13 +01:00
Moved migrations from opening board to right sidebar / Migrations.
Thanks to xet7 !
This commit is contained in:
parent
e93e72234c
commit
1b25d1d572
7 changed files with 277 additions and 62 deletions
|
|
@ -99,24 +99,9 @@ BlazeComponent.extendComponent({
|
|||
return;
|
||||
}
|
||||
|
||||
// Check if board needs comprehensive migration
|
||||
const needsMigration = await this.checkComprehensiveMigration(boardId);
|
||||
|
||||
if (needsMigration) {
|
||||
// Start comprehensive migration
|
||||
this.isMigrating.set(true);
|
||||
const success = await this.executeComprehensiveMigration(boardId);
|
||||
this.isMigrating.set(false);
|
||||
|
||||
if (success) {
|
||||
this.isBoardReady.set(true);
|
||||
} else {
|
||||
console.error('Comprehensive migration failed, setting ready to true anyway');
|
||||
this.isBoardReady.set(true); // Still show board even if migration failed
|
||||
}
|
||||
} else {
|
||||
this.isBoardReady.set(true);
|
||||
}
|
||||
// Automatic migration disabled - migrations must be run manually from sidebar
|
||||
// Board admins can run migrations from the sidebar Migrations menu
|
||||
this.isBoardReady.set(true);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error during board conversion check:', error);
|
||||
|
|
|
|||
|
|
@ -587,6 +587,10 @@ template(name="boardMenuPopup")
|
|||
| 📦
|
||||
| {{_ 'archived-items'}}
|
||||
if currentUser.isBoardAdmin
|
||||
li
|
||||
a.js-open-migrations
|
||||
| 🔧
|
||||
| {{_ 'migrations'}}
|
||||
li
|
||||
a.js-change-board-color
|
||||
| 🎨
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ const viewTitles = {
|
|||
multiselection: 'multi-selection',
|
||||
customFields: 'custom-fields',
|
||||
archives: 'archives',
|
||||
migrations: 'migrations',
|
||||
};
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
|
|
@ -271,6 +272,10 @@ Template.boardMenuPopup.events({
|
|||
Sidebar.setView('archives');
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-open-migrations'() {
|
||||
Sidebar.setView('migrations');
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-change-board-color': Popup.open('boardChangeColor'),
|
||||
'click .js-change-background-image': Popup.open('boardChangeBackgroundImage'),
|
||||
'click .js-board-info-on-my-boards': Popup.open('boardInfoOnMyBoards'),
|
||||
|
|
|
|||
69
client/components/sidebar/sidebarMigrations.jade
Normal file
69
client/components/sidebar/sidebarMigrations.jade
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
template(name='migrationsSidebar')
|
||||
if currentUser.isBoardAdmin
|
||||
.sidebar-migrations
|
||||
h3
|
||||
| 🔧
|
||||
| {{_ 'migrations'}}
|
||||
p.quiet {{_ 'migrations-description'}}
|
||||
|
||||
.migrations-list
|
||||
h4 {{_ 'board-migrations'}}
|
||||
.migration-item
|
||||
a.js-run-migration(data-migration="comprehensive")
|
||||
.migration-name
|
||||
| {{_ 'comprehensive-board-migration'}}
|
||||
.migration-status
|
||||
if comprehensiveMigrationNeeded
|
||||
span.badge.badge-warning {{_ 'migration-needed'}}
|
||||
else
|
||||
span.badge.badge-success {{_ 'migration-complete'}}
|
||||
|
||||
.migration-item
|
||||
a.js-run-migration(data-migration="fixMissingLists")
|
||||
.migration-name
|
||||
| {{_ 'fix-missing-lists-migration'}}
|
||||
.migration-status
|
||||
if fixMissingListsNeeded
|
||||
span.badge.badge-warning {{_ 'migration-needed'}}
|
||||
else
|
||||
span.badge.badge-success {{_ 'migration-complete'}}
|
||||
|
||||
hr
|
||||
h4 {{_ 'global-migrations'}}
|
||||
.migration-item
|
||||
a.js-run-migration(data-migration="fixAvatarUrls")
|
||||
.migration-name
|
||||
| {{_ 'fix-avatar-urls-migration'}}
|
||||
.migration-status
|
||||
if fixAvatarUrlsNeeded
|
||||
span.badge.badge-warning {{_ 'migration-needed'}}
|
||||
else
|
||||
span.badge.badge-success {{_ 'migration-complete'}}
|
||||
|
||||
.migration-item
|
||||
a.js-run-migration(data-migration="fixAllFileUrls")
|
||||
.migration-name
|
||||
| {{_ 'fix-all-file-urls-migration'}}
|
||||
.migration-status
|
||||
if fixAllFileUrlsNeeded
|
||||
span.badge.badge-warning {{_ 'migration-needed'}}
|
||||
else
|
||||
span.badge.badge-success {{_ 'migration-complete'}}
|
||||
else
|
||||
p.quiet {{_ 'migrations-admin-only'}}
|
||||
|
||||
template(name='runComprehensiveMigrationPopup')
|
||||
p {{_ 'run-comprehensive-migration-confirm'}}
|
||||
button.js-confirm.primary.full(type="submit") {{_ 'run-migration'}}
|
||||
|
||||
template(name='runFixMissingListsMigrationPopup')
|
||||
p {{_ 'run-fix-missing-lists-migration-confirm'}}
|
||||
button.js-confirm.primary.full(type="submit") {{_ 'run-migration'}}
|
||||
|
||||
template(name='runFixAvatarUrlsMigrationPopup')
|
||||
p {{_ 'run-fix-avatar-urls-migration-confirm'}}
|
||||
button.js-confirm.primary.full(type="submit") {{_ 'run-migration'}}
|
||||
|
||||
template(name='runFixAllFileUrlsMigrationPopup')
|
||||
p {{_ 'run-fix-all-file-urls-migration-confirm'}}
|
||||
button.js-confirm.primary.full(type="submit") {{_ 'run-migration'}}
|
||||
143
client/components/sidebar/sidebarMigrations.js
Normal file
143
client/components/sidebar/sidebarMigrations.js
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { TAPi18n } from '/imports/i18n';
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.migrationStatuses = new ReactiveVar({});
|
||||
this.loadMigrationStatuses();
|
||||
},
|
||||
|
||||
loadMigrationStatuses() {
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (!boardId) return;
|
||||
|
||||
// Check comprehensive migration
|
||||
Meteor.call('comprehensiveBoardMigration.needsMigration', boardId, (err, res) => {
|
||||
if (!err) {
|
||||
const statuses = this.migrationStatuses.get();
|
||||
statuses.comprehensive = res;
|
||||
this.migrationStatuses.set(statuses);
|
||||
}
|
||||
});
|
||||
|
||||
// Check fix missing lists migration
|
||||
Meteor.call('fixMissingListsMigration.needsMigration', boardId, (err, res) => {
|
||||
if (!err) {
|
||||
const statuses = this.migrationStatuses.get();
|
||||
statuses.fixMissingLists = res;
|
||||
this.migrationStatuses.set(statuses);
|
||||
}
|
||||
});
|
||||
|
||||
// Check fix avatar URLs migration (global)
|
||||
Meteor.call('fixAvatarUrls.needsMigration', (err, res) => {
|
||||
if (!err) {
|
||||
const statuses = this.migrationStatuses.get();
|
||||
statuses.fixAvatarUrls = res;
|
||||
this.migrationStatuses.set(statuses);
|
||||
}
|
||||
});
|
||||
|
||||
// Check fix all file URLs migration (global)
|
||||
Meteor.call('fixAllFileUrls.needsMigration', (err, res) => {
|
||||
if (!err) {
|
||||
const statuses = this.migrationStatuses.get();
|
||||
statuses.fixAllFileUrls = res;
|
||||
this.migrationStatuses.set(statuses);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
comprehensiveMigrationNeeded() {
|
||||
return this.migrationStatuses.get().comprehensive === true;
|
||||
},
|
||||
|
||||
fixMissingListsNeeded() {
|
||||
return this.migrationStatuses.get().fixMissingLists === true;
|
||||
},
|
||||
|
||||
fixAvatarUrlsNeeded() {
|
||||
return this.migrationStatuses.get().fixAvatarUrls === true;
|
||||
},
|
||||
|
||||
fixAllFileUrlsNeeded() {
|
||||
return this.migrationStatuses.get().fixAllFileUrls === true;
|
||||
},
|
||||
|
||||
runMigration(migrationType) {
|
||||
const boardId = Session.get('currentBoard');
|
||||
|
||||
let methodName;
|
||||
let methodArgs = [];
|
||||
|
||||
switch (migrationType) {
|
||||
case 'comprehensive':
|
||||
methodName = 'comprehensiveBoardMigration.execute';
|
||||
methodArgs = [boardId];
|
||||
break;
|
||||
|
||||
case 'fixMissingLists':
|
||||
methodName = 'fixMissingListsMigration.execute';
|
||||
methodArgs = [boardId];
|
||||
break;
|
||||
|
||||
case 'fixAvatarUrls':
|
||||
methodName = 'fixAvatarUrls.execute';
|
||||
break;
|
||||
|
||||
case 'fixAllFileUrls':
|
||||
methodName = 'fixAllFileUrls.execute';
|
||||
break;
|
||||
}
|
||||
|
||||
if (methodName) {
|
||||
Meteor.call(methodName, ...methodArgs, (err, result) => {
|
||||
if (err) {
|
||||
console.error('Migration failed:', err);
|
||||
// Show error notification
|
||||
Alert.error(TAPi18n.__('migration-failed') + ': ' + (err.message || err.reason));
|
||||
} else {
|
||||
console.log('Migration completed:', result);
|
||||
// Show success notification
|
||||
Alert.success(TAPi18n.__('migration-successful'));
|
||||
|
||||
// Reload migration statuses
|
||||
Meteor.setTimeout(() => {
|
||||
this.loadMigrationStatuses();
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-run-migration[data-migration="comprehensive"]': Popup.afterConfirm('runComprehensiveMigration', function() {
|
||||
const component = BlazeComponent.getComponentForElement(this);
|
||||
if (component) {
|
||||
component.runMigration('comprehensive');
|
||||
}
|
||||
}),
|
||||
'click .js-run-migration[data-migration="fixMissingLists"]': Popup.afterConfirm('runFixMissingListsMigration', function() {
|
||||
const component = BlazeComponent.getComponentForElement(this);
|
||||
if (component) {
|
||||
component.runMigration('fixMissingLists');
|
||||
}
|
||||
}),
|
||||
'click .js-run-migration[data-migration="fixAvatarUrls"]': Popup.afterConfirm('runFixAvatarUrlsMigration', function() {
|
||||
const component = BlazeComponent.getComponentForElement(this);
|
||||
if (component) {
|
||||
component.runMigration('fixAvatarUrls');
|
||||
}
|
||||
}),
|
||||
'click .js-run-migration[data-migration="fixAllFileUrls"]': Popup.afterConfirm('runFixAllFileUrlsMigration', function() {
|
||||
const component = BlazeComponent.getComponentForElement(this);
|
||||
if (component) {
|
||||
component.runMigration('fixAllFileUrls');
|
||||
}
|
||||
}),
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('migrationsSidebar');
|
||||
|
|
@ -1404,7 +1404,31 @@
|
|||
"back-to-settings": "Back to Settings",
|
||||
"board-id": "Board ID",
|
||||
"board-migration": "Board Migration",
|
||||
"board-migrations": "Board Migrations",
|
||||
"card-show-lists-on-minicard": "Show Lists on Minicard",
|
||||
"comprehensive-board-migration": "Comprehensive Board Migration",
|
||||
"comprehensive-board-migration-description": "Performs comprehensive checks and fixes for board data integrity, including list ordering, card positions, and swimlane structure.",
|
||||
"fix-missing-lists-migration": "Fix Missing Lists",
|
||||
"fix-missing-lists-migration-description": "Detects and repairs missing or corrupted lists in the board structure.",
|
||||
"fix-avatar-urls-migration": "Fix Avatar URLs",
|
||||
"fix-avatar-urls-migration-description": "Updates avatar URLs to use the correct storage backend and fixes broken avatar references.",
|
||||
"fix-all-file-urls-migration": "Fix All File URLs",
|
||||
"fix-all-file-urls-migration-description": "Updates all file attachment URLs to use the correct storage backend and fixes broken file references.",
|
||||
"global-migrations": "Global Migrations",
|
||||
"migration-needed": "Migration Needed",
|
||||
"migration-complete": "Complete",
|
||||
"migration-running": "Running...",
|
||||
"migration-successful": "Migration completed successfully",
|
||||
"migration-failed": "Migration failed",
|
||||
"migrations": "Migrations",
|
||||
"migrations-admin-only": "Only board administrators can run migrations",
|
||||
"migrations-description": "Run data integrity checks and repairs for this board. Each migration can be executed individually.",
|
||||
"no-issues-found": "No issues found",
|
||||
"run-migration": "Run Migration",
|
||||
"run-comprehensive-migration-confirm": "This will perform a comprehensive migration to check and fix board data integrity. This may take a few moments. Continue?",
|
||||
"run-fix-missing-lists-migration-confirm": "This will detect and repair missing or corrupted lists in the board structure. Continue?",
|
||||
"run-fix-avatar-urls-migration-confirm": "This will update avatar URLs across all boards to use the correct storage backend. This is a global operation. Continue?",
|
||||
"run-fix-all-file-urls-migration-confirm": "This will update all file attachment URLs across all boards to use the correct storage backend. This is a global operation. Continue?",
|
||||
"cleanup": "Cleanup",
|
||||
"cleanup-old-jobs": "Cleanup Old Jobs",
|
||||
"completed": "Completed",
|
||||
|
|
|
|||
|
|
@ -30,15 +30,11 @@ class FixAllFileUrlsMigration {
|
|||
}
|
||||
}
|
||||
|
||||
// Check for problematic attachment URLs in cards
|
||||
const cards = ReactiveCache.getCards({});
|
||||
for (const card of cards) {
|
||||
if (card.attachments) {
|
||||
for (const attachment of card.attachments) {
|
||||
if (attachment.url && this.hasProblematicUrl(attachment.url)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Check for problematic attachment URLs
|
||||
const attachments = ReactiveCache.getAttachments({});
|
||||
for (const attachment of attachments) {
|
||||
if (attachment.url && this.hasProblematicUrl(attachment.url)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -206,51 +202,40 @@ class FixAllFileUrlsMigration {
|
|||
}
|
||||
|
||||
/**
|
||||
* Fix attachment URLs in card references
|
||||
* Fix attachment URLs in the Attachments collection
|
||||
*/
|
||||
async fixCardAttachmentUrls() {
|
||||
const cards = ReactiveCache.getCards({});
|
||||
let cardsFixed = 0;
|
||||
const attachments = ReactiveCache.getAttachments({});
|
||||
let attachmentsFixed = 0;
|
||||
|
||||
for (const card of cards) {
|
||||
if (card.attachments) {
|
||||
let needsUpdate = false;
|
||||
const updatedAttachments = card.attachments.map(attachment => {
|
||||
if (attachment.url && this.hasProblematicUrl(attachment.url)) {
|
||||
try {
|
||||
const fileId = attachment._id || extractFileIdFromUrl(attachment.url, 'attachment');
|
||||
const cleanUrl = fileId ? generateUniversalAttachmentUrl(fileId) : cleanFileUrl(attachment.url, 'attachment');
|
||||
for (const attachment of attachments) {
|
||||
if (attachment.url && this.hasProblematicUrl(attachment.url)) {
|
||||
try {
|
||||
const fileId = attachment._id || extractFileIdFromUrl(attachment.url, 'attachment');
|
||||
const cleanUrl = fileId ? generateUniversalAttachmentUrl(fileId) : cleanFileUrl(attachment.url, 'attachment');
|
||||
|
||||
if (cleanUrl && cleanUrl !== attachment.url) {
|
||||
needsUpdate = true;
|
||||
return { ...attachment, url: cleanUrl };
|
||||
if (cleanUrl && cleanUrl !== attachment.url) {
|
||||
// Update attachment with fixed URL
|
||||
Attachments.update(attachment._id, {
|
||||
$set: {
|
||||
url: cleanUrl,
|
||||
modifiedAt: new Date()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error fixing card attachment URL:`, error);
|
||||
});
|
||||
|
||||
attachmentsFixed++;
|
||||
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.log(`Fixed attachment URL ${attachment._id}`);
|
||||
}
|
||||
}
|
||||
return attachment;
|
||||
});
|
||||
|
||||
if (needsUpdate) {
|
||||
// Update card with fixed attachment URLs
|
||||
Cards.update(card._id, {
|
||||
$set: {
|
||||
attachments: updatedAttachments,
|
||||
modifiedAt: new Date()
|
||||
}
|
||||
});
|
||||
|
||||
cardsFixed++;
|
||||
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.log(`Fixed attachment URLs in card ${card._id}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error fixing attachment URL:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cardsFixed;
|
||||
return attachmentsFixed;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue