From 8f9c94169937e9e44fb4242dbbe321bf0a258177 Mon Sep 17 00:00:00 2001 From: seb Date: Sun, 1 Nov 2026 05:37:15 +0100 Subject: [PATCH] u --- docs/api/routes-all.md | 8 +-- docs/api/routes.md | 8 +-- docs/api/routes/article/_sku/print.md | 4 +- docs/api/routes/article/set-prices.md | 10 ++-- docs/api/routes/labeltemplate/testprint.md | 4 +- src/api/agent/index.js | 6 +-- src/api/index.js | 2 +- src/api/middleware/agentAuth.js | 17 ++----- src/api/routes/article/attributes.js | 14 ++---- src/api/routes/article/media.js | 12 +---- src/api/routes/article/shared.js | 57 ++++++++++++++++++---- src/api/routes/article/variants.js | 8 ++- src/api/routes/articleproperty.js | 2 +- src/api/routes/backup.js | 4 +- src/api/routes/category.js | 4 +- src/api/routes/categoryproperty.js | 2 +- src/api/routes/labeltemplate.js | 28 ++++------- src/api/routes/ledger.js | 8 +-- src/api/routes/posterminal.js | 18 +++---- src/api/routes/printerjob.js | 4 +- src/api/routes/supplier.js | 4 +- src/api/routes/vfs.js | 6 +-- src/backendmonitor/server.js | 6 +-- src/services/backupService.js | 30 +++++------- src/services/companyPrinterService.js | 10 ++-- src/tools/edit-image.js | 8 +-- src/tools/javascript.js | 23 ++++----- src/tools/patch.js | 14 +----- src/tools/pdf-read.js | 10 +--- src/tools/search-images.js | 13 +---- src/tools/vfs.js | 8 --- src/util/manifestAttachment.js | 3 +- 32 files changed, 145 insertions(+), 210 deletions(-) diff --git a/docs/api/routes-all.md b/docs/api/routes-all.md index 4d955af..785c236 100644 --- a/docs/api/routes-all.md +++ b/docs/api/routes-all.md @@ -182,11 +182,11 @@ _none_ #### Toggle parent mode for an article — `POST /article/{sku}/parent` - Params: `sku: str, body: { isParent: bool, attributeNameId?: int }` - Returns: `204 void` -#### Replace POS prices by SKU (net > gross; price=net, tax=taxRate %) — `PUT /article/{sku}/prices` -- Params: `sku: str, body: { prices: { net?: num, gross?: num, price?: num, taxRate?: num, tax?: num, minmultiplier?: num }[] }` +#### Replace POS prices by SKU (net > gross) — `PUT /article/{sku}/prices` +- Params: `sku: str, body: { prices: { net?: num, gross?: num, taxRate?: num, minmultiplier?: num }[] }` - Returns: `204 void` #### Print article label — `POST /article/{sku}/print` -- Params: `sku: str, body: { templateId: int, printerId: int, quantity: int }` +- Params: `sku: str, body: { templateId: int, agentprinterId: int, quantity: int }` - Returns: `200 { success: bool }` #### List article properties — `GET /article/{sku}/property` - Params: `sku: str` @@ -559,7 +559,7 @@ _none_ - Params: `body: { name: str, width: num, height: num, dpi: int, type: str, content: { objects: T25[] } }` - Returns: `201 { labeltemplate: T9 }` #### Test print label template — `POST /labeltemplate/testprint` -- Params: `body: { printerId?: int, agentprinterId?: int, templateId?: int, objects?: any[], width?: int, height?: int, dpi?: int, sku?: str, quantity?: int }` +- Params: `body: { agentprinterId: int, templateId?: int, objects?: any[], width?: int, height?: int, dpi?: int, sku?: str, quantity?: int }` - Returns: `200 { success: bool }` #### Delete label template — `DELETE /labeltemplate/{id}` - Params: `id: int` diff --git a/docs/api/routes.md b/docs/api/routes.md index fe97b8a..790cb13 100644 --- a/docs/api/routes.md +++ b/docs/api/routes.md @@ -145,11 +145,11 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body). #### Toggle parent mode for an article — `POST /article/{sku}/parent` - Params: `sku: str, body: { isParent: bool, attributeNameId?: int }` - Returns: `204 void` -#### Replace POS prices by SKU (net > gross; price=net, tax=taxRate %) — `PUT /article/{sku}/prices` -- Params: `sku: str, body: { prices: { net?: num, gross?: num, price?: num, taxRate?: num, tax?: num, minmultiplier?: num }[] }` +#### Replace POS prices by SKU (net > gross) — `PUT /article/{sku}/prices` +- Params: `sku: str, body: { prices: { net?: num, gross?: num, taxRate?: num, minmultiplier?: num }[] }` - Returns: `204 void` #### Print article label — `POST /article/{sku}/print` -- Params: `sku: str, body: { templateId: int, printerId: int, quantity: int }` +- Params: `sku: str, body: { templateId: int, agentprinterId: int, quantity: int }` - Returns: `200 { success: bool }` #### List article properties — `GET /article/{sku}/property` - Params: `sku: str` @@ -431,7 +431,7 @@ _none_ - Params: `body: { name: str, width: num, height: num, dpi: int, type: str, content: { objects: T19[] } }` - Returns: `201 { labeltemplate: T6 }` #### Test print label template — `POST /labeltemplate/testprint` -- Params: `body: { printerId?: int, agentprinterId?: int, templateId?: int, objects?: any[], width?: int, height?: int, dpi?: int, sku?: str, quantity?: int }` +- Params: `body: { agentprinterId: int, templateId?: int, objects?: any[], width?: int, height?: int, dpi?: int, sku?: str, quantity?: int }` - Returns: `200 { success: bool }` #### Delete label template — `DELETE /labeltemplate/{id}` - Params: `id: int` diff --git a/docs/api/routes/article/_sku/print.md b/docs/api/routes/article/_sku/print.md index 7e5bcbf..e4b3ba9 100644 --- a/docs/api/routes/article/_sku/print.md +++ b/docs/api/routes/article/_sku/print.md @@ -17,13 +17,13 @@ Prints a label for the article using a label template. | Field | Required | Type | Description | |-------|----------|------|-------------| | `templateId` | yes | integer | | -| `printerId` | yes | integer | | +| `agentprinterId` | yes | integer | | | `quantity` | yes | integer | | ```json { "templateId": 1, - "printerId": 1, + "agentprinterId": 1, "quantity": 1 } ``` diff --git a/docs/api/routes/article/set-prices.md b/docs/api/routes/article/set-prices.md index e660920..49641c0 100644 --- a/docs/api/routes/article/set-prices.md +++ b/docs/api/routes/article/set-prices.md @@ -2,11 +2,11 @@ Protected. Requires a valid session cookie (`credentials: 'include'`). -Replace POS prices by SKU (net > gross; price=net, tax=taxRate %) +Replace POS prices by SKU (net > gross) Replace-all prices for the company POS customer group. -`net` takes precedence over `price` (legacy alias for net) and `gross`. -`tax` and `taxRate` both specify the tax rate percentage (e.g. 19 for 19%), not monetary tax. +`net` takes precedence over `gross`. +`taxRate` specifies the tax rate percentage (e.g. 19 for 19%), not monetary tax. When `gross` is provided, net price is computed as `gross / (1 + taxRate / 100)`. Prices are strictly scoped to the company's designated POS customer group (customergroupId returned on GET). Empty `prices` clears all POS prices. Returns the same shape as GET by SKU. @@ -23,11 +23,9 @@ Empty `prices` clears all POS prices. Returns the same shape as GET by SKU. | Field | Required | Type | Description | |-------|----------|------|-------------| | `prices` | yes | object[] | | -| `prices[].net` | no | number | Net price (excl. VAT). Either `net`, `gross`, or legacy `price` is required. | +| `prices[].net` | no | number | Net price (excl. VAT). Either `net` or `gross` is required. | | `prices[].gross` | no | number | Gross price (incl. VAT). Net is computed as `gross / (1 + taxRate / 100)`. | -| `prices[].price` | no | number | Legacy alias for `net`. | | `prices[].taxRate` | no | number | Tax rate percentage (e.g. 19 for 19%, 7 for 7%, 0 for 0%). | -| `prices[].tax` | no | number | Alias for `taxRate` percentage. | | `prices[].minmultiplier` | no | number | Defaults to 1; must be unique per request. | ```json diff --git a/docs/api/routes/labeltemplate/testprint.md b/docs/api/routes/labeltemplate/testprint.md index 3c5f63f..0965c4f 100644 --- a/docs/api/routes/labeltemplate/testprint.md +++ b/docs/api/routes/labeltemplate/testprint.md @@ -12,8 +12,7 @@ Placeholders in text and barcodes are interpolated using a sample article or the | Field | Required | Type | Description | |-------|----------|------|-------------| -| `printerId` | no | integer | | -| `agentprinterId` | no | integer | | +| `agentprinterId` | yes | integer | Agent printer ID | | `templateId` | no | integer | | | `objects` | no | any[] | | | `width` | no | integer | | @@ -24,7 +23,6 @@ Placeholders in text and barcodes are interpolated using a sample article or the ```json { - "printerId": 1, "agentprinterId": 1, "templateId": 1, "objects": [], diff --git a/src/api/agent/index.js b/src/api/agent/index.js index d57d8b0..ad58899 100644 --- a/src/api/agent/index.js +++ b/src/api/agent/index.js @@ -367,7 +367,7 @@ agentRouter.get('/events', agentAuthMiddleware, async (req, res) => { */ async function handleState(req, res) { try { - const rawState = req.body?.state !== undefined ? req.body.state : (req.body?.clientstate !== undefined ? req.body.clientstate : req.body); + const rawState = req.body?.state; setAgentState(req.agent.id, rawState); const version = extractAgentVersion(req); @@ -377,9 +377,7 @@ async function handleState(req, res) { await db.touchLastseen(req.agent.id); } - const rawPrinters = Array.isArray(rawState?.printers) - ? rawState.printers - : (Array.isArray(req.body?.printers) ? req.body.printers : null); + const rawPrinters = Array.isArray(rawState?.printers) ? rawState.printers : null; let anyAgentPrinterCreated = false; if (rawPrinters && rawPrinters.length > 0) { diff --git a/src/api/index.js b/src/api/index.js index af50259..0d14750 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -106,7 +106,7 @@ function corsMiddleware(req, res, next) { const reqHeaders = req.headers['access-control-request-headers']; res.setHeader( 'Access-Control-Allow-Headers', - reqHeaders || 'Content-Type, Authorization, x-agent-token, x-auth-token, authtoken, auth-token, token, x-token, agentversion, clientversion, version' + reqHeaders || 'Content-Type, Authorization, x-agent-token, agentversion' ); res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE,OPTIONS'); if (req.method === 'OPTIONS') { diff --git a/src/api/middleware/agentAuth.js b/src/api/middleware/agentAuth.js index 1a37a15..4facade 100644 --- a/src/api/middleware/agentAuth.js +++ b/src/api/middleware/agentAuth.js @@ -10,16 +10,12 @@ export function extractAgentToken(req) { return trimmed; } - for (const key of ['x-agent-token', 'x-auth-token', 'authtoken', 'auth-token', 'token', 'x-token']) { - const val = req.headers[key]; - if (typeof val === 'string' && val.trim()) { - return val.trim(); - } + if (typeof req.headers['x-agent-token'] === 'string' && req.headers['x-agent-token'].trim()) { + return req.headers['x-agent-token'].trim(); } - const queryToken = req.query?.authToken || req.query?.token; - if (typeof queryToken === 'string' && queryToken.trim()) { - return queryToken.trim(); + if (typeof req.query?.token === 'string' && req.query.token.trim()) { + return req.query.token.trim(); } return ''; @@ -27,13 +23,8 @@ export function extractAgentToken(req) { export function extractAgentVersion(req) { const raw = req.headers['agentversion'] || - req.headers['agent-version'] || - req.headers['clientversion'] || - req.headers['version'] || req.query?.agentversion || - req.query?.version || req.body?.agentversion || - req.body?.version || null; return raw ? String(raw).trim() : null; } diff --git a/src/api/routes/article/attributes.js b/src/api/routes/article/attributes.js index e90adf7..ebf3c9a 100644 --- a/src/api/routes/article/attributes.js +++ b/src/api/routes/article/attributes.js @@ -17,11 +17,11 @@ export function register(app, basePath = '') { * /article/{sku}/prices: * put: * tags: [Article] - * summary: Replace POS prices by SKU (net > gross; price=net, tax=taxRate %) + * summary: Replace POS prices by SKU (net > gross) * description: | * Replace-all prices for the company POS customer group. - * `net` takes precedence over `price` (legacy alias for net) and `gross`. - * `tax` and `taxRate` both specify the tax rate percentage (e.g. 19 for 19%), not monetary tax. + * `net` takes precedence over `gross`. + * `taxRate` specifies the tax rate percentage (e.g. 19 for 19%), not monetary tax. * When `gross` is provided, net price is computed as `gross / (1 + taxRate / 100)`. * Prices are strictly scoped to the company's designated POS customer group (customergroupId returned on GET). * Empty `prices` clears all POS prices. Returns the same shape as GET by SKU. @@ -50,19 +50,13 @@ export function register(app, basePath = '') { * properties: * net: * type: number - * description: Net price (excl. VAT). Either `net`, `gross`, or legacy `price` is required. + * description: Net price (excl. VAT). Either `net` or `gross` is required. * gross: * type: number * description: Gross price (incl. VAT). Net is computed as `gross / (1 + taxRate / 100)`. - * price: - * type: number - * description: Legacy alias for `net`. * taxRate: * type: number * description: Tax rate percentage (e.g. 19 for 19%, 7 for 7%, 0 for 0%). - * tax: - * type: number - * description: Alias for `taxRate` percentage. * minmultiplier: * type: number * description: Defaults to 1; must be unique per request. diff --git a/src/api/routes/article/media.js b/src/api/routes/article/media.js index 89299d3..6ee463a 100644 --- a/src/api/routes/article/media.js +++ b/src/api/routes/article/media.js @@ -14,6 +14,7 @@ import { parseFullHash, articleErrorStatus, interpolateString, + interpolateObjects, } from './shared.js'; /** @@ -433,16 +434,7 @@ export function register(app, basePath = '') { ? content : []; - const interpolatedObjects = objects.map(obj => { - const cloned = { ...obj }; - if (typeof cloned.text === 'string') { - cloned.text = interpolateString(cloned.text, article); - } - if (typeof cloned.code === 'string') { - cloned.code = interpolateString(cloned.code, article); - } - return cloned; - }); + const interpolatedObjects = interpolateObjects(objects, article); const printPayload = await renderLabel({ width: template.width, diff --git a/src/api/routes/article/shared.js b/src/api/routes/article/shared.js index 7da8ebc..c8a0cd5 100644 --- a/src/api/routes/article/shared.js +++ b/src/api/routes/article/shared.js @@ -117,20 +117,57 @@ export function articleErrorStatus(error) { return 500; } +const CURRENCY_FORMATTER = new Intl.NumberFormat('de-DE', { + style: 'currency', + currency: 'EUR', +}); + +export function formatPrice(amount) { + if (amount == null || !Number.isFinite(Number(amount))) return ''; + return CURRENCY_FORMATTER.format(Number(amount)); +} + export function interpolateString(str, article) { if (typeof str !== 'string') return str; - const net = article.net_price ?? article.prices?.[0]?.net; - const taxRate = article.prices?.[0]?.taxRate ?? 0; - const gross = net != null ? +(net * (1 + taxRate / 100)).toFixed(2) : null; - const ean = (article.eans && article.eans.length > 0) ? article.eans[0] : ''; + const basePrice = Array.isArray(article?.prices) && article.prices.length > 0 + ? article.prices[0] + : null; + const net = basePrice?.net != null && Number.isFinite(Number(basePrice.net)) + ? Number(basePrice.net) + : null; + const taxRate = basePrice?.taxRate != null && Number.isFinite(Number(basePrice.taxRate)) + ? Number(basePrice.taxRate) + : 0; + const gross = net != null + ? Math.round((net * (1 + taxRate / 100) + Number.EPSILON) * 100) / 100 + : null; return str - .replace(/\{\{(?:ARTICLE_NAME|article_name|name)\}\}/gi, article.name || '') - .replace(/\{\{SKU\}\}/gi, article.sku || '') - .replace(/\{\{(?:NET_PRICE|net_price)\}\}/gi, net != null ? net.toString() : '') - .replace(/\{\{(?:GROSS_PRICE|gross_price)\}\}/gi, gross != null ? gross.toString() : '') - .replace(/\{\{(?:PRICE|price)\}\}/gi, gross != null ? gross.toString() : (net != null ? net.toString() : '')) - .replace(/\{\{(?:EAN|ean)\}\}/gi, ean); + .replace(/\{\{name\}\}/gi, article?.name || '') + .replace(/\{\{price\}\}/gi, formatPrice(gross)); +} + +export function interpolateObjects(objects, article) { + if (!Array.isArray(objects)) return []; + const ean = Array.isArray(article?.eans) && article.eans.length > 0 ? String(article.eans[0]) : ''; + + return objects.map((obj) => { + const cloned = { ...obj }; + if (typeof cloned.text === 'string') { + cloned.text = interpolateString(cloned.text, article); + } + if (cloned.type === 'barcode' || typeof cloned.code === 'string') { + const rawCode = String(cloned.code ?? '').trim(); + if (rawCode === '0' || /^0+$/.test(rawCode)) { + if (ean) { + cloned.code = ean; + } + } else if (typeof cloned.code === 'string') { + cloned.code = interpolateString(cloned.code, article); + } + } + return cloned; + }); } // #endregion diff --git a/src/api/routes/article/variants.js b/src/api/routes/article/variants.js index 2dbb5de..652193f 100644 --- a/src/api/routes/article/variants.js +++ b/src/api/routes/article/variants.js @@ -253,7 +253,7 @@ export function register(app, basePath = '') { const parsed = parseSku(req.params.sku); if (parsed.error) return res.status(400).json({ error: parsed.error }); - if (req.body?.sku || req.body?.childSku) { + if (req.body?.sku) { return res.status(400).json({ error: 'sku is not allowed when creating a child article; omit sku to auto-generate or use PUT to attach an existing article', }); @@ -282,9 +282,7 @@ export function register(app, basePath = '') { const parsed = parseSku(req.params.sku); if (parsed.error) return res.status(400).json({ error: parsed.error }); - const childSku = typeof req.body?.sku === 'string' && req.body.sku.trim() - ? req.body.sku.trim() - : (typeof req.body?.childSku === 'string' && req.body.childSku.trim() ? req.body.childSku.trim() : ''); + const childSku = typeof req.body?.sku === 'string' ? req.body.sku.trim() : ''; if (!childSku) { return res.status(400).json({ error: 'sku is required' }); } @@ -354,7 +352,7 @@ export function register(app, basePath = '') { const parsedChild = parseSku(req.params.childSku); if (parsedChild.error) return res.status(400).json({ error: 'childSku is required' }); - const deleteArticle = req.query.deleteArticle === 'true' || req.body?.deleteArticle === true; + const deleteArticle = req.query.deleteArticle === 'true'; try { const result = await db.removeChild(req.user.id, parsed.sku, parsedChild.sku, { diff --git a/src/api/routes/articleproperty.js b/src/api/routes/articleproperty.js index 6c264d7..8ca288d 100644 --- a/src/api/routes/articleproperty.js +++ b/src/api/routes/articleproperty.js @@ -141,7 +141,7 @@ export function register(app, basePath = '') { return res.status(400).json({ error: skuParsed.error }); } - const propertiesInput = req.body?.properties ?? req.body; + const propertiesInput = req.body?.properties; if (!Array.isArray(propertiesInput)) { return res.status(400).json({ error: 'properties array required' }); } diff --git a/src/api/routes/backup.js b/src/api/routes/backup.js index c3a8eed..1d3b4b1 100644 --- a/src/api/routes/backup.js +++ b/src/api/routes/backup.js @@ -252,12 +252,12 @@ export function register(app, basePath = '') { // #region POST /backup/:id/vfs/recover // MARK: handles POST /backup/:id/vfs/recover to restore a VFS file from a backup const handleRecover = async (req, res) => { - const filePath = req.params.path || req.body?.path || req.body?.file || req.body?.filename || req.query?.path; + const filePath = req.body?.path; if (!filePath) { return res.status(400).json({ error: 'File path is required' }); } - const requestedCompanyId = req.body?.companyId ?? req.query?.companyId; + const requestedCompanyId = req.body?.companyId; const authorized = await resolveAuthorizedCompany(req, requestedCompanyId); if (authorized.error) { return res.status(authorized.status).json({ error: authorized.error }); diff --git a/src/api/routes/category.js b/src/api/routes/category.js index c63a609..2dd5628 100644 --- a/src/api/routes/category.js +++ b/src/api/routes/category.js @@ -384,8 +384,6 @@ export function register(app, basePath = '') { let includeChildren = true; if (req.query.includeChildren !== undefined) { includeChildren = req.query.includeChildren !== 'false' && req.query.includeChildren !== '0'; - } else if (req.query.variants !== undefined) { - includeChildren = req.query.variants !== 'false' && req.query.variants !== '0'; } try { @@ -395,7 +393,7 @@ export function register(app, basePath = '') { .map((a) => ({ ...a, childCount: Number(a.childCount ?? 0), - isParent: Boolean(a.isParent ?? a.is_parent), + isParent: Boolean(a.isParent), smallThumbW: a.smallThumbW ?? null, smallThumbH: a.smallThumbH ?? null, smallThumbBinary: a.smallThumbBinary ?? null, diff --git a/src/api/routes/categoryproperty.js b/src/api/routes/categoryproperty.js index c090808..eda83fc 100644 --- a/src/api/routes/categoryproperty.js +++ b/src/api/routes/categoryproperty.js @@ -153,7 +153,7 @@ export function register(app, basePath = '') { return res.status(400).json({ error: idParsed.error }); } - const propertiesInput = req.body?.properties ?? req.body; + const propertiesInput = req.body?.properties; if (!Array.isArray(propertiesInput)) { return res.status(400).json({ error: 'properties array required' }); } diff --git a/src/api/routes/labeltemplate.js b/src/api/routes/labeltemplate.js index bcb31f3..3f1ee3d 100644 --- a/src/api/routes/labeltemplate.js +++ b/src/api/routes/labeltemplate.js @@ -8,7 +8,7 @@ import { pngToZpl } from '../../services/zplService.js'; import { notifyAgentJob } from '../agent/index.js'; import { emit } from '../sse.js'; import { applyZplCopies } from './zpljob.js'; -import { interpolateString } from './article/shared.js'; +import { interpolateString, interpolateObjects } from './article/shared.js'; // #region Helpers @@ -590,16 +590,15 @@ export function register(app, basePath = '') { // Resolve sample article for placeholder interpolation let article = null; if (req.body?.sku) { - try { - const result = await articleDb.getBySku(req.user.id, req.body.sku); - article = result?.article; - } catch { - // ignore + const skuStr = String(req.body.sku).trim(); + const result = await articleDb.getBySku(req.user.id, skuStr); + if (!result?.article) { + return res.status(404).json({ error: 'Article not found' }); } - } - if (!article) { + article = result.article; + } else { article = { - name: req.body?.name || template?.name || 'Musterartikel', + name: template?.name || 'Musterartikel', sku: 'TEST-12345', prices: [{ net: 19.99, taxRate: 19 }], eans: ['4012345678901'], @@ -625,16 +624,7 @@ export function register(app, basePath = '') { objects = []; } - const interpolatedObjects = objects.map((obj) => { - const cloned = { ...obj }; - if (typeof cloned.text === 'string') { - cloned.text = interpolateString(cloned.text, article); - } - if (typeof cloned.code === 'string') { - cloned.code = interpolateString(cloned.code, article); - } - return cloned; - }); + const interpolatedObjects = interpolateObjects(objects, article); const printPayload = await renderLabel({ width: width || 448, diff --git a/src/api/routes/ledger.js b/src/api/routes/ledger.js index 3d4bb0f..d7e52eb 100644 --- a/src/api/routes/ledger.js +++ b/src/api/routes/ledger.js @@ -101,7 +101,7 @@ export function register(app, basePath = '') { } const manifestarticleId = Number(req.params.manifestarticleId); - const transactionType = req.query.transaction_type ?? req.query.transactionType; + const transactionType = req.query.transaction_type; try { const ledger = await db.listByManifestarticleId( @@ -186,7 +186,7 @@ export function register(app, basePath = '') { } const sku = req.params.sku; - const transactionType = req.query.transaction_type ?? req.query.transactionType; + const transactionType = req.query.transaction_type; try { const ledger = await db.listBySku(req.user.id, sku, transactionType); @@ -254,9 +254,9 @@ export function register(app, basePath = '') { return res.status(400).json({ error: selected.error }); } - const rawManifestarticleId = req.query.manifestarticle_id ?? req.query.manifestarticleId; + const rawManifestarticleId = req.query.manifestarticle_id; const sku = req.query.sku; - const transactionType = req.query.transaction_type ?? req.query.transactionType; + const transactionType = req.query.transaction_type; if (rawManifestarticleId == null && (sku == null || !String(sku).trim())) { return res.status(400).json({ diff --git a/src/api/routes/posterminal.js b/src/api/routes/posterminal.js index 7c0e156..d85f84c 100644 --- a/src/api/routes/posterminal.js +++ b/src/api/routes/posterminal.js @@ -190,12 +190,8 @@ export function register(app, basePath = '') { } const pairingpin = String(randomInt(100000, 999999)); - const rootCategoryId = req.body.rootCategoryId !== undefined - ? req.body.rootCategoryId - : req.body.root_category_id; - const syncRootArticles = req.body.syncRootArticles !== undefined - ? req.body.syncRootArticles - : req.body.sync_root_articles; + const rootCategoryId = req.body.rootCategoryId; + const syncRootArticles = req.body.syncRootArticles; const sort = req.body.sort !== undefined ? Number(req.body.sort) : undefined; if (sort !== undefined && !Number.isInteger(sort)) { return res.status(400).json({ error: 'sort must be an integer' }); @@ -319,14 +315,12 @@ export function register(app, basePath = '') { payload.name = req.body.name.trim(); } - if (req.body.rootCategoryId !== undefined || req.body.root_category_id !== undefined) { - const rawCat = req.body.rootCategoryId !== undefined ? req.body.rootCategoryId : req.body.root_category_id; - payload.rootCategoryId = rawCat != null ? Number(rawCat) : null; + if (req.body.rootCategoryId !== undefined) { + payload.rootCategoryId = req.body.rootCategoryId != null ? Number(req.body.rootCategoryId) : null; } - if (req.body.syncRootArticles !== undefined || req.body.sync_root_articles !== undefined) { - const rawSync = req.body.syncRootArticles !== undefined ? req.body.syncRootArticles : req.body.sync_root_articles; - payload.syncRootArticles = Boolean(rawSync); + if (req.body.syncRootArticles !== undefined) { + payload.syncRootArticles = Boolean(req.body.syncRootArticles); } if (req.body.sort !== undefined) { diff --git a/src/api/routes/printerjob.js b/src/api/routes/printerjob.js index 816ce1f..9f6a591 100644 --- a/src/api/routes/printerjob.js +++ b/src/api/routes/printerjob.js @@ -221,7 +221,7 @@ export function register(app, basePath = '') { return res.status(400).json({ error: selected.error }); } - const rawApId = req.body?.agentprinterId ?? req.body?.agentprinterid; + const rawApId = req.body?.agentprinterId; const parsedApId = parsePositiveInt(rawApId, 'agentprinterId'); if (parsedApId.error) { return res.status(400).json({ error: parsedApId.error }); @@ -302,7 +302,7 @@ export function register(app, basePath = '') { } try { - const rawApId = req.body?.agentprinterId ?? req.query?.agentprinterId; + const rawApId = req.body?.agentprinterId; const agentprinterId = rawApId != null ? Number(rawApId) : undefined; const allJobs = await printerjobDb.list(req.user.id, { agentprinterId }); const cancellable = allJobs.filter((j) => j.state === 'pending' || j.state === 'processing'); diff --git a/src/api/routes/supplier.js b/src/api/routes/supplier.js index e7c0a27..2bac16d 100644 --- a/src/api/routes/supplier.js +++ b/src/api/routes/supplier.js @@ -517,7 +517,7 @@ export function register(app, basePath = '') { return res.status(400).json({ error: idParsed.error }); } - const skus = parseSkus(req.query.skus ?? req.query.sku); + const skus = parseSkus(req.query.skus); try { const articles = await db.listArticlesBySkus(req.user.id, idParsed.id, skus); @@ -540,7 +540,7 @@ export function register(app, basePath = '') { return res.status(400).json({ error: idParsed.error }); } - const skus = parseSkus(req.body?.skus ?? req.body?.sku); + const skus = parseSkus(req.body?.skus); try { const articles = await db.listArticlesBySkus(req.user.id, idParsed.id, skus); diff --git a/src/api/routes/vfs.js b/src/api/routes/vfs.js index 1131c48..68ae1a7 100644 --- a/src/api/routes/vfs.js +++ b/src/api/routes/vfs.js @@ -685,7 +685,7 @@ export function register(app, basePath = '') { if (selected.error) { return res.status(400).json({ error: selected.error }); } - const raw = req.body?.path || req.body?.dir || req.body?.name; + const raw = req.body?.path; const parsed = parseVfsRef(raw); if (parsed.error) { return res.status(400).json({ error: parsed.error }); @@ -787,8 +787,8 @@ export function register(app, basePath = '') { if (selected.error) { return res.status(400).json({ error: selected.error }); } - const rawOld = req.body?.oldPath || req.body?.from || req.body?.source; - const rawNew = req.body?.newPath || req.body?.to || req.body?.target; + const rawOld = req.body?.oldPath; + const rawNew = req.body?.newPath; const parsedOld = parseVfsRef(rawOld); if (parsedOld.error) { return res.status(400).json({ error: `oldPath: ${parsedOld.error}` }); diff --git a/src/backendmonitor/server.js b/src/backendmonitor/server.js index 9475e47..1867151 100644 --- a/src/backendmonitor/server.js +++ b/src/backendmonitor/server.js @@ -87,7 +87,7 @@ export function buildStudioApp() { // 5. Insert row app.post('/api/tables/:table/row', async (req, res) => { try { - const result = await dbHelper.insertRow(req.params.table, req.body.row || req.body); + const result = await dbHelper.insertRow(req.params.table, req.body.row); res.json(result); } catch (err) { res.status(400).json({ error: err.message }); @@ -175,7 +175,3 @@ export async function stopBackendMonitor() { }); monitorLog.info('Backend Monitor Interface stopped'); } - -// Aliases for compatibility -export const startSqlStudio = startBackendMonitor; -export const stopSqlStudio = stopBackendMonitor; diff --git a/src/services/backupService.js b/src/services/backupService.js index 6c1eba8..a1d1842 100644 --- a/src/services/backupService.js +++ b/src/services/backupService.js @@ -116,25 +116,21 @@ async function snapshotSqliteDatabase(targetFile) { fs.unlinkSync(targetFile); } - try { - const provider = await getProvider(); - if (provider?.db && typeof provider.db.backup === 'function') { - await provider.db.backup(targetFile); - const stats = fs.statSync(targetFile); - return { success: true, sizeBytes: stats.size }; - } - - if (provider?.query) { - const escapedPath = targetFile.replace(/'/g, "''"); - await provider.query(`VACUUM INTO '${escapedPath}'`); - const stats = fs.statSync(targetFile); - return { success: true, sizeBytes: stats.size }; - } - } catch (err) { - backupLog.warn(`Online SQLite backup method failed (${err.message}), falling back to direct copy`); + const provider = await getProvider(); + if (provider?.db && typeof provider.db.backup === 'function') { + await provider.db.backup(targetFile); + const stats = fs.statSync(targetFile); + return { success: true, sizeBytes: stats.size }; } - // Direct file fallback + if (provider?.query) { + const escapedPath = targetFile.replace(/'/g, "''"); + await provider.query(`VACUUM INTO '${escapedPath}'`); + const stats = fs.statSync(targetFile); + return { success: true, sizeBytes: stats.size }; + } + + // Direct file copy when provider is offline / not connected const { sqlitePath } = resolveBackupPaths(); if (fs.existsSync(sqlitePath)) { fs.copyFileSync(sqlitePath, targetFile); diff --git a/src/services/companyPrinterService.js b/src/services/companyPrinterService.js index 9e40e76..4890dc5 100644 --- a/src/services/companyPrinterService.js +++ b/src/services/companyPrinterService.js @@ -180,19 +180,17 @@ export function extractLiveStatus(live) { const isOffline = live.status === 'offline' || live.unreachable === true || errors.unreachable === true; const online = !isOffline; - const open = Boolean(live.open ?? live.headOpen ?? errors.headOpen ?? false); - const pause = Boolean(live.pause ?? live.paused ?? errors.pause ?? false); + const open = Boolean(live.open || errors.headOpen); + const pause = Boolean(live.pause || errors.pause); const hasOtherError = Boolean( live.error || errors.other || - errors.error || Object.entries(errors).some(([key, val]) => !['headOpen', 'pause', 'unreachable'].includes(key) && Boolean(val)) ); - const rawLabelLength = live.labelLength ?? live.labellength; - const labelLength = rawLabelLength != null && Number.isFinite(Number(rawLabelLength)) - ? Number(rawLabelLength) + const labelLength = live.labelLength != null && Number.isFinite(Number(live.labelLength)) + ? Number(live.labelLength) : null; return { diff --git a/src/tools/edit-image.js b/src/tools/edit-image.js index b733a60..b99235d 100644 --- a/src/tools/edit-image.js +++ b/src/tools/edit-image.js @@ -4,7 +4,7 @@ import { optimizeImage } from 'wasm-image-optimization'; import { defineTool, ToolError } from 'libllmorbiter'; import env from '../config/env.js'; import { getImageByHash } from '../db/pic.js'; -import { prepareVfs, syncVfsToDb, extractFullHash, isReadOnlyDatabasePath } from './vfs.js'; +import { prepareVfs, extractFullHash, isReadOnlyDatabasePath } from './vfs.js'; import { resolveOpenRouterKey, resolveImageModel } from './llm-key.js'; import { TOOL_DESCRIPTIONS, TOOL_PARAMETER_DESCRIPTIONS } from './prompts.js'; @@ -482,11 +482,7 @@ export function createEditImageTool({ googleAuthId, model: fallbackModel }) { fs.mkdirSync(path.dirname(absTarget), { recursive: true }); fs.writeFileSync(absTarget, finalBuffer); - try { - await syncVfsToDb({ googleAuthId, vfsRoot }); - } catch (syncErr) { - console.warn('[edit_image] warning: failed to sync output image to modelvfs db:', syncErr.message); - } + const outFinalRel = path.relative(vfsRoot, absTarget).replace(/\\/g, '/'); diff --git a/src/tools/javascript.js b/src/tools/javascript.js index 562ccd4..715eec0 100644 --- a/src/tools/javascript.js +++ b/src/tools/javascript.js @@ -4,7 +4,7 @@ import jwt from 'jsonwebtoken'; import { defineTool, ToolError } from 'libllmorbiter'; import env from '../config/env.js'; import { TOOL_DESCRIPTIONS, TOOL_PARAMETER_DESCRIPTIONS } from './prompts.js'; -import { createVfs, prepareVfs, syncVfsToDb } from './vfs.js'; +import { createVfs, prepareVfs } from './vfs.js'; import { runSandboxJob } from '../services/javascriptSandbox.js'; const DEFAULT_TIMEOUT_MS = 20_000; @@ -367,19 +367,14 @@ export function createJavascriptExecuteTool({ googleAuthId }) { const isApplet = hasApplet; const applet = isApplet ? readApplet(vfsRoot, appletvfsfile) : null; const source = isApplet ? applet.source : code; - let report; - try { - report = await runInWorker({ - code: source, - timeoutMs: hardTimeout, - vfsRoot, - googleAuthId, - appletInput: isApplet ? (applet_input ?? {}) : undefined, - appletFile: isApplet ? appletvfsfile.trim() : undefined, - }); - } finally { - await syncVfsToDb({ googleAuthId, vfsRoot }); - } + const report = await runInWorker({ + code: source, + timeoutMs: hardTimeout, + vfsRoot, + googleAuthId, + appletInput: isApplet ? (applet_input ?? {}) : undefined, + appletFile: isApplet ? appletvfsfile.trim() : undefined, + }); if (report.timedOut) { throw new ToolError( diff --git a/src/tools/patch.js b/src/tools/patch.js index f9305df..6135e03 100644 --- a/src/tools/patch.js +++ b/src/tools/patch.js @@ -3,7 +3,7 @@ import path from 'path'; import { defineTool, ToolError } from 'libllmorbiter'; import { isReadOnlyDatabasePath } from './vfs.js'; import { isSkillPath, renderSkill, parseSkill, SKILL_EXT, skillFileName } from './skill.js'; -import { prepareVfs, syncVfsToDb } from './vfs.js'; +import { prepareVfs } from './vfs.js'; import { TOOL_DESCRIPTIONS, PATCH_PARAMETER_DESCRIPTIONS } from './prompts.js'; import { getStore } from '../db/store.js'; @@ -262,17 +262,7 @@ export function createPatchTool({ googleAuthId }) { if (isSkill) touchedSkills.add(clean); } - if (applied.length > 0) { - try { - await syncVfsToDb({ googleAuthId, vfsRoot }); - } catch (err) { - console.error('[patch] failed to sync VFS to db:', err.message); - throw new ToolError( - `Patch applied to disk but could not be persisted: ${err.message}. ` + - `Files changed: ${applied.join(', ')}.` - ); - } - } + const result = { success: true, diff --git a/src/tools/pdf-read.js b/src/tools/pdf-read.js index e0b801d..f602a02 100644 --- a/src/tools/pdf-read.js +++ b/src/tools/pdf-read.js @@ -2,7 +2,7 @@ import fs from 'fs'; import path from 'path'; import { defineTool, ToolError } from 'libllmorbiter'; import { pdfToText, splitPdfPages, pdfToPng } from 'pdfconvert-wasm'; -import { prepareVfs, syncVfsToDb } from './vfs.js'; +import { prepareVfs } from './vfs.js'; import { TOOL_DESCRIPTIONS, TOOL_PARAMETER_DESCRIPTIONS } from './prompts.js'; const IMAGE_FOLDER = 'images'; @@ -186,13 +186,7 @@ export function createPdfReadTool({ googleAuthId }) { }); } - if (savedImages.length > 0) { - try { - await syncVfsToDb({ googleAuthId, vfsRoot }); - } catch (err) { - console.warn('[pdf_read] failed to sync rendered images to VFS DB:', err?.message || err); - } - } + result.images = savedImages; if (!result.totalPages) result.totalPages = images.length; diff --git a/src/tools/search-images.js b/src/tools/search-images.js index a26eb27..49c5b83 100644 --- a/src/tools/search-images.js +++ b/src/tools/search-images.js @@ -3,7 +3,7 @@ import path from 'path'; import { optimizeImage } from 'wasm-image-optimization'; import { defineTool, ToolError } from 'libllmorbiter'; import * as dbCompanySetting from '../db/companysetting.js'; -import { prepareVfs, syncVfsToDb } from './vfs.js'; +import { prepareVfs } from './vfs.js'; import { TOOL_DESCRIPTIONS, TOOL_PARAMETER_DESCRIPTIONS } from './prompts.js'; const SERPER_ENDPOINT = 'https://google.serper.dev/images'; @@ -249,16 +249,7 @@ export function createSearchImagesTool({ googleAuthId }) { }); } - if (results.length > 0) { - try { - await syncVfsToDb({ googleAuthId, vfsRoot }); - } catch (err) { - // Non-fatal: the files are on disk and will be picked up by the next - // javascript_execute / patch sync. Losing the images because a DB write - // failed would be a worse outcome than a slightly stale store. - console.warn('[search_images] failed to persist candidates:', err.message); - } - } + const result = { query: term, diff --git a/src/tools/vfs.js b/src/tools/vfs.js index f8450b4..8aa6e24 100644 --- a/src/tools/vfs.js +++ b/src/tools/vfs.js @@ -257,14 +257,6 @@ export async function prepareVfs({ googleAuthId }) { return vfsRoot; } -/** - * Purely disk-based VFS; no database syncing needed. Kept as no-op for backward compatibility. - * @param {{ googleAuthId?: number, vfsRoot?: string }} [ctx] - */ -export async function syncVfsToDb() { - // Purely disk-based VFS; no database syncing. -} - // #endregion // #region Sandboxed VFS Factory diff --git a/src/util/manifestAttachment.js b/src/util/manifestAttachment.js index 7f4aab4..fd63122 100644 --- a/src/util/manifestAttachment.js +++ b/src/util/manifestAttachment.js @@ -13,7 +13,7 @@ const skeleCache = new Map(); export async function formatAttachment(file) { if (!file || typeof file !== 'object') return file; - let skeledash = file.skeledash || file.skelehash || file.meta?.skeledash || file.meta?.skelehash || null; + let skeledash = file.skeledash || file.meta?.skeledash || null; if (!skeledash && file.hash && skeleCache.has(file.hash)) { skeledash = skeleCache.get(file.hash); @@ -36,7 +36,6 @@ export async function formatAttachment(file) { return { ...rest, skeledash, - skelehash: skeledash, }; }