fix(#24): Implement idempotency protection for critical write operations
Some checks are pending
Docker Test / test (push) Waiting to run
Some checks are pending
Docker Test / test (push) Waiting to run
This commit is contained in:
parent
6c25464369
commit
b44e7bf46c
6 changed files with 281 additions and 4 deletions
|
|
@ -2,6 +2,7 @@ import { Router } from 'express';
|
|||
import { z } from 'zod';
|
||||
import { pool } from '../db/connection.js';
|
||||
import { requireAuth } from '../middleware/auth.js';
|
||||
import { requireIdempotencyKey } from '../middleware/idempotency.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
|
|
@ -22,7 +23,7 @@ router.get('/', async (_req, res) => {
|
|||
});
|
||||
|
||||
// POST /help-requests - Create a new help request
|
||||
router.post('/', requireAuth, async (req, res) => {
|
||||
router.post('/', requireAuth, requireIdempotencyKey, async (req, res) => {
|
||||
try {
|
||||
const parsed = z.object({
|
||||
title: z.string().min(3).max(180),
|
||||
|
|
@ -43,6 +44,11 @@ router.post('/', requireAuth, async (req, res) => {
|
|||
[req.user.userId, title, description, valueChf]
|
||||
);
|
||||
|
||||
// Cache the response for idempotent requests
|
||||
if (req.cacheIdempotentResponse) {
|
||||
await req.cacheIdempotentResponse(201, { id: result.insertId });
|
||||
}
|
||||
|
||||
res.status(201).json({ id: result.insertId });
|
||||
} catch (error) {
|
||||
console.error('Error creating help request:', error);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { Router } from 'express';
|
|||
import { z } from 'zod';
|
||||
import { pool } from '../db/connection.js';
|
||||
import { requireAuth } from '../middleware/auth.js';
|
||||
import { requireIdempotencyKey } from '../middleware/idempotency.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
|
|
@ -17,7 +18,7 @@ const negotiateSchema = z.object({
|
|||
message: z.string().max(2000).optional()
|
||||
});
|
||||
|
||||
router.post('/:requestId', requireAuth, async (req, res) => {
|
||||
router.post('/:requestId', requireAuth, requireIdempotencyKey, async (req, res) => {
|
||||
try {
|
||||
const requestId = Number(req.params.requestId);
|
||||
if (Number.isNaN(requestId)) {
|
||||
|
|
@ -39,6 +40,11 @@ router.post('/:requestId', requireAuth, async (req, res) => {
|
|||
|
||||
await pool.query('UPDATE help_requests SET status = ? WHERE id = ?', ['negotiating', requestId]);
|
||||
|
||||
// Cache the response for idempotent requests
|
||||
if (req.cacheIdempotentResponse) {
|
||||
await req.cacheIdempotentResponse(201, { id: result.insertId });
|
||||
}
|
||||
|
||||
res.status(201).json({ id: result.insertId });
|
||||
} catch (error) {
|
||||
console.error('Error in POST /offers/:requestId:', error);
|
||||
|
|
@ -46,7 +52,7 @@ router.post('/:requestId', requireAuth, async (req, res) => {
|
|||
}
|
||||
});
|
||||
|
||||
router.post('/negotiation/:offerId', requireAuth, async (req, res) => {
|
||||
router.post('/negotiation/:offerId', requireAuth, requireIdempotencyKey, async (req, res) => {
|
||||
try {
|
||||
const offerId = Number(req.params.offerId);
|
||||
if (Number.isNaN(offerId)) {
|
||||
|
|
@ -68,6 +74,11 @@ router.post('/negotiation/:offerId', requireAuth, async (req, res) => {
|
|||
|
||||
await pool.query('UPDATE offers SET status = ? WHERE id = ?', ['countered', offerId]);
|
||||
|
||||
// Cache the response for idempotent requests
|
||||
if (req.cacheIdempotentResponse) {
|
||||
await req.cacheIdempotentResponse(201, { id: result.insertId });
|
||||
}
|
||||
|
||||
res.status(201).json({ id: result.insertId });
|
||||
} catch (error) {
|
||||
console.error('Error in POST /offers/negotiation/:offerId:', error);
|
||||
|
|
@ -75,7 +86,7 @@ router.post('/negotiation/:offerId', requireAuth, async (req, res) => {
|
|||
}
|
||||
});
|
||||
|
||||
router.post('/accept/:offerId', requireAuth, async (req, res) => {
|
||||
router.post('/accept/:offerId', requireAuth, requireIdempotencyKey, async (req, res) => {
|
||||
try {
|
||||
const offerId = Number(req.params.offerId);
|
||||
if (Number.isNaN(offerId)) {
|
||||
|
|
@ -99,6 +110,11 @@ router.post('/accept/:offerId', requireAuth, async (req, res) => {
|
|||
[offer.request_id, offer.id, offer.amount_chf]
|
||||
);
|
||||
|
||||
// Cache the response for idempotent requests
|
||||
if (req.cacheIdempotentResponse) {
|
||||
await req.cacheIdempotentResponse(201, { dealId: dealResult.insertId });
|
||||
}
|
||||
|
||||
res.status(201).json({ dealId: dealResult.insertId });
|
||||
} catch (error) {
|
||||
console.error('Error in POST /offers/accept/:offerId:', error);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue