From 358e3009b29ff71744f5614aade18a9f0713caed Mon Sep 17 00:00:00 2001 From: seb Date: Sun, 25 Oct 2026 22:38:04 +0100 Subject: [PATCH] u --- API.md | 2 +- README.md | 4 +- generate-api-doc.js | 2 +- src/providers/mssql/index.js | 113 ++++++++++++++++++++++-- src/providers/mssql/queries/category.js | 2 +- src/providers/mssql/queries/pic.js | 10 +-- src/providers/mssql/schema.js | 4 +- src/providers/sqlite3/index.js | 90 ++----------------- src/providers/storage.js | 89 +++++++++++++++++++ src/store/article/addImage.js | 16 ++-- src/store/article/deleteImage.js | 36 +++----- src/store/attributevalue/addImage.js | 16 ++-- src/store/brand/addImage.js | 16 ++-- src/store/category/addImage.js | 16 ++-- src/store/category/generateImage.js | 14 +-- src/store/pic/getImageByHash.js | 7 +- src/store/pic/getThumbByHash.js | 7 +- 17 files changed, 255 insertions(+), 189 deletions(-) create mode 100644 src/providers/storage.js diff --git a/API.md b/API.md index 062c794..bc575df 100644 --- a/API.md +++ b/API.md @@ -62,7 +62,7 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met - **ordersummary**: `id PK, company_id->company, manifest_id->manifest UQ, order_number:s(100), order_date:dt, created_at:dt, entity_id?->entity, customer_name?:s(255), positions:i=0, gross_total:f=0, paid_total:f=0, ordered_quantity:f=0, shipped_quantity:f=0, is_paid:b=0, is_shipped:b=0` - **package**: `id PK, shipping_id->shipping, entityaddress_id->entityaddress, number:s(100), type:s(100), state:s(100), details?:txt` - **payment**: `id PK, account_id->account, manifest_id->manifest, type:s(100), amount:f` -- **pic**: `id PK, w:i, h:i, size:bi, hash:s(64) UQ, thumbw:i, thumbh:i, thumbhash:s(64), source:txt, fullbinary:bin, thumbbinary:bin` +- **pic**: `id PK, w:i, h:i, size:bi, hash:s(64) UQ, thumbw:i, thumbh:i, thumbhash:s(64), smallthumbw:i, smallthumbh:i, smallthumbbinary:bin, source:txt` - **posterminal**: `id PK, company_id->company, name:s(255), token?:s(255) UQ, pairingpin?:s(50), maxknownobituaryid?:i, root_category_id?->category, sync_root_articles:b=1, lastseen_at?:dt, updated_at:dt` - **posterminalcategory**: `id PK, posterminal_id->posterminal, category_id->category, UQ(posterminal_id, category_id)` - **posterminalobituary**: `id PK, posterminal_id->posterminal, entity_type:s(50), entity_id:i, created_at:dt` diff --git a/README.md b/README.md index e708342..035c488 100644 --- a/README.md +++ b/README.md @@ -563,7 +563,7 @@ Company-scoped POS terminals. Auth via selected company (`google_auth.selected_c `move` moves a category to a new parent. Both `id` (source) and `parentId` (target) are validated via `resolveCategoryAccess` — the target must be a real category of the same authorized company, else `Parent not found`. Moving into itself or into a descendant (cycle) → `Invalid parent`. The moved category gets the next sibling `sort` under the new parent. Returns `{ id, parentId, sort }`. For `POST /api/category/:id/move`. -`addImage` accepts a full-size AVIF `Buffer`. Full image: if already AVIF and ≤1000×1000, stored as-is; otherwise scaled to max 1000×1000 (`pic.fullbinary`). Thumb always scaled to max 200×200 (`pic.thumbbinary`) via `sharp`. Assigns random `fullHash` (`pic.hash`, unique), computes `thumbhash`, links `categorypic`. Auth via `resolveCategoryAccess`. Errors: `Category not found`, `Not authorized`. +`addImage` accepts a full-size AVIF `Buffer`. Full image: if already AVIF and ≤1000×1000, stored as-is; otherwise scaled to max 1000×1000 (`storageDir/fullbinary`). Thumb always scaled to max 200×200 (`storageDir/thumbbinary`) via `sharp`. Assigns random `fullHash` (`pic.hash`, unique), computes `thumbhash`, links `categorypic`. Auth via `resolveCategoryAccess`. Errors: `Category not found`, `Not authorized`. `addImage` returns: @@ -601,7 +601,7 @@ Company-scoped POS terminals. Auth via selected company (`google_auth.selected_c | `orderImages` | `(googleAuthId: string, sku: string, { fullHashes: string[] })` | `{ fullHashes: string[] }` | | `delete` | `(googleAuthId: string, sku: string)` | `void` | -`addImage` accepts a full-size AVIF `Buffer`. Full image: if already AVIF and ≤1000×1000, stored as-is; otherwise scaled to max 1000×1000 (`pic.fullbinary`). Thumb always scaled to max 200×200 (`pic.thumbbinary`) via `sharp`. Assigns random `fullHash` (`pic.hash`, unique), computes `thumbhash`, links `articlepic`. Auth like `getBySku`. Errors: `Article not found`, `Not authorized`. +`addImage` accepts a full-size AVIF `Buffer`. Full image: if already AVIF and ≤1000×1000, stored as-is; otherwise scaled to max 1000×1000 (`storageDir/fullbinary`). Thumb always scaled to max 200×200 (`storageDir/thumbbinary`) via `sharp`. Assigns random `fullHash` (`pic.hash`, unique), computes `thumbhash`, links `articlepic`. Auth like `getBySku`. Errors: `Article not found`, `Not authorized`. `addImage` returns `ImageResult`: diff --git a/generate-api-doc.js b/generate-api-doc.js index 7f5c939..56553a9 100644 --- a/generate-api-doc.js +++ b/generate-api-doc.js @@ -378,7 +378,7 @@ const SCHEMA_TABLES = [ ['ordersummary', 'id PK, company_id->company, manifest_id->manifest UQ, order_number:s(100), order_date:dt, created_at:dt, entity_id?->entity, customer_name?:s(255), positions:i=0, gross_total:f=0, paid_total:f=0, ordered_quantity:f=0, shipped_quantity:f=0, is_paid:b=0, is_shipped:b=0'], ['package', 'id PK, shipping_id->shipping, entityaddress_id->entityaddress, number:s(100), type:s(100), state:s(100), details?:txt'], ['payment', 'id PK, account_id->account, manifest_id->manifest, type:s(100), amount:f'], - ['pic', 'id PK, w:i, h:i, size:bi, hash:s(64) UQ, thumbw:i, thumbh:i, thumbhash:s(64), source:txt, fullbinary:bin, thumbbinary:bin'], + ['pic', 'id PK, w:i, h:i, size:bi, hash:s(64) UQ, thumbw:i, thumbh:i, thumbhash:s(64), smallthumbw:i, smallthumbh:i, smallthumbbinary:bin, source:txt'], ['posterminal', 'id PK, company_id->company, name:s(255), token?:s(255) UQ, pairingpin?:s(50), maxknownobituaryid?:i, root_category_id?->category, sync_root_articles:b=1, lastseen_at?:dt, updated_at:dt'], ['posterminalcategory', 'id PK, posterminal_id->posterminal, category_id->category, UQ(posterminal_id, category_id)'], ['posterminalobituary', 'id PK, posterminal_id->posterminal, entity_type:s(50), entity_id:i, created_at:dt'], diff --git a/src/providers/mssql/index.js b/src/providers/mssql/index.js index ec54c60..f936f6d 100644 --- a/src/providers/mssql/index.js +++ b/src/providers/mssql/index.js @@ -4,8 +4,11 @@ * { rows, rowsAffected, insertId } */ +import { join } from 'node:path'; +import { mkdirSync, promises as fsPromises } from 'node:fs'; import sql from 'mssql'; import { logSlowQuery } from '../provider.js'; +import { safeWrite, safeReadByHash, safeDeleteByHash } from '../storage.js'; function normalizeRows(result) { const recordset = result?.recordset ?? []; @@ -16,13 +19,57 @@ function normalizeRows(result) { } export class MssqlExecutor { - /** @param {() => import('mssql').Request} requestFactory */ - constructor(requestFactory) { + /** + * @param {() => import('mssql').Request} requestFactory + * @param {string|null} [storageDir=null] + */ + constructor(requestFactory, storageDir = null) { this.#requestFactory = requestFactory; + this.storageDir = storageDir; + this.dialect = 'mssql'; } #requestFactory; + async writePicFull(hash, buffer) { + if (!this.storageDir || !hash) return; + await safeWrite(join(this.storageDir, 'fullbinary', hash + '.avif'), buffer); + } + + async readPicFull(hash) { + if (!this.storageDir || !hash) return null; + return safeReadByHash(join(this.storageDir, 'fullbinary'), hash, '.avif'); + } + + async writePicThumb(hash, buffer) { + if (!this.storageDir || !hash) return; + await safeWrite(join(this.storageDir, 'thumbbinary', hash + '.avif'), buffer); + } + + async readPicThumb(hash) { + if (!this.storageDir || !hash) return null; + return safeReadByHash(join(this.storageDir, 'thumbbinary'), hash, '.avif'); + } + + async deletePic(hash) { + if (!this.storageDir || !hash) return; + await safeDeleteByHash(join(this.storageDir, 'fullbinary'), hash); + await safeDeleteByHash(join(this.storageDir, 'thumbbinary'), hash); + } + + async purgeFiles() { + if (!this.storageDir) return; + for (const sub of ['fullbinary', 'thumbbinary']) { + const dir = join(this.storageDir, sub); + try { + const files = await fsPromises.readdir(dir); + await Promise.all(files.map((f) => fsPromises.unlink(join(dir, f)))); + } catch (err) { + if (err.code !== 'ENOENT') throw err; + } + } + } + /** @returns {Promise} */ async query(sql, params = {}) { const start = performance.now(); @@ -73,8 +120,9 @@ export class MssqlProvider { * Either an already-connected mssql ConnectionPool, or a plain mssql * config object — in which case the pool is created lazily on first use * (and closed by {@link MssqlProvider#close}). + * @param {{ storageDir?: string|null }} [options] */ - constructor(poolOrConfig) { + constructor(poolOrConfig, { storageDir = null } = {}) { const isPool = poolOrConfig && typeof poolOrConfig.request === 'function'; if (!poolOrConfig || (!isPool && !poolOrConfig.server)) { @@ -87,6 +135,7 @@ export class MssqlProvider { // a different object after a lazy connection would leave them with null. this.pool = isPool ? poolOrConfig : new sql.ConnectionPool(poolOrConfig); this.#config = isPool ? null : poolOrConfig; + this.storageDir = storageDir || poolOrConfig?.storageDir || null; this.dialect = 'mssql'; } @@ -163,12 +212,12 @@ export class MssqlProvider { } makeExecutor(requestFactory = () => this.pool.request()) { - return new MssqlExecutor(requestFactory); + return new MssqlExecutor(requestFactory, this.storageDir); } async #withPool(fn) { const pool = await this.ensurePool(); - return fn(new MssqlExecutor(() => pool.request())); + return fn(new MssqlExecutor(() => pool.request(), this.storageDir)); } async query(sql, params = {}) { @@ -179,6 +228,45 @@ export class MssqlProvider { return this.#withPool((executor) => executor.exec(procName, options)); } + async writePicFull(hash, buffer) { + if (!this.storageDir || !hash) return; + await safeWrite(join(this.storageDir, 'fullbinary', hash + '.avif'), buffer); + } + + async readPicFull(hash) { + if (!this.storageDir || !hash) return null; + return safeReadByHash(join(this.storageDir, 'fullbinary'), hash, '.avif'); + } + + async writePicThumb(hash, buffer) { + if (!this.storageDir || !hash) return; + await safeWrite(join(this.storageDir, 'thumbbinary', hash + '.avif'), buffer); + } + + async readPicThumb(hash) { + if (!this.storageDir || !hash) return null; + return safeReadByHash(join(this.storageDir, 'thumbbinary'), hash, '.avif'); + } + + async deletePic(hash) { + if (!this.storageDir || !hash) return; + await safeDeleteByHash(join(this.storageDir, 'fullbinary'), hash); + await safeDeleteByHash(join(this.storageDir, 'thumbbinary'), hash); + } + + async purgeFiles() { + if (!this.storageDir) return; + for (const sub of ['fullbinary', 'thumbbinary']) { + const dir = join(this.storageDir, sub); + try { + const files = await fsPromises.readdir(dir); + await Promise.all(files.map((f) => fsPromises.unlink(join(dir, f)))); + } catch (err) { + if (err.code !== 'ENOENT') throw err; + } + } + } + /** * Runs `fn(runner)` inside a transaction. Commit on success, * rollback on error. `runner` has query/exec like the provider. @@ -197,6 +285,12 @@ export class MssqlProvider { runner.query = runner; runner.exec = (proc, opts) => executor.exec(proc, opts); runner.dialect = 'mssql'; + runner.writePicFull = executor.writePicFull.bind(executor); + runner.readPicFull = executor.readPicFull.bind(executor); + runner.writePicThumb = executor.writePicThumb.bind(executor); + runner.readPicThumb = executor.readPicThumb.bind(executor); + runner.deletePic = executor.deletePic.bind(executor); + runner.purgeFiles = executor.purgeFiles.bind(executor); const result = await fn(runner, tx); await tx.commit(); return result; @@ -260,12 +354,21 @@ export class MssqlProvider { } catch { // ignore } + + if (this.storageDir) { + for (const sub of ['fullbinary', 'thumbbinary']) { + mkdirSync(join(this.storageDir, sub), { recursive: true }); + } + } } async purge() { const pool = await this.ensurePool(); const { buildPurgeSql } = await import('./purgeSql.js'); await pool.request().query(buildPurgeSql()); + if (this.storageDir) { + await this.purgeFiles(); + } } close() { diff --git a/src/providers/mssql/queries/category.js b/src/providers/mssql/queries/category.js index 1818071..5c7f074 100644 --- a/src/providers/mssql/queries/category.js +++ b/src/providers/mssql/queries/category.js @@ -191,7 +191,7 @@ SELECT a.id AS articleId, p.id AS picId, p.hash, - p.fullbinary AS buffer + NULL AS buffer FROM categoryarticle ca INNER JOIN article a ON a.id = ca.article_id OUTER APPLY ( diff --git a/src/providers/mssql/queries/pic.js b/src/providers/mssql/queries/pic.js index 7d7c603..f193a0a 100644 --- a/src/providers/mssql/queries/pic.js +++ b/src/providers/mssql/queries/pic.js @@ -1,14 +1,12 @@ export const CREATE = ` INSERT INTO pic ( w, h, size, hash, thumbw, thumbh, thumbhash, - smallthumbw, smallthumbh, smallthumbbinary, - source, fullbinary, thumbbinary + smallthumbw, smallthumbh, smallthumbbinary, source ) OUTPUT INSERTED.id VALUES ( @w, @h, @size, @hash, @thumbw, @thumbh, @thumbhash, - @smallthumbw, @smallthumbh, @smallthumbbinary, - @source, @fullbinary, @thumbbinary + @smallthumbw, @smallthumbh, @smallthumbbinary, @source ) `; @@ -60,13 +58,13 @@ WHERE categoryId IS NOT NULL `; export const GET_FULL_BY_HASH = ` -SELECT w, h, size, fullbinary AS buffer +SELECT w, h, size, hash FROM pic WHERE hash = @hash `; export const GET_THUMB_BY_HASH = ` -SELECT thumbw AS w, thumbh AS h, thumbbinary AS buffer +SELECT thumbw AS w, thumbh AS h, hash FROM pic WHERE hash = @hash `; diff --git a/src/providers/mssql/schema.js b/src/providers/mssql/schema.js index ddd236d..e31697c 100644 --- a/src/providers/mssql/schema.js +++ b/src/providers/mssql/schema.js @@ -97,10 +97,8 @@ BEGIN thumbhash NVARCHAR(64) NOT NULL, smallthumbw INT NOT NULL, smallthumbh INT NOT NULL, - source NVARCHAR(MAX) NOT NULL, - fullbinary VARBINARY(MAX) NOT NULL, - thumbbinary VARBINARY(MAX) NOT NULL, smallthumbbinary VARBINARY(MAX) NOT NULL, + source NVARCHAR(MAX) NOT NULL, CONSTRAINT UQ_pic_hash UNIQUE (hash) ); END diff --git a/src/providers/sqlite3/index.js b/src/providers/sqlite3/index.js index b3e4d1c..9b288ef 100644 --- a/src/providers/sqlite3/index.js +++ b/src/providers/sqlite3/index.js @@ -51,89 +51,15 @@ function normalizeInsertId(value) { import { dirname, join } from 'node:path'; import { mkdirSync, mkdtempSync, promises as fsPromises } from 'node:fs'; import { tmpdir } from 'node:os'; +import { + extFromMimetype, + safeWrite, + findFileByHash, + safeReadByHash, + safeDeleteByHash, +} from '../storage.js'; -export function extFromMimetype(mimetype) { - if (!mimetype) return ''; - const type = mimetype.trim().toLowerCase().split(';')[0]; - const map = { - 'application/pdf': '.pdf', - 'text/plain': '.txt', - 'text/csv': '.csv', - 'text/html': '.html', - 'text/markdown': '.md', - 'image/avif': '.avif', - 'image/png': '.png', - 'image/jpeg': '.jpg', - 'image/jpg': '.jpg', - 'image/webp': '.webp', - 'image/gif': '.gif', - 'image/svg+xml': '.svg', - 'application/json': '.json', - 'application/zip': '.zip', - 'application/xml': '.xml', - 'application/octet-stream': '.bin', - }; - if (map[type]) return map[type]; - if (type.startsWith('image/')) return '.' + type.slice(6).replace('+xml', ''); - if (type.startsWith('text/')) return '.' + type.slice(5); - return ''; -} - -async function safeWrite(filePath, buffer) { - if (buffer == null) return; - const buf = Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer); - await fsPromises.writeFile(filePath, buf); -} - -async function findFileByHash(dir, hash, ext = '') { - if (!dir || !hash) return null; - if (ext) { - const candidate = join(dir, hash + ext); - try { - await fsPromises.access(candidate); - return candidate; - } catch { - // fallback - } - } - const candidateNoExt = join(dir, hash); - try { - await fsPromises.access(candidateNoExt); - return candidateNoExt; - } catch { - // fallback - } - try { - const files = await fsPromises.readdir(dir); - const match = files.find((f) => f === hash || f.startsWith(hash + '.')); - if (match) return join(dir, match); - } catch { - // ignore - } - return null; -} - -async function safeReadByHash(dir, hash, ext = '') { - const filePath = await findFileByHash(dir, hash, ext); - if (!filePath) return null; - try { - return await fsPromises.readFile(filePath); - } catch (err) { - if (err.code === 'ENOENT') return null; - throw err; - } -} - -async function safeDeleteByHash(dir, hash) { - if (!dir || !hash) return; - try { - const files = await fsPromises.readdir(dir); - const matches = files.filter((f) => f === hash || f.startsWith(hash + '.')); - await Promise.all(matches.map((f) => fsPromises.unlink(join(dir, f)))); - } catch (err) { - if (err.code !== 'ENOENT') throw err; - } -} +export { extFromMimetype }; export class BetterSqlite3Executor { /** diff --git a/src/providers/storage.js b/src/providers/storage.js new file mode 100644 index 0000000..1413e6b --- /dev/null +++ b/src/providers/storage.js @@ -0,0 +1,89 @@ +/** + * Shared filesystem storage utilities for database providers. + */ + +import { join } from 'node:path'; +import { promises as fsPromises } from 'node:fs'; + +export async function safeWrite(filePath, buffer) { + if (buffer == null) return; + const buf = Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer); + await fsPromises.writeFile(filePath, buf); +} + +export async function findFileByHash(dir, hash, ext = '') { + if (!dir || !hash) return null; + if (ext) { + const candidate = join(dir, hash + ext); + try { + await fsPromises.access(candidate); + return candidate; + } catch { + // fallback + } + } + const candidateNoExt = join(dir, hash); + try { + await fsPromises.access(candidateNoExt); + return candidateNoExt; + } catch { + // fallback + } + try { + const files = await fsPromises.readdir(dir); + const match = files.find((f) => f === hash || f.startsWith(hash + '.')); + if (match) return join(dir, match); + } catch { + // ignore + } + return null; +} + +export async function safeReadByHash(dir, hash, ext = '') { + const filePath = await findFileByHash(dir, hash, ext); + if (!filePath) return null; + try { + return await fsPromises.readFile(filePath); + } catch (err) { + if (err.code === 'ENOENT') return null; + throw err; + } +} + +export async function safeDeleteByHash(dir, hash) { + if (!dir || !hash) return; + try { + const files = await fsPromises.readdir(dir); + const matches = files.filter((f) => f === hash || f.startsWith(hash + '.')); + await Promise.all(matches.map((f) => fsPromises.unlink(join(dir, f)))); + } catch (err) { + if (err.code !== 'ENOENT') throw err; + } +} + +export function extFromMimetype(mimetype) { + if (!mimetype) return ''; + const type = mimetype.trim().toLowerCase().split(';')[0]; + const map = { + 'application/pdf': '.pdf', + 'text/plain': '.txt', + 'text/csv': '.csv', + 'text/html': '.html', + 'text/markdown': '.md', + 'image/avif': '.avif', + 'image/png': '.png', + 'image/jpeg': '.jpg', + 'image/jpg': '.jpg', + 'image/webp': '.webp', + 'image/gif': '.gif', + 'image/svg+xml': '.svg', + 'application/json': '.json', + 'application/zip': '.zip', + 'application/xml': '.xml', + 'application/octet-stream': '.bin', + }; + if (map[type]) return map[type]; + if (type.startsWith('image/')) return '.' + type.slice(6).replace('+xml', ''); + if (type.startsWith('text/')) return '.' + type.slice(5); + return ''; +} diff --git a/src/store/article/addImage.js b/src/store/article/addImage.js index 8a9eee5..44cf632 100644 --- a/src/store/article/addImage.js +++ b/src/store/article/addImage.js @@ -57,14 +57,12 @@ export default function addImage(provider) { 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); - } + 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, { @@ -79,8 +77,6 @@ export default function addImage(provider) { smallthumbh: processed.smallThumb.h, smallthumbbinary: processed.smallThumb.buffer, source: '', - fullbinary: processed.full.buffer, - thumbbinary: processed.thumb.buffer, }); picId = picResult.rows[0]?.id ?? picResult.insertId; diff --git a/src/store/article/deleteImage.js b/src/store/article/deleteImage.js index 1b30557..d35a418 100644 --- a/src/store/article/deleteImage.js +++ b/src/store/article/deleteImage.js @@ -30,26 +30,14 @@ export default function deleteImage(provider) { 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; + 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'); } + const thumbBuffer = await (runner.readPicThumb ? runner.readPicThumb(fullHash) : provider.readPicThumb?.(fullHash)); let skelehash = null; if (thumbBuffer) { @@ -84,12 +72,10 @@ export default function deleteImage(provider) { 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); - } + if (runner.deletePic) { + await runner.deletePic(fullHash); + } else if (provider?.deletePic) { + await provider.deletePic(fullHash); } await recordImageDeleteHistory(runner, { diff --git a/src/store/attributevalue/addImage.js b/src/store/attributevalue/addImage.js index 3e01ecc..6137757 100644 --- a/src/store/attributevalue/addImage.js +++ b/src/store/attributevalue/addImage.js @@ -51,14 +51,12 @@ export default function addImage(provider) { 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); - } + 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, { @@ -73,8 +71,6 @@ export default function addImage(provider) { smallthumbh: processed.smallThumb.h, smallthumbbinary: processed.smallThumb.buffer, source: '', - fullbinary: processed.full.buffer, - thumbbinary: processed.thumb.buffer, }); picId = picResult.rows[0]?.id ?? picResult.insertId; diff --git a/src/store/brand/addImage.js b/src/store/brand/addImage.js index bea472f..e462f63 100644 --- a/src/store/brand/addImage.js +++ b/src/store/brand/addImage.js @@ -51,14 +51,12 @@ export default function addImage(provider) { 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); - } + 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, { @@ -73,8 +71,6 @@ export default function addImage(provider) { smallthumbh: processed.smallThumb.h, smallthumbbinary: processed.smallThumb.buffer, source: '', - fullbinary: processed.full.buffer, - thumbbinary: processed.thumb.buffer, }); picId = picResult.rows[0]?.id ?? picResult.insertId; diff --git a/src/store/category/addImage.js b/src/store/category/addImage.js index c2ff104..f7bad74 100644 --- a/src/store/category/addImage.js +++ b/src/store/category/addImage.js @@ -56,14 +56,12 @@ export default function addImage(provider) { 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); - } + 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, { @@ -78,8 +76,6 @@ export default function addImage(provider) { smallthumbh: processed.smallThumb.h, smallthumbbinary: processed.smallThumb.buffer, source: '', - fullbinary: processed.full.buffer, - thumbbinary: processed.thumb.buffer, }); picId = picResult.rows[0]?.id ?? picResult.insertId; diff --git a/src/store/category/generateImage.js b/src/store/category/generateImage.js index 32cc192..caff921 100644 --- a/src/store/category/generateImage.js +++ b/src/store/category/generateImage.js @@ -123,17 +123,9 @@ export default function generateImage(provider) { selected.map(async (item) => { let buf = item.buffer; if (!buf || !buf.length) { - if (isSqlite) { - buf = runner.readPicFull ? await runner.readPicFull(item.hash) : await provider?.readPicFull?.(item.hash); - if (!buf) { - buf = runner.readPicThumb ? await runner.readPicThumb(item.hash) : await provider?.readPicThumb?.(item.hash); - } - } else { - const picRes = await runner.query( - 'SELECT fullbinary, thumbbinary FROM pic WHERE hash = @hash', - { hash: item.hash }, - ); - buf = picRes.rows[0]?.fullbinary ?? picRes.rows[0]?.thumbbinary; + buf = runner.readPicFull ? await runner.readPicFull(item.hash) : await provider?.readPicFull?.(item.hash); + if (!buf) { + buf = runner.readPicThumb ? await runner.readPicThumb(item.hash) : await provider?.readPicThumb?.(item.hash); } } return { ...item, buffer: buf }; diff --git a/src/store/pic/getImageByHash.js b/src/store/pic/getImageByHash.js index 3bfbf76..ddc07bb 100644 --- a/src/store/pic/getImageByHash.js +++ b/src/store/pic/getImageByHash.js @@ -31,12 +31,7 @@ export default function getImageByHash(provider) { throw new Error('Image not found'); } - let buffer; - if (provider?.dialect === 'sqlite3') { - buffer = await (runner.readPicFull ? runner.readPicFull(fullHash) : provider.readPicFull?.(fullHash)); - } else { - buffer = row.buffer ?? row.fullbinary; - } + const buffer = await (runner.readPicFull ? runner.readPicFull(fullHash) : provider.readPicFull?.(fullHash)); if (!buffer) { throw new Error('Image not found'); diff --git a/src/store/pic/getThumbByHash.js b/src/store/pic/getThumbByHash.js index 3decade..bfb0172 100644 --- a/src/store/pic/getThumbByHash.js +++ b/src/store/pic/getThumbByHash.js @@ -31,12 +31,7 @@ export default function getThumbByHash(provider) { throw new Error('Image not found'); } - let buffer; - if (provider?.dialect === 'sqlite3') { - buffer = await (runner.readPicThumb ? runner.readPicThumb(fullHash) : provider.readPicThumb?.(fullHash)); - } else { - buffer = row.buffer ?? row.thumbbinary; - } + const buffer = await (runner.readPicThumb ? runner.readPicThumb(fullHash) : provider.readPicThumb?.(fullHash)); if (!buffer) { throw new Error('Image not found');