UNPKG

@jrmc/adonis-attachment

Version:

Turn any field on your Lucid model to an attachment data type

234 lines (233 loc) 7.26 kB
/** * @jrmc/adonis-attachment * * @license MIT * @copyright Jeremy Chaufourier <jeremy@chaufourier.fr> */ import path from 'node:path'; import { AttachmentBase } from './attachment_base.js'; import { Variant } from './variant_attachment.js'; import { metaFromBuffer, metaFromFile } from '../adapters/meta.js'; export class Attachment extends AttachmentBase { originalName; variants; constructor(drive, attributes, input) { super(drive, attributes, input); this.originalName = attributes.originalName; if (attributes.disk) { this.disk = attributes.disk; } if (attributes.variants) { this.variants = []; attributes.variants.forEach((v) => { const variant = new Variant(this.drive, v); variant.disk = this.disk; this.variants.push(variant); }); } } /** * Methods */ async createVariant(key, input, options) { let meta; let folder; if (Buffer.isBuffer(input)) { meta = await metaFromBuffer(input); } else { meta = await metaFromFile(input, this.name); } if (options?.basePath && !options?.ignoreFolder) { folder = path.join(options.basePath, this.folder, this.name); } else if (options?.basePath && options?.ignoreFolder) { folder = path.join(options.basePath, this.name); } else if (options?.ignoreFolder) { folder = this.name; } else { folder = path.join(this.folder, 'variants', this.name); } const attributes = { ...meta, key, folder, }; const variant = new Variant(this.drive, attributes, input); variant.setOptions(this.options); if (this.variants === undefined) { this.variants = []; } this.variants.push(variant); return variant; } getVariant(variantName) { return this.variants?.find((v) => v.key === variantName) ?? null; } async getUrl(variantName) { if (variantName) { const variant = this.getVariant(variantName); if (variant) { variant.setOptions(this.options); const url = await variant.getUrl(); if (url) { return url; } } } return super.getUrl(); } async getSignedUrl(variantNameOrOptions, signedUrlOptions) { let variantName; let options = signedUrlOptions; if (typeof variantNameOrOptions === 'string') { variantName = variantNameOrOptions; } else if (variantNameOrOptions && !signedUrlOptions) { options = variantNameOrOptions; } if (variantName) { const variant = this.getVariant(variantName); if (variant) { variant.setOptions(this.options); const url = variant.getSignedUrl(options); if (url) { return url; } } } return super.getSignedUrl(options); } setOptions(options) { this.options = { ...this.options, ...options, }; if (!this.disk && options?.disk) { this.disk = options.disk; } if (this.variants) { this.variants.forEach((v) => { if (!v.disk) { v.disk = this.disk; } v.setOptions({ ...this.options, variants: [], }); }); } return this; } /** * Actions */ async preComputeUrl() { if (this.options?.preComputeUrl === false) { return; } await this.computeUrl(); if (this.variants) { for (const key in this.variants) { if (Object.prototype.hasOwnProperty.call(this.variants, key)) { await this.variants[key].computeUrl(); } } } } async makeName(record, attributeName) { return super.makeName(record, attributeName, this.originalName); } async moveFileForDelete() { if (this.options && this.options.rename !== true) { const originalPath = this.path; this.name = `${this.name}.trash`; const trashPath = this.path; this.originalPath = trashPath; try { await this.getDisk().move(originalPath, trashPath); } catch { // move() failed (e.g. S3 provider without ACL support): delete the original directly // so the file is cleaned up without relying on the .trash mechanism try { await this.getDisk().delete(originalPath); } catch (_) { } } } } async rollbackMoveFileForDelete() { if (this.options && this.options.rename !== true) { const trashPath = this.path; this.name = this.name.replace('.trash', ''); const originalPath = this.path; this.originalPath = originalPath; await this.getDisk().move(trashPath, originalPath); } } async remove() { await super.remove(); if (this.variants) { for (const key in this.variants) { if (Object.prototype.hasOwnProperty.call(this.variants, key)) { await this.variants[key].remove(); } } // Try deleteAll to clean up empty directory (fs only), ignore errors on S3 providers const variantPath = this.variants[0].folder; try { await this.getDisk().deleteAll(variantPath); } catch (_) { } } } /** * */ toObject() { const variants = this.variants?.map((v) => v.toObject()); return { ...super.toObject(), disk: this.disk, originalName: this.originalName, variants, }; } toJSON() { const data = { keyId: this.getKeyId(), name: this.name, originalName: this.originalName, size: this.size, extname: this.extname, mimeType: this.mimeType, meta: this.meta, }; if (this.variants) { this.variants.map(async (v) => { data[v.key] = { name: v.name, extname: v.extname, mimeType: v.mimeType, meta: v.meta, size: v.size, blurhash: v.blurhash, }; }); } if (this.url) { data.url = this.url; } if (this.variants) { this.variants.map(async (v) => { if (v.url) { data[v.key].url = v.url; } }); } return data; } }