You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
218 lines
6.4 KiB
218 lines
6.4 KiB
// cache.js
|
|
// Copyright (C) 2022 DTP Technologies, LLC
|
|
// License: Apache-2.0
|
|
|
|
'use strict';
|
|
|
|
const mongoose = require('mongoose');
|
|
const Attachment = mongoose.model('Attachment');
|
|
|
|
const { SiteService } = require('../../lib/site-lib');
|
|
|
|
class AttachmentService extends SiteService {
|
|
|
|
constructor (dtp) {
|
|
super(dtp, module.exports);
|
|
}
|
|
|
|
async start ( ) {
|
|
await super.start();
|
|
|
|
const { user: userService } = this.dtp.services;
|
|
this.populateAttachment = [
|
|
{
|
|
path: 'item',
|
|
},
|
|
{
|
|
path: 'owner',
|
|
select: userService.USER_SELECT,
|
|
},
|
|
];
|
|
|
|
this.queue = this.getJobQueue('media', this.dtp.config.jobQueues.media);
|
|
// this.template = this.loadViewTemplate('attachment/components/attachment-standalone.pug');
|
|
}
|
|
|
|
async create (owner, attachmentDefinition, file) {
|
|
const { minio: minioService } = this.dtp.services;
|
|
const NOW = new Date();
|
|
|
|
/*
|
|
* Fill in as much of the attachment as we can prior to uploading
|
|
*/
|
|
|
|
let attachment = new Attachment();
|
|
attachment.created = NOW;
|
|
|
|
attachment.ownerType = owner.type;
|
|
attachment.owner = owner._id;
|
|
|
|
attachment.itemType = attachmentDefinition.itemType;
|
|
attachment.item = mongoose.Types.ObjectId(attachmentDefinition.item._id || attachmentDefinition.item);
|
|
|
|
attachment.flags.isSensitive = attachmentDefinition.isSensitive === 'on';
|
|
|
|
/*
|
|
* Upload the original file to storage
|
|
*/
|
|
|
|
const attachmentId = attachment._id.toString();
|
|
|
|
attachment.original.bucket = process.env.MINIO_ATTACHMENT_BUCKET || 'dtp-attachments';
|
|
attachment.original.key = this.getAttachmentKey(attachment, 'original');
|
|
attachment.original.mime = file.mimetype;
|
|
attachment.original.size = file.size;
|
|
|
|
const response = await minioService.uploadFile({
|
|
bucket: attachment.file.bucket,
|
|
key: attachment.file.key,
|
|
filePath: file.path,
|
|
metadata: {
|
|
'X-DTP-Attachment-ID': attachmentId,
|
|
'Content-Type': attachment.metadata.mime,
|
|
'Content-Length': file.size,
|
|
},
|
|
});
|
|
|
|
/*
|
|
* Complete the attachment definition, and save it.
|
|
*/
|
|
|
|
attachment.original.etag = response.etag;
|
|
await attachment.save();
|
|
|
|
attachment = await this.getById(attachment._id);
|
|
|
|
await this.queue.add('attachment-ingest', { attachmentId: attachment._id });
|
|
|
|
return attachment;
|
|
}
|
|
|
|
getAttachmentKey (attachment, slug) {
|
|
const attachmentId = attachment._id.toString();
|
|
const prefix = attachmentId.slice(-4); // last 4 for best entropy
|
|
return `/attachment/${prefix}/${attachmentId}/${attachmentId}-${slug}}`;
|
|
}
|
|
|
|
/**
|
|
* Retrieves populated Attachment documents attached to an item.
|
|
* @param {String} itemType The type of item (ex: 'ChatMessage')
|
|
* @param {*} itemId The _id of the item (ex: message._id)
|
|
* @returns Array of attachments associated with the item.
|
|
*/
|
|
async getForItem (itemType, itemId) {
|
|
const attachments = await Attachment
|
|
.find({ itemType, item: itemId })
|
|
.sort({ order: 1, created: 1 })
|
|
.populate(this.populateAttachment)
|
|
.lean();
|
|
return attachments;
|
|
}
|
|
|
|
/**
|
|
* Retrieves populated Attachment documents created by a specific owner.
|
|
* @param {User} owner The owner for which Attachments are being fetched.
|
|
* @param {*} pagination Optional pagination of data set
|
|
* @returns Array of attachments owned by the specified owner.
|
|
*/
|
|
async getForOwner (owner, pagination) {
|
|
const attachments = await Attachment
|
|
.find({ ownerType: owner.type, owner: owner._id })
|
|
.sort({ order: 1, created: 1 })
|
|
.skip(pagination.skip)
|
|
.limit(pagination.cpp)
|
|
.populate(this.populateAttachment)
|
|
.lean();
|
|
return attachments;
|
|
}
|
|
|
|
/**
|
|
* Access all attachments sorted by most recent with pagination. This is for
|
|
* use by Admin tools.
|
|
* @param {*} pagination required pagination parameters (skip and cpp)
|
|
* @returns Array of attachments
|
|
*/
|
|
async getRecent (pagination) {
|
|
const attachments = await Attachment
|
|
.find()
|
|
.sort({ created: -1 })
|
|
.skip(pagination.skip)
|
|
.limit(pagination.cpp)
|
|
.populate(this.populateAttachment)
|
|
.lean();
|
|
return attachments;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {mongoose.Types.ObjectId} attachmentId The ID of the attachment
|
|
* @param {Object} options `withOriginal` true|false
|
|
* @returns A populated Attachment document configured per options.
|
|
*/
|
|
async getById (attachmentId, options) {
|
|
options = Object.assign({
|
|
withOriginal: false,
|
|
}, options || { });
|
|
|
|
let q = Attachment.findById(attachmentId);
|
|
if (options.withOriginal) {
|
|
q = q.select('+original');
|
|
}
|
|
|
|
const attachment = await q.populate(this.populateAttachment).lean();
|
|
return attachment;
|
|
}
|
|
|
|
/**
|
|
* Updates the status of an Attachment.
|
|
* @param {Attachment} attachment The attachment being modified.
|
|
* @param {*} status The new status of the attachment
|
|
*/
|
|
async setStatus (attachment, status) {
|
|
await Attachment.updateOne({ _id: attachment._id }, { $set: { status } });
|
|
}
|
|
|
|
/**
|
|
* Passes an attachment and options through a Pug template to generate HTML
|
|
* output ready to be inserted into a DOM to present the attachment in the UI.
|
|
* @param {Attachment} attachment
|
|
* @param {Object} attachmentOptions Additional options passed to the template
|
|
* @returns HTML output of the template
|
|
*/
|
|
async render (attachment, attachmentOptions) {
|
|
return this.attachmentTemplate({ attachment, attachmentOptions });
|
|
}
|
|
|
|
/**
|
|
* Removes all attachments and everything on storage about them for a
|
|
* specified User.
|
|
* @param {User} owner the owner of the attachments to be removed
|
|
*/
|
|
async removeForOwner (owner) {
|
|
const handler = this.remove.bind(this);
|
|
await Attachment
|
|
.find({ owner: owner._id })
|
|
.lean()
|
|
.cursor()
|
|
.eachAsync(handler);
|
|
}
|
|
|
|
/**
|
|
* Creates a Bull Queue job to delete an Attachment including it's processed
|
|
* and original media files.
|
|
* @param {Attachment} attachment The attachment to be deleted.
|
|
* @returns Bull Queue job handle for the newly created job to delete the
|
|
* attachment.
|
|
*/
|
|
async remove (attachment) {
|
|
this.log.info('creating job to delete attachment', { attachmentId: attachment._id });
|
|
return await this.queue.add('attachment-delete', { attachmentId: attachment._id });
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
logId: 'svc:attachment',
|
|
index: 'attachment',
|
|
className: 'AttachmentService',
|
|
create: (dtp) => { return new AttachmentService(dtp); },
|
|
};
|