orca_cloud.py 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. """Schemas for Orca Cloud auth + profile sync endpoints."""
  2. from typing import Literal
  3. from pydantic import BaseModel, Field
  4. # The three OAuth providers Orca's sign-in surface offers. Supabase
  5. # accepts the bare lowercase provider name in the authorize query string.
  6. OrcaOAuthProvider = Literal["google", "apple", "github"]
  7. class OrcaAuthStartRequest(BaseModel):
  8. """Body for ``POST /orca-cloud/auth/start``. Provider defaults to
  9. ``google`` so existing clients that send an empty body keep working."""
  10. provider: OrcaOAuthProvider = Field(default="google", description="OAuth provider to use for sign-in")
  11. class OrcaAuthStartResponse(BaseModel):
  12. """Returned by ``POST /orca-cloud/auth/start``. The frontend opens
  13. ``auth_url`` in a new tab. After the user signs in to Orca, they copy the
  14. redirected URL from their address bar and POST it to
  15. ``/orca-cloud/auth/finish`` to complete the handshake."""
  16. auth_url: str = Field(..., description="URL to open for Orca Cloud sign-in")
  17. class OrcaAuthFinishRequest(BaseModel):
  18. """Submitted by the frontend after the user pastes the callback URL from
  19. their browser. The URL contains a Supabase ``code`` (and our ``state``)
  20. that we exchange for tokens."""
  21. callback_url: str = Field(..., description="The full URL the browser was redirected to after sign-in")
  22. class OrcaAuthPasswordRequest(BaseModel):
  23. """Body for ``POST /orca-cloud/auth/password``. Whether this succeeds
  24. depends on Orca's Supabase project — their desktop client refuses
  25. password payloads, but the web sign-in offers email+password as one
  26. option. We forward the credentials and surface the server's response.
  27. ``email`` is plain ``str`` rather than Pydantic's ``EmailStr`` to avoid
  28. pulling in the optional ``email-validator`` dependency — Supabase will
  29. reject malformed addresses with a clear error itself, and the existing
  30. Bambu Cloud login schema uses the same approach."""
  31. email: str = Field(..., min_length=1)
  32. password: str = Field(..., min_length=1)
  33. class OrcaAuthStatusResponse(BaseModel):
  34. """Connection status for the Orca Cloud tab."""
  35. connected: bool
  36. email: str | None = None
  37. user_id: str | None = None
  38. class OrcaProfileMeta(BaseModel):
  39. """A single profile, shaped to match the Bambu Cloud ``SlicerSetting``
  40. schema so the frontend can render Orca profiles with the existing
  41. Bambu Cloud visual components (cards, filter bar, grouping). Per-source
  42. differences (Orca's IDs are UUIDs not Bambu's ``PFU...`` prefix; Orca
  43. types are ``machine`` / ``process`` / ``filament`` whereas Bambu uses
  44. ``printer`` / ``process`` / ``filament``) are normalized at the route
  45. layer before the response leaves the backend."""
  46. setting_id: str
  47. name: str
  48. type: str
  49. version: str | None = None
  50. user_id: str | None = None
  51. updated_time: str | None = None
  52. is_custom: bool = True
  53. class OrcaProfileListResponse(BaseModel):
  54. """Groups Orca profiles by type, matching ``SlicerSettingsResponse``."""
  55. filament: list[OrcaProfileMeta] = []
  56. printer: list[OrcaProfileMeta] = []
  57. process: list[OrcaProfileMeta] = []
  58. class OrcaProfileDetail(BaseModel):
  59. """Single profile's full content, shaped to match ``SlicerSettingDetail``
  60. so the frontend's detail modal can render it without translation."""
  61. setting_id: str
  62. name: str
  63. type: str
  64. version: str | None = None
  65. base_id: str | None = None
  66. update_time: str | None = None
  67. setting: dict