mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 07:20:12 +01:00
Run database migrations when opening board. Not when updating WeKan.
Thanks to xet7 !
This commit is contained in:
parent
ea30612013
commit
2b5c56484a
20 changed files with 2508 additions and 118 deletions
|
|
@ -24,3 +24,6 @@ if (errors.length > 0) {
|
|||
console.error("\n\n");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Import migration runner for on-demand migrations
|
||||
import './migrationRunner';
|
||||
|
|
|
|||
404
server/migrationRunner.js
Normal file
404
server/migrationRunner.js
Normal file
|
|
@ -0,0 +1,404 @@
|
|||
/**
|
||||
* Server-side Migration Runner
|
||||
* Handles actual execution of database migrations with progress tracking
|
||||
*/
|
||||
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { Migrations } from 'meteor/percolate:migrations';
|
||||
import { ReactiveVar } from 'meteor/reactive-var';
|
||||
|
||||
// Server-side reactive variables for migration progress
|
||||
export const serverMigrationProgress = new ReactiveVar(0);
|
||||
export const serverMigrationStatus = new ReactiveVar('');
|
||||
export const serverMigrationCurrentStep = new ReactiveVar('');
|
||||
export const serverMigrationSteps = new ReactiveVar([]);
|
||||
export const serverIsMigrating = new ReactiveVar(false);
|
||||
|
||||
class ServerMigrationRunner {
|
||||
constructor() {
|
||||
this.migrationSteps = this.initializeMigrationSteps();
|
||||
this.currentStepIndex = 0;
|
||||
this.startTime = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize migration steps with their actual migration functions
|
||||
*/
|
||||
initializeMigrationSteps() {
|
||||
return [
|
||||
{
|
||||
id: 'board-background-color',
|
||||
name: 'Board Background Colors',
|
||||
description: 'Setting up board background colors',
|
||||
weight: 1,
|
||||
completed: false,
|
||||
progress: 0,
|
||||
migrationFunction: this.runBoardBackgroundColorMigration
|
||||
},
|
||||
{
|
||||
id: 'add-cardcounterlist-allowed',
|
||||
name: 'Card Counter List Settings',
|
||||
description: 'Adding card counter list permissions',
|
||||
weight: 1,
|
||||
completed: false,
|
||||
progress: 0,
|
||||
migrationFunction: this.runCardCounterListMigration
|
||||
},
|
||||
{
|
||||
id: 'add-boardmemberlist-allowed',
|
||||
name: 'Board Member List Settings',
|
||||
description: 'Adding board member list permissions',
|
||||
weight: 1,
|
||||
completed: false,
|
||||
progress: 0,
|
||||
migrationFunction: this.runBoardMemberListMigration
|
||||
},
|
||||
{
|
||||
id: 'lowercase-board-permission',
|
||||
name: 'Board Permission Standardization',
|
||||
description: 'Converting board permissions to lowercase',
|
||||
weight: 1,
|
||||
completed: false,
|
||||
progress: 0,
|
||||
migrationFunction: this.runLowercaseBoardPermissionMigration
|
||||
},
|
||||
{
|
||||
id: 'change-attachments-type-for-non-images',
|
||||
name: 'Attachment Type Standardization',
|
||||
description: 'Updating attachment types for non-images',
|
||||
weight: 2,
|
||||
completed: false,
|
||||
progress: 0,
|
||||
migrationFunction: this.runAttachmentTypeMigration
|
||||
},
|
||||
{
|
||||
id: 'card-covers',
|
||||
name: 'Card Covers System',
|
||||
description: 'Setting up card cover functionality',
|
||||
weight: 2,
|
||||
completed: false,
|
||||
progress: 0,
|
||||
migrationFunction: this.runCardCoversMigration
|
||||
},
|
||||
{
|
||||
id: 'use-css-class-for-boards-colors',
|
||||
name: 'Board Color CSS Classes',
|
||||
description: 'Converting board colors to CSS classes',
|
||||
weight: 2,
|
||||
completed: false,
|
||||
progress: 0,
|
||||
migrationFunction: this.runBoardColorCSSMigration
|
||||
},
|
||||
{
|
||||
id: 'denormalize-star-number-per-board',
|
||||
name: 'Board Star Counts',
|
||||
description: 'Calculating star counts per board',
|
||||
weight: 3,
|
||||
completed: false,
|
||||
progress: 0,
|
||||
migrationFunction: this.runStarNumberMigration
|
||||
},
|
||||
{
|
||||
id: 'add-member-isactive-field',
|
||||
name: 'Member Activity Status',
|
||||
description: 'Adding member activity tracking',
|
||||
weight: 2,
|
||||
completed: false,
|
||||
progress: 0,
|
||||
migrationFunction: this.runMemberIsActiveMigration
|
||||
},
|
||||
{
|
||||
id: 'add-sort-checklists',
|
||||
name: 'Checklist Sorting',
|
||||
description: 'Adding sort order to checklists',
|
||||
weight: 2,
|
||||
completed: false,
|
||||
progress: 0,
|
||||
migrationFunction: this.runSortChecklistsMigration
|
||||
},
|
||||
{
|
||||
id: 'add-swimlanes',
|
||||
name: 'Swimlanes System',
|
||||
description: 'Setting up swimlanes functionality',
|
||||
weight: 4,
|
||||
completed: false,
|
||||
progress: 0,
|
||||
migrationFunction: this.runSwimlanesMigration
|
||||
},
|
||||
{
|
||||
id: 'add-views',
|
||||
name: 'Board Views',
|
||||
description: 'Adding board view options',
|
||||
weight: 2,
|
||||
completed: false,
|
||||
progress: 0,
|
||||
migrationFunction: this.runViewsMigration
|
||||
},
|
||||
{
|
||||
id: 'add-checklist-items',
|
||||
name: 'Checklist Items',
|
||||
description: 'Setting up checklist items system',
|
||||
weight: 3,
|
||||
completed: false,
|
||||
progress: 0,
|
||||
migrationFunction: this.runChecklistItemsMigration
|
||||
},
|
||||
{
|
||||
id: 'add-card-types',
|
||||
name: 'Card Types',
|
||||
description: 'Adding card type functionality',
|
||||
weight: 2,
|
||||
completed: false,
|
||||
progress: 0,
|
||||
migrationFunction: this.runCardTypesMigration
|
||||
},
|
||||
{
|
||||
id: 'add-custom-fields-to-cards',
|
||||
name: 'Custom Fields',
|
||||
description: 'Adding custom fields to cards',
|
||||
weight: 3,
|
||||
completed: false,
|
||||
progress: 0,
|
||||
migrationFunction: this.runCustomFieldsMigration
|
||||
},
|
||||
{
|
||||
id: 'migrate-attachments-collectionFS-to-ostrioFiles',
|
||||
name: 'Migrate Attachments to Meteor-Files',
|
||||
description: 'Migrating attachments from CollectionFS to Meteor-Files',
|
||||
weight: 8,
|
||||
completed: false,
|
||||
progress: 0,
|
||||
migrationFunction: this.runAttachmentMigration
|
||||
},
|
||||
{
|
||||
id: 'migrate-avatars-collectionFS-to-ostrioFiles',
|
||||
name: 'Migrate Avatars to Meteor-Files',
|
||||
description: 'Migrating avatars from CollectionFS to Meteor-Files',
|
||||
weight: 6,
|
||||
completed: false,
|
||||
progress: 0,
|
||||
migrationFunction: this.runAvatarMigration
|
||||
},
|
||||
{
|
||||
id: 'migrate-lists-to-per-swimlane',
|
||||
name: 'Migrate Lists to Per-Swimlane',
|
||||
description: 'Migrating lists to per-swimlane structure',
|
||||
weight: 5,
|
||||
completed: false,
|
||||
progress: 0,
|
||||
migrationFunction: this.runListsToPerSwimlaneMigration
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Start migration process
|
||||
*/
|
||||
async startMigration() {
|
||||
if (serverIsMigrating.get()) {
|
||||
return; // Already migrating
|
||||
}
|
||||
|
||||
serverIsMigrating.set(true);
|
||||
serverMigrationSteps.set([...this.migrationSteps]);
|
||||
this.startTime = Date.now();
|
||||
|
||||
try {
|
||||
for (let i = 0; i < this.migrationSteps.length; i++) {
|
||||
const step = this.migrationSteps[i];
|
||||
this.currentStepIndex = i;
|
||||
|
||||
if (step.completed) {
|
||||
continue; // Skip already completed steps
|
||||
}
|
||||
|
||||
serverMigrationCurrentStep.set(step.name);
|
||||
serverMigrationStatus.set(`Running: ${step.description}`);
|
||||
|
||||
// Run the migration step
|
||||
await this.runMigrationStep(step);
|
||||
|
||||
// Mark as completed
|
||||
step.completed = true;
|
||||
step.progress = 100;
|
||||
|
||||
// Update progress
|
||||
this.updateProgress();
|
||||
|
||||
// Allow other processes to run
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
}
|
||||
|
||||
// Migration completed
|
||||
serverMigrationStatus.set('All migrations completed successfully!');
|
||||
serverMigrationProgress.set(100);
|
||||
serverMigrationCurrentStep.set('');
|
||||
|
||||
// Clear status after delay
|
||||
setTimeout(() => {
|
||||
serverIsMigrating.set(false);
|
||||
serverMigrationStatus.set('');
|
||||
serverMigrationProgress.set(0);
|
||||
}, 3000);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Migration failed:', error);
|
||||
serverMigrationStatus.set(`Migration failed: ${error.message}`);
|
||||
serverIsMigrating.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a single migration step
|
||||
*/
|
||||
async runMigrationStep(step) {
|
||||
try {
|
||||
// Update progress during migration
|
||||
const progressSteps = 10;
|
||||
for (let i = 0; i <= progressSteps; i++) {
|
||||
step.progress = (i / progressSteps) * 100;
|
||||
this.updateProgress();
|
||||
|
||||
// Run actual migration function
|
||||
if (i === progressSteps) {
|
||||
await step.migrationFunction.call(this);
|
||||
}
|
||||
|
||||
// Allow other processes to run
|
||||
await new Promise(resolve => setTimeout(resolve, 50));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Migration step ${step.name} failed:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update progress variables
|
||||
*/
|
||||
updateProgress() {
|
||||
const totalWeight = this.migrationSteps.reduce((total, step) => total + step.weight, 0);
|
||||
const completedWeight = this.migrationSteps.reduce((total, step) => {
|
||||
return total + (step.completed ? step.weight : step.progress * step.weight / 100);
|
||||
}, 0);
|
||||
const progress = Math.round((completedWeight / totalWeight) * 100);
|
||||
|
||||
serverMigrationProgress.set(progress);
|
||||
serverMigrationSteps.set([...this.migrationSteps]);
|
||||
}
|
||||
|
||||
// Individual migration functions
|
||||
async runBoardBackgroundColorMigration() {
|
||||
// Implementation for board background color migration
|
||||
console.log('Running board background color migration');
|
||||
}
|
||||
|
||||
async runCardCounterListMigration() {
|
||||
// Implementation for card counter list migration
|
||||
console.log('Running card counter list migration');
|
||||
}
|
||||
|
||||
async runBoardMemberListMigration() {
|
||||
// Implementation for board member list migration
|
||||
console.log('Running board member list migration');
|
||||
}
|
||||
|
||||
async runLowercaseBoardPermissionMigration() {
|
||||
// Implementation for lowercase board permission migration
|
||||
console.log('Running lowercase board permission migration');
|
||||
}
|
||||
|
||||
async runAttachmentTypeMigration() {
|
||||
// Implementation for attachment type migration
|
||||
console.log('Running attachment type migration');
|
||||
}
|
||||
|
||||
async runCardCoversMigration() {
|
||||
// Implementation for card covers migration
|
||||
console.log('Running card covers migration');
|
||||
}
|
||||
|
||||
async runBoardColorCSSMigration() {
|
||||
// Implementation for board color CSS migration
|
||||
console.log('Running board color CSS migration');
|
||||
}
|
||||
|
||||
async runStarNumberMigration() {
|
||||
// Implementation for star number migration
|
||||
console.log('Running star number migration');
|
||||
}
|
||||
|
||||
async runMemberIsActiveMigration() {
|
||||
// Implementation for member is active migration
|
||||
console.log('Running member is active migration');
|
||||
}
|
||||
|
||||
async runSortChecklistsMigration() {
|
||||
// Implementation for sort checklists migration
|
||||
console.log('Running sort checklists migration');
|
||||
}
|
||||
|
||||
async runSwimlanesMigration() {
|
||||
// Implementation for swimlanes migration
|
||||
console.log('Running swimlanes migration');
|
||||
}
|
||||
|
||||
async runViewsMigration() {
|
||||
// Implementation for views migration
|
||||
console.log('Running views migration');
|
||||
}
|
||||
|
||||
async runChecklistItemsMigration() {
|
||||
// Implementation for checklist items migration
|
||||
console.log('Running checklist items migration');
|
||||
}
|
||||
|
||||
async runCardTypesMigration() {
|
||||
// Implementation for card types migration
|
||||
console.log('Running card types migration');
|
||||
}
|
||||
|
||||
async runCustomFieldsMigration() {
|
||||
// Implementation for custom fields migration
|
||||
console.log('Running custom fields migration');
|
||||
}
|
||||
|
||||
async runAttachmentMigration() {
|
||||
// Implementation for attachment migration from CollectionFS to Meteor-Files
|
||||
console.log('Running attachment migration from CollectionFS to Meteor-Files');
|
||||
}
|
||||
|
||||
async runAvatarMigration() {
|
||||
// Implementation for avatar migration from CollectionFS to Meteor-Files
|
||||
console.log('Running avatar migration from CollectionFS to Meteor-Files');
|
||||
}
|
||||
|
||||
async runListsToPerSwimlaneMigration() {
|
||||
// Implementation for lists to per-swimlane migration
|
||||
console.log('Running lists to per-swimlane migration');
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const serverMigrationRunner = new ServerMigrationRunner();
|
||||
|
||||
// Meteor methods for client-server communication
|
||||
Meteor.methods({
|
||||
'migration.start'() {
|
||||
if (!this.userId) {
|
||||
throw new Meteor.Error('not-authorized');
|
||||
}
|
||||
|
||||
return serverMigrationRunner.startMigration();
|
||||
},
|
||||
|
||||
'migration.getProgress'() {
|
||||
return {
|
||||
progress: serverMigrationProgress.get(),
|
||||
status: serverMigrationStatus.get(),
|
||||
currentStep: serverMigrationCurrentStep.get(),
|
||||
steps: serverMigrationSteps.get(),
|
||||
isMigrating: serverIsMigrating.get()
|
||||
};
|
||||
}
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue