From ed38467091c030c628f52551b69ad071c7be58a0 Mon Sep 17 00:00:00 2001 From: OpenClaw Date: Fri, 6 Mar 2026 17:00:34 +0000 Subject: [PATCH 01/11] auto(agent): Added error handling to address change request and verification routes --- backend/src/routes/addresses.js | 105 ++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 47 deletions(-) diff --git a/backend/src/routes/addresses.js b/backend/src/routes/addresses.js index dfca5dc..3cf7fac 100644 --- a/backend/src/routes/addresses.js +++ b/backend/src/routes/addresses.js @@ -16,18 +16,23 @@ router.post('/change-request', requireAuth, async (req, res) => { const verificationCode = String(randomInt(100000, 999999)); const verificationCodeHash = hashCode(verificationCode); - const [result] = await pool.query( - `INSERT INTO address_change_requests (user_id, new_address_encrypted, verification_code_hash) - VALUES (?, ?, ?)`, - [req.user.userId, encryptText(parsed.data.newAddress), verificationCodeHash] - ); + try { + const [result] = await pool.query( + `INSERT INTO address_change_requests (user_id, new_address_encrypted, verification_code_hash) + VALUES (?, ?, ?)`, + [req.user.userId, encryptText(parsed.data.newAddress), verificationCodeHash] + ); - res.status(201).json({ - requestId: result.insertId, - postalDispatch: 'pending_letter', - note: 'Verification code generated for postal letter dispatch.', - verificationCode - }); + res.status(201).json({ + requestId: result.insertId, + postalDispatch: 'pending_letter', + note: 'Verification code generated for postal letter dispatch.', + verificationCode + }); + } catch (err) { + console.error('Error in address change request:', err); + res.status(500).json({ error: 'Internal server error' }); + } }); router.post('/verify', requireAuth, async (req, res) => { @@ -36,48 +41,54 @@ router.post('/verify', requireAuth, async (req, res) => { const { requestId, code } = parsed.data; - const [rows] = await pool.query( - `SELECT id, user_id, new_address_encrypted, verification_code_hash, status - FROM address_change_requests - WHERE id = ? LIMIT 1`, - [requestId] - ); - - const request = rows[0]; - if (!request) return res.status(404).json({ error: 'Request not found' }); - if (request.user_id !== req.user.userId) return res.status(403).json({ error: 'Forbidden' }); - if (request.status !== 'pending_letter') return res.status(409).json({ error: 'Request not pending' }); - - if (hashCode(code) !== request.verification_code_hash) { - return res.status(400).json({ error: 'Invalid verification code' }); - } - - const conn = await pool.getConnection(); try { - await conn.beginTransaction(); - - await conn.query( - `UPDATE address_change_requests - SET status = 'verified', verified_at = CURRENT_TIMESTAMP - WHERE id = ?`, + const [rows] = await pool.query( + `SELECT id, user_id, new_address_encrypted, verification_code_hash, status + FROM address_change_requests + WHERE id = ? LIMIT 1`, [requestId] ); - await conn.query( - `INSERT INTO addresses (user_id, address_encrypted, postal_verified_at) - VALUES (?, ?, CURRENT_TIMESTAMP)`, - [req.user.userId, request.new_address_encrypted] - ); + const request = rows[0]; + if (!request) return res.status(404).json({ error: 'Request not found' }); + if (request.user_id !== req.user.userId) return res.status(403).json({ error: 'Forbidden' }); + if (request.status !== 'pending_letter') return res.status(409).json({ error: 'Request not pending' }); - await conn.commit(); + if (hashCode(code) !== request.verification_code_hash) { + return res.status(400).json({ error: 'Invalid verification code' }); + } + + const conn = await pool.getConnection(); + try { + await conn.beginTransaction(); + + await conn.query( + `UPDATE address_change_requests + SET status = 'verified', verified_at = CURRENT_TIMESTAMP + WHERE id = ?`, + [requestId] + ); + + await conn.query( + `INSERT INTO addresses (user_id, address_encrypted, postal_verified_at) + VALUES (?, ?, CURRENT_TIMESTAMP)`, + [req.user.userId, request.new_address_encrypted] + ); + + await conn.commit(); + } catch (err) { + await conn.rollback(); + console.error('Error in address verification transaction:', err); + throw err; + } finally { + conn.release(); + } + + res.json({ status: 'verified' }); } catch (err) { - await conn.rollback(); - throw err; - } finally { - conn.release(); + console.error('Error in address verification:', err); + res.status(500).json({ error: 'Internal server error' }); } - - res.json({ status: 'verified' }); }); -export default router; +export default router; \ No newline at end of file From c2dd24f1b3db96b45b37e725d22b33a716e7a983 Mon Sep 17 00:00:00 2001 From: OpenClaw Date: Fri, 6 Mar 2026 17:05:29 +0000 Subject: [PATCH 02/11] auto(agent): Improved error handling and input validation in addresses route --- backend/src/routes/addresses.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/backend/src/routes/addresses.js b/backend/src/routes/addresses.js index 3cf7fac..33927f9 100644 --- a/backend/src/routes/addresses.js +++ b/backend/src/routes/addresses.js @@ -10,9 +10,24 @@ const router = Router(); const hashCode = (code) => createHash('sha256').update(code).digest('hex'); router.post('/change-request', requireAuth, async (req, res) => { - const parsed = z.object({ newAddress: z.string().min(10) }).safeParse(req.body); + const parsed = z.object({ newAddress: z.string().min(10).max(500) }).safeParse(req.body); if (!parsed.success) return res.status(400).json({ error: parsed.error.flatten() }); + // Check if user already has an address + try { + const [existingRows] = await pool.query( + `SELECT id FROM addresses WHERE user_id = ? LIMIT 1`, + [req.user.userId] + ); + + if (existingRows.length === 0) { + return res.status(400).json({ error: 'User must have an existing address to request a change' }); + } + } catch (err) { + console.error('Error checking existing address:', err); + return res.status(500).json({ error: 'Internal server error' }); + } + const verificationCode = String(randomInt(100000, 999999)); const verificationCodeHash = hashCode(verificationCode); From 2d241254985a17e981b708377eb2bb95846f6a7b Mon Sep 17 00:00:00 2001 From: OpenClaw Date: Fri, 6 Mar 2026 17:06:28 +0000 Subject: [PATCH 03/11] auto(agent): Improved error handling in addresses route --- backend/src/routes/addresses.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/routes/addresses.js b/backend/src/routes/addresses.js index 33927f9..ff5c8ee 100644 --- a/backend/src/routes/addresses.js +++ b/backend/src/routes/addresses.js @@ -46,7 +46,7 @@ router.post('/change-request', requireAuth, async (req, res) => { }); } catch (err) { console.error('Error in address change request:', err); - res.status(500).json({ error: 'Internal server error' }); + return res.status(500).json({ error: 'Internal server error' }); } }); @@ -102,7 +102,7 @@ router.post('/verify', requireAuth, async (req, res) => { res.json({ status: 'verified' }); } catch (err) { console.error('Error in address verification:', err); - res.status(500).json({ error: 'Internal server error' }); + return res.status(500).json({ error: 'Internal server error' }); } }); From fe6c17309a8f85f2bbbee378e1dbfac22e368e3d Mon Sep 17 00:00:00 2001 From: OpenClaw Date: Fri, 6 Mar 2026 17:27:33 +0000 Subject: [PATCH 04/11] auto(agent): improved error handling and validation in auth.js --- backend/src/routes/auth.js | 63 +++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/backend/src/routes/auth.js b/backend/src/routes/auth.js index 67d93ab..e0a6aa0 100644 --- a/backend/src/routes/auth.js +++ b/backend/src/routes/auth.js @@ -12,14 +12,24 @@ const registerSchema = z.object({ displayName: z.string().min(2).max(120) }); +const loginSchema = z.object({ + email: z.string().email(), + password: z.string().min(1) +}); + router.post('/register', async (req, res) => { - const parsed = registerSchema.safeParse(req.body); - if (!parsed.success) return res.status(400).json({ error: parsed.error.flatten() }); - - const { email, password, displayName } = parsed.data; - const passwordHash = await bcrypt.hash(password, 12); - try { + const parsed = registerSchema.safeParse(req.body); + if (!parsed.success) { + return res.status(400).json({ + error: 'Validation failed', + details: parsed.error.flatten() + }); + } + + const { email, password, displayName } = parsed.data; + const passwordHash = await bcrypt.hash(password, 12); + const [result] = await pool.query( 'INSERT INTO users (email, password_hash, display_name) VALUES (?, ?, ?)', [email, passwordHash, displayName] @@ -28,26 +38,43 @@ router.post('/register', async (req, res) => { const token = jwt.sign({ userId: result.insertId, email }, process.env.JWT_SECRET, { expiresIn: '7d' }); return res.status(201).json({ token }); } catch (err) { - if (err.code === 'ER_DUP_ENTRY') return res.status(409).json({ error: 'Email already exists' }); + console.error('Registration error:', err); + if (err.code === 'ER_DUP_ENTRY') { + return res.status(409).json({ error: 'Email already exists' }); + } return res.status(500).json({ error: 'Registration failed' }); } }); router.post('/login', async (req, res) => { - const parsed = z.object({ email: z.string().email(), password: z.string().min(1) }).safeParse(req.body); - if (!parsed.success) return res.status(400).json({ error: parsed.error.flatten() }); + try { + const parsed = loginSchema.safeParse(req.body); + if (!parsed.success) { + return res.status(400).json({ + error: 'Validation failed', + details: parsed.error.flatten() + }); + } - const { email, password } = parsed.data; - const [rows] = await pool.query('SELECT id, email, password_hash FROM users WHERE email = ? LIMIT 1', [email]); - const user = rows[0]; + const { email, password } = parsed.data; + const [rows] = await pool.query('SELECT id, email, password_hash FROM users WHERE email = ? LIMIT 1', [email]); + const user = rows[0]; - if (!user) return res.status(401).json({ error: 'Invalid credentials' }); + if (!user) { + return res.status(401).json({ error: 'Invalid credentials' }); + } - const ok = await bcrypt.compare(password, user.password_hash); - if (!ok) return res.status(401).json({ error: 'Invalid credentials' }); + const ok = await bcrypt.compare(password, user.password_hash); + if (!ok) { + return res.status(401).json({ error: 'Invalid credentials' }); + } - const token = jwt.sign({ userId: user.id, email: user.email }, process.env.JWT_SECRET, { expiresIn: '7d' }); - return res.json({ token }); + const token = jwt.sign({ userId: user.id, email: user.email }, process.env.JWT_SECRET, { expiresIn: '7d' }); + return res.json({ token }); + } catch (err) { + console.error('Login error:', err); + return res.status(500).json({ error: 'Login failed' }); + } }); -export default router; +export default router; \ No newline at end of file From 6539762c921905deeb8e92c9556590073b76ed5a Mon Sep 17 00:00:00 2001 From: OpenClaw Date: Fri, 6 Mar 2026 17:30:29 +0000 Subject: [PATCH 05/11] auto(agent): added try/catch blocks and improved error handling in helpRequests.js --- backend/src/routes/helpRequests.js | 50 ++++++++++++++++++------------ 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/backend/src/routes/helpRequests.js b/backend/src/routes/helpRequests.js index 91a1b1a..981afa8 100644 --- a/backend/src/routes/helpRequests.js +++ b/backend/src/routes/helpRequests.js @@ -6,31 +6,41 @@ import { requireAuth } from '../middleware/auth.js'; const router = Router(); router.get('/', async (_req, res) => { - const [rows] = await pool.query( - `SELECT hr.id, hr.title, hr.description, hr.value_chf, hr.status, hr.created_at, u.display_name requester_name - FROM help_requests hr - JOIN users u ON u.id = hr.requester_id - ORDER BY hr.created_at DESC` - ); - res.json(rows); + try { + const [rows] = await pool.query( + `SELECT hr.id, hr.title, hr.description, hr.value_chf, hr.status, hr.created_at, u.display_name requester_name + FROM help_requests hr + JOIN users u ON u.id = hr.requester_id + ORDER BY hr.created_at DESC` + ); + res.json(rows); + } catch (error) { + console.error('Error fetching help requests:', error); + res.status(500).json({ error: 'Internal server error' }); + } }); router.post('/', requireAuth, async (req, res) => { - const parsed = z.object({ - title: z.string().min(3).max(180), - description: z.string().min(5), - valueChf: z.number().positive() - }).safeParse(req.body); + try { + const parsed = z.object({ + title: z.string().min(3).max(180), + description: z.string().min(5), + valueChf: z.number().positive() + }).safeParse(req.body); - if (!parsed.success) return res.status(400).json({ error: parsed.error.flatten() }); + if (!parsed.success) return res.status(400).json({ error: parsed.error.flatten() }); - const { title, description, valueChf } = parsed.data; - const [result] = await pool.query( - 'INSERT INTO help_requests (requester_id, title, description, value_chf) VALUES (?, ?, ?, ?)', - [req.user.userId, title, description, valueChf] - ); + const { title, description, valueChf } = parsed.data; + const [result] = await pool.query( + 'INSERT INTO help_requests (requester_id, title, description, value_chf) VALUES (?, ?, ?, ?)', + [req.user.userId, title, description, valueChf] + ); - res.status(201).json({ id: result.insertId }); + res.status(201).json({ id: result.insertId }); + } catch (error) { + console.error('Error creating help request:', error); + res.status(500).json({ error: 'Internal server error' }); + } }); -export default router; +export default router; \ No newline at end of file From 507f54eea0867b0251526a23c8058c7e5e23e8e7 Mon Sep 17 00:00:00 2001 From: OpenClaw Date: Fri, 6 Mar 2026 17:33:33 +0000 Subject: [PATCH 06/11] auto(agent): added try/catch blocks and improved error handling in offers.js --- backend/src/routes/offers.js | 115 +++++++++++++++++++++++------------ 1 file changed, 77 insertions(+), 38 deletions(-) diff --git a/backend/src/routes/offers.js b/backend/src/routes/offers.js index 676da1f..1fd0ba0 100644 --- a/backend/src/routes/offers.js +++ b/backend/src/routes/offers.js @@ -6,59 +6,98 @@ import { requireAuth } from '../middleware/auth.js'; const router = Router(); router.post('/:requestId', requireAuth, async (req, res) => { - const requestId = Number(req.params.requestId); - const parsed = z.object({ amountChf: z.number().positive(), message: z.string().max(2000).optional() }).safeParse(req.body); - if (!parsed.success || Number.isNaN(requestId)) return res.status(400).json({ error: 'Invalid payload' }); + try { + const requestId = Number(req.params.requestId); + if (Number.isNaN(requestId)) { + return res.status(400).json({ error: 'Invalid requestId' }); + } - const { amountChf, message } = parsed.data; - const [result] = await pool.query( - `INSERT INTO offers (request_id, helper_id, amount_chf, message) - VALUES (?, ?, ?, ?)`, - [requestId, req.user.userId, amountChf, message || null] - ); + const parsed = z.object({ + amountChf: z.number().positive(), + message: z.string().max(2000).optional() + }).safeParse(req.body); - await pool.query('UPDATE help_requests SET status = ? WHERE id = ?', ['negotiating', requestId]); + if (!parsed.success) { + return res.status(400).json({ error: 'Invalid payload' }); + } - res.status(201).json({ id: result.insertId }); + const { amountChf, message } = parsed.data; + const [result] = await pool.query( + `INSERT INTO offers (request_id, helper_id, amount_chf, message) + VALUES (?, ?, ?, ?)`, + [requestId, req.user.userId, amountChf, message || null] + ); + + await pool.query('UPDATE help_requests SET status = ? WHERE id = ?', ['negotiating', requestId]); + + res.status(201).json({ id: result.insertId }); + } catch (error) { + console.error('Error in POST /offers/:requestId:', error); + res.status(500).json({ error: 'Internal server error' }); + } }); router.post('/negotiation/:offerId', requireAuth, async (req, res) => { - const offerId = Number(req.params.offerId); - const parsed = z.object({ amountChf: z.number().positive(), message: z.string().max(2000).optional() }).safeParse(req.body); - if (!parsed.success || Number.isNaN(offerId)) return res.status(400).json({ error: 'Invalid payload' }); + try { + const offerId = Number(req.params.offerId); + if (Number.isNaN(offerId)) { + return res.status(400).json({ error: 'Invalid offerId' }); + } - const { amountChf, message } = parsed.data; - const [result] = await pool.query( - `INSERT INTO negotiations (offer_id, sender_id, amount_chf, message) - VALUES (?, ?, ?, ?)`, - [offerId, req.user.userId, amountChf, message || null] - ); + const parsed = z.object({ + amountChf: z.number().positive(), + message: z.string().max(2000).optional() + }).safeParse(req.body); - await pool.query('UPDATE offers SET status = ? WHERE id = ?', ['countered', offerId]); + if (!parsed.success) { + return res.status(400).json({ error: 'Invalid payload' }); + } - res.status(201).json({ id: result.insertId }); + const { amountChf, message } = parsed.data; + const [result] = await pool.query( + `INSERT INTO negotiations (offer_id, sender_id, amount_chf, message) + VALUES (?, ?, ?, ?)`, + [offerId, req.user.userId, amountChf, message || null] + ); + + await pool.query('UPDATE offers SET status = ? WHERE id = ?', ['countered', offerId]); + + res.status(201).json({ id: result.insertId }); + } catch (error) { + console.error('Error in POST /offers/negotiation/:offerId:', error); + res.status(500).json({ error: 'Internal server error' }); + } }); router.post('/accept/:offerId', requireAuth, async (req, res) => { - const offerId = Number(req.params.offerId); - if (Number.isNaN(offerId)) return res.status(400).json({ error: 'Invalid offerId' }); + try { + const offerId = Number(req.params.offerId); + if (Number.isNaN(offerId)) { + return res.status(400).json({ error: 'Invalid offerId' }); + } - const [offers] = await pool.query( - 'SELECT id, request_id, amount_chf FROM offers WHERE id = ? LIMIT 1', - [offerId] - ); - const offer = offers[0]; - if (!offer) return res.status(404).json({ error: 'Offer not found' }); + const [offers] = await pool.query( + 'SELECT id, request_id, amount_chf FROM offers WHERE id = ? LIMIT 1', + [offerId] + ); + const offer = offers[0]; + if (!offer) { + return res.status(404).json({ error: 'Offer not found' }); + } - await pool.query('UPDATE offers SET status = ? WHERE id = ?', ['accepted', offerId]); - await pool.query('UPDATE help_requests SET status = ? WHERE id = ?', ['agreed', offer.request_id]); + await pool.query('UPDATE offers SET status = ? WHERE id = ?', ['accepted', offerId]); + await pool.query('UPDATE help_requests SET status = ? WHERE id = ?', ['agreed', offer.request_id]); - const [dealResult] = await pool.query( - 'INSERT INTO deals (request_id, offer_id, agreed_amount_chf) VALUES (?, ?, ?)', - [offer.request_id, offer.id, offer.amount_chf] - ); + const [dealResult] = await pool.query( + 'INSERT INTO deals (request_id, offer_id, agreed_amount_chf) VALUES (?, ?, ?)', + [offer.request_id, offer.id, offer.amount_chf] + ); - res.status(201).json({ dealId: dealResult.insertId }); + res.status(201).json({ dealId: dealResult.insertId }); + } catch (error) { + console.error('Error in POST /offers/accept/:offerId:', error); + res.status(500).json({ error: 'Internal server error' }); + } }); -export default router; +export default router; \ No newline at end of file From 00661fd99e35500a208983a2b616f78ceb397c60 Mon Sep 17 00:00:00 2001 From: OpenClaw Date: Fri, 6 Mar 2026 17:34:20 +0000 Subject: [PATCH 07/11] auto(agent): added try/catch block and improved error handling in reviews.js --- backend/src/routes/reviews.js | 39 ++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/backend/src/routes/reviews.js b/backend/src/routes/reviews.js index 646e05a..3f0f2db 100644 --- a/backend/src/routes/reviews.js +++ b/backend/src/routes/reviews.js @@ -6,24 +6,35 @@ import { requireAuth } from '../middleware/auth.js'; const router = Router(); router.post('/:dealId', requireAuth, async (req, res) => { - const dealId = Number(req.params.dealId); - const parsed = z.object({ revieweeId: z.number().int().positive(), rating: z.number().int().min(1).max(5), comment: z.string().max(2000).optional() }).safeParse(req.body); + try { + const dealId = Number(req.params.dealId); + const parsed = z.object({ + revieweeId: z.number().int().positive(), + rating: z.number().int().min(1).max(5), + comment: z.string().max(2000).optional() + }).safeParse(req.body); - if (!parsed.success || Number.isNaN(dealId)) return res.status(400).json({ error: 'Invalid payload' }); + if (!parsed.success || Number.isNaN(dealId)) { + return res.status(400).json({ error: 'Invalid payload' }); + } - const now = new Date(); - const earliest = new Date(now.getTime() + 2 * 24 * 60 * 60 * 1000); - const latest = new Date(now.getTime() + 14 * 24 * 60 * 60 * 1000); + const now = new Date(); + const earliest = new Date(now.getTime() + 2 * 24 * 60 * 60 * 1000); + const latest = new Date(now.getTime() + 14 * 24 * 60 * 60 * 1000); - const { revieweeId, rating, comment } = parsed.data; + const { revieweeId, rating, comment } = parsed.data; - const [result] = await pool.query( - `INSERT INTO reviews (deal_id, reviewer_id, reviewee_id, rating, comment, earliest_prompt_at, latest_prompt_at) - VALUES (?, ?, ?, ?, ?, ?, ?)`, - [dealId, req.user.userId, revieweeId, rating, comment || null, earliest, latest] - ); + const [result] = await pool.query( + `INSERT INTO reviews (deal_id, reviewer_id, reviewee_id, rating, comment, earliest_prompt_at, latest_prompt_at) + VALUES (?, ?, ?, ?, ?, ?, ?)`, + [dealId, req.user.userId, revieweeId, rating, comment || null, earliest, latest] + ); - res.status(201).json({ id: result.insertId }); + res.status(201).json({ id: result.insertId }); + } catch (error) { + console.error('Error creating review:', error); + res.status(500).json({ error: 'Internal server error' }); + } }); -export default router; +export default router; \ No newline at end of file From b03b264c5e027f724a2bbb4af9accb8215edaf98 Mon Sep 17 00:00:00 2001 From: OpenClaw Date: Fri, 6 Mar 2026 17:39:27 +0000 Subject: [PATCH 08/11] auto(agent): Added try/catch blocks, improved input validation with Zod and added proper error handling in contacts route --- backend/src/routes/contacts.js | 185 +++++++++++++++++++++------------ 1 file changed, 119 insertions(+), 66 deletions(-) diff --git a/backend/src/routes/contacts.js b/backend/src/routes/contacts.js index 3a7a246..db4080b 100644 --- a/backend/src/routes/contacts.js +++ b/backend/src/routes/contacts.js @@ -6,92 +6,145 @@ import { requireAuth } from '../middleware/auth.js'; const router = Router(); const getDealParticipants = async (dealId) => { - const [rows] = await pool.query( - `SELECT d.id, hr.requester_id, o.helper_id - FROM deals d - JOIN help_requests hr ON hr.id = d.request_id - JOIN offers o ON o.id = d.offer_id - WHERE d.id = ? LIMIT 1`, - [dealId] - ); + try { + const [rows] = await pool.query( + `SELECT d.id, hr.requester_id, o.helper_id + FROM deals d + JOIN help_requests hr ON hr.id = d.request_id + JOIN offers o ON o.id = d.offer_id + WHERE d.id = ? LIMIT 1`, + [dealId] + ); - return rows[0] || null; + return rows[0] || null; + } catch (error) { + throw new Error('Database error while fetching deal participants'); + } }; router.post('/request', requireAuth, async (req, res) => { - const parsed = z.object({ dealId: z.number().int().positive(), targetUserId: z.number().int().positive() }).safeParse(req.body); - if (!parsed.success) return res.status(400).json({ error: parsed.error.flatten() }); + try { + const parsed = z.object({ + dealId: z.number().int().positive(), + targetUserId: z.number().int().positive() + }).safeParse(req.body); + + if (!parsed.success) { + return res.status(400).json({ error: 'Invalid input data', details: parsed.error.flatten() }); + } - const { dealId, targetUserId } = parsed.data; - const deal = await getDealParticipants(dealId); - if (!deal) return res.status(404).json({ error: 'Deal not found' }); + const { dealId, targetUserId } = parsed.data; + const deal = await getDealParticipants(dealId); + + if (!deal) { + return res.status(404).json({ error: 'Deal not found' }); + } - const participants = [deal.requester_id, deal.helper_id]; - if (!participants.includes(req.user.userId) || !participants.includes(targetUserId) || req.user.userId === targetUserId) { - return res.status(403).json({ error: 'Forbidden' }); + const participants = [deal.requester_id, deal.helper_id]; + + if (!participants.includes(req.user.userId) || !participants.includes(targetUserId) || req.user.userId === targetUserId) { + return res.status(403).json({ error: 'Forbidden' }); + } + + const [existing] = await pool.query( + `SELECT id FROM contact_exchange_requests + WHERE deal_id = ? AND requester_id = ? AND target_id = ? LIMIT 1`, + [dealId, req.user.userId, targetUserId] + ); + + if (existing.length) { + return res.status(409).json({ error: 'Request already exists' }); + } + + const [result] = await pool.query( + `INSERT INTO contact_exchange_requests (deal_id, requester_id, target_id, accepted) + VALUES (?, ?, ?, FALSE)`, + [dealId, req.user.userId, targetUserId] + ); + + res.status(201).json({ id: result.insertId }); + } catch (error) { + console.error('Error in contacts request route:', error); + res.status(500).json({ error: 'Internal server error' }); } - - const [existing] = await pool.query( - `SELECT id FROM contact_exchange_requests - WHERE deal_id = ? AND requester_id = ? AND target_id = ? LIMIT 1`, - [dealId, req.user.userId, targetUserId] - ); - if (existing.length) return res.status(409).json({ error: 'Request already exists' }); - - const [result] = await pool.query( - `INSERT INTO contact_exchange_requests (deal_id, requester_id, target_id, accepted) - VALUES (?, ?, ?, FALSE)`, - [dealId, req.user.userId, targetUserId] - ); - - res.status(201).json({ id: result.insertId }); }); router.post('/respond', requireAuth, async (req, res) => { - const parsed = z.object({ requestId: z.number().int().positive(), accept: z.boolean() }).safeParse(req.body); - if (!parsed.success) return res.status(400).json({ error: parsed.error.flatten() }); + try { + const parsed = z.object({ + requestId: z.number().int().positive(), + accept: z.boolean() + }).safeParse(req.body); + + if (!parsed.success) { + return res.status(400).json({ error: 'Invalid input data', details: parsed.error.flatten() }); + } - const { requestId, accept } = parsed.data; - const [rows] = await pool.query( - `SELECT id, target_id FROM contact_exchange_requests WHERE id = ? LIMIT 1`, - [requestId] - ); + const { requestId, accept } = parsed.data; + const [rows] = await pool.query( + `SELECT id, target_id FROM contact_exchange_requests WHERE id = ? LIMIT 1`, + [requestId] + ); - const row = rows[0]; - if (!row) return res.status(404).json({ error: 'Request not found' }); - if (row.target_id !== req.user.userId) return res.status(403).json({ error: 'Forbidden' }); + const row = rows[0]; + + if (!row) { + return res.status(404).json({ error: 'Request not found' }); + } + + if (row.target_id !== req.user.userId) { + return res.status(403).json({ error: 'Forbidden' }); + } - if (accept) { - await pool.query('UPDATE contact_exchange_requests SET accepted = TRUE WHERE id = ?', [requestId]); - return res.json({ status: 'accepted' }); + if (accept) { + await pool.query('UPDATE contact_exchange_requests SET accepted = TRUE WHERE id = ?', [requestId]); + return res.json({ status: 'accepted' }); + } + + await pool.query('DELETE FROM contact_exchange_requests WHERE id = ?', [requestId]); + res.json({ status: 'rejected' }); + } catch (error) { + console.error('Error in contacts respond route:', error); + res.status(500).json({ error: 'Internal server error' }); } - - await pool.query('DELETE FROM contact_exchange_requests WHERE id = ?', [requestId]); - res.json({ status: 'rejected' }); }); router.get('/deal/:dealId', requireAuth, async (req, res) => { - const dealId = Number(req.params.dealId); - if (Number.isNaN(dealId)) return res.status(400).json({ error: 'Invalid dealId' }); + try { + const dealId = Number(req.params.dealId); + + if (Number.isNaN(dealId)) { + return res.status(400).json({ error: 'Invalid dealId' }); + } - const deal = await getDealParticipants(dealId); - if (!deal) return res.status(404).json({ error: 'Deal not found' }); + const deal = await getDealParticipants(dealId); + + if (!deal) { + return res.status(404).json({ error: 'Deal not found' }); + } - const participants = [deal.requester_id, deal.helper_id]; - if (!participants.includes(req.user.userId)) return res.status(403).json({ error: 'Forbidden' }); + const participants = [deal.requester_id, deal.helper_id]; + + if (!participants.includes(req.user.userId)) { + return res.status(403).json({ error: 'Forbidden' }); + } - const [rows] = await pool.query( - `SELECT cer.id, cer.requester_id, cer.target_id, cer.accepted, - ru.phone_encrypted AS requester_phone_encrypted, - tu.phone_encrypted AS target_phone_encrypted - FROM contact_exchange_requests cer - JOIN users ru ON ru.id = cer.requester_id - JOIN users tu ON tu.id = cer.target_id - WHERE cer.deal_id = ? AND cer.accepted = TRUE`, - [dealId] - ); + const [rows] = await pool.query( + `SELECT cer.id, cer.requester_id, cer.target_id, cer.accepted, + ru.phone_encrypted AS requester_phone_encrypted, + tu.phone_encrypted AS target_phone_encrypted + FROM contact_exchange_requests cer + JOIN users ru ON ru.id = cer.requester_id + JOIN users tu ON tu.id = cer.target_id + WHERE cer.deal_id = ? AND cer.accepted = TRUE`, + [dealId] + ); - res.json(rows); + res.json(rows); + } catch (error) { + console.error('Error in contacts deal route:', error); + res.status(500).json({ error: 'Internal server error' }); + } }); -export default router; +export default router; \ No newline at end of file From cd7fa3bac296a352667ee03fe9372b9c35a27890 Mon Sep 17 00:00:00 2001 From: OpenClaw Date: Fri, 6 Mar 2026 17:43:24 +0000 Subject: [PATCH 09/11] auto(agent): added try/catch and proper HTTP status codes in profile.js --- backend/src/routes/profile.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/backend/src/routes/profile.js b/backend/src/routes/profile.js index caa8dad..6ff1e7b 100644 --- a/backend/src/routes/profile.js +++ b/backend/src/routes/profile.js @@ -7,13 +7,18 @@ import { encryptText } from '../services/encryption.js'; const router = Router(); router.post('/phone', requireAuth, async (req, res) => { - const parsed = z.object({ phone: z.string().min(6).max(40) }).safeParse(req.body); - if (!parsed.success) return res.status(400).json({ error: parsed.error.flatten() }); + try { + const parsed = z.object({ phone: z.string().min(6).max(40) }).safeParse(req.body); + if (!parsed.success) return res.status(400).json({ error: parsed.error.flatten() }); - const encryptedPhone = encryptText(parsed.data.phone); - await pool.query('UPDATE users SET phone_encrypted = ? WHERE id = ?', [encryptedPhone, req.user.userId]); + const encryptedPhone = encryptText(parsed.data.phone); + await pool.query('UPDATE users SET phone_encrypted = ? WHERE id = ?', [encryptedPhone, req.user.userId]); - res.json({ status: 'updated' }); + res.status(200).json({ status: 'updated' }); + } catch (error) { + console.error('Error updating phone:', error); + res.status(500).json({ error: 'Internal server error' }); + } }); -export default router; +export default router; \ No newline at end of file From 4ee009a730c80d94eb05992315e9427f288374cf Mon Sep 17 00:00:00 2001 From: OpenClaw Date: Fri, 6 Mar 2026 17:44:32 +0000 Subject: [PATCH 10/11] auto(agent): Improved error handling and input validation in addresses.js --- backend/src/routes/addresses.js | 189 ++++++++++++++++++++------------ 1 file changed, 116 insertions(+), 73 deletions(-) diff --git a/backend/src/routes/addresses.js b/backend/src/routes/addresses.js index ff5c8ee..5be09fa 100644 --- a/backend/src/routes/addresses.js +++ b/backend/src/routes/addresses.js @@ -9,100 +9,143 @@ const router = Router(); const hashCode = (code) => createHash('sha256').update(code).digest('hex'); +// Schema for change request validation +const changeRequestSchema = z.object({ + newAddress: z.string().min(10).max(500) +}); + +// Schema for verification request validation +const verifyRequestSchema = z.object({ + requestId: z.number().int().positive(), + code: z.string().regex(/^\d{6}$/) +}); + router.post('/change-request', requireAuth, async (req, res) => { - const parsed = z.object({ newAddress: z.string().min(10).max(500) }).safeParse(req.body); - if (!parsed.success) return res.status(400).json({ error: parsed.error.flatten() }); - - // Check if user already has an address try { - const [existingRows] = await pool.query( - `SELECT id FROM addresses WHERE user_id = ? LIMIT 1`, - [req.user.userId] - ); + const parsed = changeRequestSchema.safeParse(req.body); + if (!parsed.success) { + return res.status(400).json({ + error: 'Invalid input data', + details: parsed.error.flatten() + }); + } - if (existingRows.length === 0) { - return res.status(400).json({ error: 'User must have an existing address to request a change' }); + // Check if user already has an address + try { + const [existingRows] = await pool.query( + `SELECT id FROM addresses WHERE user_id = ? LIMIT 1`, + [req.user.userId] + ); + + if (existingRows.length === 0) { + return res.status(400).json({ + error: 'User must have an existing address to request a change' + }); + } + } catch (err) { + console.error('Error checking existing address:', err); + return res.status(500).json({ + error: 'Internal server error while checking existing address' + }); + } + + const verificationCode = String(randomInt(100000, 999999)); + const verificationCodeHash = hashCode(verificationCode); + + try { + const [result] = await pool.query( + `INSERT INTO address_change_requests (user_id, new_address_encrypted, verification_code_hash) + VALUES (?, ?, ?)`, + [req.user.userId, encryptText(parsed.data.newAddress), verificationCodeHash] + ); + + res.status(201).json({ + requestId: result.insertId, + postalDispatch: 'pending_letter', + note: 'Verification code generated for postal letter dispatch.', + verificationCode + }); + } catch (err) { + console.error('Error in address change request:', err); + return res.status(500).json({ + error: 'Internal server error while processing address change request' + }); } } catch (err) { - console.error('Error checking existing address:', err); - return res.status(500).json({ error: 'Internal server error' }); - } - - const verificationCode = String(randomInt(100000, 999999)); - const verificationCodeHash = hashCode(verificationCode); - - try { - const [result] = await pool.query( - `INSERT INTO address_change_requests (user_id, new_address_encrypted, verification_code_hash) - VALUES (?, ?, ?)`, - [req.user.userId, encryptText(parsed.data.newAddress), verificationCodeHash] - ); - - res.status(201).json({ - requestId: result.insertId, - postalDispatch: 'pending_letter', - note: 'Verification code generated for postal letter dispatch.', - verificationCode + console.error('Unexpected error in change-request route:', err); + return res.status(500).json({ + error: 'Unexpected internal server error' }); - } catch (err) { - console.error('Error in address change request:', err); - return res.status(500).json({ error: 'Internal server error' }); } }); router.post('/verify', requireAuth, async (req, res) => { - const parsed = z.object({ requestId: z.number().int().positive(), code: z.string().regex(/^\d{6}$/) }).safeParse(req.body); - if (!parsed.success) return res.status(400).json({ error: parsed.error.flatten() }); - - const { requestId, code } = parsed.data; - try { - const [rows] = await pool.query( - `SELECT id, user_id, new_address_encrypted, verification_code_hash, status - FROM address_change_requests - WHERE id = ? LIMIT 1`, - [requestId] - ); - - const request = rows[0]; - if (!request) return res.status(404).json({ error: 'Request not found' }); - if (request.user_id !== req.user.userId) return res.status(403).json({ error: 'Forbidden' }); - if (request.status !== 'pending_letter') return res.status(409).json({ error: 'Request not pending' }); - - if (hashCode(code) !== request.verification_code_hash) { - return res.status(400).json({ error: 'Invalid verification code' }); + const parsed = verifyRequestSchema.safeParse(req.body); + if (!parsed.success) { + return res.status(400).json({ + error: 'Invalid input data', + details: parsed.error.flatten() + }); } - const conn = await pool.getConnection(); - try { - await conn.beginTransaction(); + const { requestId, code } = parsed.data; - await conn.query( - `UPDATE address_change_requests - SET status = 'verified', verified_at = CURRENT_TIMESTAMP - WHERE id = ?`, + try { + const [rows] = await pool.query( + `SELECT id, user_id, new_address_encrypted, verification_code_hash, status + FROM address_change_requests + WHERE id = ? LIMIT 1`, [requestId] ); - await conn.query( - `INSERT INTO addresses (user_id, address_encrypted, postal_verified_at) - VALUES (?, ?, CURRENT_TIMESTAMP)`, - [req.user.userId, request.new_address_encrypted] - ); + const request = rows[0]; + if (!request) return res.status(404).json({ error: 'Request not found' }); + if (request.user_id !== req.user.userId) return res.status(403).json({ error: 'Forbidden' }); + if (request.status !== 'pending_letter') return res.status(409).json({ error: 'Request not pending' }); - await conn.commit(); + if (hashCode(code) !== request.verification_code_hash) { + return res.status(400).json({ error: 'Invalid verification code' }); + } + + const conn = await pool.getConnection(); + try { + await conn.beginTransaction(); + + await conn.query( + `UPDATE address_change_requests + SET status = 'verified', verified_at = CURRENT_TIMESTAMP + WHERE id = ?`, + [requestId] + ); + + await conn.query( + `INSERT INTO addresses (user_id, address_encrypted, postal_verified_at) + VALUES (?, ?, CURRENT_TIMESTAMP)`, + [req.user.userId, request.new_address_encrypted] + ); + + await conn.commit(); + } catch (err) { + await conn.rollback(); + console.error('Error in address verification transaction:', err); + throw err; + } finally { + conn.release(); + } + + res.json({ status: 'verified' }); } catch (err) { - await conn.rollback(); - console.error('Error in address verification transaction:', err); - throw err; - } finally { - conn.release(); + console.error('Error in address verification:', err); + return res.status(500).json({ + error: 'Internal server error while verifying address' + }); } - - res.json({ status: 'verified' }); } catch (err) { - console.error('Error in address verification:', err); - return res.status(500).json({ error: 'Internal server error' }); + console.error('Unexpected error in verify route:', err); + return res.status(500).json({ + error: 'Unexpected internal server error' + }); } }); From 51be362dc49ae062db1f267bfc0a9baaf403e489 Mon Sep 17 00:00:00 2001 From: OpenClaw Date: Fri, 6 Mar 2026 17:45:29 +0000 Subject: [PATCH 11/11] auto(agent): added validation middleware and improved error handling in auth.js --- backend/src/routes/auth.js | 45 +++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/backend/src/routes/auth.js b/backend/src/routes/auth.js index e0a6aa0..6bb8714 100644 --- a/backend/src/routes/auth.js +++ b/backend/src/routes/auth.js @@ -17,7 +17,8 @@ const loginSchema = z.object({ password: z.string().min(1) }); -router.post('/register', async (req, res) => { +// Middleware für Validierung +const validateRegister = (req, res, next) => { try { const parsed = registerSchema.safeParse(req.body); if (!parsed.success) { @@ -26,8 +27,34 @@ router.post('/register', async (req, res) => { details: parsed.error.flatten() }); } + req.validatedData = parsed.data; + next(); + } catch (err) { + console.error('Validation error:', err); + return res.status(500).json({ error: 'Internal server error during validation' }); + } +}; - const { email, password, displayName } = parsed.data; +const validateLogin = (req, res, next) => { + try { + const parsed = loginSchema.safeParse(req.body); + if (!parsed.success) { + return res.status(400).json({ + error: 'Validation failed', + details: parsed.error.flatten() + }); + } + req.validatedData = parsed.data; + next(); + } catch (err) { + console.error('Validation error:', err); + return res.status(500).json({ error: 'Internal server error during validation' }); + } +}; + +router.post('/register', validateRegister, async (req, res) => { + try { + const { email, password, displayName } = req.validatedData; const passwordHash = await bcrypt.hash(password, 12); const [result] = await pool.query( @@ -46,17 +73,9 @@ router.post('/register', async (req, res) => { } }); -router.post('/login', async (req, res) => { +router.post('/login', validateLogin, async (req, res) => { try { - const parsed = loginSchema.safeParse(req.body); - if (!parsed.success) { - return res.status(400).json({ - error: 'Validation failed', - details: parsed.error.flatten() - }); - } - - const { email, password } = parsed.data; + const { email, password } = req.validatedData; const [rows] = await pool.query('SELECT id, email, password_hash FROM users WHERE email = ? LIMIT 1', [email]); const user = rows[0]; @@ -70,7 +89,7 @@ router.post('/login', async (req, res) => { } const token = jwt.sign({ userId: user.id, email: user.email }, process.env.JWT_SECRET, { expiresIn: '7d' }); - return res.json({ token }); + return res.status(200).json({ token }); } catch (err) { console.error('Login error:', err); return res.status(500).json({ error: 'Login failed' });