def get(cls, anime_id): if not valid_uuid4(anime_id): response = {"message": get_text('anime_uuid_error')} return response, 400 anime = AnimeModel.find_by_id(anime_id) username = get_jwt_identity() anime_bookmarked = False if username: # if there is an authenticated user # get the user and check if he saved the anime user = UserModel.find_by_username(username) if user.has_user_saved_anime(anime_id): anime_bookmarked = True if anime: episodes = anime.\ episodes.\ with_entities("episode_id", "episode_number").\ order_by("episode_number").all() anime_data = { **anime_info_schema.dump(anime), "anime_bookmarked": anime_bookmarked, "episodes": episode_schema.dump(episodes) } return anime_data, 200 return { "message": get_text('anime_not_found').format(anime_id=anime_id) }, 404
def post(cls): get_admin_role_and_id() if role == "God": try: admin_info = admin_schema.load(request.get_json()) admin = AdminModel.find_by_username(admin_info["username"]) if admin: return {"message": get_text('admin_username_exists')}, 409 hashed_password = generate_password_hash( admin_info["password"]) admin_info["password"] = hashed_password admin = AdminModel(**admin_info) admin_id = admin.save_to_db() if admin_id: return { "message": get_text('admin_registered'), "admin_id": admin_id }, 201 return {"message": get_text('admin_registeration_error')}, 500 except ValidationError as error: return { "message": get_text('input_error_generic'), "info": error.messages }, 400 except Exception as ex: print(ex) return {"message": get_text('server_error_generic')}, 500
def get(cls): username = get_jwt_identity() try: user = UserModel.find_by_username(username) if user: saved_animes = user.saved_animes.\ with_entities("anime_info.anime_id", "anime_info.title", "anime_info.poster_uri").\ order_by("title").all() return { **user_min_info_schema.dump(user), "saved_animes": [ { "title": saved_anime[1], "anime_id": str(saved_anime[0]), "poster_uri": saved_anime[2] } for saved_anime in saved_animes ] }, 200 return { "message": get_text('user_not_found') }, 404 except Exception as ex: print(ex) return {"message": get_text('server_error_generic')}, 500
def delete(cls): get_admin_role_and_id() try: admin_info = admin_schema.load(request.get_json()) username = admin_info.get("username", None) password = admin_info.get("password", None) admin = AdminModel.find_by_username(username) if admin: condition_one = (admin._id == authenticated_admin_id) and\ check_password_hash(admin.password, password) condition_two = role == "God" if condition_one or condition_two: admin.delete_from_db() return {"message": get_text('admin_deleted')}, 200 return { "message": get_text('admin_unauthorized_deletion') }, 401 return {"message": get_text('admin_not_found')} except ValidationError as error: return { "message": get_text('input_error_generic'), "info": error.messages }, 400 except Exception as ex: print(ex) return {"message": get_text('server_error_generic')}, 500
def post(cls): try: form_data = auth_schema.load(request.get_json()) username = form_data.get('username', None) password = form_data.get('password', None) admin = AdminModel.find_by_username(username) if admin and check_password_hash(admin.password, password): exp_time = datetime.timedelta(hours=1) access_token = create_access_token(identity=admin, expires_delta=exp_time) refresh_token = create_refresh_token(identity=admin) return { "access_token": access_token, "refresh_token": refresh_token }, 200 return { "message": get_text('admin_auth_incorrect_credentials') }, 400 except ValidationError as error: return { "message": get_text('input_error_generic'), "info": error.messages }, 400 except Exception as ex: print(ex) return {"message": get_text('server_error_generic')}, 500
def get(cls): '''Response after successful confirmation or other cases.''' confirmation_id = request.args.get("token", None) if confirmation_id is None: return { "message": get_text('confirmation_activation_token_required') }, 400 confirmation = ConfirmationModel.find_by_id(confirmation_id) if not confirmation: return { "message": get_text('confirmation_token_not_found') }, 404 if confirmation.expired: return { "message": get_text('confirmation_token_expired') }, 400 if confirmation.confirmed: return { "message": get_text('confirmation_account_already_confirmed') }, 400 confirmation.confirmed = True confirmation.save_to_db() return { "message": get_text('confirmation_account_confirmed') }, 200
def post(cls): current_user_role = get_jwt_claims().get("role", None) if not current_user_role or current_user_role == "Regular Member": return {"message": get_text('anime_access_forbidden')}, 403 try: anime_data = anime_info_schema.load(request.get_json()) genres_list = anime_data.get("genres_list", None) if not genres_list: return {"message": get_text('anime_genres_required')}, 400 anime_data.pop("genres_list", None) anime = AnimeModel(**anime_data) anime_id = anime.save_to_db(genres_list) if anime_id: created_data = { "message": get_text('anime_created'), "anime_id": f"{anime_id}" } return created_data, 201 return {"message": get_text('anime_already_exists')}, 409 except ValidationError as error: return { "message": get_text('input_error_generic'), "info": error.messages }, 400
def page_not_found(e): PATH = request.path response = { "message_one": get_text("resource_not_found_1"), "message_two": get_text("resource_not_found_2"), "requested_path": PATH } return jsonify(response), 404
def get(cls, episode_id): if not valid_uuid4(episode_id): response = {"message": get_text('episode_uuid_error')} return response, 400 episode = EpisodeModel.find_by_id(episode_id) if episode: episode_data = episode_info_schema.dump(episode) return episode_data, 200 return {"message": get_text('episode_not_found').format(episode_id=episode_id)}, 404
def get(cls, admin_id): get_admin_role_and_id() admin = AdminModel.find_by_id(admin_id) if admin: if authenticated_admin_id == admin._id or role == "God": admin_info = admin_schema.dump(admin) return admin_info, 200 return { "message": get_text('admin_acc_info_access_forbidden') }, 403 return {"message": get_text('admin_not_found')}
def get(cls) -> Dict: try: page_number = request.args.get("page", 1, type=int) sort_by = request.args.get("sort_by", "title", type=str) if sort_by == "rating": sort_method = AnimeModel.rating.desc() else: sort_method = AnimeModel.title anime = AnimeModel.animes_list(page_number, sort_method) anime_list = { "animes": animes_list_schema.dump(anime.items), "prev_page": anime.prev_num, "next_page": anime.next_num, "current_page": anime.page, "total_pages": anime.pages } return anime_list, 200 except Exception as ex: print(ex) return {"message": get_text('server_error_generic')}, 500
def post(cls): try: current_user = get_jwt_identity() user = UserModel.find_by_username(current_user) if user: return { 'access_token': create_access_token(identity=user) }, 200 return { 'message': get_text('user_not_found') }, 404 except Exception as ex: print(ex) return {"message": get_text('server_error_generic')}, 500
def delete(cls): try: post_data = request.get_json() genre_info = genre_information_schema.load(post_data) genre = GenreModel.find_by_name(genre_info["genre_name"]) if genre: try: genre.delete_from_db() return {"message": get_text('genre_deleted')}, 200 except Exception as error: print(error) return {"message": get_text('genre_not_found_deletion')}, 404 except ValidationError as error: return {"message": get_text('input_error_generic'), "info": error.messages}, 400
def post(cls): try: current_user_role = get_jwt_claims().get("role", None) if not current_user_role or current_user_role == "Regular Member": return {"message": get_text('episode_access_forbidden')}, 403 episode_data = episode_info_schema.load(request.get_json()) episode = EpisodeModel(**episode_data) episode_id = episode.save_to_db() if episode_id: created_data = { "message": get_text('episode_created'), "episode_id": f"{episode_id}" } return created_data, 201 return {"message": get_text('episode_anime_not_found').format(anime_id=episode_data["anime_id"])}, 404 except ValidationError as error: return {"message": get_text('input_error_generic'), "info": error.messages}, 400
def post(cls): ''' Used to upload an image file. Uses JWT to retrieve user info and then saves the image to the user's folder. If there is a filename conflict, it appends a number at the end. ''' if not authorized(): return { "message": get_text('image_unauthorized') }, 401 try: # request.files = {"form_fild_name" : 'FileStorage' from werkzeug} data = image_schema.load(request.files) user_id = get_jwt_identity() folder = f'admin_{user_id}' # static/images/user_idxxx image_path = image_helper.save_image(data['image'], folder=folder) basename = image_helper.get_basename(image_path) absolute_path = f'{os.getcwd()}/static/images/{folder}' image_url = sirv_utils.upload( basename, absolute_path, SIRV_BASE_FOLDER_NAME) return { "message": get_text('image_photo_uploaded').format(basename=basename), "image_url": image_url["image_path"] }, 201 except UploadNotAllowed: extension = image_helper.get_extension(data['image']) return { "message": get_text('image_illegal_file_type').format(extension=extension) }, 400 except ValidationError as error: return {"message": get_text('input_error_generic'), "info": error.messages}, 400 except Exception as ex: print(ex) return { }, 500
def delete(cls, episode_id): if not valid_uuid4(episode_id): response = { "message": get_text('episode_uuid_error') } return response, 400 try: current_user_role = get_jwt_claims().get("role", None) if not current_user_role or current_user_role == "Regular Member": return {"message": get_text('episode_access_forbidden')}, 403 episode_to_delete = EpisodeModel.find_by_id(episode_id) if episode_to_delete: info = episode_to_delete.delete_from_db() if not info: response_message = { "message": get_text('episode_deleted').format(episode_id=episode_id) } return response_message, 200 return {"message": get_text('episode_deletion_error')}, 400 response_message = { "message": get_text('episode_not_found').format(episode_id=episode_id) } return response_message, 404 except Exception as ex: print(f"[Delete Episode]: {ex}") response_message = { "message": get_text('server_error_generic') } return response_message, 500
def delete(cls): try: user_id = get_jwt_identity() data = user_anime_save_schema.load(request.get_json()) user = UserModel.find_by_username(user_id) anime_id = data['anime_id'] if user: if user.has_user_saved_anime(anime_id): successful = user.remove_anime(anime_id) if successful: return { "message": get_text('user_anime_removing_successful') }, 200 return { "message": get_text('user_anime_removing_error') }, 400 return { "message": get_text('user_anime_not_saved_before') }, 404 return { "message": get_text('user_not_found') }, 404 except ValidationError as error: return {"message": get_text('input_error_generic'), "info": error.messages}, 400 except Exception as ex: print(ex) return {"message": get_text('server_error_generic')}, 500
def put(cls, anime_id): if not valid_uuid4(anime_id): response = {"message": get_text('anime_uuid_error')} return response, 400 current_user_role = get_jwt_claims().get("role", None) if not current_user_role or current_user_role == "Regular Member": return {"message": get_text('anime_access_forbidden')}, 403 try: anime_data = anime_info_schema.load(request.get_json()) genres_list = anime_data.get("genres_list", None) if not genres_list: return {"message": get_text('anime_genres_required')}, 400 anime_data.pop("genres_list", None) anime = AnimeModel.find_by_id(anime_id) if anime: anime.title = anime_data["title"] anime.rating = anime_data["rating"] anime.release = anime_data["release"] anime.status = anime_data["status"] anime.synopsis = anime_data["synopsis"] anime.number_of_episodes = anime_data["number_of_episodes"] anime.poster_uri = anime_data["poster_uri"] anime_id = anime.save_to_db(genres_list) if anime_id: return {"message": get_text('anime_updated')}, 200 # returns an error from model because anime title naming conflict return {"message": get_text('anime_name_exists')}, 409 # if there is no anime with the given id create new one anime = AnimeModel(**anime_data) anime_id = anime.save_to_db(genres_list) if anime_id: return { "message": get_text('anime_created'), "anime_id": anime_id }, 200 # returns an error from model because anime title naming conflict return {"message": get_text('anime_name_exists')}, 409 except ValidationError as error: return { "message": get_text('input_error_generic'), "info": error.messages }, 400
def put(cls, episode_id): if not valid_uuid4(episode_id): response = {"message": get_text('episode_uuid_error')} return response, 400 try: current_user_role = get_jwt_claims().get("role", None) if not current_user_role or current_user_role == "Regular Member": return {"message": get_text('episode_access_forbidden')}, 403 episode = EpisodeModel.find_by_id(episode_id) if episode: episode_info = episode_info_schema.load(request.get_json()) episode.episode_number = episode_info["episode_number"] episode.episode_uri_1 = episode_info.get("episode_uri_1") episode.episode_uri_2 = episode_info.get("episode_uri_2") episode.episode_uri_3 = episode_info.get("episode_uri_3") episode.episode_uri_4 = episode_info.get("episode_uri_4") episode.next_episode = episode_info.get("next_episode") episode.prev_episode = episode_info.get("prev_episode") episode.anime_id = episode_info.get("anime_id") episode_id = episode.save_to_db() if episode_id: return {"message": get_text('episode_updated')}, 200 return {"message": get_text('episode_anime_not_found').format(anime_id=episode_info["anime_id"])}, 404 return {"message": get_text('episode_not_found').format(episode_id=episode_id)}, 404 except ValidationError as error: return {"message": get_text('input_error_generic'), "info": error.messages}, 400
def post(cls): try: post_data = request.get_json() genre_data = genre_information_schema.load(post_data) genre_data["genre_name"] = genre_data["genre_name"].capitalize() genre = GenreModel.find_by_name(genre_data.get("genre_name", None)) if not genre: try: genre = GenreModel(**genre_data) genre_id = genre.save_to_db() if genre_id: return {"message": get_text('genre_created')}, 201 return {"message": get_text('genre_creation_error')}, 500 except Exception as error: print(error) return {"message": get_text('genre_already_exists')}, 409 except ValidationError as error: return {"message": get_text('input_error_generic'), "info": error.messages}, 400
def put(cls): try: data = image_schema.load(request.files) filename = f'user_{get_jwt_identity()}' folder = 'avatars' if not authorized() else 'admin_avatars' avatar_path = image_helper.find_image_any_format(filename, folder) if avatar_path: try: os.remove(avatar_path) except Exception as ex: print(ex) try: absolute_path = f'{os.getcwd()}/static/images/{folder}' ext = image_helper.get_extension(data['image'].filename) avatar = filename + ext image_helper.save_image(data["image"], folder, avatar) image_helper.change_image_type_and_resize( absolute_path, avatar) return { "message": get_text('image_avatar_uploaded') }, 201 except UploadNotAllowed: extension = image_helper.get_extension(data['image']) return { "message": get_text('image_illegal_file_type').format(extension=extension) }, 400 except ValidationError as error: return {"message": get_text('input_error_generic'), "info": error.messages}, 400 except Exception as ex: print(ex) return { }, 500
def get(cls, username: str): AVATAR_FOLDER = 'avatars' if not authorized() else 'admin_avatars' try: filename = f'user_{username}' avatar_path = image_helper.find_image_any_format( filename, AVATAR_FOLDER) if avatar_path: try: return send_file(avatar_path) except FileNotFoundError: return { "message": get_text('image_file_not_found') }, 404 return { "message": get_text('image_file_not_found') }, 404 except: return { }, 500
def delete(cls, anime_id): if not valid_uuid4(anime_id): response = {"message": get_text('anime_uuid_error')} return response, 400 current_user_role = get_jwt_claims().get("role", None) if not current_user_role or current_user_role == "Regular Member": return {"message": get_text('anime_access_forbidden')}, 403 anime = AnimeModel.find_by_id(anime_id) if anime: info = anime.delete_from_db() if not info: return { "message": get_text('anime_deleted').format(anime_id=anime_id) }, 200 return {"message": get_text('anime_deletion_error')}, 400 return { "message": get_text('anime_not_found').format(anime_id=anime_id) }, 404
def delete(cls, filename: str): if not authorized(): return { "message": get_text('image_unauthorized') }, 401 if not image_helper.is_filename_safe(filename): return { "message": get_text('image_illegal_file_name') }, 400 user_id = get_jwt_identity() user_folder = f'admin_{user_id}' try: sirv_utils.delete(filename, SIRV_BASE_FOLDER_NAME) return { "message": get_text('image_deletion_successful') } except Exception as ex: print(ex) return {"message": get_text('image_deletion_failed')}, 500
def put(cls): try: put_data = request.get_json() genre_data = genre_information_schema.load(put_data) genre_data["genre_name"] = genre_data["genre_name"].capitalize() try: genre = GenreModel.find_by_name(genre_data["genre_name"]) if not genre: genre = GenreModel(**genre_data) genre_id = genre.save_to_db() if genre_id: return {"message": get_text('genre_created')}, 201 return {"message": get_text('genre_creation_error')}, 500 genre.genre_explanation = genre_data["genre_explanation"] genre.save_to_db() return {"message": get_text('genre_updated')}, 200 except Exception as error: print(error) except ValidationError as error: return {"message": get_text('input_error_generic'), "info": error.messages}, 400
def get(cls, username: str): '''Get user confirmation. Use for testing.''' admin_id = get_jwt_claims().get("admin_id", None) if admin_id is None: return { "message": get_text('confirmation_unauthorized') }, 403 user = UserModel.find_by_username(username) if not user: return { "message": get_text('confirmation_user_not_found') }, 404 return { "username": user.username, "current_time": int(time()), "confirmations": [ confirmation_schema.dump(each) for each in user.confirmation.order_by(ConfirmationModel.expires_at) ], }, 200
def post(cls): try: token = request.args.get('token', None) new_password = request.get_json().get('new_password', None) if token is None: return { "message": get_text('reset_password_token_required') }, 401 if new_password is None: return { "message": get_text('reset_password_new_password_required') }, 400 reset_password = PasswordResetModel.find_by_id(token) if reset_password is None: return { "message": get_text('reset_password_token_invalid') }, 401 if reset_password.expired: return { "message": get_text('reset_password_token_expired') }, 400 # Token is valid user_id = reset_password.user_id user = UserModel.find_by_id(user_id) if user: user.password = generate_password_hash(new_password) user.save_to_db() # delete the associated password reset row reset_password.delete_from_db() return { "message": get_text('reset_password_password_updated') }, 200 return { "message": get_text('reset_password_user_from_token_not_found') }, 404 except Exception as ex: print(ex) return {"message": get_text('server_error_generic')}, 500
def post(cls): try: form_data = auth_schema.load(request.get_json()) username = form_data.get('username', None) password = form_data.get('password', None) user = UserModel.find_by_username(username) if user and check_password_hash(user.password, password): if user.last_confirmation is None: return { "message_1": get_text('user_account_not_activated_1'), "message_2": get_text('user_account_not_activated_2') }, 403 activated = user.last_confirmation.confirmed if not activated: return { "message_1": get_text('user_account_not_activated_1'), "message_2": get_text('user_account_not_activated_2') }, 403 exp_time = datetime.timedelta(hours=3) access_token = create_access_token( identity=user, expires_delta=exp_time ) refresh_token = create_refresh_token(identity=user) return { "name": user.name, "username": user.username, "expires_in": exp_time.total_seconds(), "access_token": access_token, "refresh_token": refresh_token }, 200 return {"message": get_text('user_incorrect_credentials')}, 400 except ValidationError as error: return {"message": get_text('input_error_generic'), "info": error.messages}, 400 except Exception as ex: print(ex) return {"message": get_text('server_error_generic')}, 500
def post(cls): '''Request for a new cofirmatin email.''' username = request.get_json().get("username", None) if username is None: return { "message": get_text('confirmation_username_required') }, 400 user = UserModel.find_by_username(username) if not user: return { "message": get_text('confirmation_user_not_found') }, 404 try: confirmation = user.last_confirmation if confirmation: if confirmation.confirmed: return { "message": get_text('confirmation_account_already_confirmed') }, 400 confirmation.force_to_expire() new_confirmation = ConfirmationModel(user._id) new_confirmation.save_to_db() token = new_confirmation.confirmation_id activation_link = f"{DOMAIN_NAME}/v1/user/activate?token={token}" SendInBlue.send_activation_email( name=user.name, email=user.email, activation_link=activation_link ) return { "message": get_text('confirmation_resend_activation_email_success') }, 200 except SendInBlueError as err: print(err) new_confirmation.delete_from_db() return {"message": get_text('confirmation_resend_activation_email_failed')}, 500 except Exception as ex: print(ex) return { "message": get_text('server_error_generic') }, 500
def get(cls, genre_name): try: # replaces '-' with spaces, and makes the first letter capital. genre_name = " ".join(genre_name.split("-")).capitalize() page_number = request.args.get("page", 1, type=int) sort_by = request.args.get("sort_by", "title", type=str) if sort_by == "rating": sort_method = AnimeModel.rating.desc() else: sort_method = AnimeModel.title genre = GenreModel.find_by_name(genre_name=genre_name) if genre: anime = genre.\ animes.\ with_entities(AnimeModel.anime_id, AnimeModel.title, AnimeModel.poster_uri, AnimeModel.rating).\ order_by(sort_method).\ paginate(page_number, 24, False) response_data = { **genre_information_schema.dump(genre), "animes": animes_list_schema.dump(anime.items), "prev_page": anime.prev_num, "next_page": anime.next_num, "current_page": anime.page, "total_pages": anime.pages, "sorted_by": sort_by } return response_data, 200 return {"message": get_text('genre_not_found')}, 404 except Exception as error: print(error)