2022-12-20 02:22:32 +02:00
import re
2022-12-20 14:28:50 +02:00
from os import makedirs , path , rename
2022-12-20 02:22:32 +02:00
from shutil import rmtree
from typing import Union
2023-02-16 15:11:29 +02:00
from classes . exceptions import AlbumAlreadyExistsError , AlbumIncorrectError , AlbumNotFoundError
from classes . models import Album , AlbumModified , SearchResultsAlbum
2022-12-20 14:28:50 +02:00
from modules . app import app
2022-12-20 02:22:32 +02:00
from modules . database import col_photos , col_albums
2022-12-20 12:37:32 +02:00
from modules . security import User , get_current_active_user
2022-12-20 02:22:32 +02:00
from bson . objectid import ObjectId
from bson . errors import InvalidId
2022-12-20 14:28:50 +02:00
from fastapi import HTTPException , Security
2022-12-20 02:22:32 +02:00
from fastapi . responses import UJSONResponse , Response
2022-12-20 14:28:50 +02:00
from starlette . status import HTTP_204_NO_CONTENT , HTTP_404_NOT_FOUND , HTTP_406_NOT_ACCEPTABLE , HTTP_409_CONFLICT
2022-12-20 02:22:32 +02:00
2023-02-16 15:11:29 +02:00
album_create_responses = {
406 : AlbumIncorrectError ( " name " , " error " ) . openapi ,
409 : AlbumAlreadyExistsError ( " name " ) . openapi
}
@app.post ( " /albums " , description = " Create album with name and title " , response_class = UJSONResponse , response_model = Album , responses = album_create_responses )
2022-12-20 14:28:50 +02:00
async def album_create ( name : str , title : str , current_user : User = Security ( get_current_active_user , scopes = [ " albums.write " ] ) ) :
2022-12-20 02:22:32 +02:00
2022-12-20 14:28:50 +02:00
if re . search ( re . compile ( ' ^[a-z,0-9,_]*$ ' ) , name ) is False :
2023-02-16 15:11:29 +02:00
raise AlbumIncorrectError ( " name " , " can only contain a-z, 0-9 and _ characters. " )
2022-12-20 14:28:50 +02:00
if 2 > len ( name ) > 20 :
2023-02-16 15:11:29 +02:00
raise AlbumIncorrectError ( " name " , " must be >2 and <20 characters. " )
2022-12-20 02:22:32 +02:00
2022-12-20 14:28:50 +02:00
if 2 > len ( title ) > 40 :
2023-02-16 15:11:29 +02:00
raise AlbumIncorrectError ( " title " , " must be >2 and <40 characters. " )
2022-12-20 02:22:32 +02:00
2022-12-20 14:28:50 +02:00
if col_albums . find_one ( { " name " : name } ) is not None :
2023-02-16 15:11:29 +02:00
raise AlbumAlreadyExistsError ( name )
2022-12-20 02:22:32 +02:00
2022-12-20 14:28:50 +02:00
makedirs ( path . join ( " data " , " users " , current_user . user , " albums " , name ) , exist_ok = True )
2022-12-20 02:22:32 +02:00
2022-12-21 00:59:47 +02:00
uploaded = col_albums . insert_one ( { " user " : current_user . user , " name " : name , " title " : title , " cover " : None } )
2022-12-20 02:22:32 +02:00
2022-12-20 14:28:50 +02:00
return UJSONResponse (
{
" id " : uploaded . inserted_id . __str__ ( ) ,
" name " : name ,
" title " : title
}
)
2022-12-20 02:22:32 +02:00
2023-02-16 15:11:29 +02:00
@app.get ( " /albums " , description = " Find album by name " , response_model = SearchResultsAlbum )
2022-12-20 14:28:50 +02:00
async def album_find ( q : str , current_user : User = Security ( get_current_active_user , scopes = [ " albums.list " ] ) ) :
2022-12-20 02:22:32 +02:00
2022-12-20 12:37:32 +02:00
output = { " results " : [ ] }
albums = list ( col_albums . find ( { " user " : current_user . user , " name " : re . compile ( q ) } ) )
2022-12-20 02:22:32 +02:00
2022-12-20 12:37:32 +02:00
for album in albums :
2023-02-16 15:11:29 +02:00
output [ " results " ] . append ( { " id " : album [ " _id " ] . __str__ ( ) , " name " : album [ " name " ] , " title " : album [ " title " ] } )
2022-12-20 02:22:32 +02:00
2022-12-20 12:37:32 +02:00
return UJSONResponse ( output )
2022-12-20 02:22:32 +02:00
2023-02-16 15:11:29 +02:00
album_patch_responses = {
404 : AlbumNotFoundError ( " id " ) . openapi ,
406 : AlbumIncorrectError ( " name " , " error " ) . openapi
}
@app.patch ( " /albums/ {id} " , description = " Modify album ' s name or title by id " , response_class = UJSONResponse , response_model = AlbumModified , responses = album_patch_responses )
2022-12-21 00:59:47 +02:00
async def album_patch ( id : str , name : Union [ str , None ] = None , title : Union [ str , None ] = None , cover : Union [ str , None ] = None , current_user : User = Security ( get_current_active_user , scopes = [ " albums.write " ] ) ) :
2022-12-20 02:22:32 +02:00
2022-12-20 14:28:50 +02:00
try :
album = col_albums . find_one ( { " _id " : ObjectId ( id ) } )
if album is None :
raise InvalidId ( id )
except InvalidId :
2023-02-16 15:11:29 +02:00
raise AlbumNotFoundError ( id )
2022-12-20 02:22:32 +02:00
2022-12-20 14:28:50 +02:00
if title is not None :
if 2 > len ( title ) > 40 :
2023-02-16 15:11:29 +02:00
raise AlbumIncorrectError ( " title " , " must be >2 and <40 characters. " )
2022-12-20 14:28:50 +02:00
else :
title = album [ " title " ]
2022-12-20 02:22:32 +02:00
2022-12-20 14:28:50 +02:00
if name is not None :
2022-12-20 02:22:32 +02:00
if re . search ( re . compile ( ' ^[a-z,0-9,_]*$ ' ) , name ) is False :
2023-02-16 15:11:29 +02:00
raise AlbumIncorrectError ( " name " , " can only contain a-z, 0-9 and _ characters. " )
2022-12-20 02:22:32 +02:00
if 2 > len ( name ) > 20 :
2023-02-16 15:11:29 +02:00
raise AlbumIncorrectError ( " name " , " must be >2 and <20 characters. " )
2022-12-20 14:28:50 +02:00
rename (
path . join ( " data " , " users " , current_user . user , " albums " , album [ " name " ] ) ,
path . join ( " data " , " users " , current_user . user , " albums " , name )
)
col_photos . update_many ( { " user " : current_user . user , " album " : album [ " name " ] } , { " $set " : { " album " : name } } )
else :
name = album [ " name " ]
2022-12-20 02:22:32 +02:00
2022-12-21 00:59:47 +02:00
if cover is not None :
image = col_photos . find_one ( { " _id " : ObjectId ( cover ) , " album " : album [ " name " ] } )
cover = image [ " _id " ] . __str__ ( ) if image is not None else album [ " cover " ]
else :
cover = album [ " cover " ]
col_albums . update_one ( { " _id " : ObjectId ( id ) } , { " $set " : { " name " : name , " title " : title , " cover " : cover } } )
2022-12-20 14:28:50 +02:00
return UJSONResponse (
{
" name " : name ,
2022-12-21 00:59:47 +02:00
" title " : title ,
" cover " : cover
2022-12-20 14:28:50 +02:00
}
)
2022-12-20 02:22:32 +02:00
2023-02-16 15:11:29 +02:00
album_put_responses = {
404 : AlbumNotFoundError ( " id " ) . openapi ,
406 : AlbumIncorrectError ( " name " , " error " ) . openapi
}
@app.put ( " /albums/ {id} " , description = " Modify album ' s name and title by id " , response_class = UJSONResponse , response_model = AlbumModified , responses = album_put_responses )
2022-12-21 00:59:47 +02:00
async def album_put ( id : str , name : str , title : str , cover : str , current_user : User = Security ( get_current_active_user , scopes = [ " albums.write " ] ) ) :
2022-12-20 02:22:32 +02:00
2022-12-20 14:28:50 +02:00
try :
album = col_albums . find_one ( { " _id " : ObjectId ( id ) } )
if album is None :
raise InvalidId ( id )
except InvalidId :
2023-02-16 15:11:29 +02:00
raise AlbumNotFoundError ( id )
2022-12-20 02:22:32 +02:00
2022-12-20 14:28:50 +02:00
if re . search ( re . compile ( ' ^[a-z,0-9,_]*$ ' ) , name ) is False :
2023-02-16 15:11:29 +02:00
raise AlbumIncorrectError ( " name " , " can only contain a-z, 0-9 and _ characters. " )
2022-12-20 02:22:32 +02:00
2022-12-20 14:28:50 +02:00
if 2 > len ( name ) > 20 :
2023-02-16 15:11:29 +02:00
raise AlbumIncorrectError ( " name " , " must be >2 and <20 characters. " )
2022-12-20 02:22:32 +02:00
2022-12-20 14:28:50 +02:00
if 2 > len ( title ) > 40 :
2023-02-16 15:11:29 +02:00
raise AlbumIncorrectError ( " title " , " must be >2 and <40 characters. " )
2022-12-20 02:22:32 +02:00
2022-12-21 00:59:47 +02:00
image = col_photos . find_one ( { " _id " : ObjectId ( cover ) , " album " : album [ " name " ] } )
cover = image [ " _id " ] . __str__ ( ) if image is not None else None # type: ignore
2022-12-20 14:28:50 +02:00
rename (
path . join ( " data " , " users " , current_user . user , " albums " , album [ " name " ] ) ,
path . join ( " data " , " users " , current_user . user , " albums " , name )
)
2022-12-20 02:22:32 +02:00
2022-12-20 14:28:50 +02:00
col_photos . update_many ( { " user " : current_user . user , " album " : album [ " name " ] } , { " $set " : { " album " : name } } )
2022-12-21 00:59:47 +02:00
col_albums . update_one ( { " _id " : ObjectId ( id ) } , { " $set " : { " name " : name , " title " : title , " cover " : cover } } )
2022-12-20 02:22:32 +02:00
2022-12-20 14:28:50 +02:00
return UJSONResponse (
{
" name " : name ,
2022-12-21 00:59:47 +02:00
" title " : title ,
" cover " : cover
2022-12-20 14:28:50 +02:00
}
)
2022-12-20 02:22:32 +02:00
2023-02-16 15:11:29 +02:00
album_delete_responses = {
404 : AlbumNotFoundError ( " id " ) . openapi
}
@app.delete ( " /album/ {id} " , description = " Delete album by id " , status_code = HTTP_204_NO_CONTENT , responses = album_delete_responses )
2022-12-20 14:28:50 +02:00
async def album_delete ( id : str , current_user : User = Security ( get_current_active_user , scopes = [ " albums.write " ] ) ) :
2022-12-20 02:22:32 +02:00
2022-12-20 14:28:50 +02:00
try :
album = col_albums . find_one_and_delete ( { " _id " : ObjectId ( id ) } )
if album is None :
raise InvalidId ( id )
except InvalidId :
2023-02-16 15:11:29 +02:00
raise AlbumNotFoundError ( id )
2022-12-20 14:28:50 +02:00
col_photos . delete_many ( { " album " : album [ " name " ] } )
rmtree ( path . join ( " data " , " users " , current_user . user , " albums " , album [ " name " ] ) )
return Response ( status_code = HTTP_204_NO_CONTENT )