| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 | import { ReactiveCache } from '/imports/reactiveCache';import { Meteor } from 'meteor/meteor';import { SimpleSchema } from 'meteor/aldeed:simple-schema';import { STORAGE_NAME_FILESYSTEM, STORAGE_NAME_GRIDFS } from '/models/lib/fileStoreStrategy';// DISABLED: S3 storage removed due to Node.js compatibility// import { STORAGE_NAME_S3 } from '/models/lib/fileStoreStrategy';// Attachment Storage Settings CollectionAttachmentStorageSettings = new Mongo.Collection('attachmentStorageSettings');// Schema for attachment storage settingsAttachmentStorageSettings.attachSchema(  new SimpleSchema({    // Default storage backend for new uploads    defaultStorage: {      type: String,      allowedValues: [STORAGE_NAME_FILESYSTEM, STORAGE_NAME_GRIDFS],      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'    },        // DISABLED: S3 storage configuration removed due to Node.js compatibility    /*    '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 methodsAttachmentStorageSettings.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;      // DISABLED: S3 storage removed due to Node.js compatibility      // 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;      // DISABLED: S3 storage removed due to Node.js compatibility      // 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 methodsif (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            },            // DISABLED: S3 storage removed due to Node.js compatibility            // 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].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;
 |