attachments.js 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import { Meteor } from 'meteor/meteor';
  2. import { FilesCollection } from 'meteor/ostrio:files';
  3. import fs from 'fs';
  4. import path from 'path';
  5. import { createBucket } from './lib/grid/createBucket';
  6. import { createOnAfterUpload } from './lib/fsHooks/createOnAfterUpload';
  7. import { createInterceptDownload } from './lib/fsHooks/createInterceptDownload';
  8. import { createOnAfterRemove } from './lib/fsHooks/createOnAfterRemove';
  9. let attachmentBucket;
  10. if (Meteor.isServer) {
  11. attachmentBucket = createBucket('attachments');
  12. }
  13. const insertActivity = (fileObj, activityType) =>
  14. Activities.insert({
  15. userId: fileObj.userId,
  16. type: 'card',
  17. activityType,
  18. attachmentId: fileObj._id,
  19. // this preserves the name so that notifications can be meaningful after
  20. // this file is removed
  21. attachmentName: fileObj.name,
  22. boardId: fileObj.meta.boardId,
  23. cardId: fileObj.meta.cardId,
  24. listId: fileObj.meta.listId,
  25. swimlaneId: fileObj.meta.swimlaneId,
  26. });
  27. // XXX Enforce a schema for the Attachments FilesCollection
  28. // see: https://github.com/VeliovGroup/Meteor-Files/wiki/Schema
  29. Attachments = new FilesCollection({
  30. debug: false, // Change to `true` for debugging
  31. collectionName: 'attachments',
  32. allowClientCode: true,
  33. storagePath() {
  34. if (process.env.WRITABLE_PATH) {
  35. return path.join(process.env.WRITABLE_PATH, 'uploads', 'attachments');
  36. }
  37. return path.normalize(`assets/app/uploads/${this.collectionName}`);
  38. },
  39. onAfterUpload: function onAfterUpload(fileRef) {
  40. createOnAfterUpload(attachmentBucket).call(this, fileRef);
  41. // If the attachment doesn't have a source field
  42. // or its source is different than import
  43. if (!fileRef.meta.source || fileRef.meta.source !== 'import') {
  44. // Add activity about adding the attachment
  45. insertActivity(fileRef, 'addAttachment');
  46. }
  47. },
  48. interceptDownload: createInterceptDownload(attachmentBucket),
  49. onAfterRemove: function onAfterRemove(files) {
  50. createOnAfterRemove(attachmentBucket).call(this, files);
  51. files.forEach(fileObj => {
  52. insertActivity(fileObj, 'deleteAttachment');
  53. });
  54. },
  55. // We authorize the attachment download either:
  56. // - if the board is public, everyone (even unconnected) can download it
  57. // - if the board is private, only board members can download it
  58. protected(fileObj) {
  59. const board = Boards.findOne(fileObj.meta.boardId);
  60. if (board.isPublic()) {
  61. return true;
  62. }
  63. return board.hasMember(this.userId);
  64. },
  65. });
  66. if (Meteor.isServer) {
  67. Attachments.allow({
  68. insert(userId, fileObj) {
  69. return allowIsBoardMember(userId, Boards.findOne(fileObj.boardId));
  70. },
  71. update(userId, fileObj) {
  72. return allowIsBoardMember(userId, Boards.findOne(fileObj.boardId));
  73. },
  74. remove(userId, fileObj) {
  75. return allowIsBoardMember(userId, Boards.findOne(fileObj.boardId));
  76. },
  77. fetch: ['meta'],
  78. });
  79. Meteor.startup(() => {
  80. Attachments.collection._ensureIndex({ 'meta.cardId': 1 });
  81. const storagePath = Attachments.storagePath();
  82. if (!fs.existsSync(storagePath)) {
  83. console.log("create storagePath because it doesn't exist: " + storagePath);
  84. fs.mkdirSync(storagePath, { recursive: true });
  85. }
  86. });
  87. }
  88. export default Attachments;