Prepare ERP source for Gitea deployment
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import imghdr
|
||||
import re
|
||||
from pathlib import Path
|
||||
from uuid import uuid4
|
||||
|
||||
from fastapi import HTTPException, UploadFile
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.modules.core.iam.models import User
|
||||
|
||||
STATIC_ROOT = Path("app/ui/static")
|
||||
PROFILE_UPLOAD_DIR = STATIC_ROOT / "uploads" / "user_profiles"
|
||||
MAX_IMAGE_BYTES = 2 * 1024 * 1024
|
||||
ALLOWED_IMAGE_TYPES = {"jpeg": ".jpg", "png": ".png", "gif": ".gif", "webp": ".webp"}
|
||||
|
||||
|
||||
def _blank_to_none(value: object) -> str | None:
|
||||
value = (str(value).strip() if value is not None else "")
|
||||
return value or None
|
||||
|
||||
|
||||
def _safe_static_path(path: str | None) -> str | None:
|
||||
path = (path or "").strip()
|
||||
if not path:
|
||||
return None
|
||||
if path.startswith("/static/"):
|
||||
return path
|
||||
if path.startswith("app/ui/static/"):
|
||||
return "/static/" + path.split("app/ui/static/", 1)[1].replace("\\", "/")
|
||||
return path
|
||||
|
||||
|
||||
def profile_photo_url(user: User | None) -> str | None:
|
||||
if not user:
|
||||
return None
|
||||
return _safe_static_path(getattr(user, "profile_photo_path", None))
|
||||
|
||||
|
||||
def user_initials(user: User | None) -> str:
|
||||
if not user:
|
||||
return "U"
|
||||
name = (getattr(user, "full_name", None) or getattr(user, "email", "") or "User").strip()
|
||||
if "@" in name and not getattr(user, "full_name", None):
|
||||
name = name.split("@", 1)[0]
|
||||
parts = [p for p in re.split(r"\s+", name) if p]
|
||||
if not parts:
|
||||
return "U"
|
||||
if len(parts) == 1:
|
||||
return parts[0][:2].upper()
|
||||
return (parts[0][0] + parts[-1][0]).upper()
|
||||
|
||||
|
||||
async def save_user_profile_photo(user: User, upload: UploadFile | None) -> str | None:
|
||||
"""Persist a profile photo and return a static path, or current path if nothing uploaded."""
|
||||
if not upload or not getattr(upload, "filename", None):
|
||||
return getattr(user, "profile_photo_path", None)
|
||||
|
||||
raw = await upload.read()
|
||||
if not raw:
|
||||
return getattr(user, "profile_photo_path", None)
|
||||
if len(raw) > MAX_IMAGE_BYTES:
|
||||
raise HTTPException(status_code=400, detail="Profile photo must be 2 MB or smaller.")
|
||||
|
||||
detected = imghdr.what(None, raw)
|
||||
suffix = ALLOWED_IMAGE_TYPES.get(detected or "")
|
||||
if not suffix:
|
||||
raise HTTPException(status_code=400, detail="Upload a valid JPG, PNG, GIF or WebP profile photo.")
|
||||
|
||||
PROFILE_UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
|
||||
filename = f"user_{user.id}_{uuid4().hex}{suffix}"
|
||||
target = PROFILE_UPLOAD_DIR / filename
|
||||
target.write_bytes(raw)
|
||||
return f"app/ui/static/uploads/user_profiles/{filename}"
|
||||
|
||||
|
||||
def update_user_public_profile(
|
||||
db: Session,
|
||||
user: User,
|
||||
*,
|
||||
qualification: object = None,
|
||||
designation: object = None,
|
||||
mobile: object = None,
|
||||
bio: object = None,
|
||||
profile_photo_path: str | None = None,
|
||||
) -> User:
|
||||
user.qualification = _blank_to_none(qualification)
|
||||
user.designation = _blank_to_none(designation)
|
||||
user.mobile = _blank_to_none(mobile)
|
||||
user.bio = _blank_to_none(bio)
|
||||
if profile_photo_path is not None:
|
||||
user.profile_photo_path = profile_photo_path
|
||||
db.add(user)
|
||||
return user
|
||||
Reference in New Issue
Block a user