diff --git a/client/src/components/CardModal/Attachments/Attachments.jsx b/client/src/components/CardModal/Attachments/Attachments.jsx index 4ca211b0..4e0c85b1 100644 --- a/client/src/components/CardModal/Attachments/Attachments.jsx +++ b/client/src/components/CardModal/Attachments/Attachments.jsx @@ -57,11 +57,8 @@ const Attachments = React.memo( }, [toggleAllVisible]); const galleryItemsNode = items.map((item, index) => { - const props = item.coverUrl - ? { - width: item.imageWidth, - height: item.imageHeight, - } + const props = item.image + ? item.image : { content: ( @@ -91,7 +88,7 @@ const Attachments = React.memo( createdAt={item.createdAt} isCover={item.isCover} isPersisted={item.isPersisted} - onClick={item.coverUrl ? open : undefined} + onClick={item.image ? open : undefined} onCoverSelect={() => handleCoverSelect(item.id)} onCoverDeselect={handleCoverDeselect} onUpdate={(data) => handleUpdate(item.id, data)} diff --git a/client/src/models/Attachment.js b/client/src/models/Attachment.js index 521721a4..acd13b4c 100644 --- a/client/src/models/Attachment.js +++ b/client/src/models/Attachment.js @@ -9,6 +9,7 @@ export default class extends Model { id: attr(), url: attr(), coverUrl: attr(), + image: attr(), name: attr(), cardId: fk({ to: 'Card', diff --git a/server/api/controllers/attachments/create.js b/server/api/controllers/attachments/create.js index a36dd437..a6347356 100644 --- a/server/api/controllers/attachments/create.js +++ b/server/api/controllers/attachments/create.js @@ -54,10 +54,8 @@ module.exports = { const attachment = await sails.helpers.attachments.createOne( { - dirname: file.extra.dirname, + ...file.extra, filename: file.filename, - isImage: file.extra.isImage, - name: file.extra.name, }, currentUser, card, diff --git a/server/api/controllers/attachments/download-thumbnail.js b/server/api/controllers/attachments/download-thumbnail.js index 0d7444b9..9da37b70 100644 --- a/server/api/controllers/attachments/download-thumbnail.js +++ b/server/api/controllers/attachments/download-thumbnail.js @@ -46,7 +46,7 @@ module.exports = { } } - if (!attachment.isImage) { + if (!attachment.image) { throw Errors.ATTACHMENT_NOT_FOUND; } diff --git a/server/api/controllers/attachments/download.js b/server/api/controllers/attachments/download.js index 871f7501..631ace2c 100644 --- a/server/api/controllers/attachments/download.js +++ b/server/api/controllers/attachments/download.js @@ -53,7 +53,7 @@ module.exports = { } this.res.type(attachment.filename); - if (!attachment.isImage && path.extname(attachment.filename) !== '.pdf') { + if (!attachment.image && path.extname(attachment.filename) !== '.pdf') { this.res.set('Content-Disposition', 'attachment'); } this.res.set('Cache-Control', 'private, max-age=900'); // TODO: move to config diff --git a/server/api/helpers/attachments/create-one.js b/server/api/helpers/attachments/create-one.js index 23e879a5..70e19143 100644 --- a/server/api/helpers/attachments/create-one.js +++ b/server/api/helpers/attachments/create-one.js @@ -38,7 +38,7 @@ module.exports = { inputs.request, ); - if (!inputs.card.coverAttachmentId && attachment.isImage) { + if (!inputs.card.coverAttachmentId && attachment.image) { await sails.helpers.cards.updateOne(inputs.card, { coverAttachmentId: attachment.id, }); diff --git a/server/api/helpers/utils/create-attachment-receiver.js b/server/api/helpers/utils/create-attachment-receiver.js index 9420c1be..93827348 100644 --- a/server/api/helpers/utils/create-attachment-receiver.js +++ b/server/api/helpers/utils/create-attachment-receiver.js @@ -43,15 +43,20 @@ module.exports = { await writeFile(path.join(rootPath, filename), buffer); const image = sharp(buffer); - let imageMetadata; + let metadata; try { - imageMetadata = await image.metadata(); + metadata = await image.metadata(); } catch (error) {} // eslint-disable-line no-empty - if (imageMetadata) { + const extra = { + dirname, + name: file.filename, + }; + + if (metadata && !['svg', 'pdf'].includes(metadata.format)) { let cover256Buffer; - if (imageMetadata.height > imageMetadata.width) { + if (metadata.height > metadata.width) { cover256Buffer = await image .resize(256, 320) .jpeg({ @@ -75,17 +80,16 @@ module.exports = { fs.mkdirSync(thumbnailsPath); await writeFile(path.join(thumbnailsPath, 'cover-256.jpg'), cover256Buffer); + + extra.image = _.pick(metadata, ['width', 'height']); + } else { + extra.image = null; } - // eslint-disable-next-line no-param-reassign - file.extra = { - dirname, - isImage: !!imageMetadata, - name: file.filename, - }; - - // eslint-disable-next-line no-param-reassign - file.filename = filename; + Object.assign(file, { + filename, + extra, + }); return done(); } catch (error) { diff --git a/server/api/models/Attachment.js b/server/api/models/Attachment.js index 93f5419a..e765b1d3 100644 --- a/server/api/models/Attachment.js +++ b/server/api/models/Attachment.js @@ -19,20 +19,8 @@ module.exports = { type: 'string', required: true, }, - isImage: { - type: 'boolean', - required: true, - columnName: 'is_image', - }, - imageWidth: { - type: 'number', - allowNull: true, - columnName: 'image_width', - }, - imageHeight: { - type: 'number', - allowNull: true, - columnName: 'image_height', + image: { + type: 'json', }, name: { type: 'string', @@ -61,9 +49,9 @@ module.exports = { customToJSON() { return { - ..._.omit(this, ['dirname', 'filename', 'isImage']), + ..._.omit(this, ['dirname', 'filename']), url: `${sails.config.custom.attachmentsUrl}/${this.id}/download/${this.filename}`, - coverUrl: this.isImage + coverUrl: this.image ? `${sails.config.custom.attachmentsUrl}/${this.id}/download/thumbnails/cover-256.jpg` : null, }; diff --git a/server/db/migrations/20220523131229_add_image_sizes_to_attachment_table.js b/server/db/migrations/20220523131229_add_image_sizes_to_attachment_table.js deleted file mode 100644 index 6d8f8ce1..00000000 --- a/server/db/migrations/20220523131229_add_image_sizes_to_attachment_table.js +++ /dev/null @@ -1,45 +0,0 @@ -const path = require('path'); -const sharp = require('sharp'); - -const getConfig = require('../../get-config'); - -module.exports.up = async (knex) => { - await knex.schema.table('attachment', (table) => { - /* Columns */ - - table.integer('image_width'); - table.integer('image_height'); - }); - - const config = await getConfig(); - const attachments = await knex('attachment'); - - // eslint-disable-next-line no-restricted-syntax - for (attachment of attachments) { - if (attachment.is_image) { - const image = sharp( - path.join(config.custom.attachmentsPath, attachment.dirname, attachment.filename), - ); - - let metadata; - try { - metadata = await image.metadata(); // eslint-disable-line no-await-in-loop - } catch (error) { - continue; // eslint-disable-line no-continue - } - - // eslint-disable-next-line no-await-in-loop - await knex('attachment') - .update({ - image_width: metadata.width, - image_height: metadata.height, - }) - .where('id', attachment.id); - } - } -}; - -module.exports.down = (knex) => - knex.schema.table('attachment', (table) => { - table.dropColumns('image_width', 'image_height'); - }); diff --git a/server/db/migrations/20220523131229_add_image_to_attachment_table.js b/server/db/migrations/20220523131229_add_image_to_attachment_table.js new file mode 100644 index 00000000..cf88e5d4 --- /dev/null +++ b/server/db/migrations/20220523131229_add_image_to_attachment_table.js @@ -0,0 +1,73 @@ +const path = require('path'); +const sharp = require('sharp'); +const _ = require('lodash'); + +const getConfig = require('../../get-config'); + +module.exports.up = async (knex) => { + await knex.schema.table('attachment', (table) => { + /* Columns */ + + table.jsonb('image'); + }); + + const config = await getConfig(); + const attachments = await knex('attachment'); + + // eslint-disable-next-line no-restricted-syntax + for (attachment of attachments) { + if (attachment.is_image) { + const image = sharp( + path.join(config.custom.attachmentsPath, attachment.dirname, attachment.filename), + ); + + let metadata; + try { + metadata = await image.metadata(); // eslint-disable-line no-await-in-loop + } catch (error) { + continue; // eslint-disable-line no-continue + } + + if (!['svg', 'pdf'].includes(metadata.format)) { + // eslint-disable-next-line no-await-in-loop + await knex('attachment') + .update({ + image: _.pick(metadata, ['width', 'height']), + }) + .where('id', attachment.id); + } + } + } + + return knex.schema.table('attachment', (table) => { + table.dropColumn('is_image'); + }); +}; + +module.exports.down = async (knex) => { + await knex.schema.table('attachment', (table) => { + /* Columns */ + + table.boolean('is_image'); + }); + + const attachments = await knex('attachment'); + + // eslint-disable-next-line no-restricted-syntax + for (attachment of attachments) { + // eslint-disable-next-line no-await-in-loop + await knex('attachment') + .update({ + is_image: !!attachment.image, + }) + .where('id', attachment.id); + } + + await knex.schema.table('attachment', (table) => { + table.dropColumn('image'); + }); + + return knex.schema.alterTable('attachment', (table) => { + table.boolean('is_image').notNullable().alter(); + }); +};