Files
OpenSquawk/server/api/bug-reports/index.post.ts
itsrubberduck ccab0e7116 feat(pm): add bug-report button with screenshot annotation and admin panel
- New BugReport MongoDB model (comment, contact, userId, screenshot, pmState, status)
- POST /api/bug-reports — authenticated submit; emails emanuel@faktorxmensch.com on receipt
- GET/PATCH /api/admin/bug-reports + /[id] — admin list, detail with screenshot, status toggle
- /pm: "Bug" button in HUD captures viewport screenshot (html2canvas), shows annotation
  canvas where testers can draw arrows; submits comment + contact + state snapshot
- /admin: new "Bug Reports" tab with open-count badge, screenshot expand, "Erledigt" toggle,
  and "In /pm öffnen" link that restores captured engine state via ?restoreBugReport=<id>

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 10:05:25 +02:00

45 lines
1.6 KiB
TypeScript

import { defineEventHandler, readBody, createError } from 'h3'
import { requireUserSession } from '../../utils/auth'
import { BugReport } from '../../models/BugReport'
import { sendMail } from '../../utils/notifications'
export default defineEventHandler(async (event) => {
const user = await requireUserSession(event)
const body = await readBody(event)
const comment = String(body?.comment ?? '').trim()
if (!comment) {
throw createError({ statusCode: 400, statusMessage: 'Kommentar ist erforderlich' })
}
const contact = String(
body?.contact || [user.name, user.email].filter(Boolean).join(' — ')
).slice(0, 200)
const report = await BugReport.create({
comment: comment.slice(0, 4000),
contact,
userId: user._id,
screenshot: body?.screenshot || undefined,
pmState: body?.pmState || undefined,
})
const adminUrl = `${process.env.APP_URL || 'https://app.opensquawk.de'}/admin`
const stateInfo = body?.pmState?.currentStateId
? `State: ${body.pmState.currentStateId} (Flow: ${body.pmState.flowSlug || '—'})`
: ''
await sendMail({
to: 'emanuel@faktorxmensch.com',
subject: `[OpenSquawk Bug] ${contact}`,
html: `<h2>Neuer Bug Report</h2>
<p><strong>Von:</strong> ${contact}</p>
<p><strong>Kommentar:</strong><br>${comment.replace(/\n/g, '<br>')}</p>
${stateInfo ? `<p><strong>${stateInfo}</strong></p>` : ''}
<p><a href="${adminUrl}">Im Admin-Panel ansehen →</a></p>`,
text: `Bug Report von ${contact}\n\n${comment}\n${stateInfo}\n\nAdmin: ${adminUrl}`,
}).catch(() => {})
return { success: true, id: String(report._id) }
})