122 lines
4.4 KiB
JavaScript
122 lines
4.4 KiB
JavaScript
|
|
// Feedback route — proxies bug reports and feature requests to GitLab Issues API
|
||
|
|
// Keeps the GitLab PAT server-side so it's never exposed to the browser.
|
||
|
|
|
||
|
|
const express = require('express');
|
||
|
|
const https = require('https');
|
||
|
|
const http = require('http');
|
||
|
|
|
||
|
|
function createFeedbackRouter(db, requireAuth) {
|
||
|
|
const router = express.Router();
|
||
|
|
|
||
|
|
const GITLAB_URL = process.env.GITLAB_URL || '';
|
||
|
|
const GITLAB_PROJECT_ID = process.env.GITLAB_PROJECT_ID || '';
|
||
|
|
const GITLAB_PAT = process.env.GITLAB_PAT || '';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* POST /api/feedback
|
||
|
|
*
|
||
|
|
* Create a GitLab issue from a bug report or feature request.
|
||
|
|
* Available to all authenticated users.
|
||
|
|
*
|
||
|
|
* @body {string} type - "bug" or "feature"
|
||
|
|
* @body {string} title - Issue title
|
||
|
|
* @body {string} description - Issue description
|
||
|
|
* @body {string} [page] - Which dashboard page the user was on
|
||
|
|
*/
|
||
|
|
router.post('/', requireAuth, async (req, res) => {
|
||
|
|
if (!GITLAB_URL || !GITLAB_PROJECT_ID || !GITLAB_PAT) {
|
||
|
|
return res.status(503).json({ error: 'Feedback integration not configured' });
|
||
|
|
}
|
||
|
|
|
||
|
|
const { type, title, description, page } = req.body;
|
||
|
|
|
||
|
|
if (!type || !title || !description) {
|
||
|
|
return res.status(400).json({ error: 'type, title, and description are required' });
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!['bug', 'feature'].includes(type)) {
|
||
|
|
return res.status(400).json({ error: 'type must be "bug" or "feature"' });
|
||
|
|
}
|
||
|
|
|
||
|
|
const labels = type === 'bug' ? 'bug' : 'enhancement';
|
||
|
|
const prefix = type === 'bug' ? '🐛 Bug' : '✨ Feature Request';
|
||
|
|
const username = req.user?.username || 'unknown';
|
||
|
|
|
||
|
|
const body = [
|
||
|
|
`**Submitted by:** ${username}`,
|
||
|
|
page ? `**Page:** ${page}` : null,
|
||
|
|
`**Type:** ${prefix}`,
|
||
|
|
'',
|
||
|
|
'---',
|
||
|
|
'',
|
||
|
|
description,
|
||
|
|
].filter(Boolean).join('\n');
|
||
|
|
|
||
|
|
const postData = JSON.stringify({
|
||
|
|
title: `[${prefix}] ${title}`,
|
||
|
|
description: body,
|
||
|
|
labels,
|
||
|
|
});
|
||
|
|
|
||
|
|
const apiUrl = `${GITLAB_URL.replace(/\/$/, '')}/api/v4/projects/${encodeURIComponent(GITLAB_PROJECT_ID)}/issues`;
|
||
|
|
|
||
|
|
try {
|
||
|
|
const result = await new Promise((resolve, reject) => {
|
||
|
|
const parsed = new URL(apiUrl);
|
||
|
|
const transport = parsed.protocol === 'https:' ? https : http;
|
||
|
|
|
||
|
|
const reqOpts = {
|
||
|
|
method: 'POST',
|
||
|
|
hostname: parsed.hostname,
|
||
|
|
port: parsed.port,
|
||
|
|
path: parsed.pathname + parsed.search,
|
||
|
|
headers: {
|
||
|
|
'Content-Type': 'application/json',
|
||
|
|
'PRIVATE-TOKEN': GITLAB_PAT,
|
||
|
|
'Content-Length': Buffer.byteLength(postData),
|
||
|
|
},
|
||
|
|
rejectAuthorized: false,
|
||
|
|
};
|
||
|
|
|
||
|
|
const apiReq = transport.request(reqOpts, (apiRes) => {
|
||
|
|
let data = '';
|
||
|
|
apiRes.on('data', chunk => data += chunk);
|
||
|
|
apiRes.on('end', () => {
|
||
|
|
try {
|
||
|
|
resolve({ status: apiRes.statusCode, body: JSON.parse(data) });
|
||
|
|
} catch {
|
||
|
|
resolve({ status: apiRes.statusCode, body: data });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
apiReq.on('error', reject);
|
||
|
|
apiReq.write(postData);
|
||
|
|
apiReq.end();
|
||
|
|
});
|
||
|
|
|
||
|
|
if (result.status === 201) {
|
||
|
|
console.log(`[Feedback] Issue #${result.body.iid} created by ${username}: ${title}`);
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
issue: {
|
||
|
|
id: result.body.iid,
|
||
|
|
url: result.body.web_url,
|
||
|
|
title: result.body.title,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
console.error(`[Feedback] GitLab API returned ${result.status}:`, result.body);
|
||
|
|
res.status(502).json({ error: 'GitLab API error', details: result.body });
|
||
|
|
}
|
||
|
|
} catch (err) {
|
||
|
|
console.error('[Feedback] Request failed:', err.message);
|
||
|
|
res.status(502).json({ error: 'Failed to connect to GitLab' });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
return router;
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = createFeedbackRouter;
|