const express = require('express'); const { body, param, query } = require('express-validator'); const { pool } = require('../models/db'); const { authenticate, requireRole } = require('../middleware/auth'); const validate = require('../middleware/validate'); const Notifications = require('../models/notifications'); const { sendNotificationEmail } = require('../utils/email'); const router = express.Router(); // Submit a bug report (authenticated users) router.post('/', authenticate, validate([ body('title').trim().isLength({ min: 5, max: 200 }).withMessage('Title must be 5-200 characters'), body('description').trim().isLength({ min: 10, max: 5000 }).withMessage('Description must be 10-5000 characters'), body('pageUrl').optional().trim().isLength({ max: 500 }) ]), async (req, res, next) => { try { const { title, description, pageUrl } = req.body; const result = await pool.query( `INSERT INTO bug_reports (user_id, title, description, page_url) VALUES ($1, $2, $3, $4) RETURNING *`, [req.user.id, title, description, pageUrl || null] ); res.status(201).json({ message: 'Bug report submitted. Thank you for helping improve GamerComp!', report: result.rows[0] }); } catch (error) { next(error); } } ); // Get my bug reports (authenticated users) router.get('/my-reports', authenticate, async (req, res, next) => { try { const result = await pool.query( `SELECT id, title, status, priority, created_at, resolved_at FROM bug_reports WHERE user_id = $1 ORDER BY created_at DESC`, [req.user.id] ); res.json({ reports: result.rows }); } catch (error) { next(error); } } ); // Admin: Get all bug reports router.get('/admin', authenticate, requireRole('admin'), async (req, res, next) => { try { const { status, priority } = req.query; let query = ` SELECT br.*, u.username as reporter_username, u.email as reporter_email, resolver.username as resolver_username FROM bug_reports br LEFT JOIN users u ON br.user_id = u.id LEFT JOIN users resolver ON br.resolved_by = resolver.id WHERE 1=1 `; const params = []; if (status && status !== 'all') { params.push(status); query += ` AND br.status = $${params.length}`; } if (priority && priority !== 'all') { params.push(priority); query += ` AND br.priority = $${params.length}`; } query += ` ORDER BY CASE br.priority WHEN 'critical' THEN 1 WHEN 'high' THEN 2 WHEN 'normal' THEN 3 WHEN 'low' THEN 4 END, br.created_at DESC`; const result = await pool.query(query, params); // Get counts by status const countsResult = await pool.query(` SELECT status, COUNT(*) as count FROM bug_reports GROUP BY status `); const counts = {}; countsResult.rows.forEach(row => { counts[row.status] = parseInt(row.count); }); res.json({ reports: result.rows, counts }); } catch (error) { next(error); } } ); // Admin: Update bug report status router.put('/admin/:id', authenticate, requireRole('admin'), validate([ param('id').isInt(), body('status').isIn(['open', 'in_progress', 'resolved', 'closed', 'wont_fix']), body('priority').optional().isIn(['low', 'normal', 'high', 'critical']), body('adminNotes').optional().trim().isLength({ max: 2000 }) ]), async (req, res, next) => { try { const { id } = req.params; const { status, priority, adminNotes } = req.body; // Get the current bug report const current = await pool.query( 'SELECT * FROM bug_reports WHERE id = $1', [id] ); if (current.rows.length === 0) { return res.status(404).json({ error: 'Bug report not found' }); } const bug = current.rows[0]; const wasResolved = ['resolved', 'closed'].includes(bug.status); const isNowResolved = ['resolved', 'closed'].includes(status); // Update the bug report const isResolvingNow = ['resolved', 'closed'].includes(status); const result = await pool.query( `UPDATE bug_reports SET status = $2, priority = COALESCE($3, priority), admin_notes = COALESCE($4, admin_notes), resolved_at = CASE WHEN $5 AND resolved_at IS NULL THEN NOW() ELSE resolved_at END, resolved_by = CASE WHEN $5 AND resolved_by IS NULL THEN $6 ELSE resolved_by END, updated_at = NOW() WHERE id = $1 RETURNING *`, [id, status, priority || null, adminNotes || null, isResolvingNow, req.user.id] ); // If status changed to resolved/closed and user exists, notify them if (!wasResolved && isNowResolved && bug.user_id) { const statusText = status === 'resolved' ? 'has been fixed' : 'has been closed'; const notifMessage = `Your bug report "${bug.title}" ${statusText}. Thank you for helping improve GamerComp!`; await Notifications.create({ userId: bug.user_id, type: 'bug_resolved', title: 'Bug Report Updated', message: notifMessage, link: null }); // Send email notification const userResult = await pool.query('SELECT email FROM users WHERE id = $1', [bug.user_id]); if (userResult.rows.length > 0 && userResult.rows[0].email) { sendNotificationEmail( { email: userResult.rows[0].email }, { title: 'Bug Report Updated', message: notifMessage } ).catch(() => {}); } } res.json({ message: 'Bug report updated', report: result.rows[0] }); } catch (error) { next(error); } } ); // Admin: Delete bug report router.delete('/admin/:id', authenticate, requireRole('admin'), validate([param('id').isInt()]), async (req, res, next) => { try { const { id } = req.params; await pool.query('DELETE FROM bug_reports WHERE id = $1', [id]); res.json({ message: 'Bug report deleted' }); } catch (error) { next(error); } } ); module.exports = router;