@jrmc/adonis-attachment
Version:
Turn any field on your Lucid model to an attachment data type
234 lines (233 loc) • 7.26 kB
JavaScript
/**
* @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;
}
}