github_backup.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. """Pydantic schemas for GitHub backup configuration."""
  2. import re
  3. from datetime import datetime
  4. from pydantic import BaseModel, Field, field_validator
  5. from backend.app.core.compat import StrEnum
  6. class ScheduleType(StrEnum):
  7. """Backup schedule types."""
  8. HOURLY = "hourly"
  9. DAILY = "daily"
  10. WEEKLY = "weekly"
  11. class GitHubBackupConfigCreate(BaseModel):
  12. """Schema for creating/updating GitHub backup config."""
  13. repository_url: str = Field(..., min_length=1, max_length=500, description="GitHub repository URL")
  14. access_token: str = Field(..., min_length=1, description="Personal Access Token")
  15. branch: str = Field(default="main", max_length=100, description="Branch to push to")
  16. schedule_enabled: bool = Field(default=False, description="Enable scheduled backups")
  17. schedule_type: ScheduleType = Field(default=ScheduleType.DAILY, description="Schedule frequency")
  18. backup_kprofiles: bool = Field(default=True, description="Backup K-profiles")
  19. backup_cloud_profiles: bool = Field(default=True, description="Backup Bambu Cloud profiles")
  20. backup_settings: bool = Field(default=False, description="Backup app settings")
  21. backup_spools: bool = Field(default=False, description="Backup spool inventory")
  22. backup_archives: bool = Field(default=False, description="Backup print archive history")
  23. enabled: bool = Field(default=True, description="Enable backup feature")
  24. @field_validator("repository_url")
  25. @classmethod
  26. def validate_repo_url(cls, v: str) -> str:
  27. """Validate GitHub repository URL format."""
  28. # Accept various GitHub URL formats
  29. patterns = [
  30. r"^https://github\.com/[\w.-]+/[\w.-]+(?:\.git)?$",
  31. r"^git@github\.com:[\w.-]+/[\w.-]+(?:\.git)?$",
  32. ]
  33. v = v.strip().rstrip("/")
  34. if not any(re.match(p, v) for p in patterns):
  35. raise ValueError("Invalid GitHub repository URL. Expected format: https://github.com/owner/repo")
  36. return v
  37. class GitHubBackupConfigUpdate(BaseModel):
  38. """Schema for updating GitHub backup config (all fields optional)."""
  39. repository_url: str | None = Field(default=None, max_length=500)
  40. access_token: str | None = Field(default=None)
  41. branch: str | None = Field(default=None, max_length=100)
  42. schedule_enabled: bool | None = None
  43. schedule_type: ScheduleType | None = None
  44. backup_kprofiles: bool | None = None
  45. backup_cloud_profiles: bool | None = None
  46. backup_settings: bool | None = None
  47. backup_spools: bool | None = None
  48. backup_archives: bool | None = None
  49. enabled: bool | None = None
  50. @field_validator("repository_url")
  51. @classmethod
  52. def validate_repo_url(cls, v: str | None) -> str | None:
  53. if v is None:
  54. return v
  55. patterns = [
  56. r"^https://github\.com/[\w.-]+/[\w.-]+(?:\.git)?$",
  57. r"^git@github\.com:[\w.-]+/[\w.-]+(?:\.git)?$",
  58. ]
  59. v = v.strip().rstrip("/")
  60. if not any(re.match(p, v) for p in patterns):
  61. raise ValueError("Invalid GitHub repository URL")
  62. return v
  63. class GitHubBackupConfigResponse(BaseModel):
  64. """Schema for GitHub backup config API response."""
  65. id: int
  66. repository_url: str
  67. has_token: bool = Field(description="Whether an access token is configured")
  68. branch: str
  69. schedule_enabled: bool
  70. schedule_type: str
  71. backup_kprofiles: bool
  72. backup_cloud_profiles: bool
  73. backup_settings: bool
  74. backup_spools: bool
  75. backup_archives: bool
  76. enabled: bool
  77. last_backup_at: datetime | None
  78. last_backup_status: str | None
  79. last_backup_message: str | None
  80. last_backup_commit_sha: str | None
  81. next_scheduled_run: datetime | None
  82. created_at: datetime
  83. updated_at: datetime
  84. class Config:
  85. from_attributes = True
  86. class GitHubBackupLogResponse(BaseModel):
  87. """Schema for backup log API response."""
  88. id: int
  89. config_id: int
  90. started_at: datetime
  91. completed_at: datetime | None
  92. status: str
  93. trigger: str
  94. commit_sha: str | None
  95. files_changed: int
  96. error_message: str | None
  97. class Config:
  98. from_attributes = True
  99. class GitHubBackupStatus(BaseModel):
  100. """Schema for current backup status."""
  101. configured: bool = Field(description="Whether backup is configured")
  102. enabled: bool = Field(description="Whether backup is enabled")
  103. is_running: bool = Field(description="Whether a backup is currently running")
  104. progress: str | None = Field(default=None, description="Current backup progress message")
  105. last_backup_at: datetime | None
  106. last_backup_status: str | None
  107. next_scheduled_run: datetime | None
  108. class GitHubTestConnectionResponse(BaseModel):
  109. """Schema for test connection response."""
  110. success: bool
  111. message: str
  112. repo_name: str | None = None
  113. permissions: dict | None = None
  114. class GitHubBackupTriggerResponse(BaseModel):
  115. """Schema for manual backup trigger response."""
  116. success: bool
  117. message: str
  118. log_id: int | None = None
  119. commit_sha: str | None = None
  120. files_changed: int = 0