// 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'); const { requireAuth } = require('../middleware/auth'); function createFeedbackRouter() { 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 || ''; 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;