Skip to content

Bug Report System Architecture & Developer Guide ​

In-app bug reporting for customer dashboard users. Reports are persisted to MongoDB, attachments uploaded to S3, and email notifications (with attachments) sent to the org admin via the existing email service.


Why ​

Customers previously had no in-app way to report bugs they relied on external channels (email, Slack, WhatsApp). This created friction, lost context (no device/browser info), and made it hard to track reports per org. The bug report system solves this by embedding a structured form directly in the dashboard with automatic metadata collection.


Architecture Overview ​


How It Works ​

1. User Flow ​

  1. User clicks "Report Bug" button in the top bar (between Help and Credits)
  2. A slide-in Sheet panel opens from the right with a smooth transition
  3. Metadata (email, userId, orgId, device, browser, page path) is auto-collected on open
  4. User fills in Summary, Details (max 2000 words), and Severity (Critical/High/Medium/Low)
  5. User optionally attaches up to 5 files (images/videos, max 5MB each) via drag-drop or paste
  6. On submit: form data + metadata + files sent as multipart/form-data to the API
  7. Success → toast + panel closes + form resets. Error → toast + panel stays open with data preserved.

2. Backend Flow ​

  1. POST /bug-reports hits verifyJwt → uploadMiddleware (multer, 5 files, 5MB limit) → submitBugReport
  2. Controller validates required fields (summary, details, severity)
  3. Creates a BugReport document in MongoDB with status "open"
  4. Uploads each attachment to S3 at path: {orgId}/{userId}/bugreport/{bugReportId}/{filename}
  5. Updates the document with S3 keys
  6. Fires email notification to org admin (fire-and-forget) includes file attachments in the email
  7. Returns 201 { success: true, bugReportId }

3. Email Notification ​

  • Looks up org admin via Organization.ownerUID → User.email
  • HTML email includes: summary, severity (color-coded), reporter email, timestamp, attachment count
  • Actual files are attached to the email (photos/videos viewable by the admin)
  • Sent via BullMQ queue (3 retries with exponential backoff)
  • Failure is logged but does NOT affect the API response

File Structure ​

backend/client_backend/src/
├── models/
│   └── BugReport.ts                    # Mongoose model + IBugReport interface
├── controllers/
│   └── bug-report.controller.ts        # submitBugReport handler
├── routes/
│   └── bug-report.routes.ts            # POST /bug-reports (JWT + multer)
├── services/
│   ├── bug-report-s3.service.ts        # buildBugReportS3Key + uploadBugReportAttachment
│   └── bug-report-email.service.ts     # sendBugReportNotification (with attachments)
├── middleware/
│   └── upload.middleware.ts            # multer config (memory storage, 5MB, MIME filter)
└── app.ts                              # Route registration

app/src/
├── components/features/bug-report/
│   ├── BugReportButton.tsx             # Top bar trigger button
│   ├── BugReportPanel.tsx              # Sheet form (react-hook-form + zod)
│   ├── AttachmentUploader.tsx          # Drag-drop + paste + validation
│   ├── metadataCollector.ts            # Device/browser/page metadata
│   ├── bugReportSchema.ts             # Zod schema (2000 word limit on details)
│   └── index.ts                        # Barrel export
├── services/
│   └── bugReport.service.ts            # API client (FormData POST)
└── layouts/
    └── SidebarLayout.tsx               # Button + Panel integration

Data Model ​

BugReport Collection ​

FieldTypeRequiredDefaultIndexedDescription
summaryStringYesNoBrief bug description
detailsStringYesNoDetailed steps to reproduce
severityStringYes"medium"Nocritical/high/medium/low
attachmentsString[]No[]NoArray of S3 keys
reporterEmailStringYesYesReporter's email
reporterUserIdStringYesYesReporter's user ID
orgIdStringYesYesOrganization ID
deviceInfoStringNo""NoOS name/version
browserInfoStringNo""NoBrowser name/version
pagePathStringNo""NoPage where bug was reported
statusStringNo"open"Noopen/in-progress/resolved/closed
createdAtDateAutoNoMongoose timestamps
updatedAtDateAutoNoMongoose timestamps

S3 Path Format ​

{bucket}/{orgId}/{userId}/bugreport/{bugReportId}/{filename}

Example: oktivo-recordings/org_abc/user_xyz/bugreport/65f2a1b3c4d5e6f7/screenshot.png


API Reference ​

POST /bug-reports ​

Auth: JWT required
Content-Type: multipart/form-data

FieldTypeRequiredDescription
summarystringYesBug title (min 1 char)
detailsstringYesDescription (min 1 char, max 2000 words)
severitystringYescritical / high / medium / low
deviceInfostringNoAuto-collected OS info
browserInfostringNoAuto-collected browser info
pagePathstringNoAuto-collected current page path
attachmentsFile[]NoMax 5 files, 5MB each, image/video only

Allowed MIME types: image/png, image/jpeg, image/gif, image/webp, video/mp4, video/webm

Responses:

StatusBodyWhen
201{ success: true, bugReportId: "..." }Report saved successfully
400{ success: false, error: "..." }Validation failure
500{ success: false, error: "..." }S3 upload or DB failure

Error Handling ​

ScenarioFrontendBackend
Empty required fieldInline validation error (zod)400 response
File too large (>5MB)Toast error, file rejected400 from multer
Invalid MIME typeToast error, file rejected400 from multer
S3 upload failureError toast, form preserved500 response
DB save failureError toast, form preserved500 response
Email send failureNot visible to user (fire-and-forget)Logged, still returns 201
Network errorError toast, form data preserved, panel open

Validation Rules ​

FieldFrontend (Zod)Backend (Controller)
summarymin 1 charnon-empty string
detailsmin 1 char, max 2000 wordsnon-empty string
severityenum: critical/high/medium/lowmust be in valid set
filesMIME whitelist + 5MB + max 5 filesmulter fileFilter + limits

UI/UX Details ​

  • Location: Top bar button between Help and Credits in SidebarLayout
  • Panel: Sheet component sliding in from the right side
  • Transitions: Smooth slide animation (500ms open, 300ms close) with backdrop blur via tw-animate-css
  • Word counter: Live "X / 2000 words" display below the Details textarea
  • Attachments: Drag-and-drop zone + clipboard paste, image thumbnails, remove button per file
  • Loading state: Submit button disabled with spinner during submission
  • Success: Toast notification → panel auto-closes → form resets
  • Error: Toast with error message → panel stays open → all form data preserved for retry

Dependencies Added ​

PackagePurposeLocation
tw-animate-cssTailwind v4 animation utilities (slide/fade)app

Configuration ​

No new environment variables required. Uses existing:

  • S3 config (bucket, region, credentials) from config/index.ts
  • Email config (Resend API key, default from) from email service initialization
  • JWT middleware for authentication

Future Considerations ​

  • Admin dashboard to view/manage/resolve bug reports
  • Status update notifications back to the reporter
  • Rate limiting on bug report submissions (prevent spam)
  • File type expansion (PDF, logs)
  • Bug report categories/tags for better organization

Last updated: