import { ADD as MSSQL_ADD_ARTICLEPIC, GET_NEXT_SORT as MSSQL_GET_NEXT_SORT, } from '../../providers/mssql/queries/articlepic.js'; import { ADD as SQLITE_ADD_ARTICLEPIC, GET_NEXT_SORT as SQLITE_GET_NEXT_SORT, } from '../../providers/sqlite3/queries/articlepic.js'; import { CREATE as MSSQL_CREATE_PIC } from '../../providers/mssql/queries/pic.js'; import { CREATE as SQLITE_CREATE_PIC } from '../../providers/sqlite3/queries/pic.js'; import processAvifImage from '../../util/processAvifImage.js'; import newFullHash from '../../util/newFullHash.js'; import resolveArticleBySku from './resolveArticleBySku.js'; import makeRunner from '../runner.js'; export default function addImage(provider) { const resolve = resolveArticleBySku(provider); const isSqlite = provider?.dialect === 'sqlite3'; const getSortSql = isSqlite ? SQLITE_GET_NEXT_SORT : MSSQL_GET_NEXT_SORT; const createPicSql = isSqlite ? SQLITE_CREATE_PIC : MSSQL_CREATE_PIC; const addArticlePicSql = isSqlite ? SQLITE_ADD_ARTICLEPIC : MSSQL_ADD_ARTICLEPIC; /** * Adds an image to an article by its SKU, processing full, thumb, and 32px-edge small-thumb AVIF variants. * * @param {number} googleAuthId - The id of the Google auth user. * @param {string} sku - The article SKU. * @param {Buffer} image - The source image buffer. * @param {object} [tx] - Optional transaction to run within. * @returns {Promise<{picId: number, sort: number, full: {w: number, h: number, size: number}, thumb: {w: number, h: number}, smallThumb: {w: number, h: number}, fullHash: string, thumbHash: string}>} The added image info. * * @example * const result = await store.article.addImage(42, 'SKU-1', imageBuffer); * // => { picId, sort, full: { w, h, size }, thumb: { w, h }, smallThumb: { w, h }, fullHash, thumbHash } */ return async (googleAuthId, sku, image, tx) => { const runAdd = async (runner) => { const { articleId } = await resolve(googleAuthId, sku, runner); const processed = await processAvifImage(image); const sortResult = await runner.query(getSortSql, { articleId }); const sort = sortResult.rows[0].sort; const fullHash = newFullHash(processed.full.buffer); let picId; const existingPic = await runner.query( 'SELECT id FROM pic WHERE hash = @fullHash', { fullHash }, ); if (existingPic.rows[0]) { picId = existingPic.rows[0].id; } else { if (isSqlite) { if (runner.writePicFull) { await runner.writePicFull(fullHash, processed.full.buffer); await runner.writePicThumb(fullHash, processed.thumb.buffer); } else if (provider?.writePicFull) { await provider.writePicFull(fullHash, processed.full.buffer); await provider.writePicThumb(fullHash, processed.thumb.buffer); } } const picResult = await runner.query(createPicSql, { w: processed.full.w, h: processed.full.h, size: processed.full.size, hash: fullHash, thumbw: processed.thumb.w, thumbh: processed.thumb.h, thumbhash: processed.thumb.hash, smallthumbw: processed.smallThumb.w, smallthumbh: processed.smallThumb.h, smallthumbbinary: processed.smallThumb.buffer, source: '', fullbinary: processed.full.buffer, thumbbinary: processed.thumb.buffer, }); picId = picResult.rows[0]?.id ?? picResult.insertId; } const existingArticlePic = await runner.query( 'SELECT id, sort FROM articlepic WHERE article_id = @articleId AND pic_id = @picId', { articleId, picId }, ); let effectiveSort = sort; if (existingArticlePic.rows[0]) { effectiveSort = existingArticlePic.rows[0].sort; } else { await runner.query(addArticlePicSql, { articleId, picId, sort, }); } return { picId, sort: effectiveSort, full: { w: processed.full.w, h: processed.full.h, size: processed.full.size, }, thumb: { w: processed.thumb.w, h: processed.thumb.h, }, smallThumb: { w: processed.smallThumb.w, h: processed.smallThumb.h, }, fullHash, thumbHash: processed.thumb.hash, }; }; if (tx) { return runAdd(makeRunner(provider, tx)); } if (typeof provider?.transaction === 'function') { return provider.transaction(runAdd); } return runAdd(makeRunner(provider)); }; }