- Fix inconsistent screenshotUrl fields (removed, SAS generated on-demand) - Fix blob path pattern to match feedbackScreenshots container - Clarify flow: direct upload to final container (no temp/move) - Add rate limiting specs to endpoint table - Clarify access control: users submit but cannot view (security) - Remove sas.ts from appendix (not created) - Align size limits to 5MB consistently - Add missing screenshotContentType and screenshotSizeBytes
12 KiB
12 KiB
User-Initiated Issue Reporting with Screenshots — Implementation Roadmap
Location:
docs/devops/USER_ISSUE_REPORTING_ROADMAP.md
Status: Draft
Created: 2026-03-02
Overview
Enable users to self-report issues with automatic/manual screenshots and comments. This extends the existing feedback module to support rich media attachments, leveraging the blob module for Azure Blob storage.
Use Cases
- Bug Report with Screenshot — User encounters error → taps "Report Issue" → screenshot captured → typed description submitted
- Feature Request with Mockup — User uploads annotated screenshot showing desired UI change
- Crash Auto-Report — App crashes → on restart, user prompted to "Send crash report with screenshot"
- Support Chat Attachment — Screenshots attached to existing feedback for back-and-forth with support team
Current State
Existing Feedback Module
| Component | Status |
|---|---|
POST /api/feedback |
✅ Creates feedback (type, title, body, screen) |
GET /api/feedback |
✅ Admin list/query |
PUT /api/feedback/:id |
✅ Admin triage (status, adminNotes) |
| Screenshot support | ❌ Only text screen field |
| Multiple attachments | ❌ Not supported |
| Threaded comments | ❌ Not supported |
Existing Blob Module (Reusable)
| Component | Status |
|---|---|
POST /api/blob/sas |
✅ Generate SAS URL for direct upload |
| Azure Blob integration | ✅ Storage + lifecycle |
| Container management | ✅ Per-product isolation |
Target Architecture
User Flow:
1. User taps "Report Issue" in app
2. Client captures screenshot (optional annotation)
3. Client requests SAS URL: POST /api/feedback/sas
4. Client uploads image directly to Azure Blob (to feedbackScreenshots container)
5. Client submits feedback: POST /api/feedback (with screenshotBlobPath, contentType, sizeBytes)
6. Admin views feedback in dashboard with image preview (fresh SAS URL generated on-demand)
Data Model Changes
Option A: Inline Screenshot (Simple)
// feedback/types.ts — Add to existing FeedbackDoc
interface FeedbackDoc {
// ... existing fields ...
// Screenshot attachment (single) — SAS URLs generated on-demand, not stored
screenshotBlobPath?: string; // "feedbackScreenshots/{productId}/{feedbackId}/{screenshotId}.png"
screenshotContentType?: 'image/png' | 'image/jpeg' | 'image/webp';
screenshotSizeBytes?: number; // Actual uploaded size
// Device context for debugging
deviceContext?: {
osVersion: string;
appVersion: string;
deviceModel: string;
screenResolution: string;
locale: string;
};
}
// Add to CreateFeedbackSchema
screenshotBlobPath: z.string().optional(),
screenshotContentType: z.enum(['image/png', 'image/jpeg', 'image/webp']).optional(),
screenshotSizeBytes: z.number().int().min(1).max(5 * 1024 * 1024).optional(), // 5MB limit
deviceContext: z.object({
osVersion: z.string(),
appVersion: z.string(),
deviceModel: z.string(),
screenResolution: z.string(),
locale: z.string(),
}).optional(),
Option B: Separate FeedbackAttachments Container (Extensible)
// feedback/types.ts — New attachment model
interface FeedbackAttachmentDoc {
id: string; // att_<uuid>
feedbackId: string; // Parent feedback (partition key)
productId: string;
// Blob storage
blobPath: string; // "feedback/{productId}/{feedbackId}/{id}.png"
blobUrl: string; // SAS URL (refreshed on fetch)
containerName: string; // e.g., "user-feedback"
// Metadata
fileName: string;
contentType: 'image/png' | 'image/jpeg' | 'image/webp';
sizeBytes: number;
width: number;
height: number;
// Capture context
capturedAt: string;
trigger: 'manual' | 'auto_crash' | 'auto_error';
screenName?: string;
createdAt: string;
}
interface FeedbackDoc {
// ... existing fields ...
// Reference to attachments
attachmentCount: number; // Denormalized counter
hasScreenshot: boolean; // Quick check for UI
}
// API changes
POST /api/feedback/:id/attachments // Add attachment to existing feedback
GET /api/feedback/:id/attachments // List attachments
DELETE /api/feedback/:id/attachments/:attId // Remove attachment (admin)
Recommendation: Start with Option A (single screenshot), migrate to Option B if multi-attachment demand arises.
API Specification
New Endpoints
| Method | Endpoint | Auth | Rate Limit | Description |
|---|---|---|---|---|
POST |
/api/feedback/sas |
User | 5 per 10 min | Get SAS URL for screenshot upload |
POST |
/api/feedback |
User | 10 per hour | Submit feedback (with optional screenshotBlobPath) |
GET |
/api/feedback/:id/screenshot |
Admin | 30 per hour | Get fresh SAS URL for viewing screenshot |
DELETE |
/api/feedback/:id/screenshot |
Admin | 10 per hour | Delete screenshot (GDPR/privacy) |
SAS Generation Endpoint
// POST /api/feedback/sas
// Request
{
"contentType": "image/png",
"sizeHint": 1024000 // Optional: 1MB hint for validation
}
// Response (201)
{
"blobPath": "feedbackScreenshots/lysnrai/fb_abc123/screenshot_xyz.png",
"uploadUrl": "https://bytelyst.blob.core.windows.net/...?sv=...",
"expiresIn": 300, // 5 minutes
"maxSizeBytes": 5242880 // 5MB limit
}
Submit Feedback with Screenshot
// POST /api/feedback
// Request
{
"type": "bug",
"title": "App crashes when tapping record",
"body": "Steps: 1. Open app 2. Tap red button 3. Crash",
"screen": "RecordingScreen",
"screenshotBlobPath": "feedbackScreenshots/lysnrai/fb_abc123/screenshot_xyz.png",
"deviceContext": {
"osVersion": "iOS 17.4",
"appVersion": "2.3.1",
"deviceModel": "iPhone15,2",
"screenResolution": "393x852",
"locale": "en-US"
}
}
Implementation Phases
Phase 1: Server Foundation (2-3 days) — ✅ COMPLETE
1.1 Data Model Extension — acfbd7c
- Add
screenshotBlobPathtoFeedbackDocinterface - Add
deviceContexttoFeedbackDocinterface - Update
CreateFeedbackSchemawith new fields - Add
feedback_screenshotscontainer tocosmos-init.ts(if Option B) — Skipped (using blob storage)
1.2 Repository Layer — 8d2ba9c
- Extend
createFeedback()to handle screenshot metadata - Add
generateScreenshotSas()function (wrapper around blob module) - Add
getFeedbackWithScreenshot()with fresh SAS URL generation - Add
hasScreenshot()helper for quick UI checks
1.3 API Routes — cfbaa92
POST /api/feedback/sas— Generate upload URL- Rate limit: 5 requests per 10 minutes per user
- Validate content type (image/* only)
- Return blob path + SAS URL
- Update
POST /api/feedback— Accept screenshot metadata GET /api/feedback/:id/screenshot— Get fresh view URLDELETE /api/feedback/:id/screenshot— Admin delete
1.4 Integration — ✅ Already wired in server.ts
- Wire new routes into
server.ts— Feedback routes already registered - Add blob container
feedbackScreenshotsto blob module config —d876bb0 - Set lifecycle policy: 90-day TTL for user screenshots — TODO: Azure lifecycle policy
1.5 Testing — e712968
- Unit tests for SAS generation (schema validation)
- Unit tests for feedback with screenshot submission
- Integration test: full flow (SAS → upload → submit → view) — TODO: requires blob storage
- GDPR deletion test — TODO: Phase 3
Phase 2: Client SDK Updates (3-4 days)
2.1 TypeScript SDK (@bytelyst/feedback-client)
FeedbackClient.submitWithScreenshot(params)- Internal flow: get SAS → upload blob → submit feedback
- Progress callbacks for upload
FeedbackClient.captureAndSubmit()— Auto-capture current screen
2.2 Swift SDK (iOS)
FeedbackManager.submit(title:body:screenshot:)FeedbackManager.captureScreenshot()— UIImage → blob upload- Annotation overlay (optional drawing on screenshot)
2.3 Kotlin SDK (Android)
FeedbackManager.submitWithScreenshot()- MediaProjection integration for screenshot capture
- Composable for in-app feedback sheet with screenshot preview
Phase 3: Admin Dashboard UI (2-3 days)
3.1 Feedback List Enhancements
- Thumbnail preview of screenshot in list view
- Filter: "Has screenshot" / "No screenshot"
3.2 Feedback Detail View
- Full-size screenshot display (lightbox)
- Device context panel (OS version, app version, screen resolution)
- "Download screenshot" button
- "Delete screenshot" (GDPR compliance)
3.3 Client Library
lib/feedback-client.ts— AddgetScreenshotUrl(feedbackId)
Privacy & Security Considerations
PII Handling
- Screenshots may contain sensitive user data
- Blur regions API (client-side before upload)
- Auto-redact: Detect and blur common PII areas (email fields, phone numbers)
- User consent: "This screenshot may include your data. Continue?"
Retention
- Screenshot TTL: 90 days (match feedback lifecycle)
- On feedback deletion → cascade delete screenshot blob
- User-initiated deletion: "Delete my feedback and screenshot"
Access Control
- Users submit screenshots but cannot directly view/download them (security)
- Admins can view all screenshots for their product via fresh SAS URLs
- SAS URLs are time-limited (15 minutes) and generated on-demand per view
Open Questions
| # | Question | Impact | Suggested Answer |
|---|---|---|---|
| 1 | Max screenshot size? | Storage cost | 5MB limit, WebP compression recommended |
| 2 | Multiple screenshots per feedback? | Complexity | Start with 1, add array support later |
| 3 | Screenshot annotation/drawing? | Client complexity | Phase 2.2 for iOS, others skip |
| 4 | Auto-capture on crash? | Privacy risk | Opt-in only, show preview before submit |
| 5 | Video screen recording? | Storage cost | Future Phase 4, not now |
| 6 | Anonymous feedback allowed? | Auth complexity | No — require auth for accountability |
Success Metrics
- Adoption: 30% of bug reports include screenshot within 3 months
- Resolution time: -20% time-to-resolution for reports with screenshots
- Storage: <100GB/month for screenshots (at 1000 reports/day, 100KB avg)
Appendix: File Changes
New Files
services/platform-service/src/modules/feedback/
└── attachment.types.ts # (if Option B)
Modified Files
services/platform-service/src/modules/feedback/
├── types.ts # Add screenshot fields
├── repository.ts # Add SAS functions
└── routes.ts # Add SAS endpoint
services/platform-service/src/lib/cosmos-init.ts
└── Add feedback_attachments container (if Option B)
services/platform-service/src/server.ts
└── Register feedback SAS routes
Last Updated: 2026-03-02
Bugs Fixed During Review (2026-03-02)
| # | Bug | Fix |
|---|---|---|
| 1 | Inconsistent fields: screenshotUrl/screenshotUrlExpiresAt not used |
Removed - SAS URLs generated on-demand |
| 2 | Blob path pattern mismatch | Updated to feedbackScreenshots/{productId}/{feedbackId}/{screenshotId}.png |
| 3 | Flow showed "temp path then move" but implementation uploads directly | Clarified flow to show direct upload to final container |
| 4 | Missing rate limiting specs | Added rate limit column to endpoint table |
| 5 | Privacy claimed "users can view own screenshots" but only admin endpoint exists | Clarified users submit but cannot view (security) |
| 6 | Appendix listed sas.ts file that wasn't created |
Removed - SAS logic is in routes.ts |
| 7 | Inconsistent size limits: 5MB vs 10MB | Aligned to 5MB throughout |
| 8 | Missing screenshotContentType and screenshotSizeBytes |
Added to data model example |