Python 68.8%
TypeScript 18.6%
CSS 8.7%
JavaScript 3.3%
HTML 0.6%
1# -----------------------------------------------------------------------------2# Rent-Ka — Rental listings aggregator (Canada, outside Québec)3# Author: Simon-Pierre Boucher — contact@spboucher.ai4# profile.py : personnalisation du profil membre5# · PUT /api/me/profile — nom affiché, bio, ville, téléphone, site web,6# réseaux sociaux (instagram, facebook, x, linkedin, tiktok, youtube)7# · POST /api/me/avatar — téléversement de photo (Pillow : recadrage carré8# centré + 512 px + JPEG), servie depuis /uploads/avatars/…9# · DELETE /api/me/avatar — retour à l'avatar Google10# Le nom affiché (display_name) prime sur le nom Google, qui est réécrasé11# à chaque connexion ; l'avatar téléversé (avatar_url) prime sur `picture`.12# -----------------------------------------------------------------------------13from __future__ import annotations1415import io16import json17import time18from pathlib import Path1920from fastapi import APIRouter, HTTPException, Request, UploadFile21from pydantic import BaseModel2223from . import db24from .auth import current_user2526router = APIRouter(prefix="/api")2728ROOT = Path(__file__).resolve().parent.parent29AVATAR_DIR = ROOT / "data" / "uploads" / "avatars"30AVATAR_MAX_BYTES = 8 * 1024 * 1024 # 8 Mo avant traitement31AVATAR_SIZE = 512 # carré final (px)3233SOCIAL_KEYS = ("instagram", "facebook", "x", "linkedin", "tiktok", "youtube")343536def _require(request: Request) -> dict:37 user = current_user(request)38 if user is None:39 raise HTTPException(401, "non connecté")40 return user414243class ProfileUpdate(BaseModel):44 display_name: str = ""45 bio: str = ""46 city: str = ""47 phone: str = ""48 website: str = ""49 socials: dict[str, str] = {}505152@router.put("/me/profile")53def update_profile(request: Request, body: ProfileUpdate):54 user = _require(request)55 socials = {k: (body.socials.get(k) or "").strip()[:200]56 for k in SOCIAL_KEYS if (body.socials.get(k) or "").strip()}57 con = db.connect()58 con.execute(59 """UPDATE users SET display_name=?, bio=?, city=?, phone=?,60 website=?, socials=? WHERE id=?""",61 (body.display_name.strip()[:80], body.bio.strip()[:280],62 body.city.strip()[:60], body.phone.strip()[:40],63 body.website.strip()[:300],64 json.dumps(socials, ensure_ascii=False) if socials else "",65 user["uid"]))66 con.commit()67 con.close()68 return {"ok": True}697071@router.post("/me/avatar")72async def upload_avatar(request: Request, file: UploadFile):73 """Photo de profil : recadrée carrée, 512 px, JPEG — remplace l'ancienne."""74 user = _require(request)75 data = await file.read()76 if len(data) > AVATAR_MAX_BYTES:77 raise HTTPException(413, "image trop lourde (max 8 Mo)")78 try:79 from PIL import Image, ImageOps80 img = Image.open(io.BytesIO(data))81 img.load()82 img = ImageOps.exif_transpose(img).convert("RGB")83 except Exception:84 raise HTTPException(400, "fichier illisible — envoyez une image (JPG, PNG, WebP…)")85 # recadrage carré centré puis 512 px86 side = min(img.size)87 left = (img.width - side) // 288 top = (img.height - side) // 289 img = img.crop((left, top, left + side, top + side))90 img = img.resize((AVATAR_SIZE, AVATAR_SIZE), Image.LANCZOS)9192 con = db.connect()93 row = con.execute("SELECT ka_id, avatar_url FROM users WHERE id=?",94 (user["uid"],)).fetchone()95 ka = (row["ka_id"] or f"uid{user['uid']}") if row else f"uid{user['uid']}"96 AVATAR_DIR.mkdir(parents=True, exist_ok=True)97 fname = f"{ka}-{int(time.time())}.jpg"98 img.save(AVATAR_DIR / fname, "JPEG", quality=88)99 # purge des anciennes photos de ce membre100 for old in AVATAR_DIR.glob(f"{ka}-*.jpg"):101 if old.name != fname:102 old.unlink(missing_ok=True)103 url = f"/uploads/avatars/{fname}"104 con.execute("UPDATE users SET avatar_url=? WHERE id=?", (url, user["uid"]))105 con.commit()106 con.close()107 return {"avatar_url": url}108109110@router.delete("/me/avatar")111def delete_avatar(request: Request):112 """Retire la photo téléversée (retour à l'avatar Google)."""113 user = _require(request)114 con = db.connect()115 row = con.execute("SELECT ka_id FROM users WHERE id=?",116 (user["uid"],)).fetchone()117 if row and row["ka_id"]:118 for old in AVATAR_DIR.glob(f"{row['ka_id']}-*.jpg"):119 old.unlink(missing_ok=True)120 con.execute("UPDATE users SET avatar_url=NULL WHERE id=?", (user["uid"],))121 con.commit()122 con.close()123 return {"ok": True}124