Spaces:
Sleeping
Sleeping
Download backend/customizations.py from Success-dev/receipter-pro: direct link, hf CLI and curl.
- Browser
- Download file 3.69 kB
-
https://huggingface.co/spaces/Success-dev/receipter-pro/resolve/main/backend/customizations.py
- Command line
-
hf download hf://spaces/Success-dev/receipter-pro/backend/customizations.py
-
curl -L -o customizations.py https://huggingface.co/spaces/Success-dev/receipter-pro/resolve/main/backend/customizations.py
3.69 kB
| """ | |
| Saved email "customizations" — a named header + footer + brand color a company designs once | |
| and picks per campaign, instead of redesigning them on every send. Stored per org at | |
| orgs/{org_id}/customizations/{id}. The frontend merges the chosen customization around the | |
| message body into the final HTML (frontend/src/lib/emailFrame.ts), so sending itself is | |
| unchanged and never needs to know which customization was used. | |
| """ | |
| from __future__ import annotations | |
| import re | |
| import uuid | |
| from datetime import datetime, timezone | |
| from backend import firestore_client as db | |
| from backend import orgs | |
| _COLOR_RE = re.compile(r"^#[0-9a-fA-F]{6}$") | |
| _MAX_NAME = 80 | |
| _MAX_HTML = 20_000 | |
| def _now() -> str: | |
| return datetime.now(timezone.utc).isoformat() | |
| def _prefix(org_id: str) -> str: | |
| return f"orgs/{org_id}/customizations/" | |
| def _path(org_id: str, customization_id: str) -> str: | |
| return f"{_prefix(org_id)}{customization_id}" | |
| def _clean(name: str, brand_color: str, logo_url: str, header_text: str, footer_html: str, footer_bg: str) -> dict: | |
| name = name.strip() | |
| if not name or len(name) > _MAX_NAME: | |
| raise ValueError(f"Name is required and must be at most {_MAX_NAME} characters") | |
| if not _COLOR_RE.match(brand_color): | |
| raise ValueError("Brand color must be a hex color like #c0392b") | |
| if not _COLOR_RE.match(footer_bg): | |
| raise ValueError("Footer background must be a hex color like #f4f4f4") | |
| logo_url = logo_url.strip() | |
| if logo_url and not logo_url.lower().startswith("https://"): | |
| raise ValueError("Logo URL must start with https://") | |
| if len(footer_html) > _MAX_HTML or len(header_text) > 200: | |
| raise ValueError("Header or footer is too long") | |
| return { | |
| "name": name, | |
| "brand_color": brand_color, | |
| "logo_url": logo_url, | |
| "header_text": header_text.strip(), | |
| "footer_html": footer_html, | |
| "footer_bg": footer_bg, | |
| } | |
| def create_customization(org_id: str, actor_uid: str, name: str, brand_color: str, logo_url: str, | |
| header_text: str, footer_html: str, footer_bg: str = "#f4f4f4") -> str: | |
| orgs.require_permission(org_id, actor_uid, "edit_emails") | |
| data = _clean(name, brand_color, logo_url, header_text, footer_html, footer_bg) | |
| customization_id = uuid.uuid4().hex | |
| db.set_doc(_path(org_id, customization_id), data | {"created_at": _now(), "updated_at": _now()}) | |
| return customization_id | |
| def update_customization(org_id: str, actor_uid: str, customization_id: str, name: str, brand_color: str, | |
| logo_url: str, header_text: str, footer_html: str, footer_bg: str = "#f4f4f4") -> None: | |
| orgs.require_permission(org_id, actor_uid, "edit_emails") | |
| get_customization(org_id, customization_id) | |
| data = _clean(name, brand_color, logo_url, header_text, footer_html, footer_bg) | |
| db.update_doc(_path(org_id, customization_id), data | {"updated_at": _now()}) | |
| def delete_customization(org_id: str, actor_uid: str, customization_id: str) -> None: | |
| orgs.require_permission(org_id, actor_uid, "edit_emails") | |
| get_customization(org_id, customization_id) | |
| db.delete_doc(_path(org_id, customization_id)) | |
| def get_customization(org_id: str, customization_id: str) -> dict: | |
| doc = db.get_doc(_path(org_id, customization_id)) | |
| if doc is None: | |
| raise orgs.NotFoundError(f"No customization {customization_id!r} in org {org_id!r}") | |
| return doc | |
| def list_customizations(org_id: str, actor_uid: str) -> dict[str, dict]: | |
| orgs.require_permission(org_id, actor_uid) | |
| prefix = _prefix(org_id) | |
| return {path[len(prefix):]: data for path, data in db.query_prefix(prefix).items()} | |