feat: implement backend avatar update

This commit is contained in:
swve 2024-01-20 00:41:37 +01:00
parent cec178c479
commit a51a128fcb
11 changed files with 103 additions and 19 deletions

2
apps/api/.gitignore vendored
View file

@ -10,7 +10,7 @@ __pycache__/
.vscode/ .vscode/
# Learnhouse # Learnhouse
content/org_* content/*
# Flyio # Flyio
fly.toml fly.toml

View file

@ -1,5 +1,5 @@
from typing import Literal from typing import Literal
from fastapi import APIRouter, Depends, Request from fastapi import APIRouter, Depends, Request, UploadFile
from sqlmodel import Session from sqlmodel import Session
from src.security.auth import get_current_user from src.security.auth import get_current_user
from src.core.events.database import get_db_session from src.core.events.database import get_db_session
@ -22,6 +22,7 @@ from src.services.users.users import (
read_user_by_id, read_user_by_id,
read_user_by_uuid, read_user_by_uuid,
update_user, update_user,
update_user_avatar,
update_user_password, update_user_password,
) )
@ -137,6 +138,20 @@ async def api_update_user(
return await update_user(request, db_session, user_id, current_user, user_object) return await update_user(request, db_session, user_id, current_user, user_object)
@router.put("/update_avatar/{user_id}", response_model=UserRead, tags=["users"])
async def api_update_avatar_user(
*,
request: Request,
db_session: Session = Depends(get_db_session),
current_user: PublicUser = Depends(get_current_user),
avatar_file: UploadFile | None = None,
) -> UserRead:
"""
Update User
"""
return await update_user_avatar(request, db_session, current_user, avatar_file)
@router.put("/change_password/{user_id}", response_model=UserRead, tags=["users"]) @router.put("/change_password/{user_id}", response_model=UserRead, tags=["users"])
async def api_update_user_password( async def api_update_user_password(
*, *,

View file

@ -50,7 +50,8 @@ async def upload_file_and_return_file_object(
await upload_content( await upload_content(
f"courses/{course_uuid}/activities/{activity_uuid}/dynamic/blocks/{type_of_block}/{block_id}", f"courses/{course_uuid}/activities/{activity_uuid}/dynamic/blocks/{type_of_block}/{block_id}",
org_uuid=org_uuid, type_of_dir='orgs',
uuid=org_uuid,
file_binary=file_binary, file_binary=file_binary,
file_and_format=f"{file_id}.{file_format}", file_and_format=f"{file_id}.{file_format}",
) )

View file

@ -8,6 +8,7 @@ async def upload_pdf(pdf_file, activity_uuid, org_uuid, course_uuid):
try: try:
await upload_content( await upload_content(
f"courses/{course_uuid}/activities/{activity_uuid}/documentpdf", f"courses/{course_uuid}/activities/{activity_uuid}/documentpdf",
"orgs",
org_uuid, org_uuid,
contents, contents,
f"documentpdf.{pdf_format}", f"documentpdf.{pdf_format}",

View file

@ -10,6 +10,7 @@ async def upload_video(video_file, activity_uuid, org_uuid, course_uuid):
await upload_content( await upload_content(
f"courses/{course_uuid}/activities/{activity_uuid}/video", f"courses/{course_uuid}/activities/{activity_uuid}/video",
org_uuid, org_uuid,
org_uuid,
contents, contents,
f"video.{video_format}", f"video.{video_format}",
) )

View file

@ -213,7 +213,7 @@ async def update_course_thumbnail(
if thumbnail_file and thumbnail_file.filename: if thumbnail_file and thumbnail_file.filename:
name_in_disk = f"{course_uuid}_thumbnail_{uuid4()}.{thumbnail_file.filename.split('.')[-1]}" name_in_disk = f"{course_uuid}_thumbnail_{uuid4()}.{thumbnail_file.filename.split('.')[-1]}"
await upload_thumbnail( await upload_thumbnail(
thumbnail_file, name_in_disk, org.org_uuid, course.course_uuid thumbnail_file, name_in_disk, 'users', course.course_uuid
) )
# Update course # Update course

View file

@ -1,13 +1,13 @@
from src.services.utils.upload_content import upload_content from src.services.utils.upload_content import upload_content
async def upload_thumbnail(thumbnail_file, name_in_disk, org_id, course_id): async def upload_thumbnail(thumbnail_file, name_in_disk, org_uuid, course_id):
contents = thumbnail_file.file.read() contents = thumbnail_file.file.read()
try: try:
await upload_content( await upload_content(
f"courses/{course_id}/thumbnails", f"courses/{course_id}/thumbnails",
org_id, "orgs",
org_uuid,
contents, contents,
f"{name_in_disk}", f"{name_in_disk}",
) )

View file

@ -10,6 +10,7 @@ async def upload_org_logo(logo_file, org_uuid):
await upload_content( await upload_content(
"logos", "logos",
org_uuid, org_uuid,
org_uuid,
contents, contents,
name_in_disk, name_in_disk,
) )

View file

@ -0,0 +1,16 @@
from src.services.utils.upload_content import upload_content
async def upload_avatar(avatar_file, name_in_disk, user_uuid):
contents = avatar_file.file.read()
try:
await upload_content(
f"avatars",
"users",
user_uuid,
contents,
f"{name_in_disk}",
)
except Exception:
return {"message": "There was an error uploading the file"}

View file

@ -1,8 +1,9 @@
from datetime import datetime from datetime import datetime
from typing import Literal from typing import Literal
from uuid import uuid4 from uuid import uuid4
from fastapi import HTTPException, Request, status from fastapi import HTTPException, Request, UploadFile, status
from sqlmodel import Session, select from sqlmodel import Session, select
from src.services.users.avatars import upload_avatar
from src.db.roles import Role, RoleRead from src.db.roles import Role, RoleRead
from src.security.rbac.rbac import ( from src.security.rbac.rbac import (
authorization_verify_based_on_roles_and_authorship, authorization_verify_based_on_roles_and_authorship,
@ -195,6 +196,49 @@ async def update_user(
return user return user
async def update_user_avatar(
request: Request,
db_session: Session,
current_user: PublicUser | AnonymousUser,
avatar_file: UploadFile | None = None,
):
# Get user
statement = select(User).where(User.id == current_user.id)
user = db_session.exec(statement).first()
if not user:
raise HTTPException(
status_code=400,
detail="User does not exist",
)
# RBAC check
await rbac_check(request, current_user, "update", user.user_uuid, db_session)
# Upload thumbnail
if avatar_file and avatar_file.filename:
name_in_disk = f"{user.user_uuid}_avatar_{uuid4()}.{avatar_file.filename.split('.')[-1]}"
await upload_avatar(avatar_file, name_in_disk, user.user_uuid)
# Update course
if name_in_disk:
user.avatar_image = name_in_disk
else:
raise HTTPException(
status_code=500,
detail="Issue with Avatar upload",
)
# Update user in database
db_session.add(user)
db_session.commit()
db_session.refresh(user)
user = UserRead.from_orm(user)
return user
async def update_user_password( async def update_user_password(
request: Request, request: Request,
db_session: Session, db_session: Session,

View file

@ -1,3 +1,4 @@
from typing import Literal
import boto3 import boto3
from botocore.exceptions import ClientError from botocore.exceptions import ClientError
import os import os
@ -6,7 +7,11 @@ from config.config import get_learnhouse_config
async def upload_content( async def upload_content(
directory: str, org_uuid: str, file_binary: bytes, file_and_format: str directory: str,
type_of_dir: Literal["orgs", "users"],
uuid: str, # org_uuid or user_uuid
file_binary: bytes,
file_and_format: str,
): ):
# Get Learnhouse Config # Get Learnhouse Config
learnhouse_config = get_learnhouse_config() learnhouse_config = get_learnhouse_config()
@ -16,12 +21,12 @@ async def upload_content(
if content_delivery == "filesystem": if content_delivery == "filesystem":
# create folder for activity # create folder for activity
if not os.path.exists(f"content/{org_uuid}/{directory}"): if not os.path.exists(f"content/{type_of_dir}/{uuid}/{directory}"):
# create folder for activity # create folder for activity
os.makedirs(f"content/{org_uuid}/{directory}") os.makedirs(f"content/{type_of_dir}/{uuid}/{directory}")
# upload file to server # upload file to server
with open( with open(
f"content/{org_uuid}/{directory}/{file_and_format}", f"content/{type_of_dir}/{uuid}/{directory}/{file_and_format}",
"wb", "wb",
) as f: ) as f:
f.write(file_binary) f.write(file_binary)
@ -37,13 +42,13 @@ async def upload_content(
) )
# Create folder for activity # Create folder for activity
if not os.path.exists(f"content/{org_uuid}/{directory}"): if not os.path.exists(f"content/{type_of_dir}/{uuid}/{directory}"):
# create folder for activity # create folder for activity
os.makedirs(f"content/{org_uuid}/{directory}") os.makedirs(f"content/{type_of_dir}/{uuid}/{directory}")
# Upload file to server # Upload file to server
with open( with open(
f"content/{org_uuid}/{directory}/{file_and_format}", f"content/{type_of_dir}/{uuid}/{directory}/{file_and_format}",
"wb", "wb",
) as f: ) as f:
f.write(file_binary) f.write(file_binary)
@ -52,9 +57,9 @@ async def upload_content(
print("Uploading to s3 using boto3...") print("Uploading to s3 using boto3...")
try: try:
s3.upload_file( s3.upload_file(
f"content/{org_uuid}/{directory}/{file_and_format}", f"content/{type_of_dir}/{uuid}/{directory}/{file_and_format}",
"learnhouse-media", "learnhouse-media",
f"content/{org_uuid}/{directory}/{file_and_format}", f"content/{type_of_dir}/{uuid}/{directory}/{file_and_format}",
) )
except ClientError as e: except ClientError as e:
print(e) print(e)
@ -63,7 +68,7 @@ async def upload_content(
try: try:
s3.head_object( s3.head_object(
Bucket="learnhouse-media", Bucket="learnhouse-media",
Key=f"content/{org_uuid}/{directory}/{file_and_format}", Key=f"content/{type_of_dir}/{uuid}/{directory}/{file_and_format}",
) )
print("File upload successful!") print("File upload successful!")
except Exception as e: except Exception as e: