bug_report.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. """Bug report endpoint for submitting user bug reports to GitHub."""
  2. import logging
  3. from fastapi import APIRouter, Query
  4. from pydantic import BaseModel
  5. from backend.app.api.routes.support import (
  6. _apply_log_level,
  7. _collect_support_info,
  8. _get_debug_setting,
  9. _get_recent_sanitized_logs,
  10. _set_debug_setting,
  11. )
  12. from backend.app.core.auth import RequirePermissionIfAuthEnabled
  13. from backend.app.core.database import async_session
  14. from backend.app.core.permissions import Permission
  15. from backend.app.models.user import User
  16. from backend.app.services.bug_report import submit_report
  17. from backend.app.services.printer_manager import printer_manager
  18. router = APIRouter(prefix="/bug-report", tags=["bug-report"])
  19. logger = logging.getLogger(__name__)
  20. class BugReportRequest(BaseModel):
  21. description: str
  22. email: str | None = None
  23. screenshot_base64: str | None = None
  24. include_support_info: bool = True
  25. debug_logs: str | None = None
  26. class BugReportResponse(BaseModel):
  27. success: bool
  28. message: str
  29. issue_url: str | None = None
  30. issue_number: int | None = None
  31. class StartLoggingResponse(BaseModel):
  32. started: bool
  33. was_debug: bool
  34. class StopLoggingResponse(BaseModel):
  35. logs: str
  36. @router.post("/start-logging", response_model=StartLoggingResponse)
  37. async def start_logging(
  38. _: User | None = RequirePermissionIfAuthEnabled(Permission.SETTINGS_UPDATE),
  39. ):
  40. """Enable debug logging and push all printers for fresh data."""
  41. async with async_session() as db:
  42. was_debug, _ = await _get_debug_setting(db)
  43. if not was_debug:
  44. async with async_session() as db:
  45. await _set_debug_setting(db, True)
  46. _apply_log_level(True)
  47. logger.info("Bug report: enabled debug logging")
  48. for printer_id in list(printer_manager._clients.keys()):
  49. try:
  50. printer_manager.request_status_update(printer_id)
  51. except Exception:
  52. logger.debug("Failed to push_all for printer %s", printer_id)
  53. return StartLoggingResponse(started=True, was_debug=was_debug)
  54. @router.post("/stop-logging", response_model=StopLoggingResponse)
  55. async def stop_logging(
  56. was_debug: bool = Query(default=False),
  57. _: User | None = RequirePermissionIfAuthEnabled(Permission.SETTINGS_READ),
  58. ):
  59. """Collect logs and restore previous log level."""
  60. logs = await _get_recent_sanitized_logs()
  61. if not was_debug:
  62. async with async_session() as db:
  63. await _set_debug_setting(db, False)
  64. _apply_log_level(False)
  65. logger.info("Bug report: restored normal logging")
  66. return StopLoggingResponse(logs=logs)
  67. @router.post("/submit", response_model=BugReportResponse)
  68. async def submit_bug_report(
  69. report: BugReportRequest,
  70. _: User | None = RequirePermissionIfAuthEnabled(Permission.SETTINGS_READ),
  71. ):
  72. """Submit a bug report. Requires auth when authentication is enabled."""
  73. support_info = None
  74. if report.include_support_info:
  75. try:
  76. support_info = await _collect_support_info()
  77. if report.debug_logs:
  78. support_info["recent_logs"] = report.debug_logs
  79. except Exception:
  80. logger.exception("Failed to collect support info for bug report")
  81. result = await submit_report(
  82. description=report.description,
  83. reporter_email=report.email,
  84. screenshot_base64=report.screenshot_base64,
  85. support_info=support_info,
  86. )
  87. return BugReportResponse(**result)