mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-12-28 05:05:12 -05:00
Feature/user photo storage (#877)
* add default assets for user profile * add recipe avatar * change user_id to UUID * add profile image upload * setup image cache keys * cleanup tests and add image tests * purge user data on delete * new user repository tests * add user_id validator for int -> UUID conversion * delete depreciated route * force set content type * refactor tests to use temp directory * validate parent exists before createing * set user_id to correct type * update instruction id * reset primary key on migration
This commit is contained in:
@@ -22,7 +22,7 @@ async def create_api_token(
|
||||
):
|
||||
"""Create api_token in the Database"""
|
||||
|
||||
token_data = {"long_token": True, "id": current_user.id}
|
||||
token_data = {"long_token": True, "id": str(current_user.id)}
|
||||
|
||||
five_years = timedelta(1825)
|
||||
token = create_access_token(token_data, five_years)
|
||||
@@ -30,7 +30,7 @@ async def create_api_token(
|
||||
token_model = CreateToken(
|
||||
name=token_name.name,
|
||||
token=token,
|
||||
parent_id=current_user.id,
|
||||
user_id=current_user.id,
|
||||
)
|
||||
|
||||
db = get_database(session)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from fastapi import BackgroundTasks, Depends, HTTPException, status
|
||||
from pydantic import UUID4
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core import security
|
||||
@@ -39,15 +40,15 @@ async def create_user(
|
||||
|
||||
|
||||
@admin_router.get("/{id}", response_model=UserOut)
|
||||
async def get_user(id: int, session: Session = Depends(generate_session)):
|
||||
async def get_user(id: UUID4, session: Session = Depends(generate_session)):
|
||||
db = get_database(session)
|
||||
return db.users.get(id)
|
||||
|
||||
|
||||
@admin_router.delete("/{id}")
|
||||
def delete_user(
|
||||
id: UUID4,
|
||||
background_tasks: BackgroundTasks,
|
||||
id: int,
|
||||
session: Session = Depends(generate_session),
|
||||
current_user: PrivateUser = Depends(get_current_user),
|
||||
):
|
||||
@@ -55,7 +56,7 @@ def delete_user(
|
||||
|
||||
assert_user_change_allowed(id, current_user)
|
||||
|
||||
if id == 1:
|
||||
if id == 1: # TODO: identify super_user
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="SUPER_USER")
|
||||
|
||||
try:
|
||||
@@ -75,7 +76,7 @@ async def get_logged_in_user(
|
||||
|
||||
@user_router.put("/{id}")
|
||||
async def update_user(
|
||||
id: int,
|
||||
id: UUID4,
|
||||
new_data: UserBase,
|
||||
current_user: PrivateUser = Depends(get_current_user),
|
||||
session: Session = Depends(generate_session),
|
||||
|
||||
@@ -1,51 +1,49 @@
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import Depends, File, HTTPException, UploadFile, status
|
||||
from fastapi.responses import FileResponse
|
||||
from fastapi.routing import APIRouter
|
||||
from pydantic import UUID4
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.config import get_app_dirs
|
||||
|
||||
app_dirs = get_app_dirs()
|
||||
from mealie import utils
|
||||
from mealie.core.dependencies import get_current_user
|
||||
from mealie.core.dependencies.dependencies import temporary_dir
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.routers import UserAPIRouter
|
||||
from mealie.routes.users._helpers import assert_user_change_allowed
|
||||
from mealie.schema.user import PrivateUser
|
||||
from mealie.services.image import minify
|
||||
|
||||
public_router = APIRouter(prefix="", tags=["Users: Images"])
|
||||
user_router = UserAPIRouter(prefix="", tags=["Users: Images"])
|
||||
|
||||
|
||||
@public_router.get("/{id}/image")
|
||||
async def get_user_image(id: str):
|
||||
"""Returns a users profile picture"""
|
||||
user_dir = app_dirs.USER_DIR.joinpath(id)
|
||||
for recipe_image in user_dir.glob("profile_image.*"):
|
||||
return FileResponse(recipe_image)
|
||||
else:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||
|
||||
|
||||
@user_router.post("/{id}/image")
|
||||
def update_user_image(
|
||||
id: str,
|
||||
profile_image: UploadFile = File(...),
|
||||
id: UUID4,
|
||||
profile: UploadFile = File(...),
|
||||
temp_dir: Path = Depends(temporary_dir),
|
||||
current_user: PrivateUser = Depends(get_current_user),
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
"""Updates a User Image"""
|
||||
|
||||
assert_user_change_allowed(id, current_user)
|
||||
|
||||
extension = profile_image.filename.split(".")[-1]
|
||||
temp_img = temp_dir.joinpath(profile.filename)
|
||||
|
||||
app_dirs.USER_DIR.joinpath(id).mkdir(parents=True, exist_ok=True)
|
||||
with temp_img.open("wb") as buffer:
|
||||
shutil.copyfileobj(profile.file, buffer)
|
||||
|
||||
[x.unlink() for x in app_dirs.USER_DIR.joinpath(id).glob("profile_image.*")]
|
||||
image = minify.to_webp(temp_img)
|
||||
dest = PrivateUser.get_directory(id) / "profile.webp"
|
||||
|
||||
dest = app_dirs.USER_DIR.joinpath(id, f"profile_image.{extension}")
|
||||
shutil.copyfile(image, dest)
|
||||
|
||||
with dest.open("wb") as buffer:
|
||||
shutil.copyfileobj(profile_image.file, buffer)
|
||||
db = get_database(session)
|
||||
|
||||
db.users.patch(id, {"cache_key": utils.new_cache_key()})
|
||||
|
||||
if not dest.is_file:
|
||||
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
Reference in New Issue
Block a user