wekan/models/attachmentStorageSettings.js

384 lines
9.3 KiB
JavaScript

import { ReactiveCache } from '/imports/reactiveCache';
import { Meteor } from 'meteor/meteor';
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
import { STORAGE_NAME_FILESYSTEM, STORAGE_NAME_GRIDFS, STORAGE_NAME_S3 } from '/models/lib/fileStoreStrategy';
// Attachment Storage Settings Collection
AttachmentStorageSettings = new Mongo.Collection('attachmentStorageSettings');
// Schema for attachment storage settings
AttachmentStorageSettings.attachSchema(
new SimpleSchema({
// Default storage backend for new uploads
defaultStorage: {
type: String,
allowedValues: [STORAGE_NAME_FILESYSTEM, STORAGE_NAME_GRIDFS, STORAGE_NAME_S3],
defaultValue: STORAGE_NAME_FILESYSTEM,
label: 'Default Storage Backend'
},
// Storage backend configuration
storageConfig: {
type: Object,
optional: true,
label: 'Storage Configuration'
},
'storageConfig.filesystem': {
type: Object,
optional: true,
label: 'Filesystem Configuration'
},
'storageConfig.filesystem.enabled': {
type: Boolean,
defaultValue: true,
label: 'Filesystem Storage Enabled'
},
'storageConfig.filesystem.path': {
type: String,
optional: true,
label: 'Filesystem Storage Path'
},
'storageConfig.gridfs': {
type: Object,
optional: true,
label: 'GridFS Configuration'
},
'storageConfig.gridfs.enabled': {
type: Boolean,
defaultValue: true,
label: 'GridFS Storage Enabled'
},
'storageConfig.s3': {
type: Object,
optional: true,
label: 'S3 Configuration'
},
'storageConfig.s3.enabled': {
type: Boolean,
defaultValue: false,
label: 'S3 Storage Enabled'
},
'storageConfig.s3.endpoint': {
type: String,
optional: true,
label: 'S3 Endpoint'
},
'storageConfig.s3.bucket': {
type: String,
optional: true,
label: 'S3 Bucket'
},
'storageConfig.s3.region': {
type: String,
optional: true,
label: 'S3 Region'
},
'storageConfig.s3.sslEnabled': {
type: Boolean,
defaultValue: true,
label: 'S3 SSL Enabled'
},
'storageConfig.s3.port': {
type: Number,
defaultValue: 443,
label: 'S3 Port'
},
// Upload settings
uploadSettings: {
type: Object,
optional: true,
label: 'Upload Settings'
},
'uploadSettings.maxFileSize': {
type: Number,
optional: true,
label: 'Maximum File Size (bytes)'
},
'uploadSettings.allowedMimeTypes': {
type: Array,
optional: true,
label: 'Allowed MIME Types'
},
'uploadSettings.allowedMimeTypes.$': {
type: String,
label: 'MIME Type'
},
// Migration settings
migrationSettings: {
type: Object,
optional: true,
label: 'Migration Settings'
},
'migrationSettings.autoMigrate': {
type: Boolean,
defaultValue: false,
label: 'Auto Migrate to Default Storage'
},
'migrationSettings.batchSize': {
type: Number,
defaultValue: 10,
min: 1,
max: 100,
label: 'Migration Batch Size'
},
'migrationSettings.delayMs': {
type: Number,
defaultValue: 1000,
min: 100,
max: 10000,
label: 'Migration Delay (ms)'
},
'migrationSettings.cpuThreshold': {
type: Number,
defaultValue: 70,
min: 10,
max: 90,
label: 'CPU Threshold (%)'
},
// Metadata
createdAt: {
type: Date,
autoValue() {
if (this.isInsert) {
return new Date();
} else if (this.isUpsert) {
return { $setOnInsert: new Date() };
} else {
this.unset();
}
},
label: 'Created At'
},
updatedAt: {
type: Date,
autoValue() {
if (this.isUpdate || this.isUpsert) {
return new Date();
}
},
label: 'Updated At'
},
createdBy: {
type: String,
optional: true,
label: 'Created By'
},
updatedBy: {
type: String,
optional: true,
label: 'Updated By'
}
})
);
// Helper methods
AttachmentStorageSettings.helpers({
// Get default storage backend
getDefaultStorage() {
return this.defaultStorage || STORAGE_NAME_FILESYSTEM;
},
// Check if storage backend is enabled
isStorageEnabled(storageName) {
if (!this.storageConfig) return false;
switch (storageName) {
case STORAGE_NAME_FILESYSTEM:
return this.storageConfig.filesystem?.enabled !== false;
case STORAGE_NAME_GRIDFS:
return this.storageConfig.gridfs?.enabled !== false;
case STORAGE_NAME_S3:
return this.storageConfig.s3?.enabled === true;
default:
return false;
}
},
// Get storage configuration
getStorageConfig(storageName) {
if (!this.storageConfig) return null;
switch (storageName) {
case STORAGE_NAME_FILESYSTEM:
return this.storageConfig.filesystem;
case STORAGE_NAME_GRIDFS:
return this.storageConfig.gridfs;
case STORAGE_NAME_S3:
return this.storageConfig.s3;
default:
return null;
}
},
// Get upload settings
getUploadSettings() {
return this.uploadSettings || {};
},
// Get migration settings
getMigrationSettings() {
return this.migrationSettings || {};
}
});
// Server-side methods
if (Meteor.isServer) {
// Get or create default settings
Meteor.methods({
'getAttachmentStorageSettings'() {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
const user = ReactiveCache.getUser(this.userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
let settings = AttachmentStorageSettings.findOne({});
if (!settings) {
// Create default settings
settings = {
defaultStorage: STORAGE_NAME_FILESYSTEM,
storageConfig: {
filesystem: {
enabled: true,
path: process.env.WRITABLE_PATH ? `${process.env.WRITABLE_PATH}/attachments` : '/data/attachments'
},
gridfs: {
enabled: true
},
s3: {
enabled: false
}
},
uploadSettings: {
maxFileSize: process.env.ATTACHMENTS_UPLOAD_MAX_SIZE ? parseInt(process.env.ATTACHMENTS_UPLOAD_MAX_SIZE) : 0,
allowedMimeTypes: process.env.ATTACHMENTS_UPLOAD_MIME_TYPES ? process.env.ATTACHMENTS_UPLOAD_MIME_TYPES.split(',').map(t => t.trim()) : []
},
migrationSettings: {
autoMigrate: false,
batchSize: 10,
delayMs: 1000,
cpuThreshold: 70
},
createdBy: this.userId,
updatedBy: this.userId
};
AttachmentStorageSettings.insert(settings);
settings = AttachmentStorageSettings.findOne({});
}
return settings;
},
'updateAttachmentStorageSettings'(settings) {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
const user = ReactiveCache.getUser(this.userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
// Validate settings
const schema = AttachmentStorageSettings.simpleSchema();
schema.validate(settings);
// Update settings
const result = AttachmentStorageSettings.upsert(
{},
{
$set: {
...settings,
updatedBy: this.userId,
updatedAt: new Date()
}
}
);
return result;
},
'getDefaultAttachmentStorage'() {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
const settings = AttachmentStorageSettings.findOne({});
return settings ? settings.getDefaultStorage() : STORAGE_NAME_FILESYSTEM;
},
'setDefaultAttachmentStorage'(storageName) {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
const user = ReactiveCache.getUser(this.userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
if (![STORAGE_NAME_FILESYSTEM, STORAGE_NAME_GRIDFS, STORAGE_NAME_S3].includes(storageName)) {
throw new Meteor.Error('invalid-storage', 'Invalid storage backend');
}
const result = AttachmentStorageSettings.upsert(
{},
{
$set: {
defaultStorage: storageName,
updatedBy: this.userId,
updatedAt: new Date()
}
}
);
return result;
}
});
// Publication for settings
Meteor.publish('attachmentStorageSettings', function() {
if (!this.userId) {
return this.ready();
}
const user = ReactiveCache.getUser(this.userId);
if (!user || !user.isAdmin) {
return this.ready();
}
return AttachmentStorageSettings.find({});
});
}
export default AttachmentStorageSettings;