file meta
This commit is contained in:
@@ -782,7 +782,8 @@ BEGIN
|
||||
content VARBINARY(MAX) NOT NULL,
|
||||
thumbavif VARBINARY(MAX) NULL,
|
||||
thumbx INT NULL,
|
||||
thumby INT NULL
|
||||
thumby INT NULL,
|
||||
meta NVARCHAR(MAX) NULL
|
||||
);
|
||||
END
|
||||
`;
|
||||
|
||||
@@ -74,28 +74,44 @@ DELETE FROM manifestarticle
|
||||
WHERE manifest_id = @manifestId
|
||||
`;
|
||||
|
||||
export const CREATE_LINE = `
|
||||
INSERT INTO manifestarticle (
|
||||
manifest_id,
|
||||
sku,
|
||||
name,
|
||||
pu,
|
||||
amount,
|
||||
net,
|
||||
comment,
|
||||
rebate,
|
||||
taxrate
|
||||
)
|
||||
OUTPUT INSERTED.id
|
||||
VALUES (
|
||||
@manifestId,
|
||||
@sku,
|
||||
@name,
|
||||
@pu,
|
||||
@amount,
|
||||
@net,
|
||||
@comment,
|
||||
@rebate,
|
||||
@taxrate
|
||||
)
|
||||
export const GET_NEXT_ATTACHMENT_SORT = `
|
||||
SELECT ISNULL(MAX(ma.sort), -1) + 1 AS sort
|
||||
FROM manifestattachment ma
|
||||
WHERE ma.manifest_id = @manifestId
|
||||
`;
|
||||
|
||||
export const CREATE_FILE = `
|
||||
INSERT INTO [file] (hash, mimetype, name, size, created, modified, content, thumbavif, thumbx, thumby, meta)
|
||||
OUTPUT INSERTED.id
|
||||
VALUES (@hash, @mimetype, @name, @size, @created, @modified, @content, CONVERT(VARBINARY(MAX), @thumbavif), @thumbx, @thumby, @meta)
|
||||
`;
|
||||
|
||||
export const CREATE_ATTACHMENT = `
|
||||
INSERT INTO manifestattachment (manifest_id, file_id, sort)
|
||||
OUTPUT INSERTED.id
|
||||
VALUES (@manifestId, @fileId, @sort)
|
||||
`;
|
||||
|
||||
export const LIST_DELIVERY_FILES = `
|
||||
SELECT
|
||||
ma.id,
|
||||
ma.manifest_id AS manifestId,
|
||||
ma.file_id AS fileId,
|
||||
ma.sort,
|
||||
f.hash,
|
||||
f.mimetype,
|
||||
f.name,
|
||||
f.size,
|
||||
f.created,
|
||||
f.modified,
|
||||
f.thumbavif,
|
||||
f.thumbx,
|
||||
f.thumby,
|
||||
f.meta
|
||||
FROM manifestattachment ma
|
||||
INNER JOIN [file] f ON f.id = ma.file_id
|
||||
INNER JOIN manifest m ON m.id = ma.manifest_id
|
||||
WHERE m.company_id = @companyId
|
||||
AND m.[type] = @type
|
||||
ORDER BY ma.manifest_id, ma.sort ASC, ma.id ASC
|
||||
`;
|
||||
|
||||
@@ -5,9 +5,9 @@ WHERE id = @manifestId AND company_id = @companyId
|
||||
`;
|
||||
|
||||
export const CREATE_FILE = `
|
||||
INSERT INTO [file] (hash, mimetype, name, size, created, modified, content, thumbavif, thumbx, thumby)
|
||||
INSERT INTO [file] (hash, mimetype, name, size, created, modified, content, thumbavif, thumbx, thumby, meta)
|
||||
OUTPUT INSERTED.id
|
||||
VALUES (@hash, @mimetype, @name, @size, @created, @modified, @content, CONVERT(VARBINARY(MAX), @thumbavif), @thumbx, @thumby)
|
||||
VALUES (@hash, @mimetype, @name, @size, @created, @modified, @content, CONVERT(VARBINARY(MAX), @thumbavif), @thumbx, @thumby, @meta)
|
||||
`;
|
||||
|
||||
export const GET_NEXT_SORT = `
|
||||
@@ -36,7 +36,8 @@ SELECT
|
||||
f.modified,
|
||||
f.thumbavif,
|
||||
f.thumbx,
|
||||
f.thumby
|
||||
f.thumby,
|
||||
f.meta
|
||||
FROM manifestattachment ma
|
||||
INNER JOIN [file] f ON f.id = ma.file_id
|
||||
WHERE ma.manifest_id = @manifestId
|
||||
@@ -58,7 +59,8 @@ SELECT
|
||||
f.content,
|
||||
f.thumbavif,
|
||||
f.thumbx,
|
||||
f.thumby
|
||||
f.thumby,
|
||||
f.meta
|
||||
FROM manifestattachment ma
|
||||
INNER JOIN [file] f ON f.id = ma.file_id
|
||||
WHERE ma.manifest_id = @manifestId
|
||||
|
||||
172
src/store/delivery/attachments.js
Normal file
172
src/store/delivery/attachments.js
Normal file
@@ -0,0 +1,172 @@
|
||||
import { createHash } from 'node:crypto';
|
||||
import { LIST_BY_MANIFEST } from '../../sql/manifestattachment.js';
|
||||
import request from '../request.js';
|
||||
|
||||
import { parseMeta } from '../manifestattachment/listByManifestId.js';
|
||||
|
||||
const ALLOWED_MIMETYPES = new Set([
|
||||
'application/pdf',
|
||||
'text/plain',
|
||||
]);
|
||||
|
||||
export function shapeAttachmentMeta(row) {
|
||||
return {
|
||||
id: row.id,
|
||||
manifestId: row.manifestId,
|
||||
fileId: row.fileId,
|
||||
sort: row.sort,
|
||||
hash: row.hash,
|
||||
mimetype: row.mimetype,
|
||||
name: row.name,
|
||||
size: Number(row.size),
|
||||
created: row.created,
|
||||
modified: row.modified,
|
||||
thumbavif: row.thumbavif ?? null,
|
||||
thumbx: row.thumbx ?? null,
|
||||
thumby: row.thumby ?? null,
|
||||
meta: parseMeta(row.meta),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads attachment metadata (without binary content) for a manifest.
|
||||
*/
|
||||
export async function loadAttachments(pool, tx, manifestId) {
|
||||
const result = await request(pool, tx)
|
||||
.input('manifestId', manifestId)
|
||||
.query(LIST_BY_MANIFEST);
|
||||
|
||||
return result.recordset.map(shapeAttachmentMeta);
|
||||
}
|
||||
|
||||
function normalizeAttachment(raw, index) {
|
||||
if (typeof raw !== 'object' || raw === null) {
|
||||
throw new Error('Invalid file');
|
||||
}
|
||||
|
||||
const mimetype =
|
||||
typeof raw.mimetype === 'string' ? raw.mimetype.trim().toLowerCase() : '';
|
||||
if (!ALLOWED_MIMETYPES.has(mimetype)) {
|
||||
throw new Error('Invalid file');
|
||||
}
|
||||
|
||||
const name = typeof raw.name === 'string' ? raw.name.trim() : '';
|
||||
if (!name) {
|
||||
throw new Error('Invalid file');
|
||||
}
|
||||
|
||||
if (raw.content == null) {
|
||||
throw new Error('Invalid file');
|
||||
}
|
||||
const content = Buffer.isBuffer(raw.content)
|
||||
? raw.content
|
||||
: Buffer.from(raw.content);
|
||||
|
||||
const size = Number.isFinite(raw.size) && raw.size >= 0
|
||||
? Number(raw.size)
|
||||
: content.length;
|
||||
|
||||
const hash =
|
||||
typeof raw.hash === 'string' && raw.hash.trim()
|
||||
? raw.hash.trim()
|
||||
: createHash('sha256').update(content).digest('hex');
|
||||
|
||||
const parseDate = (value) => {
|
||||
if (value instanceof Date && !isNaN(value)) return value;
|
||||
if (typeof value === 'string' || typeof value === 'number') {
|
||||
const parsed = new Date(value);
|
||||
return isNaN(parsed) ? null : parsed;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const created = parseDate(raw.created ?? raw.createdAt) ?? new Date();
|
||||
const modified = parseDate(raw.modified ?? raw.modifiedAt) ?? created;
|
||||
|
||||
let meta = null;
|
||||
if (typeof raw.meta === 'object' && raw.meta !== null && !Array.isArray(raw.meta)) {
|
||||
meta = {};
|
||||
for (const [key, value] of Object.entries(raw.meta)) {
|
||||
if (value === undefined || value === null) continue;
|
||||
meta[key] = String(value);
|
||||
}
|
||||
if (Object.keys(meta).length === 0) {
|
||||
meta = null;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
mimetype,
|
||||
name,
|
||||
content,
|
||||
size,
|
||||
hash,
|
||||
created,
|
||||
modified,
|
||||
meta,
|
||||
sort: Number.isFinite(raw.sort) ? Number(raw.sort) : null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts pdf/txt attachments for a delivery (manifest) inside an existing
|
||||
* transaction. Sort orders continue after the current maximum.
|
||||
*
|
||||
* @param {object} pool
|
||||
* @param {object} tx - Active transaction.
|
||||
* @param {object} sql - SQL statements from delivery.js.
|
||||
* @param {number} manifestId
|
||||
* @param {Array<{mimetype: string, name: string, content: Buffer, sort?: number, size?: number, created?: Date|string, modified?: Date|string, hash?: string}>} [files]
|
||||
* @returns {Promise<Array<object>>} Attachment metadata rows.
|
||||
*/
|
||||
export async function insertAttachments(pool, tx, sql, manifestId, files) {
|
||||
const attachments = (files ?? []).map(normalizeAttachment);
|
||||
|
||||
const sortRes = await request(pool, tx)
|
||||
.input('manifestId', manifestId)
|
||||
.query(sql.GET_NEXT_ATTACHMENT_SORT);
|
||||
let nextSort = sortRes.recordset[0].sort;
|
||||
|
||||
const results = [];
|
||||
for (const a of attachments) {
|
||||
const sort = a.sort ?? nextSort;
|
||||
nextSort = Math.max(nextSort, sort) + 1;
|
||||
|
||||
const fileRes = await request(pool, tx)
|
||||
.input('hash', a.hash)
|
||||
.input('mimetype', a.mimetype)
|
||||
.input('name', a.name)
|
||||
.input('size', a.size)
|
||||
.input('created', a.created)
|
||||
.input('modified', a.modified)
|
||||
.input('content', a.content)
|
||||
.input('thumbavif', null)
|
||||
.input('thumbx', null)
|
||||
.input('thumby', null)
|
||||
.input('meta', a.meta === null ? null : JSON.stringify(a.meta))
|
||||
.query(sql.CREATE_FILE);
|
||||
|
||||
const fileId = fileRes.recordset[0].id;
|
||||
|
||||
await request(pool, tx)
|
||||
.input('manifestId', manifestId)
|
||||
.input('fileId', fileId)
|
||||
.input('sort', sort)
|
||||
.query(sql.CREATE_ATTACHMENT);
|
||||
|
||||
results.push({
|
||||
manifestId,
|
||||
fileId,
|
||||
sort,
|
||||
hash: a.hash,
|
||||
mimetype: a.mimetype,
|
||||
name: a.name,
|
||||
size: a.size,
|
||||
created: a.created,
|
||||
modified: a.modified,
|
||||
meta: a.meta,
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
@@ -1,10 +1,17 @@
|
||||
import { CREATE_MANIFEST, DELIVERY_TYPE } from '../../sql/delivery.js';
|
||||
import {
|
||||
CREATE_MANIFEST,
|
||||
CREATE_FILE,
|
||||
CREATE_ATTACHMENT,
|
||||
GET_NEXT_ATTACHMENT_SORT,
|
||||
DELIVERY_TYPE,
|
||||
} from '../../sql/delivery.js';
|
||||
import request from '../request.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import nextNumber from '../numbers/nextNumber.js';
|
||||
import taxEnsure from '../tax/ensure.js';
|
||||
import { insertLines, normalizeLines, parseDate } from '../manifestLines.js';
|
||||
import { loadDelivery } from './helpers.js';
|
||||
import { insertAttachments } from './attachments.js';
|
||||
import { assertSupplier } from './writeLines.js';
|
||||
|
||||
export default function create(pool) {
|
||||
@@ -73,6 +80,18 @@ export default function create(pool) {
|
||||
ensureTax,
|
||||
});
|
||||
|
||||
await insertAttachments(
|
||||
pool,
|
||||
tx,
|
||||
{
|
||||
GET_NEXT_ATTACHMENT_SORT,
|
||||
CREATE_FILE,
|
||||
CREATE_ATTACHMENT,
|
||||
},
|
||||
manifestId,
|
||||
body.files,
|
||||
);
|
||||
|
||||
const delivery = await loadDelivery(pool, tx, companyId, manifestId);
|
||||
await tx.commit();
|
||||
return delivery;
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from '../../sql/delivery.js';
|
||||
import request from '../request.js';
|
||||
import { shapeLine } from '../manifestLines.js';
|
||||
import { loadAttachments } from './attachments.js';
|
||||
|
||||
export function shapeSummary(row) {
|
||||
return {
|
||||
@@ -33,8 +34,11 @@ export async function loadDelivery(pool, tx, companyId, id) {
|
||||
.input('manifestId', id)
|
||||
.query(GET_DELIVERY_LINES);
|
||||
|
||||
const files = await loadAttachments(pool, tx, id);
|
||||
|
||||
return {
|
||||
...shapeSummary(row),
|
||||
lines: lines.recordset.map(shapeLine),
|
||||
files,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { DELIVERY_TYPE, LIST_DELIVERIES } from '../../sql/delivery.js';
|
||||
import { DELIVERY_TYPE, LIST_DELIVERIES, LIST_DELIVERY_FILES } from '../../sql/delivery.js';
|
||||
import request from '../request.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import { shapeSummary } from './helpers.js';
|
||||
import { shapeAttachmentMeta } from './attachments.js';
|
||||
|
||||
export default function list(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
@@ -24,6 +26,22 @@ export default function list(pool) {
|
||||
.input('type', DELIVERY_TYPE)
|
||||
.query(LIST_DELIVERIES);
|
||||
|
||||
return result.recordset.map(shapeSummary);
|
||||
const filesResult = await pool
|
||||
.request()
|
||||
.input('companyId', companyId)
|
||||
.input('type', DELIVERY_TYPE)
|
||||
.query(LIST_DELIVERY_FILES);
|
||||
|
||||
const filesByManifest = new Map();
|
||||
for (const row of filesResult.recordset) {
|
||||
const list = filesByManifest.get(row.manifestId) ?? [];
|
||||
list.push(shapeAttachmentMeta(row));
|
||||
filesByManifest.set(row.manifestId, list);
|
||||
}
|
||||
|
||||
return result.recordset.map((row) => ({
|
||||
...shapeSummary(row),
|
||||
files: filesByManifest.get(row.id) ?? [],
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ import {
|
||||
DELETE_LINES_BY_MANIFEST,
|
||||
DELIVERY_TYPE,
|
||||
GET_DELIVERY_HEADER,
|
||||
GET_NEXT_ATTACHMENT_SORT,
|
||||
CREATE_FILE,
|
||||
CREATE_ATTACHMENT,
|
||||
UPDATE_MANIFEST,
|
||||
} from '../../sql/delivery.js';
|
||||
import request from '../request.js';
|
||||
@@ -9,6 +12,7 @@ import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyA
|
||||
import taxEnsure from '../tax/ensure.js';
|
||||
import { insertLines, normalizeLines, parseDate } from '../manifestLines.js';
|
||||
import { loadDelivery, shapeSummary } from './helpers.js';
|
||||
import { insertAttachments } from './attachments.js';
|
||||
import { assertSupplier } from './writeLines.js';
|
||||
|
||||
export default function update(pool) {
|
||||
@@ -46,8 +50,9 @@ export default function update(pool) {
|
||||
);
|
||||
const hasExternalNumber = Object.prototype.hasOwnProperty.call(patch, 'externalNumber');
|
||||
const hasLines = Object.prototype.hasOwnProperty.call(patch, 'lines');
|
||||
const hasFiles = Object.prototype.hasOwnProperty.call(patch, 'files');
|
||||
|
||||
if (!hasNumber && !hasDate && !hasSupplierId && !hasExternalNumber && !hasLines) {
|
||||
if (!hasNumber && !hasDate && !hasSupplierId && !hasExternalNumber && !hasLines && !hasFiles) {
|
||||
throw new Error('Invalid delivery');
|
||||
}
|
||||
|
||||
@@ -130,6 +135,20 @@ export default function update(pool) {
|
||||
});
|
||||
}
|
||||
|
||||
if (hasFiles) {
|
||||
await insertAttachments(
|
||||
pool,
|
||||
tx,
|
||||
{
|
||||
GET_NEXT_ATTACHMENT_SORT,
|
||||
CREATE_FILE,
|
||||
CREATE_ATTACHMENT,
|
||||
},
|
||||
id,
|
||||
patch.files,
|
||||
);
|
||||
}
|
||||
|
||||
const delivery = await loadDelivery(pool, tx, companyId, id);
|
||||
await tx.commit();
|
||||
return delivery;
|
||||
|
||||
@@ -30,7 +30,8 @@ export default function add(pool) {
|
||||
* @param {Buffer} [input.thumbavif] - Optional AVIF thumbnail buffer.
|
||||
* @param {number} [input.thumbx] - Optional thumbnail width.
|
||||
* @param {number} [input.thumby] - Optional thumbnail height.
|
||||
* @returns {Promise<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, size: number, created: Date, modified: Date, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null}>} The created attachment record.
|
||||
* @param {object} [input.meta] - Optional string key/value metadata stored on the file row.
|
||||
* @returns {Promise<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, size: number, created: Date, modified: Date, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null, meta: object|null}>} The created attachment record.
|
||||
* @throws {Error} If the manifest is not found or file input is invalid.
|
||||
*
|
||||
* @example
|
||||
@@ -42,7 +43,7 @@ export default function add(pool) {
|
||||
* // => { id, manifestId, fileId, sort, hash, mimetype, name, size, created, modified, thumbavif, thumbx, thumby }
|
||||
*/
|
||||
return async (googleAuthId, manifestId, input, ...rest) => {
|
||||
let rawMimetype, rawName, rawContent, rawHash, rawThumbavif, rawThumbx, rawThumby, rawSort, rawSize, rawCreated, rawModified;
|
||||
let rawMimetype, rawName, rawContent, rawHash, rawThumbavif, rawThumbx, rawThumby, rawSort, rawSize, rawCreated, rawModified, rawMeta;
|
||||
if (typeof input === 'object' && input !== null && !Buffer.isBuffer(input)) {
|
||||
rawMimetype = input.mimetype;
|
||||
rawName = input.name;
|
||||
@@ -55,6 +56,7 @@ export default function add(pool) {
|
||||
rawSize = input.size;
|
||||
rawCreated = input.created ?? input.createdAt;
|
||||
rawModified = input.modified ?? input.modifiedAt ?? input.lastModified;
|
||||
rawMeta = input.meta;
|
||||
} else {
|
||||
rawMimetype = input;
|
||||
rawName = rest[0];
|
||||
@@ -105,6 +107,19 @@ export default function add(pool) {
|
||||
: null;
|
||||
const modified = parsedModified && !isNaN(parsedModified) ? parsedModified : created;
|
||||
|
||||
// Simple string key/value metadata; only plain string values are kept.
|
||||
let meta = null;
|
||||
if (typeof rawMeta === 'object' && rawMeta !== null && !Array.isArray(rawMeta)) {
|
||||
meta = {};
|
||||
for (const [key, value] of Object.entries(rawMeta)) {
|
||||
if (value === undefined || value === null) continue;
|
||||
meta[key] = String(value);
|
||||
}
|
||||
if (Object.keys(meta).length === 0) {
|
||||
meta = null;
|
||||
}
|
||||
}
|
||||
|
||||
const hash =
|
||||
typeof rawHash === 'string' && rawHash.trim()
|
||||
? rawHash.trim()
|
||||
@@ -168,6 +183,7 @@ export default function add(pool) {
|
||||
.input('thumbavif', thumbavif)
|
||||
.input('thumbx', thumbx)
|
||||
.input('thumby', thumby)
|
||||
.input('meta', meta === null ? null : JSON.stringify(meta))
|
||||
.query(CREATE_FILE);
|
||||
|
||||
const fileId = fileRes.recordset[0].id;
|
||||
@@ -196,6 +212,7 @@ export default function add(pool) {
|
||||
thumbavif,
|
||||
thumbx,
|
||||
thumby,
|
||||
meta,
|
||||
};
|
||||
} catch (err) {
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { GET_BY_ID } from '../../sql/manifestattachment.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import resolveManifestAccess from './resolveManifestAccess.js';
|
||||
import { parseMeta } from './listByManifestId.js';
|
||||
import request from '../request.js';
|
||||
|
||||
export default function getById(pool) {
|
||||
@@ -13,7 +14,7 @@ export default function getById(pool) {
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} manifestId - The manifest ID.
|
||||
* @param {number} id - The attachment ID or file ID.
|
||||
* @returns {Promise<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, size: number, created: Date, modified: Date, content: Buffer, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null}>} The attachment record with binary content.
|
||||
* @returns {Promise<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, size: number, created: Date, modified: Date, content: Buffer, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null, meta: object|null}>} The attachment record with binary content.
|
||||
* @throws {Error} If the manifest or attachment is not found, or user is not authorized.
|
||||
*
|
||||
* @example
|
||||
@@ -53,6 +54,7 @@ export default function getById(pool) {
|
||||
thumbavif: row.thumbavif ?? null,
|
||||
thumbx: row.thumbx ?? null,
|
||||
thumby: row.thumby ?? null,
|
||||
meta: parseMeta(row.meta),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,6 +3,18 @@ import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyA
|
||||
import resolveManifestAccess from './resolveManifestAccess.js';
|
||||
import request from '../request.js';
|
||||
|
||||
/** Parse the file.meta JSON text column into an object (string key/value). */
|
||||
export function parseMeta(raw) {
|
||||
if (raw == null) return null;
|
||||
if (typeof raw === 'object') return raw;
|
||||
try {
|
||||
const parsed = JSON.parse(raw);
|
||||
return typeof parsed === 'object' && parsed !== null ? parsed : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function shape(row) {
|
||||
return {
|
||||
id: row.id,
|
||||
@@ -18,6 +30,7 @@ function shape(row) {
|
||||
thumbavif: row.thumbavif ?? null,
|
||||
thumbx: row.thumbx ?? null,
|
||||
thumby: row.thumby ?? null,
|
||||
meta: parseMeta(row.meta),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -30,7 +43,7 @@ export default function listByManifestId(pool) {
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} manifestId - The manifest ID.
|
||||
* @returns {Promise<Array<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, size: number, created: Date, modified: Date, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null}>>} A list of attachment metadata objects.
|
||||
* @returns {Promise<Array<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, size: number, created: Date, modified: Date, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null, meta: object|null}>>} A list of attachment metadata objects.
|
||||
* @throws {Error} If the manifest is not found or the user is not authorized.
|
||||
*
|
||||
* @example
|
||||
|
||||
Reference in New Issue
Block a user