mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 15:30:13 +01:00
Fixed sidebar migrations to be per-board, not global. Clarified translations.
Thanks to xet7 !
This commit is contained in:
parent
bc5854dd29
commit
e4638d5fbc
5 changed files with 160 additions and 66 deletions
|
|
@ -58,8 +58,6 @@ template(name='migrationsSidebar')
|
||||||
else
|
else
|
||||||
span.badge.badge-success {{_ 'migration-complete'}}
|
span.badge.badge-success {{_ 'migration-complete'}}
|
||||||
|
|
||||||
hr
|
|
||||||
h4 {{_ 'global-migrations'}}
|
|
||||||
.migration-item
|
.migration-item
|
||||||
a.js-run-migration(data-migration="fixAvatarUrls")
|
a.js-run-migration(data-migration="fixAvatarUrls")
|
||||||
.migration-name
|
.migration-name
|
||||||
|
|
|
||||||
|
|
@ -57,8 +57,8 @@ BlazeComponent.extendComponent({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check fix avatar URLs migration (global)
|
// Check fix avatar URLs migration (board-specific)
|
||||||
Meteor.call('fixAvatarUrls.needsMigration', (err, res) => {
|
Meteor.call('fixAvatarUrls.needsMigration', boardId, (err, res) => {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
const statuses = this.migrationStatuses.get();
|
const statuses = this.migrationStatuses.get();
|
||||||
statuses.fixAvatarUrls = res;
|
statuses.fixAvatarUrls = res;
|
||||||
|
|
@ -66,8 +66,8 @@ BlazeComponent.extendComponent({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check fix all file URLs migration (global)
|
// Check fix all file URLs migration (board-specific)
|
||||||
Meteor.call('fixAllFileUrls.needsMigration', (err, res) => {
|
Meteor.call('fixAllFileUrls.needsMigration', boardId, (err, res) => {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
const statuses = this.migrationStatuses.get();
|
const statuses = this.migrationStatuses.get();
|
||||||
statuses.fixAllFileUrls = res;
|
statuses.fixAllFileUrls = res;
|
||||||
|
|
@ -190,10 +190,12 @@ BlazeComponent.extendComponent({
|
||||||
|
|
||||||
case 'fixAvatarUrls':
|
case 'fixAvatarUrls':
|
||||||
methodName = 'fixAvatarUrls.execute';
|
methodName = 'fixAvatarUrls.execute';
|
||||||
|
methodArgs = [boardId];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'fixAllFileUrls':
|
case 'fixAllFileUrls':
|
||||||
methodName = 'fixAllFileUrls.execute';
|
methodName = 'fixAllFileUrls.execute';
|
||||||
|
methodArgs = [boardId];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -231,12 +233,12 @@ BlazeComponent.extendComponent({
|
||||||
{ step: 'fix_missing_ids', name: 'Fix Missing IDs', duration: 600 },
|
{ step: 'fix_missing_ids', name: 'Fix Missing IDs', duration: 600 },
|
||||||
],
|
],
|
||||||
fixAvatarUrls: [
|
fixAvatarUrls: [
|
||||||
{ step: 'scan_users', name: 'Scan Users', duration: 500 },
|
{ step: 'scan_users', name: 'Checking board member avatars', duration: 500 },
|
||||||
{ step: 'fix_urls', name: 'Fix Avatar URLs', duration: 900 },
|
{ step: 'fix_urls', name: 'Fixing avatar URLs', duration: 900 },
|
||||||
],
|
],
|
||||||
fixAllFileUrls: [
|
fixAllFileUrls: [
|
||||||
{ step: 'scan_files', name: 'Scan Files', duration: 600 },
|
{ step: 'scan_files', name: 'Checking board file attachments', duration: 600 },
|
||||||
{ step: 'fix_urls', name: 'Fix File URLs', duration: 1000 },
|
{ step: 'fix_urls', name: 'Fixing file URLs', duration: 1000 },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1419,10 +1419,9 @@
|
||||||
"fix-missing-lists-migration": "Fix Missing Lists",
|
"fix-missing-lists-migration": "Fix Missing Lists",
|
||||||
"fix-missing-lists-migration-description": "Detects and repairs missing or corrupted lists in the board structure.",
|
"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": "Fix Avatar URLs",
|
||||||
"fix-avatar-urls-migration-description": "Updates avatar URLs to use the correct storage backend and fixes broken avatar references.",
|
"fix-avatar-urls-migration-description": "Updates avatar URLs for board members 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": "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.",
|
"fix-all-file-urls-migration-description": "Updates all file attachment URLs on this board to use the correct storage backend and fixes broken file references.",
|
||||||
"global-migrations": "Global Migrations",
|
|
||||||
"migration-needed": "Migration Needed",
|
"migration-needed": "Migration Needed",
|
||||||
"migration-complete": "Complete",
|
"migration-complete": "Complete",
|
||||||
"migration-running": "Running...",
|
"migration-running": "Running...",
|
||||||
|
|
@ -1438,8 +1437,8 @@
|
||||||
"run-restore-lost-cards-migration-confirm": "This will create a 'Lost Cards' swimlane and restore all cards and lists with missing swimlaneId or listId. This only affects non-archived items. Continue?",
|
"run-restore-lost-cards-migration-confirm": "This will create a 'Lost Cards' swimlane and restore all cards and lists with missing swimlaneId or listId. This only affects non-archived items. Continue?",
|
||||||
"run-restore-all-archived-migration-confirm": "This will restore ALL archived swimlanes, lists, and cards, making them visible again. Any items with missing IDs will be automatically fixed. This cannot be easily undone. Continue?",
|
"run-restore-all-archived-migration-confirm": "This will restore ALL archived swimlanes, lists, and cards, making them visible again. Any items with missing IDs will be automatically fixed. This cannot be easily undone. Continue?",
|
||||||
"run-fix-missing-lists-migration-confirm": "This will detect and repair missing or corrupted lists in the board structure. 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-avatar-urls-migration-confirm": "This will update avatar URLs for board members to use the correct storage backend. 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?",
|
"run-fix-all-file-urls-migration-confirm": "This will update all file attachment URLs on this board to use the correct storage backend. Continue?",
|
||||||
"restore-lost-cards-nothing-to-restore": "No lost swimlanes, lists, or cards to restore",
|
"restore-lost-cards-nothing-to-restore": "No lost swimlanes, lists, or cards to restore",
|
||||||
|
|
||||||
"migration-progress-title": "Board Migration in Progress",
|
"migration-progress-title": "Board Migration in Progress",
|
||||||
|
|
@ -1466,9 +1465,9 @@
|
||||||
"step-restore-cards": "Restore Cards",
|
"step-restore-cards": "Restore Cards",
|
||||||
"step-restore-swimlanes": "Restore Swimlanes",
|
"step-restore-swimlanes": "Restore Swimlanes",
|
||||||
"step-fix-missing-ids": "Fix Missing IDs",
|
"step-fix-missing-ids": "Fix Missing IDs",
|
||||||
"step-scan-users": "Scan Users",
|
"step-scan-users": "Checking board member avatars",
|
||||||
"step-scan-files": "Scan Files",
|
"step-scan-files": "Checking board file attachments",
|
||||||
"step-fix-file-urls": "Fix File URLs",
|
"step-fix-file-urls": "Fixing file URLs",
|
||||||
"cleanup": "Cleanup",
|
"cleanup": "Cleanup",
|
||||||
"cleanup-old-jobs": "Cleanup Old Jobs",
|
"cleanup-old-jobs": "Cleanup Old Jobs",
|
||||||
"completed": "Completed",
|
"completed": "Completed",
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,14 @@
|
||||||
* Ensures all attachment and avatar URLs are universal and work regardless of ROOT_URL and PORT settings
|
* Ensures all attachment and avatar URLs are universal and work regardless of ROOT_URL and PORT settings
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Meteor } from 'meteor/meteor';
|
||||||
|
import { check } from 'meteor/check';
|
||||||
import { ReactiveCache } from '/imports/reactiveCache';
|
import { ReactiveCache } from '/imports/reactiveCache';
|
||||||
|
import Boards from '/models/boards';
|
||||||
import Users from '/models/users';
|
import Users from '/models/users';
|
||||||
import Attachments from '/models/attachments';
|
import Attachments from '/models/attachments';
|
||||||
import Avatars from '/models/avatars';
|
import Avatars from '/models/avatars';
|
||||||
|
import Cards from '/models/cards';
|
||||||
import { generateUniversalAttachmentUrl, generateUniversalAvatarUrl, cleanFileUrl, extractFileIdFromUrl, isUniversalFileUrl } from '/models/lib/universalUrlGenerator';
|
import { generateUniversalAttachmentUrl, generateUniversalAvatarUrl, cleanFileUrl, extractFileIdFromUrl, isUniversalFileUrl } from '/models/lib/universalUrlGenerator';
|
||||||
|
|
||||||
class FixAllFileUrlsMigration {
|
class FixAllFileUrlsMigration {
|
||||||
|
|
@ -16,11 +20,19 @@ class FixAllFileUrlsMigration {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if migration is needed
|
* Check if migration is needed for a board
|
||||||
*/
|
*/
|
||||||
needsMigration() {
|
needsMigration(boardId) {
|
||||||
// Check for problematic avatar URLs
|
// Get all users who are members of this board
|
||||||
const users = ReactiveCache.getUsers({});
|
const board = ReactiveCache.getBoard(boardId);
|
||||||
|
if (!board || !board.members) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const memberIds = board.members.map(m => m.userId);
|
||||||
|
|
||||||
|
// Check for problematic avatar URLs for board members
|
||||||
|
const users = ReactiveCache.getUsers({ _id: { $in: memberIds } });
|
||||||
for (const user of users) {
|
for (const user of users) {
|
||||||
if (user.profile && user.profile.avatarUrl) {
|
if (user.profile && user.profile.avatarUrl) {
|
||||||
const avatarUrl = user.profile.avatarUrl;
|
const avatarUrl = user.profile.avatarUrl;
|
||||||
|
|
@ -30,8 +42,11 @@ class FixAllFileUrlsMigration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for problematic attachment URLs
|
// Check for problematic attachment URLs on this board
|
||||||
const attachments = ReactiveCache.getAttachments({});
|
const cards = ReactiveCache.getCards({ boardId });
|
||||||
|
const cardIds = cards.map(c => c._id);
|
||||||
|
const attachments = ReactiveCache.getAttachments({ cardId: { $in: cardIds } });
|
||||||
|
|
||||||
for (const attachment of attachments) {
|
for (const attachment of attachments) {
|
||||||
if (attachment.url && this.hasProblematicUrl(attachment.url)) {
|
if (attachment.url && this.hasProblematicUrl(attachment.url)) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -78,46 +93,53 @@ class FixAllFileUrlsMigration {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the migration
|
* Execute the migration for a board
|
||||||
*/
|
*/
|
||||||
async execute() {
|
async execute(boardId) {
|
||||||
let filesFixed = 0;
|
let filesFixed = 0;
|
||||||
let errors = [];
|
let errors = [];
|
||||||
|
|
||||||
console.log(`Starting universal file URL migration...`);
|
console.log(`Starting universal file URL migration for board ${boardId}...`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Fix avatar URLs
|
// Fix avatar URLs for board members
|
||||||
const avatarFixed = await this.fixAvatarUrls();
|
const avatarFixed = await this.fixAvatarUrls(boardId);
|
||||||
filesFixed += avatarFixed;
|
filesFixed += avatarFixed;
|
||||||
|
|
||||||
// Fix attachment URLs
|
// Fix attachment URLs for board cards
|
||||||
const attachmentFixed = await this.fixAttachmentUrls();
|
const attachmentFixed = await this.fixAttachmentUrls(boardId);
|
||||||
filesFixed += attachmentFixed;
|
filesFixed += attachmentFixed;
|
||||||
|
|
||||||
// Fix card attachment references
|
// Fix card attachment references
|
||||||
const cardFixed = await this.fixCardAttachmentUrls();
|
const cardFixed = await this.fixCardAttachmentUrls(boardId);
|
||||||
filesFixed += cardFixed;
|
filesFixed += cardFixed;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error during file URL migration:', error);
|
console.error('Error during file URL migration for board', boardId, ':', error);
|
||||||
errors.push(error.message);
|
errors.push(error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Universal file URL migration completed. Fixed ${filesFixed} file URLs.`);
|
console.log(`Universal file URL migration completed for board ${boardId}. Fixed ${filesFixed} file URLs.`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: errors.length === 0,
|
success: errors.length === 0,
|
||||||
filesFixed,
|
filesFixed,
|
||||||
errors
|
errors,
|
||||||
|
changes: [`Fixed ${filesFixed} file URLs for this board`]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fix avatar URLs in user profiles
|
* Fix avatar URLs in user profiles for board members
|
||||||
*/
|
*/
|
||||||
async fixAvatarUrls() {
|
async fixAvatarUrls(boardId) {
|
||||||
const users = ReactiveCache.getUsers({});
|
const board = ReactiveCache.getBoard(boardId);
|
||||||
|
if (!board || !board.members) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const memberIds = board.members.map(m => m.userId);
|
||||||
|
const users = ReactiveCache.getUsers({ _id: { $in: memberIds } });
|
||||||
let avatarsFixed = 0;
|
let avatarsFixed = 0;
|
||||||
|
|
||||||
for (const user of users) {
|
for (const user of users) {
|
||||||
|
|
@ -164,10 +186,12 @@ class FixAllFileUrlsMigration {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fix attachment URLs in attachment records
|
* Fix attachment URLs in attachment records for this board
|
||||||
*/
|
*/
|
||||||
async fixAttachmentUrls() {
|
async fixAttachmentUrls(boardId) {
|
||||||
const attachments = ReactiveCache.getAttachments({});
|
const cards = ReactiveCache.getCards({ boardId });
|
||||||
|
const cardIds = cards.map(c => c._id);
|
||||||
|
const attachments = ReactiveCache.getAttachments({ cardId: { $in: cardIds } });
|
||||||
let attachmentsFixed = 0;
|
let attachmentsFixed = 0;
|
||||||
|
|
||||||
for (const attachment of attachments) {
|
for (const attachment of attachments) {
|
||||||
|
|
@ -202,10 +226,12 @@ class FixAllFileUrlsMigration {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fix attachment URLs in the Attachments collection
|
* Fix attachment URLs in the Attachments collection for this board
|
||||||
*/
|
*/
|
||||||
async fixCardAttachmentUrls() {
|
async fixCardAttachmentUrls(boardId) {
|
||||||
const attachments = ReactiveCache.getAttachments({});
|
const cards = ReactiveCache.getCards({ boardId });
|
||||||
|
const cardIds = cards.map(c => c._id);
|
||||||
|
const attachments = ReactiveCache.getAttachments({ cardId: { $in: cardIds } });
|
||||||
let attachmentsFixed = 0;
|
let attachmentsFixed = 0;
|
||||||
|
|
||||||
for (const attachment of attachments) {
|
for (const attachment of attachments) {
|
||||||
|
|
@ -244,19 +270,43 @@ export const fixAllFileUrlsMigration = new FixAllFileUrlsMigration();
|
||||||
|
|
||||||
// Meteor methods
|
// Meteor methods
|
||||||
Meteor.methods({
|
Meteor.methods({
|
||||||
'fixAllFileUrls.execute'() {
|
'fixAllFileUrls.execute'(boardId) {
|
||||||
|
check(boardId, String);
|
||||||
|
|
||||||
if (!this.userId) {
|
if (!this.userId) {
|
||||||
throw new Meteor.Error('not-authorized');
|
throw new Meteor.Error('not-authorized', 'You must be logged in');
|
||||||
}
|
}
|
||||||
|
|
||||||
return fixAllFileUrlsMigration.execute();
|
// Check if user is board admin
|
||||||
|
const board = ReactiveCache.getBoard(boardId);
|
||||||
|
if (!board) {
|
||||||
|
throw new Meteor.Error('board-not-found', 'Board not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = ReactiveCache.getUser(this.userId);
|
||||||
|
if (!user) {
|
||||||
|
throw new Meteor.Error('user-not-found', 'User not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only board admins can run migrations
|
||||||
|
const isBoardAdmin = board.members && board.members.some(
|
||||||
|
member => member.userId === this.userId && member.isAdmin
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isBoardAdmin && !user.isAdmin) {
|
||||||
|
throw new Meteor.Error('not-authorized', 'Only board administrators can run migrations');
|
||||||
|
}
|
||||||
|
|
||||||
|
return fixAllFileUrlsMigration.execute(boardId);
|
||||||
},
|
},
|
||||||
|
|
||||||
'fixAllFileUrls.needsMigration'() {
|
'fixAllFileUrls.needsMigration'(boardId) {
|
||||||
|
check(boardId, String);
|
||||||
|
|
||||||
if (!this.userId) {
|
if (!this.userId) {
|
||||||
throw new Meteor.Error('not-authorized');
|
throw new Meteor.Error('not-authorized', 'You must be logged in');
|
||||||
}
|
}
|
||||||
|
|
||||||
return fixAllFileUrlsMigration.needsMigration();
|
return fixAllFileUrlsMigration.needsMigration(boardId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,10 @@
|
||||||
* Removes problematic auth parameters from existing avatar URLs
|
* Removes problematic auth parameters from existing avatar URLs
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Meteor } from 'meteor/meteor';
|
||||||
|
import { check } from 'meteor/check';
|
||||||
import { ReactiveCache } from '/imports/reactiveCache';
|
import { ReactiveCache } from '/imports/reactiveCache';
|
||||||
|
import Boards from '/models/boards';
|
||||||
import Users from '/models/users';
|
import Users from '/models/users';
|
||||||
import { generateUniversalAvatarUrl, cleanFileUrl, extractFileIdFromUrl, isUniversalFileUrl } from '/models/lib/universalUrlGenerator';
|
import { generateUniversalAvatarUrl, cleanFileUrl, extractFileIdFromUrl, isUniversalFileUrl } from '/models/lib/universalUrlGenerator';
|
||||||
|
|
||||||
|
|
@ -14,10 +17,17 @@ class FixAvatarUrlsMigration {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if migration is needed
|
* Check if migration is needed for a board
|
||||||
*/
|
*/
|
||||||
needsMigration() {
|
needsMigration(boardId) {
|
||||||
const users = ReactiveCache.getUsers({});
|
// Get all users who are members of this board
|
||||||
|
const board = ReactiveCache.getBoard(boardId);
|
||||||
|
if (!board || !board.members) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const memberIds = board.members.map(m => m.userId);
|
||||||
|
const users = ReactiveCache.getUsers({ _id: { $in: memberIds } });
|
||||||
|
|
||||||
for (const user of users) {
|
for (const user of users) {
|
||||||
if (user.profile && user.profile.avatarUrl) {
|
if (user.profile && user.profile.avatarUrl) {
|
||||||
|
|
@ -32,13 +42,23 @@ class FixAvatarUrlsMigration {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the migration
|
* Execute the migration for a board
|
||||||
*/
|
*/
|
||||||
async execute() {
|
async execute(boardId) {
|
||||||
const users = ReactiveCache.getUsers({});
|
// Get all users who are members of this board
|
||||||
|
const board = ReactiveCache.getBoard(boardId);
|
||||||
|
if (!board || !board.members) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Board not found or has no members'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const memberIds = board.members.map(m => m.userId);
|
||||||
|
const users = ReactiveCache.getUsers({ _id: { $in: memberIds } });
|
||||||
let avatarsFixed = 0;
|
let avatarsFixed = 0;
|
||||||
|
|
||||||
console.log(`Starting avatar URL fix migration...`);
|
console.log(`Starting avatar URL fix migration for board ${boardId}...`);
|
||||||
|
|
||||||
for (const user of users) {
|
for (const user of users) {
|
||||||
if (user.profile && user.profile.avatarUrl) {
|
if (user.profile && user.profile.avatarUrl) {
|
||||||
|
|
@ -96,11 +116,12 @@ class FixAvatarUrlsMigration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Avatar URL fix migration completed. Fixed ${avatarsFixed} avatar URLs.`);
|
console.log(`Avatar URL fix migration completed for board ${boardId}. Fixed ${avatarsFixed} avatar URLs.`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
avatarsFixed
|
avatarsFixed,
|
||||||
|
changes: [`Fixed ${avatarsFixed} avatar URLs for board members`]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -110,19 +131,43 @@ export const fixAvatarUrlsMigration = new FixAvatarUrlsMigration();
|
||||||
|
|
||||||
// Meteor method
|
// Meteor method
|
||||||
Meteor.methods({
|
Meteor.methods({
|
||||||
'fixAvatarUrls.execute'() {
|
'fixAvatarUrls.execute'(boardId) {
|
||||||
|
check(boardId, String);
|
||||||
|
|
||||||
if (!this.userId) {
|
if (!this.userId) {
|
||||||
throw new Meteor.Error('not-authorized');
|
throw new Meteor.Error('not-authorized', 'You must be logged in');
|
||||||
}
|
}
|
||||||
|
|
||||||
return fixAvatarUrlsMigration.execute();
|
// Check if user is board admin
|
||||||
|
const board = ReactiveCache.getBoard(boardId);
|
||||||
|
if (!board) {
|
||||||
|
throw new Meteor.Error('board-not-found', 'Board not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = ReactiveCache.getUser(this.userId);
|
||||||
|
if (!user) {
|
||||||
|
throw new Meteor.Error('user-not-found', 'User not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only board admins can run migrations
|
||||||
|
const isBoardAdmin = board.members && board.members.some(
|
||||||
|
member => member.userId === this.userId && member.isAdmin
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isBoardAdmin && !user.isAdmin) {
|
||||||
|
throw new Meteor.Error('not-authorized', 'Only board administrators can run migrations');
|
||||||
|
}
|
||||||
|
|
||||||
|
return fixAvatarUrlsMigration.execute(boardId);
|
||||||
},
|
},
|
||||||
|
|
||||||
'fixAvatarUrls.needsMigration'() {
|
'fixAvatarUrls.needsMigration'(boardId) {
|
||||||
|
check(boardId, String);
|
||||||
|
|
||||||
if (!this.userId) {
|
if (!this.userId) {
|
||||||
throw new Meteor.Error('not-authorized');
|
throw new Meteor.Error('not-authorized', 'You must be logged in');
|
||||||
}
|
}
|
||||||
|
|
||||||
return fixAvatarUrlsMigration.needsMigration();
|
return fixAvatarUrlsMigration.needsMigration(boardId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue