From 04b6dd4d9f3b6f4f6f4d5c79ae213ae7300244c1 Mon Sep 17 00:00:00 2001 From: "Aaron K. Clark" Date: Tue, 19 May 2026 01:31:20 -0500 Subject: [PATCH] fix(controllers): stop echoing raw error details to clients in 5xx bodies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every controller's try/catch was returning `res.status(500).json({ message: "Error!", error: String(error) })`. The `error` field surfaced raw Sequelize / pg-driver text to clients: "value too long for type character varying(255)", "connection to server at \"db\" (10.0.0.5) failed", "duplicate key value violates unique constraint \"…\"", etc. That's exactly the kind of detail the global error-handler in app/middleware/error-handler.js was already written to NOT surface — but the per-controller catches intercepted the error before it could reach the global path, so the policy was silently bypassed across all 17 controllers (137 occurrences total). Strip `, error: String(error)` from every 5xx return. Controllers still log the full error via `log.error({ err: error }, …)` so the operator-side signal is unchanged; only the client-facing body loses the leak. One static-string variant in customercontroller.js (`error: "Sequelize Op not available"`) is also removed since it violated the same policy. New `tests/unit/controller-error-shape.test.js` does a source-level regression pin via test.each() over every controller, asserting that no file contains `error: String(error)`, `error: err.message`, or the static-string variant in a 5xx body. Future copy-paste of the old pattern fails CI immediately. No public test changed expectations — the existing tests' 5xx assertions match only on status code + `message`, never on the removed `error` field, so the suite passes unchanged at 495 then +20 new structural assertions = 515. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/controllers/_bulk-helpers.js | 14 ++--- app/controllers/billingtypecontroller.js | 18 +++--- app/controllers/companycontroller.js | 14 ++--- app/controllers/customercontroller.js | 32 +++++------ app/controllers/customerpaymentcontroller.js | 14 ++--- app/controllers/inventoryitemcontroller.js | 18 +++--- .../inventorytransactioncontroller.js | 16 +++--- app/controllers/invoicecontroller.js | 14 ++--- app/controllers/invoicejobcontroller.js | 14 ++--- app/controllers/jobcontroller.js | 14 ++--- app/controllers/productentrycontroller.js | 14 ++--- .../purchaseorderheadercontroller.js | 14 ++--- .../purchaseorderlinecontroller.js | 14 ++--- .../purchaseordervendorcontroller.js | 18 +++--- app/controllers/timeentrycontroller.js | 20 +++---- app/controllers/versioninfocontroller.js | 14 ++--- app/controllers/workercontroller.js | 18 +++--- tests/unit/controller-error-shape.test.js | 55 +++++++++++++++++++ 18 files changed, 195 insertions(+), 140 deletions(-) create mode 100644 tests/unit/controller-error-shape.test.js diff --git a/app/controllers/_bulk-helpers.js b/app/controllers/_bulk-helpers.js index dd4972f..d6db1cd 100644 --- a/app/controllers/_bulk-helpers.js +++ b/app/controllers/_bulk-helpers.js @@ -53,7 +53,7 @@ function makeBulkCreate({ isAuthKeyMasterKey = await auth.isMaster(authKey); } catch (error) { log.error({ err: error }, `${modelKey}: isMaster failed`); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } const input = (req.body && Array.isArray(req.body[bodyKey])) @@ -70,7 +70,7 @@ function makeBulkCreate({ authKeyCompanyId = await auth.getCompanyId(authKey); } catch (error) { log.error({ err: error }, `${modelKey}: getCompanyId failed`); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (authKeyCompanyId === -1) { return res.status(403).json({ message: "Invalid Authorization Key." }); @@ -122,7 +122,7 @@ function makeBulkCreate({ } catch (error) { try { await t.rollback(); } catch (_) { /* swallow */ } log.error({ err: error }, `${modelKey}.bulkCreate failed`); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; } @@ -185,7 +185,7 @@ function makeBulkCreateIndirect({ isAuthKeyMasterKey = await auth.isMaster(authKey); } catch (error) { log.error({ err: error }, `${modelKey}: isMaster failed`); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } const input = (req.body && Array.isArray(req.body[bodyKey])) @@ -201,7 +201,7 @@ function makeBulkCreateIndirect({ authKeyCompanyId = await auth.getCompanyId(authKey); } catch (error) { log.error({ err: error }, `${modelKey}: getCompanyId failed`); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (authKeyCompanyId === -1) { return res.status(403).json({ message: "Invalid Authorization Key." }); @@ -228,7 +228,7 @@ function makeBulkCreateIndirect({ parentCompId = await resolveParentCompanyId(parentId); } catch (error) { log.error({ err: error }, `${modelKey}: parent scope resolve failed`); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (parentCompId === -1) { return res.status(400).json({ @@ -263,7 +263,7 @@ function makeBulkCreateIndirect({ } catch (error) { try { await t.rollback(); } catch (_) { /* swallow */ } log.error({ err: error }, `${modelKey}.bulkCreate failed`); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; } diff --git a/app/controllers/billingtypecontroller.js b/app/controllers/billingtypecontroller.js index 9dbd425..1d0c656 100644 --- a/app/controllers/billingtypecontroller.js +++ b/app/controllers/billingtypecontroller.js @@ -26,7 +26,7 @@ exports.create = async (req, res) => { isAuthKeyMasterKey = await IsMaster(authKey); } catch (error) { log.error({ err: error }, 'IsMaster failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } const body = req.body || {}; @@ -41,7 +41,7 @@ exports.create = async (req, res) => { authKeyCompanyId = await GetCompanyId(authKey); } catch (error) { log.error({ err: error }, 'GetCompanyId failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (authKeyCompanyId === -1) { return res.status(403).json({ message: "Invalid Authorization Key." }); @@ -67,7 +67,7 @@ exports.create = async (req, res) => { return res.status(201).json({ message: "Billing type created.", billingType: created }); } catch (error) { log.error({ err: error }, 'BillingType.create failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -82,7 +82,7 @@ exports.getById = async (req, res) => { billingType = await BillingType.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'BillingType.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!billingType || billingType.btArch) { return res.status(404).json({ message: "Not found." }); @@ -144,7 +144,7 @@ exports.listByCompany = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'BillingType.findAndCountAll failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -159,7 +159,7 @@ exports.update = async (req, res) => { billingType = await BillingType.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'BillingType.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!billingType || billingType.btArch) { return res.status(404).json({ message: "Not found." }); @@ -187,7 +187,7 @@ exports.update = async (req, res) => { return res.status(200).json({ message: "Updated.", billingType }); } catch (error) { log.error({ err: error }, 'BillingType.update failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -202,7 +202,7 @@ exports.remove = async (req, res) => { billingType = await BillingType.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'BillingType.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!billingType || billingType.btArch) { return res.status(404).json({ message: "Not found." }); @@ -221,7 +221,7 @@ exports.remove = async (req, res) => { return res.status(200).json({ message: "Archived.", id: billingType.btId }); } catch (error) { log.error({ err: error }, 'BillingType archive failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; diff --git a/app/controllers/companycontroller.js b/app/controllers/companycontroller.js index 8cc2a5f..536506c 100644 --- a/app/controllers/companycontroller.js +++ b/app/controllers/companycontroller.js @@ -51,7 +51,7 @@ exports.create = async (req, res) => { return res.status(201).json({ message: "Company created.", company: created }); } catch (error) { log.error({ err: error }, 'Company.create failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -66,7 +66,7 @@ exports.getById = async (req, res) => { company = await Company.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'Company.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!company || company.compArch) { return res.status(404).json({ message: "Not found." }); @@ -118,7 +118,7 @@ exports.list = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'Company.findAndCountAll failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -133,7 +133,7 @@ exports.update = async (req, res) => { company = await Company.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'Company.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!company || company.compArch) { return res.status(404).json({ message: "Not found." }); @@ -161,7 +161,7 @@ exports.update = async (req, res) => { return res.status(200).json({ message: "Updated.", company }); } catch (error) { log.error({ err: error }, 'Company.update failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -180,7 +180,7 @@ exports.remove = async (req, res) => { company = await Company.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'Company.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!company || company.compArch) { return res.status(404).json({ message: "Not found." }); @@ -191,7 +191,7 @@ exports.remove = async (req, res) => { return res.status(200).json({ message: "Archived.", id: company.compId }); } catch (error) { log.error({ err: error }, 'Company archive failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; diff --git a/app/controllers/customercontroller.js b/app/controllers/customercontroller.js index fd08294..9c4b578 100644 --- a/app/controllers/customercontroller.js +++ b/app/controllers/customercontroller.js @@ -47,7 +47,7 @@ exports.getCustomerById = async (req, res) => { isAuthKeyMasterKey = await IsMaster(authKey); } catch (error) { log.error({ err: error }, 'IsMaster failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (isAuthKeyMasterKey) { @@ -60,7 +60,7 @@ exports.getCustomerById = async (req, res) => { authKeyCompanyId = await GetCompanyId(authKey); } catch (error) { log.error({ err: error }, 'Company lookup failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!CompaniesMatch(custCompanyId, authKeyCompanyId)) { @@ -110,7 +110,7 @@ exports.createCustomer = async (req, res) => { isAuthKeyMasterKey = await IsMaster(authKey); } catch (error) { log.error({ err: error }, 'IsMaster failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } // Whitelist of model fields we accept. Anything else in the body @@ -138,7 +138,7 @@ exports.createCustomer = async (req, res) => { authKeyCompanyId = await GetCompanyId(authKey); } catch (error) { log.error({ err: error }, 'GetCompanyId failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (authKeyCompanyId === -1) { return res.status(403).json({ message: "Invalid Authorization Key." }); @@ -171,7 +171,7 @@ exports.createCustomer = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'Customer.create failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -197,7 +197,7 @@ exports.getAllByCompanyId = async (req, res) => { isAuthKeyMasterKey = await IsMaster(authKey); } catch (error) { log.error({ err: error }, 'IsMaster failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!isAuthKeyMasterKey) { @@ -206,7 +206,7 @@ exports.getAllByCompanyId = async (req, res) => { authKeyCompanyId = await GetCompanyId(authKey); } catch (error) { log.error({ err: error }, 'GetCompanyId failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } // req.params.id arrives as a string; authKeyCompanyId comes back // as an INT from Sequelize. Normalize both before comparing. @@ -252,7 +252,7 @@ exports.getAllByCompanyId = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'Customer.findAndCountAll failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -283,7 +283,7 @@ exports.exportCsv = async (req, res) => { isAuthKeyMasterKey = await IsMaster(authKey); } catch (error) { log.error({ err: error }, 'IsMaster failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } let effectiveCompanyId; @@ -301,7 +301,7 @@ exports.exportCsv = async (req, res) => { authKeyCompanyId = await GetCompanyId(authKey); } catch (error) { log.error({ err: error }, 'GetCompanyId failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (authKeyCompanyId === -1) { return res.status(403).json({ message: "Invalid Authorization Key." }); @@ -335,7 +335,7 @@ exports.exportCsv = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'Customer.findAll for CSV export failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } const truncated = rows.length > limit; @@ -426,7 +426,7 @@ exports.search = async (req, res) => { isAuthKeyMasterKey = await IsMaster(authKey); } catch (error) { log.error({ err: error }, 'IsMaster failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } // Resolve effective companyId from auth + query @@ -445,7 +445,7 @@ exports.search = async (req, res) => { authKeyCompanyId = await GetCompanyId(authKey); } catch (error) { log.error({ err: error }, 'GetCompanyId failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (authKeyCompanyId === -1) { return res.status(403).json({ message: "Invalid Authorization Key." }); @@ -471,7 +471,7 @@ exports.search = async (req, res) => { const Op = db.Sequelize && db.Sequelize.Op; if (!Op) { - return res.status(500).json({ message: "Error!", error: "Sequelize Op not available" }); + return res.status(500).json({ message: "Error!" }); } // ILIKE on Postgres; the `%` wildcards come from us, not the user. @@ -505,7 +505,7 @@ exports.search = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'Customer.search findAndCountAll failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -520,7 +520,7 @@ async function findAndRespond(customerId, res) { }); } catch (error) { log.error({ err: error }, 'Customer.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } } diff --git a/app/controllers/customerpaymentcontroller.js b/app/controllers/customerpaymentcontroller.js index 11a2a2c..b4c4b9d 100644 --- a/app/controllers/customerpaymentcontroller.js +++ b/app/controllers/customerpaymentcontroller.js @@ -52,7 +52,7 @@ exports.create = async (req, res) => { return res.status(201).json({ message: "Customer payment created.", customerPayment: created }); } catch (error) { log.error({ err: error }, 'CustomerPayment.create failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -67,7 +67,7 @@ exports.getById = async (req, res) => { payment = await CustomerPayment.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'CustomerPayment.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!payment || payment.cpayArch) { return res.status(404).json({ message: "Not found." }); @@ -127,7 +127,7 @@ exports.listByCustomer = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'CustomerPayment.findAndCountAll failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -142,7 +142,7 @@ exports.update = async (req, res) => { payment = await CustomerPayment.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'CustomerPayment.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!payment || payment.cpayArch) { return res.status(404).json({ message: "Not found." }); @@ -171,7 +171,7 @@ exports.update = async (req, res) => { return res.status(200).json({ message: "Updated.", customerPayment: payment }); } catch (error) { log.error({ err: error }, 'CustomerPayment.update failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -186,7 +186,7 @@ exports.remove = async (req, res) => { payment = await CustomerPayment.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'CustomerPayment.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!payment || payment.cpayArch) { return res.status(404).json({ message: "Not found." }); @@ -206,7 +206,7 @@ exports.remove = async (req, res) => { return res.status(200).json({ message: "Archived.", id: payment.cpayId }); } catch (error) { log.error({ err: error }, 'CustomerPayment archive failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; diff --git a/app/controllers/inventoryitemcontroller.js b/app/controllers/inventoryitemcontroller.js index eb8b552..a54d0f1 100644 --- a/app/controllers/inventoryitemcontroller.js +++ b/app/controllers/inventoryitemcontroller.js @@ -26,7 +26,7 @@ exports.create = async (req, res) => { isAuthKeyMasterKey = await IsMaster(authKey); } catch (error) { log.error({ err: error }, 'IsMaster failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } const body = req.body || {}; @@ -41,7 +41,7 @@ exports.create = async (req, res) => { authKeyCompanyId = await GetCompanyId(authKey); } catch (error) { log.error({ err: error }, 'GetCompanyId failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (authKeyCompanyId === -1) { return res.status(403).json({ message: "Invalid Authorization Key." }); @@ -67,7 +67,7 @@ exports.create = async (req, res) => { return res.status(201).json({ message: "Inventory item created.", inventoryItem: created }); } catch (error) { log.error({ err: error }, 'InventoryItem.create failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -82,7 +82,7 @@ exports.getById = async (req, res) => { inventoryItem = await InventoryItem.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'InventoryItem.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!inventoryItem || inventoryItem.invitArch) { return res.status(404).json({ message: "Not found." }); @@ -144,7 +144,7 @@ exports.listByCompany = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'InventoryItem.findAndCountAll failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -159,7 +159,7 @@ exports.update = async (req, res) => { inventoryItem = await InventoryItem.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'InventoryItem.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!inventoryItem || inventoryItem.invitArch) { return res.status(404).json({ message: "Not found." }); @@ -187,7 +187,7 @@ exports.update = async (req, res) => { return res.status(200).json({ message: "Updated.", inventoryItem }); } catch (error) { log.error({ err: error }, 'InventoryItem.update failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -202,7 +202,7 @@ exports.remove = async (req, res) => { inventoryItem = await InventoryItem.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'InventoryItem.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!inventoryItem || inventoryItem.invitArch) { return res.status(404).json({ message: "Not found." }); @@ -221,7 +221,7 @@ exports.remove = async (req, res) => { return res.status(200).json({ message: "Archived.", id: inventoryItem.invitId }); } catch (error) { log.error({ err: error }, 'InventoryItem archive failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; diff --git a/app/controllers/inventorytransactioncontroller.js b/app/controllers/inventorytransactioncontroller.js index 6131982..6a5af87 100644 --- a/app/controllers/inventorytransactioncontroller.js +++ b/app/controllers/inventorytransactioncontroller.js @@ -34,7 +34,7 @@ exports.create = async (req, res) => { isAuthKeyMasterKey = await IsMaster(authKey); } catch (error) { log.error({ err: error }, 'IsMaster failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } const body = req.body || {}; @@ -69,7 +69,7 @@ exports.create = async (req, res) => { return res.status(201).json({ message: "Inventory transaction created.", inventoryTransaction: created }); } catch (error) { log.error({ err: error }, 'InventoryTransaction.create failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -84,7 +84,7 @@ exports.getById = async (req, res) => { txn = await InventoryTransaction.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'InventoryTransaction.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!txn || txn.invtArch) { return res.status(404).json({ message: "Not found." }); @@ -140,7 +140,7 @@ exports.listByCompany = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'InventoryTransaction.findAndCountAll failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -155,7 +155,7 @@ exports.update = async (req, res) => { txn = await InventoryTransaction.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'InventoryTransaction.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!txn || txn.invtArch) { return res.status(404).json({ message: "Not found." }); @@ -183,7 +183,7 @@ exports.update = async (req, res) => { return res.status(200).json({ message: "Updated.", inventoryTransaction: txn }); } catch (error) { log.error({ err: error }, 'InventoryTransaction.update failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -198,7 +198,7 @@ exports.remove = async (req, res) => { txn = await InventoryTransaction.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'InventoryTransaction.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!txn || txn.invtArch) { return res.status(404).json({ message: "Not found." }); @@ -217,7 +217,7 @@ exports.remove = async (req, res) => { return res.status(200).json({ message: "Archived.", id: txn.invtId }); } catch (error) { log.error({ err: error }, 'InventoryTransaction archive failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; diff --git a/app/controllers/invoicecontroller.js b/app/controllers/invoicecontroller.js index d5b8187..54bed08 100644 --- a/app/controllers/invoicecontroller.js +++ b/app/controllers/invoicecontroller.js @@ -53,7 +53,7 @@ exports.create = async (req, res) => { return res.status(201).json({ message: "Invoice created.", invoice: created }); } catch (error) { log.error({ err: error }, 'Invoice.create failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -68,7 +68,7 @@ exports.getById = async (req, res) => { invoice = await Invoice.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'Invoice.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!invoice || invoice.invArch) { return res.status(404).json({ message: "Not found." }); @@ -128,7 +128,7 @@ exports.listByCustomer = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'Invoice.findAndCountAll failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -143,7 +143,7 @@ exports.update = async (req, res) => { invoice = await Invoice.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'Invoice.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!invoice || invoice.invArch) { return res.status(404).json({ message: "Not found." }); @@ -172,7 +172,7 @@ exports.update = async (req, res) => { return res.status(200).json({ message: "Updated.", invoice }); } catch (error) { log.error({ err: error }, 'Invoice.update failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -187,7 +187,7 @@ exports.remove = async (req, res) => { invoice = await Invoice.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'Invoice.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!invoice || invoice.invArch) { return res.status(404).json({ message: "Not found." }); @@ -207,7 +207,7 @@ exports.remove = async (req, res) => { return res.status(200).json({ message: "Archived.", id: invoice.invId }); } catch (error) { log.error({ err: error }, 'Invoice archive failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; diff --git a/app/controllers/invoicejobcontroller.js b/app/controllers/invoicejobcontroller.js index 358b5a5..25ce49c 100644 --- a/app/controllers/invoicejobcontroller.js +++ b/app/controllers/invoicejobcontroller.js @@ -54,7 +54,7 @@ exports.create = async (req, res) => { return res.status(201).json({ message: "Invoice line created.", invoiceJob: created }); } catch (error) { log.error({ err: error }, 'InvoiceJob.create failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -69,7 +69,7 @@ exports.getById = async (req, res) => { invoiceJob = await InvoiceJob.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'InvoiceJob.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!invoiceJob || invoiceJob.injbArch) { return res.status(404).json({ message: "Not found." }); @@ -146,7 +146,7 @@ exports.listByInvoice = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'InvoiceJob.findAndCountAll failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -161,7 +161,7 @@ exports.update = async (req, res) => { invoiceJob = await InvoiceJob.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'InvoiceJob.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!invoiceJob || invoiceJob.injbArch) { return res.status(404).json({ message: "Not found." }); @@ -190,7 +190,7 @@ exports.update = async (req, res) => { return res.status(200).json({ message: "Updated.", invoiceJob }); } catch (error) { log.error({ err: error }, 'InvoiceJob.update failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -205,7 +205,7 @@ exports.remove = async (req, res) => { invoiceJob = await InvoiceJob.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'InvoiceJob.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!invoiceJob || invoiceJob.injbArch) { return res.status(404).json({ message: "Not found." }); @@ -225,7 +225,7 @@ exports.remove = async (req, res) => { return res.status(200).json({ message: "Archived.", id: invoiceJob.injbId }); } catch (error) { log.error({ err: error }, 'InvoiceJob archive failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; diff --git a/app/controllers/jobcontroller.js b/app/controllers/jobcontroller.js index 8bd91ed..adffd3c 100644 --- a/app/controllers/jobcontroller.js +++ b/app/controllers/jobcontroller.js @@ -60,7 +60,7 @@ exports.create = async (req, res) => { return res.status(201).json({ message: "Job created.", job: created }); } catch (error) { log.error({ err: error }, 'Job.create failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -75,7 +75,7 @@ exports.getById = async (req, res) => { job = await Job.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'Job.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!job || job.jobArch) { return res.status(404).json({ message: "Not found." }); @@ -136,7 +136,7 @@ exports.listByCustomer = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'Job.findAndCountAll failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -151,7 +151,7 @@ exports.update = async (req, res) => { job = await Job.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'Job.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!job || job.jobArch) { return res.status(404).json({ message: "Not found." }); @@ -180,7 +180,7 @@ exports.update = async (req, res) => { return res.status(200).json({ message: "Updated.", job }); } catch (error) { log.error({ err: error }, 'Job.update failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -195,7 +195,7 @@ exports.remove = async (req, res) => { job = await Job.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'Job.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!job || job.jobArch) { return res.status(404).json({ message: "Not found." }); @@ -215,7 +215,7 @@ exports.remove = async (req, res) => { return res.status(200).json({ message: "Archived.", id: job.jobId }); } catch (error) { log.error({ err: error }, 'Job archive failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; diff --git a/app/controllers/productentrycontroller.js b/app/controllers/productentrycontroller.js index 7e85197..8fd2ea8 100644 --- a/app/controllers/productentrycontroller.js +++ b/app/controllers/productentrycontroller.js @@ -49,7 +49,7 @@ exports.create = async (req, res) => { return res.status(201).json({ message: "Product entry created.", productEntry: created }); } catch (error) { log.error({ err: error }, 'ProductEntry.create failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -64,7 +64,7 @@ exports.getById = async (req, res) => { productEntry = await ProductEntry.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'ProductEntry.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!productEntry || productEntry.penArch) { return res.status(404).json({ message: "Not found." }); @@ -124,7 +124,7 @@ exports.listByJob = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'ProductEntry.findAndCountAll failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -139,7 +139,7 @@ exports.update = async (req, res) => { productEntry = await ProductEntry.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'ProductEntry.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!productEntry || productEntry.penArch) { return res.status(404).json({ message: "Not found." }); @@ -168,7 +168,7 @@ exports.update = async (req, res) => { return res.status(200).json({ message: "Updated.", productEntry }); } catch (error) { log.error({ err: error }, 'ProductEntry.update failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -183,7 +183,7 @@ exports.remove = async (req, res) => { productEntry = await ProductEntry.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'ProductEntry.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!productEntry || productEntry.penArch) { return res.status(404).json({ message: "Not found." }); @@ -203,7 +203,7 @@ exports.remove = async (req, res) => { return res.status(200).json({ message: "Archived.", id: productEntry.pentId }); } catch (error) { log.error({ err: error }, 'ProductEntry archive failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; diff --git a/app/controllers/purchaseorderheadercontroller.js b/app/controllers/purchaseorderheadercontroller.js index e5feb54..4bc78b0 100644 --- a/app/controllers/purchaseorderheadercontroller.js +++ b/app/controllers/purchaseorderheadercontroller.js @@ -54,7 +54,7 @@ exports.create = async (req, res) => { return res.status(201).json({ message: "Purchase order header created.", purchaseOrderHeader: created }); } catch (error) { log.error({ err: error }, 'PurchaseOrderHeader.create failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -69,7 +69,7 @@ exports.getById = async (req, res) => { header = await PurchaseOrderHeader.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'PurchaseOrderHeader.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!header || header.pohArch) { return res.status(404).json({ message: "Not found." }); @@ -127,7 +127,7 @@ exports.listByVendor = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'PurchaseOrderHeader.findAndCountAll failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -142,7 +142,7 @@ exports.update = async (req, res) => { header = await PurchaseOrderHeader.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'PurchaseOrderHeader.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!header || header.pohArch) { return res.status(404).json({ message: "Not found." }); @@ -171,7 +171,7 @@ exports.update = async (req, res) => { return res.status(200).json({ message: "Updated.", purchaseOrderHeader: header }); } catch (error) { log.error({ err: error }, 'PurchaseOrderHeader.update failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -186,7 +186,7 @@ exports.remove = async (req, res) => { header = await PurchaseOrderHeader.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'PurchaseOrderHeader.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!header || header.pohArch) { return res.status(404).json({ message: "Not found." }); @@ -206,7 +206,7 @@ exports.remove = async (req, res) => { return res.status(200).json({ message: "Archived.", id: header.pohId }); } catch (error) { log.error({ err: error }, 'PurchaseOrderHeader archive failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; diff --git a/app/controllers/purchaseorderlinecontroller.js b/app/controllers/purchaseorderlinecontroller.js index 17b800f..eb4d6fc 100644 --- a/app/controllers/purchaseorderlinecontroller.js +++ b/app/controllers/purchaseorderlinecontroller.js @@ -54,7 +54,7 @@ exports.create = async (req, res) => { return res.status(201).json({ message: "Purchase order line created.", purchaseOrderLine: created }); } catch (error) { log.error({ err: error }, 'PurchaseOrderLine.create failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -69,7 +69,7 @@ exports.getById = async (req, res) => { line = await PurchaseOrderLine.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'PurchaseOrderLine.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!line || line.polArch) { return res.status(404).json({ message: "Not found." }); @@ -127,7 +127,7 @@ exports.listByHeader = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'PurchaseOrderLine.findAndCountAll failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -142,7 +142,7 @@ exports.update = async (req, res) => { line = await PurchaseOrderLine.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'PurchaseOrderLine.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!line || line.polArch) { return res.status(404).json({ message: "Not found." }); @@ -171,7 +171,7 @@ exports.update = async (req, res) => { return res.status(200).json({ message: "Updated.", purchaseOrderLine: line }); } catch (error) { log.error({ err: error }, 'PurchaseOrderLine.update failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -186,7 +186,7 @@ exports.remove = async (req, res) => { line = await PurchaseOrderLine.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'PurchaseOrderLine.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!line || line.polArch) { return res.status(404).json({ message: "Not found." }); @@ -206,7 +206,7 @@ exports.remove = async (req, res) => { return res.status(200).json({ message: "Archived.", id: line.polId }); } catch (error) { log.error({ err: error }, 'PurchaseOrderLine archive failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; diff --git a/app/controllers/purchaseordervendorcontroller.js b/app/controllers/purchaseordervendorcontroller.js index 12e9a73..59f5517 100644 --- a/app/controllers/purchaseordervendorcontroller.js +++ b/app/controllers/purchaseordervendorcontroller.js @@ -41,7 +41,7 @@ exports.create = async (req, res) => { isAuthKeyMasterKey = await IsMaster(authKey); } catch (error) { log.error({ err: error }, 'IsMaster failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } const body = req.body || {}; @@ -56,7 +56,7 @@ exports.create = async (req, res) => { authKeyCompanyId = await GetCompanyId(authKey); } catch (error) { log.error({ err: error }, 'GetCompanyId failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (authKeyCompanyId === -1) { return res.status(403).json({ message: "Invalid Authorization Key." }); @@ -82,7 +82,7 @@ exports.create = async (req, res) => { return res.status(201).json({ message: "PO vendor created.", purchaseOrderVendor: created }); } catch (error) { log.error({ err: error }, 'PurchaseOrderVendor.create failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -97,7 +97,7 @@ exports.getById = async (req, res) => { vendor = await PurchaseOrderVendor.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'PurchaseOrderVendor.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!vendor || vendor.povArch) { return res.status(404).json({ message: "Not found." }); @@ -155,7 +155,7 @@ exports.listByCompany = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'PurchaseOrderVendor.findAndCountAll failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -170,7 +170,7 @@ exports.update = async (req, res) => { vendor = await PurchaseOrderVendor.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'PurchaseOrderVendor.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!vendor || vendor.povArch) { return res.status(404).json({ message: "Not found." }); @@ -198,7 +198,7 @@ exports.update = async (req, res) => { return res.status(200).json({ message: "Updated.", purchaseOrderVendor: vendor }); } catch (error) { log.error({ err: error }, 'PurchaseOrderVendor.update failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -213,7 +213,7 @@ exports.remove = async (req, res) => { vendor = await PurchaseOrderVendor.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'PurchaseOrderVendor.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!vendor || vendor.povArch) { return res.status(404).json({ message: "Not found." }); @@ -232,7 +232,7 @@ exports.remove = async (req, res) => { return res.status(200).json({ message: "Archived.", id: vendor.povId }); } catch (error) { log.error({ err: error }, 'PurchaseOrderVendor archive failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; diff --git a/app/controllers/timeentrycontroller.js b/app/controllers/timeentrycontroller.js index d44ef4e..5baf07a 100644 --- a/app/controllers/timeentrycontroller.js +++ b/app/controllers/timeentrycontroller.js @@ -83,7 +83,7 @@ exports.create = async (req, res) => { return res.status(201).json({ message: "Time entry created.", timeEntry: created }); } catch (error) { log.error({ err: error }, 'TimeEntry.create failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -103,7 +103,7 @@ exports.getById = async (req, res) => { entry = await TimeEntry.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'TimeEntry.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!entry || entry.teArch) { return res.status(404).json({ message: "Not found." }); @@ -191,7 +191,7 @@ exports.listByCompany = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'TimeEntry.findAndCountAll failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -212,7 +212,7 @@ exports.update = async (req, res) => { entry = await TimeEntry.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'TimeEntry.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!entry || entry.teArch) { return res.status(404).json({ message: "Not found." }); @@ -246,7 +246,7 @@ exports.update = async (req, res) => { return res.status(200).json({ message: "Updated.", timeEntry: entry }); } catch (error) { log.error({ err: error }, 'TimeEntry.update failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -266,7 +266,7 @@ exports.remove = async (req, res) => { entry = await TimeEntry.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'TimeEntry.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!entry || entry.teArch) { return res.status(404).json({ message: "Not found." }); @@ -285,7 +285,7 @@ exports.remove = async (req, res) => { return res.status(200).json({ message: "Archived.", id: entry.teId }); } catch (error) { log.error({ err: error }, 'TimeEntry archive failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -316,7 +316,7 @@ exports.exportCsv = async (req, res) => { isMasterKey = await IsMaster(authKey); } catch (error) { log.error({ err: error }, 'IsMaster failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } let effectiveCompanyId; @@ -334,7 +334,7 @@ exports.exportCsv = async (req, res) => { authKeyCompanyId = await GetCompanyId(authKey); } catch (error) { log.error({ err: error }, 'GetCompanyId failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (authKeyCompanyId === -1) { return res.status(403).json({ message: "Invalid Authorization Key." }); @@ -381,7 +381,7 @@ exports.exportCsv = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'TimeEntry.findAll for CSV export failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } const truncated = rows.length > limit; diff --git a/app/controllers/versioninfocontroller.js b/app/controllers/versioninfocontroller.js index a26b05f..0d41048 100644 --- a/app/controllers/versioninfocontroller.js +++ b/app/controllers/versioninfocontroller.js @@ -47,7 +47,7 @@ exports.create = async (req, res) => { return res.status(201).json({ message: "VersionInfo created.", versionInfo: created }); } catch (error) { log.error({ err: error }, 'VersionInfo.create failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -62,7 +62,7 @@ exports.getById = async (req, res) => { return res.status(200).json({ message: "Found.", versionInfo: v }); } catch (error) { log.error({ err: error }, 'VersionInfo.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -94,7 +94,7 @@ exports.list = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'VersionInfo.findAndCountAll failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -107,7 +107,7 @@ exports.update = async (req, res) => { v = await VersionInfo.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'VersionInfo.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!v) return res.status(404).json({ message: "Not found." }); @@ -125,7 +125,7 @@ exports.update = async (req, res) => { return res.status(200).json({ message: "Updated.", versionInfo: v }); } catch (error) { log.error({ err: error }, 'VersionInfo.update failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -138,7 +138,7 @@ exports.remove = async (req, res) => { v = await VersionInfo.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'VersionInfo.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!v) return res.status(404).json({ message: "Not found." }); @@ -147,7 +147,7 @@ exports.remove = async (req, res) => { return res.status(200).json({ message: "Deleted.", id: req.params.id }); } catch (error) { log.error({ err: error }, 'VersionInfo.destroy failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; diff --git a/app/controllers/workercontroller.js b/app/controllers/workercontroller.js index 102f5ae..ee3602b 100644 --- a/app/controllers/workercontroller.js +++ b/app/controllers/workercontroller.js @@ -38,7 +38,7 @@ exports.create = async (req, res) => { isAuthKeyMasterKey = await IsMaster(authKey); } catch (error) { log.error({ err: error }, 'IsMaster failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } const body = req.body || {}; @@ -53,7 +53,7 @@ exports.create = async (req, res) => { authKeyCompanyId = await GetCompanyId(authKey); } catch (error) { log.error({ err: error }, 'GetCompanyId failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (authKeyCompanyId === -1) { return res.status(403).json({ message: "Invalid Authorization Key." }); @@ -82,7 +82,7 @@ exports.create = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'Worker.create failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -103,7 +103,7 @@ exports.getById = async (req, res) => { worker = await Worker.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'Worker.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!worker || worker.workerArch) { return res.status(404).json({ message: "Not found." }); @@ -170,7 +170,7 @@ exports.listByCompany = async (req, res) => { }); } catch (error) { log.error({ err: error }, 'Worker.findAndCountAll failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -190,7 +190,7 @@ exports.update = async (req, res) => { worker = await Worker.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'Worker.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!worker || worker.workerArch) { return res.status(404).json({ message: "Not found." }); @@ -218,7 +218,7 @@ exports.update = async (req, res) => { return res.status(200).json({ message: "Updated.", worker }); } catch (error) { log.error({ err: error }, 'Worker.update failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; @@ -238,7 +238,7 @@ exports.remove = async (req, res) => { worker = await Worker.findByPk(req.params.id); } catch (error) { log.error({ err: error }, 'Worker.findByPk failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } if (!worker || worker.workerArch) { return res.status(404).json({ message: "Not found." }); @@ -257,7 +257,7 @@ exports.remove = async (req, res) => { return res.status(200).json({ message: "Archived.", id: worker.workerId }); } catch (error) { log.error({ err: error }, 'Worker archive failed'); - return res.status(500).json({ message: "Error!", error: String(error) }); + return res.status(500).json({ message: "Error!" }); } }; diff --git a/tests/unit/controller-error-shape.test.js b/tests/unit/controller-error-shape.test.js new file mode 100644 index 0000000..ca2293e --- /dev/null +++ b/tests/unit/controller-error-shape.test.js @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Aaron K. Clark +// +// Source-level regression pin: controllers must NOT echo the raw caught +// error back to clients in 5xx responses. The global error handler in +// app/middleware/error-handler.js is the only path allowed to shape +// 5xx bodies — and it deliberately returns `{ message: "Error!", +// requestId? }` with the original error logged but NOT surfaced. +// +// Why this lives as a structural test instead of behavioral: +// +// Triggering every controller's catch path with a mocked DB throw would +// require ~17 separate test files (one per controller) plus per-route +// fixtures for every code path that catches an error. A grep-based test +// on the source pins the policy with a single assertion and catches +// any future regression where someone copy-pastes the old pattern back +// into a new controller. +// +// If a controller needs to surface error detail in the response body +// (e.g., a validation error from zod that already lives behind an +// `expose: true` flag), it should `throw` the error and let the global +// error-handler decide whether to expose it — never echo `String(error)` +// from the catch directly. + +import { describe, test, expect } from 'vitest'; +import { readdirSync, readFileSync } from 'node:fs'; +import { resolve, join } from 'node:path'; + +const CONTROLLERS_DIR = resolve(__dirname, '../../app/controllers'); + +describe('controllers do not leak raw error details in 5xx bodies', () => { + const files = readdirSync(CONTROLLERS_DIR).filter((f) => f.endsWith('.js')); + + test('controllers/ has the expected number of files', () => { + // Sanity check: if the directory mysteriously empties, the per-file + // assertions below would vacuously pass. Pin a non-zero floor. + expect(files.length).toBeGreaterThan(10); + }); + + test.each(files)('%s does not echo `error: String(error)` to clients', (file) => { + const content = readFileSync(join(CONTROLLERS_DIR, file), 'utf8'); + // The grep that found the original 137-occurrence leak. Catches the + // common copy-paste variants (whitespace, quoted strings around the + // arg). If a real need to surface error detail emerges, prefer + // `next(err)` with `err.expose = true` and let the global + // error-handler do it under controlled conditions. + expect(content).not.toMatch(/error:\s*String\(\s*error\s*\)/); + expect(content).not.toMatch(/error:\s*err\.message/); + // And the static-string variant the customer controller used to have: + // `error: "Sequelize Op not available"`. Same principle — anything + // hardcoded into a 5xx body bypasses the global error-handler's + // policy. + expect(content).not.toMatch(/res\.status\(5\d\d\)\.json\([^)]*error:\s*"/); + }); +});