main
py 80 lines 2.63 KB
Raw
1 import base64
2 import re
3 from typing import Optional
4
5 from fastapi import HTTPException
6 from pydantic import BaseModel
7 from pydantic import ConfigDict
8 from pydantic import Field
9 from pydantic import field_validator
10
11
12 class UpdatePortalSettingsRequest(BaseModel):
13 title: Optional[str] = Field(None, max_length=255, description="Portal title. Set to null to restore default.")
14 logo_base64: Optional[str] = Field(None, description="Base64 encoded logo image. Set to null to restore default.")
15 logo_mime_type: Optional[str] = Field(None, max_length=50, description="MIME type of the logo. Set to null to restore default.")
16
17 @field_validator("logo_base64")
18 @classmethod
19 def validate_base64(cls, v):
20 if v is None:
21 return v
22
23 # Remove data URL prefix if present
24 if v.startswith("data:"):
25 v = v.split(",", 1)[1] if "," in v else v
26
27 # Check size (limit to 5MB base64 = ~3.75MB original)
28 max_size = 5 * 1024 * 1024 # 5MB
29 if len(v) > max_size:
30 raise HTTPException(
31 status_code=400,
32 detail=f"Logo file too large. Maximum size is {max_size // (1024 * 1024)}MB (base64-encoded)",
33 )
34
35 # Validate base64 format
36 if not re.match(r"^[A-Za-z0-9+/]*={0,2}$", v):
37 raise HTTPException(status_code=400, detail="Invalid base64 encoded string for logo_base64")
38
39 # Optional: Try to decode to verify it's valid base64
40 try:
41 base64.b64decode(v)
42 except Exception:
43 raise HTTPException(status_code=400, detail="Invalid base64 data - cannot decode")
44
45 return v
46
47 @field_validator("logo_mime_type")
48 @classmethod
49 def validate_mime_type(cls, v):
50 if v is None:
51 return v
52
53 allowed_types = ["image/png", "image/jpeg", "image/jpg", "image/gif", "image/svg+xml", "image/webp"]
54 if v not in allowed_types:
55 raise HTTPException(status_code=400, detail=f"Invalid MIME type. Allowed types are: {', '.join(allowed_types)}")
56 return v
57
58 model_config = ConfigDict(
59 json_schema_extra={"example": {"title": "My Custom Portal", "logo_base64": "iVBORw0KGgoAAAANS...", "logo_mime_type": "image/png"}},
60 )
61
62
63 class PortalSettingsData(BaseModel):
64 id: int
65 title: str
66 logo_base64: Optional[str] = None
67 logo_mime_type: Optional[str] = None
68 updated_at: str
69 model_config = ConfigDict(from_attributes=True)
70
71
72 class PortalSettingsResponse(BaseModel):
73 success: bool
74 message: str
75 settings: Optional[PortalSettingsData] = None
76
77
78 class UpdatePortalSettingsResponse(BaseModel):
79 success: bool
80 message: str