ProjectCraft / utils /share.py
yania-n's picture
fix: full codebase audit - 18 bugs fixed across 9 files
386a178
Raw History Blame Contribute Delete
2.17 kB
"""Share link encoding/decoding β€” base64-encodes compressed plan JSON for URL sharing."""
import base64
import json
import zlib
from urllib.parse import parse_qs, urlparse
from core.planner import ProjectPlan
def encode_plan(plan: ProjectPlan) -> str:
"""
Encode a ProjectPlan to a URL-safe base64 string.
Process: model_dump(exclude_none=True) -> json.dumps -> zlib.compress -> base64 urlsafe encode
Returns encoded string, or empty string on failure.
"""
try:
plan_dict = plan.model_dump(exclude_none=True)
json_bytes = json.dumps(plan_dict, separators=(",", ":")).encode("utf-8")
compressed = zlib.compress(json_bytes)
token = base64.urlsafe_b64encode(compressed).decode("ascii")
return token
except Exception:
return ""
def decode_plan(token: str) -> ProjectPlan | None:
"""
Decode a share token back to a ProjectPlan.
Process: base64 urlsafe decode -> zlib.decompress -> json.loads -> parse_plan_json
Returns ProjectPlan on success, None on any failure.
"""
try:
compressed = base64.urlsafe_b64decode(token.encode("ascii"))
json_bytes = zlib.decompress(compressed)
plan_dict = json.loads(json_bytes.decode("utf-8"))
# plan_dict is already a dict β€” use model_validate directly
return ProjectPlan.model_validate(plan_dict)
except Exception:
return None
def build_share_url(base_url: str, plan: ProjectPlan) -> str:
"""
Build a full share URL: {base_url}?share={encoded_token}
Returns empty string if encoding fails.
"""
token = encode_plan(plan)
if not token:
return ""
separator = "&" if "?" in base_url else "?"
return f"{base_url}{separator}share={token}"
def parse_share_url(url: str) -> ProjectPlan | None:
"""
Extract and decode the ?share= token from a URL.
Returns ProjectPlan or None.
"""
try:
parsed = urlparse(url)
params = parse_qs(parsed.query)
tokens = params.get("share", [])
if not tokens:
return None
return decode_plan(tokens[0])
except Exception:
return None