Initial commit
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user