140 lines
5.9 KiB
JavaScript
140 lines
5.9 KiB
JavaScript
const express = require('express');
|
|
const { errorReportHelpers, logToFile, pool } = require('../mariadb');
|
|
const { validateSession } = require('../sessionModel');
|
|
const { exec } = require('child_process');
|
|
const execPromise = (cmd) => new Promise((resolve, reject) => exec(cmd, { timeout: 30000 }, (err, stdout) => err ? reject(err) : resolve(stdout)));
|
|
const router = express.Router();
|
|
|
|
// Middleware: require valid session (logged-in user)
|
|
const requireSession = async (req, res, next) => {
|
|
const sessionId = req.headers['x-session-id'] || (req.body && req.body.sessionId) || (req.query && req.query.sessionId);
|
|
if (!sessionId) return res.status(401).json({ error: 'Login required' });
|
|
const session = await validateSession(sessionId);
|
|
if (!session || !session.data || !session.data.playerName) {
|
|
return res.status(401).json({ error: 'Invalid or expired session' });
|
|
}
|
|
req.session = session;
|
|
req.sessionPlayerName = session.data.playerName;
|
|
next();
|
|
};
|
|
|
|
// POST /api/errors - Submit error report (requires login)
|
|
router.post('/', requireSession, async (req, res) => {
|
|
try {
|
|
const { title, description, category, pageUrl } = req.body;
|
|
if (!title || !description) {
|
|
return res.status(400).json({ error: 'Title and description are required' });
|
|
}
|
|
const reportId = await errorReportHelpers.create({
|
|
playerName: req.sessionPlayerName,
|
|
title,
|
|
description,
|
|
category: category || 'other',
|
|
pageUrl: pageUrl || ''
|
|
});
|
|
logToFile(`API: Error report #${reportId} created by ${req.sessionPlayerName}`);
|
|
|
|
// Also create a GitHub issue
|
|
let issueUrl = null;
|
|
try {
|
|
const body = [
|
|
`**Category:** ${category || 'other'}\n`,
|
|
`**Reported by:** ${req.sessionPlayerName}\n`,
|
|
`**Page:** ${pageUrl || 'N/A'}\n`,
|
|
`**Description:**\n${description}`
|
|
].join('\n');
|
|
const label = category && category !== 'other' ? category : 'other';
|
|
const result = await execPromise(`cd /nas/git/dwroller && gh issue create --title "Error: ${title}" --body '${body.replace(/'/g, "\\'")}' --label '${label}' --repo alexpolo1/dwroller`);
|
|
// gh issue create outputs: https://github.com/...#issue-xxx
|
|
issueUrl = result.trim();
|
|
logToFile(`API: GitHub issue created: ${issueUrl}`);
|
|
// Update DB with the GitHub issue URL
|
|
await pool.execute('UPDATE error_reports SET github_issue_url = ? WHERE id = ?', [issueUrl, reportId]);
|
|
} catch (ghError) {
|
|
logToFile('API: Failed to create GitHub issue', ghError);
|
|
// Don't fail the request — the DB report still succeeded
|
|
}
|
|
|
|
res.json({ success: true, id: reportId, githubIssueUrl: issueUrl });
|
|
} catch (error) {
|
|
logToFile('API: Error creating report', error);
|
|
res.status(500).json({ error: String(error) });
|
|
}
|
|
});
|
|
|
|
// GET /api/errors - List error reports (requires login, GM sees all, player sees their own)
|
|
router.get('/', requireSession, async (req, res) => {
|
|
try {
|
|
const isGM = req.sessionPlayerName && req.sessionPlayerName.toLowerCase() === 'gm';
|
|
const reports = isGM
|
|
? await errorReportHelpers.getAll()
|
|
: await errorReportHelpers.getByPlayer(req.sessionPlayerName);
|
|
res.json(reports);
|
|
} catch (error) {
|
|
logToFile('API: Error listing reports', error);
|
|
res.status(500).json({ error: String(error) });
|
|
}
|
|
});
|
|
|
|
// GET /api/errors/:id - Get one error report (requires login)
|
|
router.get('/:id', requireSession, async (req, res) => {
|
|
try {
|
|
const report = await errorReportHelpers.getById(parseInt(req.params.id));
|
|
if (!report) return res.status(404).json({ error: 'Not found' });
|
|
// Players can only see their own reports, GM sees all
|
|
const isGM = req.sessionPlayerName && req.sessionPlayerName.toLowerCase() === 'gm';
|
|
if (!isGM && report.player_name !== req.sessionPlayerName) {
|
|
return res.status(403).json({ error: 'Access denied' });
|
|
}
|
|
res.json(report);
|
|
} catch (error) {
|
|
logToFile('API: Error getting report', error);
|
|
res.status(500).json({ error: String(error) });
|
|
}
|
|
});
|
|
|
|
// PUT /api/errors/:id/resolve - Resolve error report (GM only)
|
|
router.put('/:id/resolve', requireSession, async (req, res) => {
|
|
try {
|
|
const isGM = req.sessionPlayerName && req.sessionPlayerName.toLowerCase() === 'gm';
|
|
if (!isGM) return res.status(403).json({ error: 'GM access required' });
|
|
const { note } = req.body;
|
|
await errorReportHelpers.resolve(parseInt(req.params.id), req.sessionPlayerName, note || '');
|
|
logToFile(`API: Error report #${req.params.id} resolved by ${req.sessionPlayerName}`);
|
|
res.json({ success: true });
|
|
} catch (error) {
|
|
logToFile('API: Error resolving report', error);
|
|
res.status(500).json({ error: String(error) });
|
|
}
|
|
});
|
|
|
|
// PUT /api/errors/:id/status - Update status (GM only)
|
|
router.put('/:id/status', requireSession, async (req, res) => {
|
|
try {
|
|
const isGM = req.sessionPlayerName && req.sessionPlayerName.toLowerCase() === 'gm';
|
|
if (!isGM) return res.status(403).json({ error: 'GM access required' });
|
|
const { status } = req.body;
|
|
await errorReportHelpers.updateStatus(parseInt(req.params.id), status);
|
|
res.json({ success: true });
|
|
} catch (error) {
|
|
logToFile('API: Error updating status', error);
|
|
res.status(500).json({ error: String(error) });
|
|
}
|
|
});
|
|
|
|
// DELETE /api/errors/:id - Delete error report (GM only)
|
|
router.delete('/:id', requireSession, async (req, res) => {
|
|
try {
|
|
const isGM = req.sessionPlayerName && req.sessionPlayerName.toLowerCase() === 'gm';
|
|
if (!isGM) return res.status(403).json({ error: 'GM access required' });
|
|
await errorReportHelpers.delete(parseInt(req.params.id));
|
|
logToFile(`API: Error report #${req.params.id} deleted by ${req.sessionPlayerName}`);
|
|
res.json({ success: true });
|
|
} catch (error) {
|
|
logToFile('API: Error deleting report', error);
|
|
res.status(500).json({ error: String(error) });
|
|
}
|
|
});
|
|
|
|
console.log('Error report routes registered');
|
|
module.exports = router; |