attachment.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import loggerFactory from '~/utils/logger';
  2. import { AttachmentType } from '../interfaces/attachment';
  3. import { Attachment } from '../models/attachment';
  4. const fs = require('fs');
  5. const mongoose = require('mongoose');
  6. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  7. const logger = loggerFactory('growi:service:AttachmentService');
  8. const createReadStream = (filePath) => {
  9. return fs.createReadStream(filePath, {
  10. flags: 'r', encoding: null, fd: null, mode: '0666', autoClose: true,
  11. });
  12. };
  13. /**
  14. * the service class for Attachment and file-uploader
  15. */
  16. class AttachmentService {
  17. /** @type {Array<(pageId: string, file: Express.Multer.File, readable: Readable) => Promise<void>>} */
  18. attachHandlers = [];
  19. detachHandlers = [];
  20. /** @type {import('~/server/crowi').default} Crowi instance */
  21. crowi;
  22. /** @param {import('~/server/crowi').default} crowi Crowi instance */
  23. constructor(crowi) {
  24. this.crowi = crowi;
  25. }
  26. async createAttachment(file, user, pageId = null, attachmentType, disposeTmpFileCallback) {
  27. const { fileUploadService } = this.crowi;
  28. // check limit
  29. const res = await fileUploadService.checkLimit(file.size);
  30. if (!res.isUploadable) {
  31. throw new Error(res.errorMessage);
  32. }
  33. // create an Attachment document and upload file
  34. let attachment;
  35. try {
  36. attachment = Attachment.createWithoutSave(pageId, user, file.originalname, file.mimetype, file.size, attachmentType);
  37. await fileUploadService.uploadAttachment(createReadStream(file.path), attachment);
  38. await attachment.save();
  39. let fileStreamForAttachedHandler;
  40. if (this.attachHandlers.length !== 0) {
  41. fileStreamForAttachedHandler = createReadStream(file.path);
  42. }
  43. const attachedHandlerPromises = this.attachHandlers.map((handler) => {
  44. return handler(pageId, file, fileStreamForAttachedHandler);
  45. });
  46. // Do not await, run in background
  47. Promise.all(attachedHandlerPromises).then(() => {
  48. disposeTmpFileCallback?.(file);
  49. });
  50. }
  51. catch (err) {
  52. logger.error('Error while creating attachment', err);
  53. disposeTmpFileCallback?.(file);
  54. throw err;
  55. }
  56. return attachment;
  57. }
  58. async removeAllAttachments(attachments) {
  59. const { fileUploadService } = this.crowi;
  60. const attachmentsCollection = mongoose.connection.collection('attachments');
  61. const unorderAttachmentsBulkOp = attachmentsCollection.initializeUnorderedBulkOp();
  62. if (attachments.length === 0) {
  63. return;
  64. }
  65. attachments.forEach((attachment) => {
  66. unorderAttachmentsBulkOp.find({ _id: attachment._id }).delete();
  67. });
  68. await unorderAttachmentsBulkOp.execute();
  69. await fileUploadService.deleteFiles(attachments);
  70. return;
  71. }
  72. async removeAttachment(attachmentId) {
  73. const { fileUploadService } = this.crowi;
  74. const attachment = await Attachment.findById(attachmentId);
  75. await fileUploadService.deleteFile(attachment);
  76. await attachment.remove();
  77. return;
  78. }
  79. async isBrandLogoExist() {
  80. const query = { attachmentType: AttachmentType.BRAND_LOGO };
  81. const count = await Attachment.countDocuments(query);
  82. return count >= 1;
  83. }
  84. /**
  85. * Register a handler that will be called after attachment creation
  86. * @param {(pageId: string, file: Express.Multer.File, readable: Readable) => Promise<void>} handler
  87. */
  88. addAttachHandler(handler) {
  89. this.attachHandlers.push(handler);
  90. }
  91. /**
  92. * Register a handler that will be called before attachment deletion
  93. * @param {(attachment: Attachment) => Promise<void>} handler
  94. */
  95. addDetachHandler(handler) {
  96. this.detachHandlers.push(handler);
  97. }
  98. }
  99. module.exports = AttachmentService;