From 3b0336d9b473ad5ba590228c693866f727b90931 Mon Sep 17 00:00:00 2001 From: Thastertyn Date: Mon, 17 Feb 2025 21:59:41 +0100 Subject: [PATCH] [rewrite] Removed old code, updated .vscode --- backend/app/api/__init__.py | 9 -- backend/app/api/routes/__init__.py | 15 --- backend/app/api/routes/cart_routes.py | 79 ------------ backend/app/api/routes/error_routes.py | 38 ------ backend/app/api/routes/main_routes.py | 12 -- .../routes/product/product_create_route.py | 33 ----- .../routes/product/product_delete_route.py | 19 --- .../api/routes/product/product_info_route.py | 25 ---- .../api/routes/product/product_page_route.py | 22 ---- backend/app/api/routes/user/delete_route.py | 22 ---- backend/app/api/routes/user/login_route.py | 33 ----- backend/app/api/routes/user/logout_route.py | 20 --- backend/app/api/routes/user/register_route.py | 39 ------ backend/app/api/routes/user/update_route.py | 40 ------ backend/app/db/cart_db.py | 0 backend/app/db/product_db.py | 118 ------------------ backend/app/db/user_db.py | 79 ------------ backend/app/doc/cart_swag.py | 118 ------------------ backend/app/doc/main_swag.py | 18 --- backend/app/doc/product_swag.py | 73 ----------- backend/app/doc/root_swag.py | 18 --- backend/app/doc/user_swag.py | 116 ----------------- backend/app/jwt_utils.py | 13 -- backend/app/mail/mail.py | 17 --- backend/app/mail/message_content.py | 4 - backend/app/messages/api_errors.py | 15 --- .../api_responses/product_responses.py | 6 - .../messages/api_responses/user_responses.py | 26 ---- .../app/messages/mail_responses/user_email.py | 26 ---- backend/app/models/user_model.py | 7 +- backend/app/utils/jwt_utils.py | 13 -- backend/requirements.txt | 36 ------ 32 files changed, 4 insertions(+), 1105 deletions(-) delete mode 100644 backend/app/api/__init__.py delete mode 100644 backend/app/api/routes/__init__.py delete mode 100644 backend/app/api/routes/cart_routes.py delete mode 100644 backend/app/api/routes/error_routes.py delete mode 100644 backend/app/api/routes/main_routes.py delete mode 100644 backend/app/api/routes/product/product_create_route.py delete mode 100644 backend/app/api/routes/product/product_delete_route.py delete mode 100644 backend/app/api/routes/product/product_info_route.py delete mode 100644 backend/app/api/routes/product/product_page_route.py delete mode 100644 backend/app/api/routes/user/delete_route.py delete mode 100644 backend/app/api/routes/user/login_route.py delete mode 100644 backend/app/api/routes/user/logout_route.py delete mode 100644 backend/app/api/routes/user/register_route.py delete mode 100644 backend/app/api/routes/user/update_route.py delete mode 100644 backend/app/db/cart_db.py delete mode 100644 backend/app/db/product_db.py delete mode 100644 backend/app/db/user_db.py delete mode 100644 backend/app/doc/cart_swag.py delete mode 100644 backend/app/doc/main_swag.py delete mode 100644 backend/app/doc/product_swag.py delete mode 100644 backend/app/doc/root_swag.py delete mode 100644 backend/app/doc/user_swag.py delete mode 100644 backend/app/jwt_utils.py delete mode 100644 backend/app/mail/mail.py delete mode 100644 backend/app/mail/message_content.py delete mode 100644 backend/app/messages/api_errors.py delete mode 100644 backend/app/messages/api_responses/product_responses.py delete mode 100644 backend/app/messages/api_responses/user_responses.py delete mode 100644 backend/app/messages/mail_responses/user_email.py delete mode 100644 backend/app/utils/jwt_utils.py delete mode 100644 backend/requirements.txt diff --git a/backend/app/api/__init__.py b/backend/app/api/__init__.py deleted file mode 100644 index ef8e1ea..0000000 --- a/backend/app/api/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from flask import Blueprint - -bp_errors = Blueprint('errors', __name__) -bp = Blueprint('api', __name__) -bp_product = Blueprint('products', __name__, url_prefix="/products") -bp_user = Blueprint('user', __name__, url_prefix="/user") -bp_cart = Blueprint('cart', __name__, url_prefix="/cart") - -from . import routes \ No newline at end of file diff --git a/backend/app/api/routes/__init__.py b/backend/app/api/routes/__init__.py deleted file mode 100644 index 7fd9edf..0000000 --- a/backend/app/api/routes/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -from app.api.routes.user import ( - register_route, - login_route, - logout_route, - update_route, - delete_route, -) -from app.api.routes.product import ( - product_create_route, - product_delete_route, - product_info_route, - product_page_route, -) - -from app.api.routes import main_routes, error_routes, cart_routes diff --git a/backend/app/api/routes/cart_routes.py b/backend/app/api/routes/cart_routes.py deleted file mode 100644 index d30db8a..0000000 --- a/backend/app/api/routes/cart_routes.py +++ /dev/null @@ -1,79 +0,0 @@ -from flask import jsonify, abort, request -from flask_jwt_extended import jwt_required, get_jwt_identity - -from app.doc.cart_swag import ( - show_cart_swagger, - add_to_cart_swagger, - remove_from_cart_swagger, - update_count_in_cart_swagger, - purchase_swagger, -) - -from flasgger import swag_from - -from app.api import bp_cart - -from app.services.cart_service import CartService - - -@bp_cart.route("", methods=["GET"]) -@jwt_required() -@swag_from(show_cart_swagger) -def show_cart(): - user_id = get_jwt_identity() - - result, status_code = CartService.show_cart(user_id) - - return result, status_code - - -@bp_cart.route("/add/", methods=["PUT"]) -@jwt_required() -@swag_from(add_to_cart_swagger) -def add_to_cart(product_id: int): - user_id = get_jwt_identity() - count = request.args.get("count", default=1, type=int) - - if count < 1: - return abort(400) - - result, status_code = CartService.add_to_cart(user_id, product_id, count) - - return result, status_code - - -@bp_cart.route("/remove/", methods=["DELETE"]) -@jwt_required() -@swag_from(remove_from_cart_swagger) -def remove_from_cart(product_id: int): - user_id = get_jwt_identity() - - result, status_code = CartService.delete_from_cart(user_id, product_id) - - return result, status_code - - -@bp_cart.route("/update/", methods=["PUT"]) -@jwt_required() -@swag_from(update_count_in_cart_swagger) -def update_count_in_cart(product_id: int): - user_id = get_jwt_identity() - count = request.args.get("count", type=int) - - if not count: - return abort(400) - - result, status_code = CartService.update_count(user_id, product_id, count) - - return result, status_code - - -@bp_cart.route("/purchase", methods=["GET"]) -@jwt_required() -@swag_from(purchase_swagger) -def purchase(): - user_id = get_jwt_identity() - - result, status_code = CartService.purchase(user_id) - - return result, status_code diff --git a/backend/app/api/routes/error_routes.py b/backend/app/api/routes/error_routes.py deleted file mode 100644 index e974e4d..0000000 --- a/backend/app/api/routes/error_routes.py +++ /dev/null @@ -1,38 +0,0 @@ -from app.api import bp_errors - - -@bp_errors.app_errorhandler(400) -def bad_request(e): - return { - "msg": "The request was incorrectly formatted, or contained invalid data" - }, 400 - - -@bp_errors.app_errorhandler(401) -def unauthorized(e): - return {"msg": "Failed to authorize the request"}, 401 - - -@bp_errors.app_errorhandler(403) -def forbidden(e): - return {"msg": "You shall not pass"}, 403 - - -@bp_errors.app_errorhandler(404) -def not_found(e): - return {"msg": "The requested resource was not found"}, 404 - - -@bp_errors.app_errorhandler(405) -def method_not_allowed(e): - return {"msg": "The method used is not allowed in current context"}, 405 - - -@bp_errors.app_errorhandler(500) -def internal_error(e): - return {"msg": "An error occurred on he server"}, 500 - - -@bp_errors.app_errorhandler(501) -def unimplemented_error(e): - return {"msg": "This function has not been implemented yet. Check back soon!"}, 501 diff --git a/backend/app/api/routes/main_routes.py b/backend/app/api/routes/main_routes.py deleted file mode 100644 index bc54eed..0000000 --- a/backend/app/api/routes/main_routes.py +++ /dev/null @@ -1,12 +0,0 @@ -from flask import jsonify -from flasgger import swag_from - -from app.doc.root_swag import root_swagger - -from app.api import bp - - -@bp.route("/") -@swag_from(root_swagger) -def hello(): - return jsonify({"message": "Hello, Flask!"}) diff --git a/backend/app/api/routes/product/product_create_route.py b/backend/app/api/routes/product/product_create_route.py deleted file mode 100644 index 848f5fc..0000000 --- a/backend/app/api/routes/product/product_create_route.py +++ /dev/null @@ -1,33 +0,0 @@ -from flask import jsonify, abort, request -from flask_jwt_extended import jwt_required, get_jwt_identity - -from app.doc.product_swag import create_product_swagger - -from flasgger import swag_from - -from app.api import bp_product - -from app.services.product import product_create_service - - -@bp_product.route("/create", methods=["POST"]) -@swag_from(create_product_swagger) -@jwt_required() -def create_product_listing(): - user_id = get_jwt_identity() - name = request.json.get("name") - price = request.json.get("price") - - if name is None or price is None: - return abort(400) - - float_price = float(price) - - if not isinstance(float_price, float): - return abort(400) - - result, status_code = product_create_service.create_product( - user_id, name, float_price - ) - - return jsonify(result), status_code diff --git a/backend/app/api/routes/product/product_delete_route.py b/backend/app/api/routes/product/product_delete_route.py deleted file mode 100644 index be6e4b5..0000000 --- a/backend/app/api/routes/product/product_delete_route.py +++ /dev/null @@ -1,19 +0,0 @@ -from flask import jsonify, abort, request -from flask_jwt_extended import jwt_required, get_jwt_identity - -from flasgger import swag_from - -from app.api import bp_product - -from app.services.product import product_delete_service - - -@bp_product.route("//delete", methods=["DELETE"]) -@jwt_required() -def delete_product(product_id: int): - user_id = get_jwt_identity() - - - result, status_code = product_delete_service.delete_product(user_id, product_id) - - return jsonify(result), status_code diff --git a/backend/app/api/routes/product/product_info_route.py b/backend/app/api/routes/product/product_info_route.py deleted file mode 100644 index dd36ee7..0000000 --- a/backend/app/api/routes/product/product_info_route.py +++ /dev/null @@ -1,25 +0,0 @@ -from flask import jsonify, request - -from app.doc.product_swag import get_product_info_swagger - -from flasgger import swag_from - -from app.api import bp_product - -from app.services.product import product_info_service - - -@bp_product.route("/", methods=["GET"]) -@swag_from(get_product_info_swagger) -def get_product_info(product_id: int): - fields = ["name", "price", "image", "image_name", "seller"] - - fields_param = request.args.get("fields") - - fields_param_list = fields_param.split(",") if fields_param else fields - - common_fields = list(set(fields) & set(fields_param_list)) - - result, status_code = product_info_service.product_info(product_id) - - return jsonify(result), status_code diff --git a/backend/app/api/routes/product/product_page_route.py b/backend/app/api/routes/product/product_page_route.py deleted file mode 100644 index b2cc2b4..0000000 --- a/backend/app/api/routes/product/product_page_route.py +++ /dev/null @@ -1,22 +0,0 @@ -from flask import jsonify, abort, request - -from app.doc.product_swag import get_products_swagger - -from flasgger import swag_from - -from app.api import bp_product - -from app.services.product import product_list_service - - -@bp_product.route("", methods=["GET"]) -@swag_from(get_products_swagger) -def get_products(): - page = request.args.get("page", default=0, type=int) - - if page < 0: - return abort(400) - - result, status_code = product_list_service.product_list(page) - - return jsonify(result), status_code diff --git a/backend/app/api/routes/user/delete_route.py b/backend/app/api/routes/user/delete_route.py deleted file mode 100644 index 512cb9b..0000000 --- a/backend/app/api/routes/user/delete_route.py +++ /dev/null @@ -1,22 +0,0 @@ -from app.api import bp_user -from flask_jwt_extended import jwt_required, get_jwt_identity, get_jwt -from flask import request, abort - -from flasgger import swag_from - -from app.doc.user_swag import delete_swagger -from app.services.user import delete_service, logout_service - - -@bp_user.route("/delete", methods=["DELETE"]) -@swag_from(delete_swagger) -@jwt_required() -def delete_user(): - user_id = get_jwt_identity() - - result, status_code = delete_service.delete_user(user_id) - - jwt = get_jwt() - logout_service.logout(jwt, user_id, True) - - return result, status_code diff --git a/backend/app/api/routes/user/login_route.py b/backend/app/api/routes/user/login_route.py deleted file mode 100644 index 527fcb8..0000000 --- a/backend/app/api/routes/user/login_route.py +++ /dev/null @@ -1,33 +0,0 @@ -from app.api import bp_user -from flask import request, jsonify - -from flasgger import swag_from - -import app.messages.api_responses.user_responses as response -import app.messages.api_errors as errors -from app.doc.user_swag import login_swagger - -from app.services.user import login_service - -@bp_user.route("/login", methods=["POST"]) -@swag_from(login_swagger) -def login(): - data = request.get_json() - - if not data: - result, status_code = errors.NOT_JSON - return jsonify(result), status_code - - required_fields = ["username", "password"] - missing_fields = [field for field in required_fields if field not in data] - - if missing_fields: - result, status_code = errors.MISSING_FIELDS(missing_fields) - return jsonify(result), status_code - - username = data["username"] - password = data["password"] - - result, status_code = login_service.login(username, password) - - return result, status_code diff --git a/backend/app/api/routes/user/logout_route.py b/backend/app/api/routes/user/logout_route.py deleted file mode 100644 index 4c1b3fc..0000000 --- a/backend/app/api/routes/user/logout_route.py +++ /dev/null @@ -1,20 +0,0 @@ -from app.api import bp_user - -from flasgger import swag_from - -from flask_jwt_extended import get_jwt_identity, jwt_required, get_jwt - -from app.doc.user_swag import logout_swagger -from app.services.user import logout_service - - -@bp_user.route("/logout", methods=["DELETE"]) -@swag_from(logout_swagger) -@jwt_required() -def logout(): - jwt = get_jwt() - user_id = get_jwt_identity() - - result, status_code = logout_service.logout(jwt, user_id, True) - - return result, status_code diff --git a/backend/app/api/routes/user/register_route.py b/backend/app/api/routes/user/register_route.py deleted file mode 100644 index 6d0107d..0000000 --- a/backend/app/api/routes/user/register_route.py +++ /dev/null @@ -1,39 +0,0 @@ -from app.api import bp_user -from flask import request, jsonify - -from app.services.user import register_service - -from app.doc.user_swag import register_swagger -import app.messages.api_responses.user_responses as response -import app.messages.api_errors as errors - - -from flasgger import swag_from - - -@bp_user.route("/register", methods=["POST"]) -@swag_from(register_swagger) -def register(): - data = request.get_json() - - if not data: - result, status_code = errors.NOT_JSON - return jsonify(result), status_code - - required_fields = ["username", "displayname", "email", "password"] - missing_fields = [field for field in required_fields if field not in data] - - if missing_fields: - result, status_code = errors.MISSING_FIELDS(missing_fields) - return jsonify(result), status_code - - username = data["username"] - displayname = data["displayname"] - email = data["email"] - password = data["password"] - - result, status_code = register_service.register( - username, displayname, email, password - ) - - return jsonify(result), status_code diff --git a/backend/app/api/routes/user/update_route.py b/backend/app/api/routes/user/update_route.py deleted file mode 100644 index 5eb5db3..0000000 --- a/backend/app/api/routes/user/update_route.py +++ /dev/null @@ -1,40 +0,0 @@ -from app.api import bp_user -from flask import request, jsonify -from flask_jwt_extended import jwt_required, get_jwt_identity, get_jwt - -from flasgger import swag_from - -import app.messages.api_errors as errors -from app.doc.user_swag import update_swagger - -from app.services.user import logout_service, update_user_service - - -@bp_user.route("/update", methods=["PUT"]) -@swag_from(update_swagger) -@jwt_required() -def update_user(): - data = request.get_json() - - possible_fields = ["new_username", "new_displayname", "new_email", "new_password"] - selected_fields = [field for field in possible_fields if field in data] - - if not selected_fields: - result, status_code = errors.NO_FIELD_PROVIDED(possible_fields) - return jsonify(result), status_code - - user_id = get_jwt_identity() - - new_username = data.get("new_username") - new_displayname = data.get("new_displayname") - new_email = data.get("new_email") - new_password = data.get("new_password") - - result, status_code = update_user_service.update_user(user_id, new_username, new_displayname, new_email, new_password) - - if status_code < 300: - jwt = get_jwt() - logout_service.logout(jwt, user_id, False) - - return result, status_code - diff --git a/backend/app/db/cart_db.py b/backend/app/db/cart_db.py deleted file mode 100644 index e69de29..0000000 diff --git a/backend/app/db/product_db.py b/backend/app/db/product_db.py deleted file mode 100644 index 37a87a4..0000000 --- a/backend/app/db/product_db.py +++ /dev/null @@ -1,118 +0,0 @@ -from typing import Optional - -from app.extensions import db_connection - -from app.models.product_model import Product - - -def fetch_products(page: int = 0) -> Optional[list[Product]]: - cursor = db_connection.cursor(dictionary=True) - - offset = 10 * page - cursor.execute( - "select product.id, user.displayname as seller, product.name, product.price_pc from product inner join user on user.id = product.seller_id order by product.id limit 10 offset %s", - (offset,), - ) - results = cursor.fetchall() - - if len(results) < 1: - return None - - result_products: list[Product] = [] - - for row in results: - result_products.append( - Product( - product_id=row["id"], - seller_id=row["seller_id"], - name=row["name"], - price=row["price"], - creation_date=row["creation_date"], - ) - ) - - return result_products - -def fetch_product_by_id(product_id: int) -> Optional[Product]: - """ - Fetches specific product info - - :param product_id: ID of product to be updated. - :type product_id: int - """ - - cursor = db_connection.cursor(dictionary=True) - - cursor.execute("select * from product where id = %s", (product_id,)) - result = cursor.fetchone() - - if cursor.rowcount != 1: - return None - - result_product = Product( - product_id=result["id"], - seller_id=result["seller_id"], - name=result["name"], - price=result["price"], - creation_date=result["creation_date"], - ) - - return result_product - - -def fetch_product_extended_by_id(product_id: int) -> Optional[Product]: - """ - Fetches specific product info including the seller n - - :param product_id: ID of product to be updated. - :type product_id: int - """ - - cursor = db_connection.cursor(dictionary=True) - - cursor.execute("select * from product inner join user on user.id = product.seller_id where product.id = %s", (product_id,)) - result = cursor.fetchone() - - if cursor.rowcount != 1: - return None - - result_product = Product( - product_id=result["id"], - seller_id=result["seller_id"], - seller_name=result["displayname"], - name=result["name"], - price=result["price"], - creation_date=result["creation_date"], - ) - - return result_product - -def insert_product(product: Product): - """ - Creates a new product listing - - :param seller_id: User ID - :type seller_id: str - :param name: New product's name - :type name: str - :param price: New product's price - :type price: float - """ - - cursor = db_connection.cursor() - - cursor.execute( - "insert into product(seller_id, name, price_pc) values (%s, %s, %s)", - (product.seller_id, product.name, round(product.price, 2)), - ) - db_connection.commit() - - -def delete_product(product: Product): - cursor = db_connection.cursor() - - cursor.execute( - "delete from product where id = %s", - (product.product_id,), - ) - db_connection.commit() diff --git a/backend/app/db/user_db.py b/backend/app/db/user_db.py deleted file mode 100644 index e4cf4f3..0000000 --- a/backend/app/db/user_db.py +++ /dev/null @@ -1,79 +0,0 @@ -from typing import Optional - -from app.extensions import db_connection - -from app.models.user_model import User - - -def fetch_by_username(username: str) -> Optional[User]: - cursor = db_connection.cursor(dictionary=True) - - cursor.execute("select * from user where username = %s", (username,)) - - result = cursor.fetchone() - - result_user = ( - User( - user_id=result["id"], - username=result["username"], - displayname=result["displayname"], - email=result["email"], - password=result["password"], - role_id=result["role_id"], - creation_date=result["creation_date"], - ) - if result - else None - ) - - return result_user - - -def fetch_by_id(user_id: int) -> Optional[User]: - cursor = db_connection.cursor(dictionary=True) - - cursor.execute("select * from user where id = %s", (user_id,)) - - result = cursor.fetchone() - result_user = ( - User( - user_id=result["id"], - username=result["username"], - displayname=result["displayname"], - email=result["email"], - password=result["password"], - role_id=result["role_id"], - creation_date=result["creation_date"], - ) - if result - else None - ) - - return result_user - - -def insert_user(new_user: User): - cursor = db_connection.cursor(dictionary=True) - - cursor.execute( - "insert into user (username, displayname, email, password) values (%s, %s, %s, %s)", - (new_user.username, new_user.displayname, new_user.email, new_user.password), - ) - db_connection.commit() - - -def delete_user(user: User): - cursor = db_connection.cursor(dictionary=True) - - cursor.execute("delete from user where id = %s", (user.user_id,)) - db_connection.commit() - - -def update_user(user: User): - cursor = db_connection.cursor(dictionary=True) - - cursor.execute( - "update user set username=%s, displayname=%s, email=%s, password=%s where id = %s", - (user.username, user.displayname, user.email, user.password, user.user_id), - ) - db_connection.commit() diff --git a/backend/app/doc/cart_swag.py b/backend/app/doc/cart_swag.py deleted file mode 100644 index 557f5c8..0000000 --- a/backend/app/doc/cart_swag.py +++ /dev/null @@ -1,118 +0,0 @@ -show_cart_swagger = { - "tags": ["Cart"], - "security": [ - {"JWT": []} - ], - "responses": { - "200": { - "description": "Current content of user's shopping cart", - "schema": { - "items": { - "count": {"type": "int"}, - "date_added": {"type": "string"}, - "name": {"type": "string"}, - "price_subtotal": {"type": "string"} - }, - "example": [ - { - "count": 5, - "date_added": "Fri, 08 Mar 2024 08:43:09 GMT", - "name": "Tablet", - "price_subtotal": "1499.95" - }, - { - "count": 2, - "date_added": "Fri, 08 Mar 2024 06:43:09 GMT", - "name": "Laptop", - "price_subtotal": "999.95" - } - ] - } - } - } -} - -add_to_cart_swagger ={ - "tags": ["Cart"], - "security": [ - {"JWT": []} - ], - "parameters": [ - { - "name": "product_id", - "description": "ID of product to add to cart.", - "in": "path", - "type": "int", - }, - { - "name": "count", - "description": "Count of the products. If not provided, defaults to 1", - "in": "query", - "type": "int", - "default": 1, - "minimum": 1, - "required": False - } - ], - "responses": { - "200": {"description": "Successfully added a product to cart"}, - "400": {"description": "Causes:\n- Count is < 1"} - } -} - -remove_from_cart_swagger = { - "tags": ["Cart"], - "security": [{"JWT": []}], - "parameters": [ - { - "name": "product_id", - "in": "path", - "type": "integer", - "description": "ID of the product to be removed from the cart", - "required": True - } - ], - "responses": { - "200": {"description": "Successfully removed item from the cart"}, - "400": {"description": "Bad Request - Invalid input"}, - "500": {"description": "Internal Server Error"} - } -} - -update_count_in_cart_swagger = { - "tags": ["Cart"], - "security": [{"JWT": []}], - "description": "Updates the count of products in the user's cart. If the count is less than or equal to 0, the product will be removed from the cart.", - "parameters": [ - { - "name": "product_id", - "in": "path", - "type": "integer", - "description": "ID of the product to update in the cart", - "required": True - }, - { - "name": "count", - "in": "query", - "type": "integer", - "description": "New count of the product in the cart", - "required": True - } - ], - "responses": { - "200": {"description": "Successfully updated item count in the cart"}, - "400": {"description": "Bad Request - Invalid input"}, - "500": {"description": "Internal Server Error"} - } -} - -purchase_swagger = { - "tags": ["Cart"], - "security": [{"JWT": []}], - "description": "Purchases the contents of the user's cart. This action creates a new purchase, moves items from the cart to the purchase history, and clears the cart.", - "responses": { - "200": {"description": "Successfully completed the purchase"}, - "400": {"description": "Bad Request - Invalid input or cart is empty"}, - "500": {"description": "Internal Server Error"} - } -} diff --git a/backend/app/doc/main_swag.py b/backend/app/doc/main_swag.py deleted file mode 100644 index 428443b..0000000 --- a/backend/app/doc/main_swag.py +++ /dev/null @@ -1,18 +0,0 @@ -main_swagger = { - "info": { - "title": "Swag Shop", - "version": "0.1", - "description": "Simple shop API using flask and co.\nFeatures include:\n- Not working\n- Successful registration of users\n- Adding items to cart\n- I don't know", - }, - "host": "localhost:1236", - "schemes": "http", - "securityDefinitions": { - "JWT": { - "type": "apiKey", - "scheme": "bearer", - "name": "Authorization", - "in": "header", - "description": "JWT Authorization header using the Bearer scheme.\n*Make sure to prefix the token with **Bearer**!*" - } - } -} \ No newline at end of file diff --git a/backend/app/doc/product_swag.py b/backend/app/doc/product_swag.py deleted file mode 100644 index 4ab4feb..0000000 --- a/backend/app/doc/product_swag.py +++ /dev/null @@ -1,73 +0,0 @@ -get_products_swagger = { - "methods": ["GET"], - "tags": ["Products"], - "parameters": [ - - ], - "responses": - { - "200": - { - "description": "Get a page of products", - "schema": - { - "type": "object", - "properties": { - "message": {"type": "string", "example": "Hello, Flask!"} - } - } - } - } -} - -get_product_info_swagger = { - "tags": ["Products"], - "parameters": [ - { - "name": "id", - "in": "path", - "type": "integer", - "description": "ID of the product to fetch information for", - "required": True - }, - { - "name": "fields", - "in": "query", - "type": "string", - "description": "Comma-separated list of fields to include in the response", - "required": False - } - ], - "responses": { - "200": {"description": "Successfully fetched product information"}, - "400": {"description": "Bad Request - Invalid input or product doesn't exist"}, - "500": {"description": "Internal Server Error"} - } -} - -create_product_swagger = { - "methods": ["POST"], - "tags": ["Products"], - "security": [{"JWT": []}], - "parameters": [ - { - "name": "name", - "in": "body", - "type": "string", - "description": "Name for the new product", - "required": True - }, - { - "name": "price", - "in": "body", - "type": "float", - "description": "Price of the product", - "required": True - } - ], - "responses": { - "200": {"description": "Successfully fetched product information"}, - "400": {"description": "Bad Request - Invalid input or missing input"}, - "500": {"description": "Internal Server Error"} - } -} \ No newline at end of file diff --git a/backend/app/doc/root_swag.py b/backend/app/doc/root_swag.py deleted file mode 100644 index c8b803b..0000000 --- a/backend/app/doc/root_swag.py +++ /dev/null @@ -1,18 +0,0 @@ -root_swagger = { - "methods": ["GET"], - "responses": - { - "200": - { - "description": "A hello world json", - "schema": - { - "type": "object", - "properties": - { - "message": {"type": "string", "example": "Hello, Flask!"} - } - } - } - } -} \ No newline at end of file diff --git a/backend/app/doc/user_swag.py b/backend/app/doc/user_swag.py deleted file mode 100644 index 26dcf32..0000000 --- a/backend/app/doc/user_swag.py +++ /dev/null @@ -1,116 +0,0 @@ -register_swagger = { - "methods": ["POST"], - "tags": ["User"], - "description": "Registers a new user in the app. Also sends a notification to the user via the provided email", - "parameters": [ - { - "in": "body", - "name": "body", - "description": 'Username, displayname and password of the new user\n- Username can be only lowercase and up to 64 characters\n- Displayname can contain special characters (. _ -) and lower and upper characters\n- Password must be at least 8 characters long, contain both lower and upper characters, numbers and special characters\n- Email has to be in format "name@domain.tld" and up to 64 characters long in total', - "required": True, - "schema": { - "type": "object", - "properties": { - "username": {"type": "string", "example": "mycoolusername"}, - "email": {"type": "string", "example": "mymail@dot.com"}, - "displayname": {"type": "string", "example": "MyCoolDisplayName"}, - "password": {"type": "string", "example": "My5tr0ngP@55w0rd"}, - }, - }, - } - ], -} - -login_swagger = { - "methods": ["POST"], - "tags": ["User"], - "description": "Logs in using username and password and returns a JWT token for further authorization of requests.\n**The token is valid for 1 hour**", - "parameters": [ - { - "in": "body", - "name": "body", - "description": "Username and password payload", - "required": True, - "schema": { - "type": "object", - "properties": { - "username": {"type": "string", "example": "mycoolusername"}, - "password": {"type": "string", "example": "MyStrongPassword123"}, - }, - }, - } - ], - "responses": { - "200": { - "description": "Returns a fresh token", - "schema": { - "type": "object", - "properties": { - "token": { - "type": "string", - "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTcxMDMyMjkyOCwianRpIjoiZDFhYzQxZDktZjA4NC00MmYzLThlMWUtZWFmZjJiNGU1MDAyIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6MjMwMDEsIm5iZiI6MTcxMDMyMjkyOCwiZXhwIjoxNzEwMzI2NTI4fQ.SW7LAi1j5vDOEIvzeN-sy0eHPP9PFJFkXYY029O35w0", - } - }, - }, - }, - "400": { - "description": "Possible causes:\n- Missing username or password from request.\n- Nonexistent username" - }, - "401": {"description": "Password is incorrect"}, - }, -} - -logout_swagger = { - "methods": ["DELETE"], - "tags": ["User"], - "security": [{"JWT": []}], - "description": "Logs out the user via provided JWT token", - "parameters": [], - "responses": {"200": {"description": "User successfully logged out"}}, -} - -update_swagger = { - "methods": ["PUT"], - "tags": ["User"], - "security": [{"JWT": []}], - "description": "Updates user attributes.", - "parameters": [ - { - "in": "body", - "name": "body", - "description": "Attributes to update for the user.", - "required": True, - "schema": { - "type": "object", - "properties": { - "new_username": {"type": "string", "example": "mycoolusername"}, - "new_email": {"type": "string", "example": "mymail@dot.com"}, - "new_displayname": { - "type": "string", - "example": "MyCoolDisplayName", - }, - "new_password": {"type": "string", "example": "My5tr0ngP@55w0rd"}, - }, - }, - } - ], - "responses": { - "200": {"description": "User attributes updated successfully."}, - "400": {"description": "Bad request. Check the request body for errors."}, - "401": {"description": "Unauthorized. User must be logged in."}, - "409": {"description": "Conflict. Check the response message for details."}, - "500": { - "description": "Internal server error. Contact the system administrator." - }, - }, -} - - -delete_swagger = { - "methods": ["DELETE"], - "tags": ["User"], - "security": [{"JWT": []}], - "description": "Deletes a user via JWT token", - "parameters": [], - "responses": {"200": {"description": "User successfully deleted"}}, -} diff --git a/backend/app/jwt_utils.py b/backend/app/jwt_utils.py deleted file mode 100644 index 3fff27c..0000000 --- a/backend/app/jwt_utils.py +++ /dev/null @@ -1,13 +0,0 @@ -from app.extensions import jwt_redis_blocklist - -from . import jwt_manager - -from app import app - - -@jwt_manager.token_in_blocklist_loader -def check_if_token_is_revoked(jwt_header, jwt_payload: dict) -> bool: - jti = jwt_payload["jti"] - token_in_redis = jwt_redis_blocklist.get(jti) - - return token_in_redis is not None diff --git a/backend/app/mail/mail.py b/backend/app/mail/mail.py deleted file mode 100644 index 6fe2e96..0000000 --- a/backend/app/mail/mail.py +++ /dev/null @@ -1,17 +0,0 @@ -from flask_mail import Message - -from app import flask_mail - -from app.mail.message_content import MessageContent - - -def send_mail(message: MessageContent, recipient: str): - - msg = Message(subject=message.subject, recipients=[recipient], body=message.body) - - try: - flask_mail.send(msg) - return True - except Exception as e: - print(f"Failed to send email. Error: {e}") - return False diff --git a/backend/app/mail/message_content.py b/backend/app/mail/message_content.py deleted file mode 100644 index 1d787e9..0000000 --- a/backend/app/mail/message_content.py +++ /dev/null @@ -1,4 +0,0 @@ -class MessageContent: - def __init__(self, subject, body): - self.subject = subject - self.body = body diff --git a/backend/app/messages/api_errors.py b/backend/app/messages/api_errors.py deleted file mode 100644 index c95f7f9..0000000 --- a/backend/app/messages/api_errors.py +++ /dev/null @@ -1,15 +0,0 @@ -NOT_JSON = {"msg": "Request body must be JSON"}, 400 - - -def UNKNOWN_DATABASE_ERROR(e): - return {"msg": f"An unknown error occurred within the database. {e}"}, 500 - - -def MISSING_FIELDS(fields): - return {"msg": f"Missing required fields: {', '.join(fields)}"}, 400 - - -def NO_FIELD_PROVIDED(possible_fields): - return { - "msg": f"No field was provided. At least one of the following is required: {', '.join(possible_fields)}" - }, 400 diff --git a/backend/app/messages/api_responses/product_responses.py b/backend/app/messages/api_responses/product_responses.py deleted file mode 100644 index 117db98..0000000 --- a/backend/app/messages/api_responses/product_responses.py +++ /dev/null @@ -1,6 +0,0 @@ -PRODUCT_LISTING_CREATED_SUCCESSFULLY = {"msg": "Successfully created a brand new product."}, 201 - -NOT_OWNER_OF_PRODUCT = {"msg": "You don't own this product, therefore you cannot delete it!"}, 400 -UNKNOWN_PRODUCT = {"msg": "The product you tried fetching is not known. Try a different product ID."}, 400 - -SCROLLED_TOO_FAR = {"msg": "You scrolled too far in the pages. Try going back a little again."}, 400 \ No newline at end of file diff --git a/backend/app/messages/api_responses/user_responses.py b/backend/app/messages/api_responses/user_responses.py deleted file mode 100644 index 6749600..0000000 --- a/backend/app/messages/api_responses/user_responses.py +++ /dev/null @@ -1,26 +0,0 @@ -USER_CREATED_SUCCESSFULLY = {"msg": "User created successfully."}, 201 -USER_LOGGED_OUT_SUCCESSFULLY = {"msg": "Successfully logged out"}, 200 -USER_DELETED_SUCCESSFULLY = {"msg": "User successfully deleted"}, 200 - - -def USER_ACCOUNT_UPDATED_SUCCESSFULLY(updated_attributes): - return {"msg": f"Successfully updated your accounts {', '.join(updated_attributes)}"}, 200 - -INVALID_USERNAME_FORMAT = { - "msg": "Username is in incorrect format. It must be between 1 and 64 lowercase characters." -}, 400 -INVALID_DISPLAYNAME_FORMAT = { - "msg": "Display name is in incorrect format. It must contain only letters, '.', '-', or '_' and be between 1 and 64 characters." -}, 400 -INVALID_EMAIL_FORMAT = {"msg": "Email is in incorrect format."}, 400 -INVALID_PASSWORD_FORMAT = { - "msg": "Password is in incorrect format. It must be between 8 and 64 characters and contain at least one uppercase letter, one lowercase letter, one digit, and one special character" -}, 400 - -EMAIL_ALREADY_IN_USE = {"msg": "Email already in use."}, 409 -USERNAME_ALREADY_IN_USE = {"msg": "Username already in use."}, 409 - -USERNAME_NOT_FOUND = {"msg": "Username not found"}, 400 -INCORRECT_PASSWORD = {"msg": "Incorrect password"}, 401 - -UNKNOWN_ERROR = {"msg": "An unknown error occurred with user"}, 500 diff --git a/backend/app/messages/mail_responses/user_email.py b/backend/app/messages/mail_responses/user_email.py deleted file mode 100644 index 9772315..0000000 --- a/backend/app/messages/mail_responses/user_email.py +++ /dev/null @@ -1,26 +0,0 @@ -from app.mail.message_content import MessageContent - -USER_EMAIL_SUCCESSFULLY_REGISTERED = MessageContent( - subject="Successfully registered!", - body="Congratulations! Your account has been successfully created.\nThis mail also serves as a test that the email address is correct", -) - -USER_EMAIL_SUCCESSFULLY_LOGGED_IN = MessageContent( - subject="New Login detected!", - body="A new login token has been created", -) - -USER_EMAIL_SUCCESSFULLY_LOGGED_OUT = MessageContent( - subject="Successfully logged out", - body="A login has been revoked. No further action is needed.", -) - -USER_EMAIL_SUCCESSFULLY_UPDATED_ACCOUNT = MessageContent( - subject="Account updated", - body="Your account has been successfully updated. This also means you have been logged out of everywhere", -) - -USER_EMAIL_SUCCESSFULLY_DELETED_ACCOUNT = MessageContent( - subject="Account Deleted!", - body="Your account has been deleted. No further action needed", -) \ No newline at end of file diff --git a/backend/app/models/user_model.py b/backend/app/models/user_model.py index 66df1d3..904b800 100644 --- a/backend/app/models/user_model.py +++ b/backend/app/models/user_model.py @@ -1,4 +1,5 @@ from sqlalchemy import ForeignKey, Column, Integer, JSON, TIMESTAMP, String, Enum +from sqlalchemy.sql import func from sqlalchemy.orm import relationship from .base_model import Base @@ -16,9 +17,9 @@ class User(Base): first_name = Column(String(64), nullable=True) last_name = Column(String(64), nullable=True) phone_number = Column(String(15), nullable=True) - created_at = Column(TIMESTAMP, default="CURRENT_TIMESTAMP", nullable=True) - updated_at = Column(TIMESTAMP, default="CURRENT_TIMESTAMP", nullable=True) - last_login = Column(TIMESTAMP, default="CURRENT_TIMESTAMP", nullable=True) + created_at = Column(TIMESTAMP, default=func.now, nullable=True) + updated_at = Column(TIMESTAMP, default=func.now, onupdate=func.now, nullable=True) + last_login = Column(TIMESTAMP, nullable=True) profile_picture = Column(String(100), nullable=True) preferences = Column(JSON, nullable=True) diff --git a/backend/app/utils/jwt_utils.py b/backend/app/utils/jwt_utils.py deleted file mode 100644 index 3fff27c..0000000 --- a/backend/app/utils/jwt_utils.py +++ /dev/null @@ -1,13 +0,0 @@ -from app.extensions import jwt_redis_blocklist - -from . import jwt_manager - -from app import app - - -@jwt_manager.token_in_blocklist_loader -def check_if_token_is_revoked(jwt_header, jwt_payload: dict) -> bool: - jti = jwt_payload["jti"] - token_in_redis = jwt_redis_blocklist.get(jti) - - return token_in_redis is not None diff --git a/backend/requirements.txt b/backend/requirements.txt deleted file mode 100644 index 676ae4c..0000000 --- a/backend/requirements.txt +++ /dev/null @@ -1,36 +0,0 @@ -annotated-types==0.7.0 -anyio==4.6.2.post1 -certifi==2024.8.30 -click==8.1.7 -dnspython==2.7.0 -email_validator==2.2.0 -exceptiongroup==1.2.2 -fastapi==0.115.5 -fastapi-cli==0.0.5 -h11==0.14.0 -httpcore==1.0.7 -httptools==0.6.4 -httpx==0.28.0 -idna==3.10 -Jinja2==3.1.4 -markdown-it-py==3.0.0 -MarkupSafe==3.0.2 -mdurl==0.1.2 -passlib==1.7.4 -pydantic==2.10.2 -pydantic_core==2.27.1 -Pygments==2.18.0 -PyJWT==2.10.1 -python-dotenv==1.0.1 -python-multipart==0.0.19 -PyYAML==6.0.2 -rich==13.9.4 -shellingham==1.5.4 -sniffio==1.3.1 -starlette==0.41.3 -typer==0.14.0 -typing_extensions==4.12.2 -uvicorn==0.32.1 -uvloop==0.21.0 -watchfiles==1.0.0 -websockets==14.1