@jrmc/adonis-attachment
Version:
Turn any field on your Lucid model to an attachment data type
120 lines (119 loc) • 4.56 kB
JavaScript
import logger from '@adonisjs/core/services/logger';
import string from '@adonisjs/core/helpers/string';
import db from '@adonisjs/lucid/services/db';
import attachmentManager from '../services/main.js';
import * as errors from './errors.js';
import { streamToTempFile } from './utils/helpers.js';
export class ConverterManager {
#record;
#attributeName;
#options;
#filters;
constructor({ record, attributeName, options, filters }) {
this.#record = record;
this.#attributeName = attributeName;
this.#options = options;
this.#filters = filters;
}
async run() {
const attachments = this.#record.getAttachments({
attributeName: this.#attributeName,
});
const variants = [];
await this.#purge(attachments);
if (!attachments || !this.#options.variants || !this.#options.variants.length) {
return;
}
for await (const key of this.#options.variants) {
if (this.#filters?.variants !== undefined && !this.#filters?.variants?.includes(key)) {
continue;
}
const converter = (await attachmentManager.getConverter(key));
if (converter) {
for await (const attachment of attachments) {
const variant = await this.#generate({
key,
attachment,
converter
});
variants.push(variant);
}
}
}
return this.#commit(attachments, () => {
for (let i = 0; i < variants.length; i++) {
attachmentManager.remove(variants[i]);
}
});
}
async #generate({ key, attachment, converter }) {
let input = attachment.input;
if (!input) {
input = await streamToTempFile(await attachment.getStream());
}
const output = await converter.handle({
input,
options: converter.options,
});
if (output === undefined) {
throw new errors.E_CANNOT_PATH_BY_CONVERTER();
}
const variant = await attachment.createVariant(key, output);
if (converter.options.blurhash) {
if ((typeof converter.options.blurhash !== 'boolean' &&
converter.options.blurhash.enabled === true) ||
converter.options.blurhash === true) {
try {
const options = typeof converter.options.blurhash !== 'boolean'
? converter.options.blurhash
: undefined;
await variant.generateBlurhash(options);
}
catch (error) {
logger.error(error.message);
}
}
}
await attachmentManager.write(variant);
return variant;
}
async #commit(attachments, rollback) {
const Model = this.#record.row.constructor;
const id = this.#record.row.$attributes['id'];
const data = {};
const index = string.snakeCase(this.#attributeName);
if (Array.isArray(this.#record.row.$original[this.#attributeName])) {
data[index] = JSON.stringify(attachments.map((att) => att.toObject()));
}
else {
data[index] = JSON.stringify(attachments[0].toObject());
}
const trx = await db.transaction();
trx.after('rollback', rollback);
try {
await trx.query().from(Model.table).where('id', id).update(data);
return trx.commit();
}
catch (error) {
return trx.rollback();
}
}
async #purge(attachments) {
return Promise.all(attachments.map(async (attachment) => {
if (attachment.variants?.length) {
await Promise.all(attachment.variants.map(async (variant) => {
if (this.#filters?.variants !== undefined && !this.#filters?.variants?.includes(variant.key)) {
return;
}
return attachmentManager.remove(variant);
}));
if (this.#filters?.variants !== undefined) {
attachment.variants = await attachment.variants.filter((variant) => !this.#filters?.variants?.includes(variant.key));
}
else {
attachment.variants = [];
}
}
}));
}
}