diff --git a/backend/sql/dispute-schema.sql b/backend/sql/dispute-schema.sql deleted file mode 100644 index 8342140..0000000 --- a/backend/sql/dispute-schema.sql +++ /dev/null @@ -1,28 +0,0 @@ -CREATE TABLE IF NOT EXISTS disputes ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - deal_id BIGINT NOT NULL, - opened_by_user_id BIGINT NOT NULL, - status ENUM('open','evidence','mediation','resolved','cancelled') NOT NULL DEFAULT 'open', - reason_code VARCHAR(64) NOT NULL, - summary TEXT NOT NULL, - requested_outcome VARCHAR(64) NOT NULL, - final_decision VARCHAR(64) NULL, - final_reason TEXT NULL, - decided_by_user_id BIGINT NULL, - decided_at TIMESTAMP NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - FOREIGN KEY (deal_id) REFERENCES deals(id), - FOREIGN KEY (opened_by_user_id) REFERENCES users(id) -); - -CREATE TABLE IF NOT EXISTS dispute_events ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - dispute_id BIGINT NOT NULL, - event_type VARCHAR(64) NOT NULL, - actor_user_id BIGINT NOT NULL, - payload_json JSON NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (dispute_id) REFERENCES disputes(id), - FOREIGN KEY (actor_user_id) REFERENCES users(id) -); \ No newline at end of file diff --git a/backend/src/disputes/dispute-service.js b/backend/src/disputes/dispute-service.js deleted file mode 100644 index 77cc58c..0000000 --- a/backend/src/disputes/dispute-service.js +++ /dev/null @@ -1,68 +0,0 @@ -import { DB } from '../db/index.js'; - -export class DisputeService { - constructor(db) { - this.db = db; - } - - async createDispute(disputeData) { - const { deal_id, opened_by_user_id, reason_code, summary, requested_outcome } = disputeData; - - const result = await this.db.query( - `INSERT INTO disputes (deal_id, opened_by_user_id, reason_code, summary, requested_outcome) - VALUES (?, ?, ?, ?, ?)`, - [deal_id, opened_by_user_id, reason_code, summary, requested_outcome] - ); - - const disputeId = result.insertId; - return await this.getDisputeById(disputeId); - } - - async getDisputeById(id) { - const [rows] = await this.db.query('SELECT * FROM disputes WHERE id = ?', [id]); - return rows.length > 0 ? rows[0] : null; - } - - async updateDisputeStatus(disputeId, status, updatedByUserId) { - await this.db.query( - 'UPDATE disputes SET status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?', - [status, disputeId] - ); - - // Log the status change as an event - await this.logDisputeEvent(disputeId, 'status_change', updatedByUserId, { - old_status: 'open', - new_status: status - }); - } - - async addEvidence(disputeId, userId, evidenceData) { - // Log the evidence addition as an event - await this.logDisputeEvent(disputeId, 'evidence_added', userId, evidenceData); - } - - async resolveDispute(disputeId, resolvedByUserId, decision, reason) { - await this.db.query( - `UPDATE disputes - SET status = 'resolved', final_decision = ?, final_reason = ?, decided_by_user_id = ?, decided_at = CURRENT_TIMESTAMP - WHERE id = ?`, - [decision, reason, resolvedByUserId, disputeId] - ); - - // Log the resolution as an event - await this.logDisputeEvent(disputeId, 'resolved', resolvedByUserId, { decision, reason }); - } - - async logDisputeEvent(disputeId, eventType, actorUserId, payload) { - await this.db.query( - `INSERT INTO dispute_events (dispute_id, event_type, actor_user_id, payload_json) - VALUES (?, ?, ?, ?)`, - [disputeId, eventType, actorUserId, JSON.stringify(payload)] - ); - } - - async getDisputeEvents(disputeId) { - const [rows] = await this.db.query('SELECT * FROM dispute_events WHERE dispute_id = ? ORDER BY created_at ASC', [disputeId]); - return rows; - } -} \ No newline at end of file diff --git a/backend/src/disputes/dispute-service.ts b/backend/src/disputes/dispute-service.ts deleted file mode 100644 index f0c71c5..0000000 --- a/backend/src/disputes/dispute-service.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { DB } from '../db'; -import { Dispute, DisputeEvent, DisputeStatus, DisputeReasonCode, DisputeOutcome } from './types'; - -export class DisputeService { - constructor(private db: DB) {} - - async createDispute(disputeData: Omit): Promise { - const { deal_id, opened_by_user_id, reason_code, summary, requested_outcome } = disputeData; - - const result = await this.db.query( - `INSERT INTO disputes (deal_id, opened_by_user_id, reason_code, summary, requested_outcome) - VALUES (?, ?, ?, ?, ?)`, - [deal_id, opened_by_user_id, reason_code, summary, requested_outcome] - ); - - const disputeId = result.insertId; - return await this.getDisputeById(disputeId); - } - - async getDisputeById(id: number): Promise { - const [rows] = await this.db.query('SELECT * FROM disputes WHERE id = ?', [id]); - return rows.length > 0 ? rows[0] as Dispute : null; - } - - async updateDisputeStatus(disputeId: number, status: DisputeStatus, updatedByUserId: number): Promise { - await this.db.query( - 'UPDATE disputes SET status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?', - [status, disputeId] - ); - - // Log the status change as an event - await this.logDisputeEvent(disputeId, 'status_change', updatedByUserId, { - old_status: 'open', - new_status: status - }); - } - - async addEvidence(disputeId: number, userId: number, evidenceData: any): Promise { - // Log the evidence addition as an event - await this.logDisputeEvent(disputeId, 'evidence_added', userId, evidenceData); - } - - async resolveDispute(disputeId: number, resolvedByUserId: number, decision: DisputeOutcome, reason: string): Promise { - await this.db.query( - `UPDATE disputes - SET status = 'resolved', final_decision = ?, final_reason = ?, decided_by_user_id = ?, decided_at = CURRENT_TIMESTAMP - WHERE id = ?`, - [decision, reason, resolvedByUserId, disputeId] - ); - - // Log the resolution as an event - await this.logDisputeEvent(disputeId, 'resolved', resolvedByUserId, { decision, reason }); - } - - async logDisputeEvent(disputeId: number, eventType: string, actorUserId: number, payload: any): Promise { - await this.db.query( - `INSERT INTO dispute_events (dispute_id, event_type, actor_user_id, payload_json) - VALUES (?, ?, ?, ?)`, - [disputeId, eventType, actorUserId, JSON.stringify(payload)] - ); - } - - async getDisputeEvents(disputeId: number): Promise { - const [rows] = await this.db.query('SELECT * FROM dispute_events WHERE dispute_id = ? ORDER BY created_at ASC', [disputeId]); - return rows as DisputeEvent[]; - } -} \ No newline at end of file diff --git a/backend/src/disputes/index.ts b/backend/src/disputes/index.ts deleted file mode 100644 index 073ba43..0000000 --- a/backend/src/disputes/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './dispute-service'; -export * from './types'; \ No newline at end of file diff --git a/backend/src/disputes/types.js b/backend/src/disputes/types.js deleted file mode 100644 index 2dee539..0000000 --- a/backend/src/disputes/types.js +++ /dev/null @@ -1,51 +0,0 @@ -export const DisputeStatus = { - OPEN: 'open', - EVIDENCE: 'evidence', - MEDIATION: 'mediation', - RESOLVED: 'resolved', - CANCELLED: 'cancelled' -}; - -export const DisputeReasonCode = { - NO_SHOW: 'NO_SHOW', - QUALITY_ISSUE: 'QUALITY_ISSUE', - PAYMENT_DISPUTE: 'PAYMENT_DISPUTE', - ABUSE: 'ABUSE', - OTHER: 'OTHER' -}; - -export const DisputeOutcome = { - REFUND: 'refund', - PARTIAL_REFUND: 'partial_refund', - COMPLETE_WORK: 'complete_work', - OTHER: 'other' -}; - -export class Dispute { - constructor(id, deal_id, opened_by_user_id, status, reason_code, summary, requested_outcome, final_decision = null, final_reason = null, decided_by_user_id = null, decided_at = null, created_at, updated_at) { - this.id = id; - this.deal_id = deal_id; - this.opened_by_user_id = opened_by_user_id; - this.status = status; - this.reason_code = reason_code; - this.summary = summary; - this.requested_outcome = requested_outcome; - this.final_decision = final_decision; - this.final_reason = final_reason; - this.decided_by_user_id = decided_by_user_id; - this.decided_at = decided_at; - this.created_at = created_at; - this.updated_at = updated_at; - } -} - -export class DisputeEvent { - constructor(id, dispute_id, event_type, actor_user_id, payload_json, created_at) { - this.id = id; - this.dispute_id = dispute_id; - this.event_type = event_type; - this.actor_user_id = actor_user_id; - this.payload_json = payload_json; - this.created_at = created_at; - } -} \ No newline at end of file diff --git a/backend/src/disputes/types.ts b/backend/src/disputes/types.ts deleted file mode 100644 index 16d759e..0000000 --- a/backend/src/disputes/types.ts +++ /dev/null @@ -1,30 +0,0 @@ -export type DisputeStatus = 'open' | 'evidence' | 'mediation' | 'resolved' | 'cancelled'; - -export type DisputeReasonCode = 'NO_SHOW' | 'QUALITY_ISSUE' | 'PAYMENT_DISPUTE' | 'ABUSE' | 'OTHER'; - -export type DisputeOutcome = 'refund' | 'partial_refund' | 'complete_work' | 'other'; - -export interface Dispute { - id: number; - deal_id: number; - opened_by_user_id: number; - status: DisputeStatus; - reason_code: DisputeReasonCode; - summary: string; - requested_outcome: DisputeOutcome; - final_decision?: DisputeOutcome | null; - final_reason?: string | null; - decided_by_user_id?: number | null; - decided_at?: string | null; - created_at: string; - updated_at: string; -} - -export interface DisputeEvent { - id: number; - dispute_id: number; - event_type: string; - actor_user_id: number; - payload_json: string; - created_at: string; -} \ No newline at end of file diff --git a/backend/src/routes/disputes.js b/backend/src/routes/disputes.js deleted file mode 100644 index 09fe0bd..0000000 --- a/backend/src/routes/disputes.js +++ /dev/null @@ -1,103 +0,0 @@ -import express from 'express'; -import { DisputeService } from '../disputes/dispute-service.js'; -import { DB } from '../db/index.js'; - -const router = express.Router(); -const disputeService = new DisputeService(new DB()); - -// Create a new dispute -router.post('/', async (req, res) => { - try { - const { deal_id, opened_by_user_id, reason_code, summary, requested_outcome } = req.body; - - const dispute = await disputeService.createDispute({ - deal_id, - opened_by_user_id, - reason_code, - summary, - requested_outcome - }); - - res.status(201).json(dispute); - } catch (error) { - console.error('Error creating dispute:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Get dispute by ID -router.get('/:id', async (req, res) => { - try { - const { id } = req.params; - const dispute = await disputeService.getDisputeById(parseInt(id)); - - if (!dispute) { - return res.status(404).json({ error: 'Dispute not found' }); - } - - res.json(dispute); - } catch (error) { - console.error('Error fetching dispute:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Update dispute status -router.post('/:id/status', async (req, res) => { - try { - const { id } = req.params; - const { status, updated_by_user_id } = req.body; - - await disputeService.updateDisputeStatus(parseInt(id), status, updated_by_user_id); - - res.json({ message: 'Dispute status updated successfully' }); - } catch (error) { - console.error('Error updating dispute status:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Add evidence to a dispute -router.post('/:id/evidence', async (req, res) => { - try { - const { id } = req.params; - const { user_id, evidence_data } = req.body; - - await disputeService.addEvidence(parseInt(id), user_id, evidence_data); - - res.json({ message: 'Evidence added successfully' }); - } catch (error) { - console.error('Error adding evidence:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Resolve a dispute -router.post('/:id/resolve', async (req, res) => { - try { - const { id } = req.params; - const { resolved_by_user_id, decision, reason } = req.body; - - await disputeService.resolveDispute(parseInt(id), resolved_by_user_id, decision, reason); - - res.json({ message: 'Dispute resolved successfully' }); - } catch (error) { - console.error('Error resolving dispute:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Get dispute events -router.get('/:id/events', async (req, res) => { - try { - const { id } = req.params; - const events = await disputeService.getDisputeEvents(parseInt(id)); - - res.json(events); - } catch (error) { - console.error('Error fetching dispute events:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -export default router; \ No newline at end of file diff --git a/backend/src/routes/disputes.ts b/backend/src/routes/disputes.ts deleted file mode 100644 index 959342f..0000000 --- a/backend/src/routes/disputes.ts +++ /dev/null @@ -1,103 +0,0 @@ -import express, { Request, Response } from 'express'; -import { DisputeService } from '../disputes/dispute-service'; -import { DB } from '../db'; - -const router = express.Router(); -const disputeService = new DisputeService(new DB()); - -// Create a new dispute -router.post('/', async (req: Request, res: Response) => { - try { - const { deal_id, opened_by_user_id, reason_code, summary, requested_outcome } = req.body; - - const dispute = await disputeService.createDispute({ - deal_id, - opened_by_user_id, - reason_code, - summary, - requested_outcome - }); - - res.status(201).json(dispute); - } catch (error) { - console.error('Error creating dispute:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Get dispute by ID -router.get('/:id', async (req: Request, res: Response) => { - try { - const { id } = req.params; - const dispute = await disputeService.getDisputeById(parseInt(id)); - - if (!dispute) { - return res.status(404).json({ error: 'Dispute not found' }); - } - - res.json(dispute); - } catch (error) { - console.error('Error fetching dispute:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Update dispute status -router.post('/:id/status', async (req: Request, res: Response) => { - try { - const { id } = req.params; - const { status, updated_by_user_id } = req.body; - - await disputeService.updateDisputeStatus(parseInt(id), status, updated_by_user_id); - - res.json({ message: 'Dispute status updated successfully' }); - } catch (error) { - console.error('Error updating dispute status:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Add evidence to a dispute -router.post('/:id/evidence', async (req: Request, res: Response) => { - try { - const { id } = req.params; - const { user_id, evidence_data } = req.body; - - await disputeService.addEvidence(parseInt(id), user_id, evidence_data); - - res.json({ message: 'Evidence added successfully' }); - } catch (error) { - console.error('Error adding evidence:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Resolve a dispute -router.post('/:id/resolve', async (req: Request, res: Response) => { - try { - const { id } = req.params; - const { resolved_by_user_id, decision, reason } = req.body; - - await disputeService.resolveDispute(parseInt(id), resolved_by_user_id, decision, reason); - - res.json({ message: 'Dispute resolved successfully' }); - } catch (error) { - console.error('Error resolving dispute:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Get dispute events -router.get('/:id/events', async (req: Request, res: Response) => { - try { - const { id } = req.params; - const events = await disputeService.getDisputeEvents(parseInt(id)); - - res.json(events); - } catch (error) { - console.error('Error fetching dispute events:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -export default router; \ No newline at end of file diff --git a/backend/src/server.js b/backend/src/server.js index 5178f9c..4d0b590 100644 --- a/backend/src/server.js +++ b/backend/src/server.js @@ -7,7 +7,6 @@ import reviewRoutes from './routes/reviews.js'; import addressRoutes from './routes/addresses.js'; import contactRoutes from './routes/contacts.js'; import profileRoutes from './routes/profile.js'; -import disputeRoutes from './routes/disputes.js'; // import { requestLogger } from './middleware/logger.js'; // Temporarily removed for compatibility import { rateLimit, authRateLimit } from '../middleware/rateLimit.cjs'; import { requireRole } from '../middleware/role.middleware.js'; @@ -51,7 +50,6 @@ app.use('/reviews', rateLimit({ max: 50 }), reviewRoutes); app.use('/addresses', rateLimit({ max: 50 }), addressRoutes); app.use('/contacts', rateLimit({ max: 50 }), contactRoutes); app.use('/profile', rateLimit({ max: 50 }), profileRoutes); -app.use('/disputes', rateLimit({ max: 50 }), disputeRoutes); const port = Number(process.env.PORT || 3000); app.listen(port, () => {