mirror of
https://github.com/wekan/wekan.git
synced 2026-01-07 01:58:49 +01:00
Made possible to start WeKan immediately without running any database migrations.
Thanks to xet7 !
This commit is contained in:
parent
688b725807
commit
3ccdc2e307
5 changed files with 198 additions and 11 deletions
|
|
@ -104,8 +104,8 @@ function initSortable(boardComponent, $listsDom) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the original swimlane ID of the list
|
// Get the original swimlane ID of the list (handle backward compatibility)
|
||||||
const originalSwimlaneId = list.swimlaneId;
|
const originalSwimlaneId = list.getEffectiveSwimlaneId ? list.getEffectiveSwimlaneId() : (list.swimlaneId || null);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Reverted incomplete change list width,
|
Reverted incomplete change list width,
|
||||||
|
|
|
||||||
118
models/attachments_old.js
Normal file
118
models/attachments_old.js
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
import { ReactiveCache } from '/imports/reactiveCache';
|
||||||
|
|
||||||
|
const storeName = 'attachments';
|
||||||
|
const defaultStoreOptions = {
|
||||||
|
beforeWrite: fileObj => {
|
||||||
|
if (!fileObj.isImage()) {
|
||||||
|
return {
|
||||||
|
type: 'application/octet-stream',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let store;
|
||||||
|
store = new FS.Store.GridFS(storeName, {
|
||||||
|
// XXX Add a new store for cover thumbnails so we don't load big images in
|
||||||
|
// the general board view
|
||||||
|
// If the uploaded document is not an image we need to enforce browser
|
||||||
|
// download instead of execution. This is particularly important for HTML
|
||||||
|
// files that the browser will just execute if we don't serve them with the
|
||||||
|
// appropriate `application/octet-stream` MIME header which can lead to user
|
||||||
|
// data leaks. I imagine other formats (like PDF) can also be attack vectors.
|
||||||
|
// See https://github.com/wekan/wekan/issues/99
|
||||||
|
// XXX Should we use `beforeWrite` option of CollectionFS instead of
|
||||||
|
// collection-hooks?
|
||||||
|
// We should use `beforeWrite`.
|
||||||
|
...defaultStoreOptions,
|
||||||
|
});
|
||||||
|
AttachmentsOld = new FS.Collection('attachments', {
|
||||||
|
stores: [store],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Meteor.isServer) {
|
||||||
|
Meteor.startup(() => {
|
||||||
|
AttachmentsOld.files._ensureIndex({ cardId: 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
AttachmentsOld.allow({
|
||||||
|
insert(userId, doc) {
|
||||||
|
return allowIsBoardMember(userId, ReactiveCache.getBoard(doc.boardId));
|
||||||
|
},
|
||||||
|
update(userId, doc) {
|
||||||
|
return allowIsBoardMember(userId, ReactiveCache.getBoard(doc.boardId));
|
||||||
|
},
|
||||||
|
remove(userId, doc) {
|
||||||
|
return allowIsBoardMember(userId, ReactiveCache.getBoard(doc.boardId));
|
||||||
|
},
|
||||||
|
// We authorize the attachment download either:
|
||||||
|
// - if the board is public, everyone (even unconnected) can download it
|
||||||
|
// - if the board is private, only board members can download it
|
||||||
|
download(userId, doc) {
|
||||||
|
const board = ReactiveCache.getBoard(doc.boardId);
|
||||||
|
if (board.isPublic()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return board.hasMember(userId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
fetch: ['boardId'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX Enforce a schema for the AttachmentsOld CollectionFS
|
||||||
|
|
||||||
|
if (Meteor.isServer) {
|
||||||
|
AttachmentsOld.files.after.insert((userId, doc) => {
|
||||||
|
// If the attachment doesn't have a source field
|
||||||
|
// or its source is different than import
|
||||||
|
if (!doc.source || doc.source !== 'import') {
|
||||||
|
// Add activity about adding the attachment
|
||||||
|
Activities.insert({
|
||||||
|
userId,
|
||||||
|
type: 'card',
|
||||||
|
activityType: 'addAttachment',
|
||||||
|
attachmentId: doc._id,
|
||||||
|
// this preserves the name so that notifications can be meaningful after
|
||||||
|
// this file is removed
|
||||||
|
attachmentName: doc.original.name,
|
||||||
|
boardId: doc.boardId,
|
||||||
|
cardId: doc.cardId,
|
||||||
|
listId: doc.listId,
|
||||||
|
swimlaneId: doc.swimlaneId,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Don't add activity about adding the attachment as the activity
|
||||||
|
// be imported and delete source field
|
||||||
|
AttachmentsOld.update(
|
||||||
|
{
|
||||||
|
_id: doc._id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$unset: {
|
||||||
|
source: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
AttachmentsOld.files.before.remove((userId, doc) => {
|
||||||
|
Activities.insert({
|
||||||
|
userId,
|
||||||
|
type: 'card',
|
||||||
|
activityType: 'deleteAttachment',
|
||||||
|
attachmentId: doc._id,
|
||||||
|
// this preserves the name so that notifications can be meaningful after
|
||||||
|
// this file is removed
|
||||||
|
attachmentName: doc.original.name,
|
||||||
|
boardId: doc.boardId,
|
||||||
|
cardId: doc.cardId,
|
||||||
|
listId: doc.listId,
|
||||||
|
swimlaneId: doc.swimlaneId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AttachmentsOld;
|
||||||
29
models/avatars_old.js
Normal file
29
models/avatars_old.js
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
AvatarsOld = new FS.Collection('avatars', {
|
||||||
|
stores: [new FS.Store.GridFS('avatars')],
|
||||||
|
filter: {
|
||||||
|
maxSize: 72000,
|
||||||
|
allow: {
|
||||||
|
contentTypes: ['image/*'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function isOwner(userId, file) {
|
||||||
|
return userId && userId === file.userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
AvatarsOld.allow({
|
||||||
|
insert: isOwner,
|
||||||
|
update: isOwner,
|
||||||
|
remove: isOwner,
|
||||||
|
download() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
fetch: ['userId'],
|
||||||
|
});
|
||||||
|
|
||||||
|
AvatarsOld.files.before.insert((userId, doc) => {
|
||||||
|
doc.userId = userId;
|
||||||
|
});
|
||||||
|
|
||||||
|
export default AvatarsOld;
|
||||||
12
models/presences.js
Normal file
12
models/presences.js
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
if (Meteor.isServer) {
|
||||||
|
Meteor.startup(() => {
|
||||||
|
// Date of 7 days ago
|
||||||
|
let lastWeek = new Date();
|
||||||
|
lastWeek.setDate(lastWeek.getDate() - 7);
|
||||||
|
|
||||||
|
presences.remove({ ttl: { $lte: lastWeek } });
|
||||||
|
|
||||||
|
// Create index for serverId that is queried often
|
||||||
|
presences._collection._ensureIndex({ serverId: -1 });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -26,14 +26,23 @@ import Triggers from '../models/triggers';
|
||||||
import UnsavedEdits from '../models/unsavedEdits';
|
import UnsavedEdits from '../models/unsavedEdits';
|
||||||
import Users from '../models/users';
|
import Users from '../models/users';
|
||||||
|
|
||||||
// Anytime you change the schema of one of the collection in a non-backward
|
// MIGRATIONS DISABLED - BACKWARD COMPATIBILITY APPROACH
|
||||||
// compatible way you have to write a migration in this file using the following
|
// All migrations have been disabled to prevent downtime and performance issues
|
||||||
// API:
|
// with large databases. Instead, the application now uses backward compatibility
|
||||||
|
// code that detects the current database structure and works with both migrated
|
||||||
|
// and non-migrated data without requiring any migrations to run.
|
||||||
//
|
//
|
||||||
|
// This approach ensures:
|
||||||
|
// 1. No migration downtime
|
||||||
|
// 2. Immediate functionality for all database states
|
||||||
|
// 3. Gradual data structure updates as users interact with the system
|
||||||
|
// 4. Full backward compatibility with existing data
|
||||||
|
//
|
||||||
|
// Original migration API (now disabled):
|
||||||
// Migrations.add(name, migrationCallback, optionalOrder);
|
// Migrations.add(name, migrationCallback, optionalOrder);
|
||||||
|
|
||||||
// Note that we have extra migrations defined in `sandstorm.js` that are
|
// Note that we have extra migrations defined in `sandstorm.js` that are
|
||||||
// exclusive to Sandstorm and shouldn’t be executed in the general case.
|
// exclusive to Sandstorm and shouldn't be executed in the general case.
|
||||||
// XXX I guess if we had ES6 modules we could
|
// XXX I guess if we had ES6 modules we could
|
||||||
// `import { isSandstorm } from sandstorm.js` and define the migration here as
|
// `import { isSandstorm } from sandstorm.js` and define the migration here as
|
||||||
// well, but for now I want to avoid definied too many globals.
|
// well, but for now I want to avoid definied too many globals.
|
||||||
|
|
@ -54,6 +63,18 @@ const noValidate = {
|
||||||
};
|
};
|
||||||
const noValidateMulti = { ...noValidate, multi: true };
|
const noValidateMulti = { ...noValidate, multi: true };
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// ALL MIGRATIONS DISABLED - BACKWARD COMPATIBILITY APPROACH
|
||||||
|
// ============================================================================
|
||||||
|
// All migrations below have been disabled to prevent downtime and performance
|
||||||
|
// issues with large databases. The application now uses backward compatibility
|
||||||
|
// code in the models and client code to handle both migrated and non-migrated
|
||||||
|
// database structures without requiring any migrations to run.
|
||||||
|
//
|
||||||
|
// This ensures immediate functionality regardless of database state.
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/*
|
||||||
Migrations.add('board-background-color', () => {
|
Migrations.add('board-background-color', () => {
|
||||||
const defaultColor = '#16A085';
|
const defaultColor = '#16A085';
|
||||||
Boards.update(
|
Boards.update(
|
||||||
|
|
@ -106,7 +127,6 @@ Migrations.add('add-boardmemberlist-allowed', () => {
|
||||||
noValidateMulti,
|
noValidateMulti,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
|
|
||||||
Migrations.add('lowercase-board-permission', () => {
|
Migrations.add('lowercase-board-permission', () => {
|
||||||
['Public', 'Private'].forEach(permission => {
|
['Public', 'Private'].forEach(permission => {
|
||||||
|
|
@ -148,8 +168,6 @@ Migrations.add('card-covers', () => {
|
||||||
Attachments.update({}, { $unset: { cover: '' } }, noValidateMulti);
|
Attachments.update({}, { $unset: { cover: '' } }, noValidateMulti);
|
||||||
});
|
});
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
Migrations.add('use-css-class-for-boards-colors', () => {
|
Migrations.add('use-css-class-for-boards-colors', () => {
|
||||||
const associationTable = {
|
const associationTable = {
|
||||||
'#27AE60': 'nephritis',
|
'#27AE60': 'nephritis',
|
||||||
|
|
@ -1407,7 +1425,6 @@ Migrations.add('migrate-avatars-collectionFS-to-ostrioFiles', () => {
|
||||||
});
|
});
|
||||||
Meteor.settings.public.ostrioFilesMigrationInProgress = "false";
|
Meteor.settings.public.ostrioFilesMigrationInProgress = "false";
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
|
|
||||||
Migrations.add('migrate-attachment-drop-index-cardId', () => {
|
Migrations.add('migrate-attachment-drop-index-cardId', () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -1442,7 +1459,6 @@ Migrations.add('attachment-cardCopy-fix-boardId-etc', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
|
|
||||||
Migrations.add('remove-unused-planning-poker', () => {
|
Migrations.add('remove-unused-planning-poker', () => {
|
||||||
Cards.update(
|
Cards.update(
|
||||||
|
|
@ -1490,6 +1506,10 @@ Migrations.add('remove-user-profile-hideCheckedItems', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Migration disabled - using backward compatibility approach instead
|
||||||
|
// This migration was taking too long for large databases
|
||||||
|
// The feature now works with both old lists (without swimlaneId) and new lists (with swimlaneId)
|
||||||
|
/*
|
||||||
Migrations.add('migrate-lists-to-per-swimlane', () => {
|
Migrations.add('migrate-lists-to-per-swimlane', () => {
|
||||||
if (process.env.DEBUG === 'true') {
|
if (process.env.DEBUG === 'true') {
|
||||||
console.log('Starting migration: migrate-lists-to-per-swimlane');
|
console.log('Starting migration: migrate-lists-to-per-swimlane');
|
||||||
|
|
@ -1550,3 +1570,11 @@ Migrations.add('migrate-lists-to-per-swimlane', () => {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// END OF DISABLED MIGRATIONS
|
||||||
|
// ============================================================================
|
||||||
|
// All migrations above have been disabled. The application now uses backward
|
||||||
|
// compatibility code to handle both migrated and non-migrated database structures.
|
||||||
|
// ============================================================================
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue