110 lines
3.8 KiB
JavaScript
110 lines
3.8 KiB
JavaScript
import sharp from 'sharp';
|
|
import { encodeRGBA } from 'skelehash-wasm';
|
|
import { DELETE_BY_ARTICLE_AND_HASH as MSSQL_QUERY } from '../../providers/mssql/queries/pic.js';
|
|
import { DELETE_BY_ARTICLE_AND_HASH as SQLITE_QUERY } from '../../providers/sqlite3/queries/pic.js';
|
|
import resolveArticleBySku from './resolveArticleBySku.js';
|
|
import { recordImageDeleteHistory } from '../articlehistory/record.js';
|
|
import makeRunner from '../runner.js';
|
|
|
|
export default function deleteImage(provider) {
|
|
const resolve = resolveArticleBySku(provider);
|
|
const sql = provider?.dialect === 'sqlite3' ? SQLITE_QUERY : MSSQL_QUERY;
|
|
|
|
/**
|
|
* Deletes an image from an article by its SKU and full image hash.
|
|
*
|
|
* @param {number} googleAuthId - The id of the Google auth user.
|
|
* @param {string} sku - The article SKU.
|
|
* @param {string} fullHash - The full image hash to delete.
|
|
* @param {object} [tx] - Optional transaction.
|
|
* @returns {Promise<void>}
|
|
* @throws {Error} If the image is not found.
|
|
*
|
|
* @example
|
|
* await store.article.deleteImage(42, 'SKU-1', 'abc123');
|
|
*/
|
|
return async (googleAuthId, sku, fullHash, tx) => {
|
|
const runDelete = async (runner) => {
|
|
const { articleId, companyuserId } = await resolve(googleAuthId, sku, runner);
|
|
|
|
let thumbBuffer;
|
|
if (provider?.dialect === 'sqlite3') {
|
|
const exists = await runner.query(
|
|
'SELECT ap.id FROM articlepic ap INNER JOIN pic p ON p.id = ap.pic_id WHERE ap.article_id = @articleId AND p.hash = @fullHash',
|
|
{ articleId, fullHash },
|
|
);
|
|
if (!exists.rows[0]) {
|
|
throw new Error('Image not found');
|
|
}
|
|
thumbBuffer = await (runner.readPicThumb ? runner.readPicThumb(fullHash) : provider.readPicThumb?.(fullHash));
|
|
} else {
|
|
const picResult = await runner.query(
|
|
'SELECT p.thumbbinary FROM pic p INNER JOIN articlepic ap ON ap.pic_id = p.id WHERE ap.article_id = @articleId AND p.hash = @fullHash',
|
|
{ articleId, fullHash },
|
|
);
|
|
if (!picResult.rows[0]) {
|
|
throw new Error('Image not found');
|
|
}
|
|
thumbBuffer = picResult.rows[0]?.thumbbinary;
|
|
}
|
|
|
|
let skelehash = null;
|
|
if (thumbBuffer) {
|
|
const { data, info } = await sharp(thumbBuffer).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
const res = await encodeRGBA(info.width, info.height, data);
|
|
skelehash = res?.base62;
|
|
}
|
|
|
|
if (!skelehash) {
|
|
const historyRows = await runner.query(
|
|
'SELECT meta FROM articlehistory WHERE article_id = @articleId AND meta LIKE @pattern',
|
|
{ articleId, pattern: `%"${fullHash}"%` },
|
|
);
|
|
for (const row of historyRows.rows || []) {
|
|
try {
|
|
const m = typeof row.meta === 'string' ? JSON.parse(row.meta) : row.meta;
|
|
if (m?.fullHash === fullHash && m?.skelehash) {
|
|
skelehash = m.skelehash;
|
|
break;
|
|
}
|
|
} catch {}
|
|
}
|
|
}
|
|
|
|
if (!skelehash) {
|
|
throw new Error(`Failed to resolve skelehash for image ${fullHash}`);
|
|
}
|
|
|
|
const result = await runner.query(sql, { articleId, fullHash });
|
|
|
|
if (!result.rowsAffected) {
|
|
throw new Error('Image not found');
|
|
}
|
|
|
|
if (provider?.dialect === 'sqlite3') {
|
|
if (runner.deletePic) {
|
|
await runner.deletePic(fullHash);
|
|
} else if (provider?.deletePic) {
|
|
await provider.deletePic(fullHash);
|
|
}
|
|
}
|
|
|
|
await recordImageDeleteHistory(runner, {
|
|
articleId,
|
|
companyuserId,
|
|
fullHash,
|
|
skelehash,
|
|
});
|
|
};
|
|
|
|
if (tx) {
|
|
return runDelete(makeRunner(provider, tx));
|
|
}
|
|
if (typeof provider?.transaction === 'function') {
|
|
return provider.transaction(runDelete);
|
|
}
|
|
return runDelete(makeRunner(provider));
|
|
};
|
|
}
|
|
|