Compare commits

..

No commits in common. "56bc0f2d69775054dd0f8c090308f820e26c5ebb" and "e6ed37ad5e4e1af760b194e80877e78128ca13f1" have entirely different histories.

12 changed files with 117 additions and 509 deletions

215
README.md
View File

@ -1,215 +0,0 @@
# SHOP API
Simple API (still WIP)
# Routes
## Hello World
### `GET /`
- **Description:** A simple route that returns a JSON message saying 'Hello, Flask!'.
- **Response:**
- JSON with the following structure:
```json
{
"message": "Hello, Flask!"
}
```
- **Status Code:**
- 200: Success.
## Users
### `POST /register`
- **Description:** Register a new user.
- **Request Body:**
- JSON with the following fields:
- `username` (string): User's username.
- `displayname` (string): User's display name.
- `email` (string): User's email address.
- `password` (string): User's password.
- **Response:**
- JSON indicating the success of the registration.
- **Status Codes:**
- 200: Success.
- 400: Bad Request (if any required field is missing).
### `POST /login`
- **Description:** Log in a user.
- **Request Body:**
- JSON with the following fields:
- `username` (string): User's username.
- `password` (string): User's password.
- **Response:**
- JSON containing authentication token and user information.
- **Status Codes:**
- 200: Success.
- 400: Bad Request (if any required field is missing).
### `DELETE /logout`
- **Description:** Log out a user by invalidating the JWT token.
- **Authentication:**
- Requires a valid JWT token.
- **Response:**
- JSON indicating the success of the logout.
- **Status Codes:**
- 200: Success.
- 401: Unauthorized (if JWT token is missing or invalid).
### `PUT /update/username`
- **Description:** Update the username of the authenticated user.
- **Authentication:**
- Requires a valid JWT token.
- **Request Body:**
- JSON with the following field:
- `new_username` (string): New username.
- **Response:**
- JSON indicating the success of the username update.
- **Status Codes:**
- 200: Success.
- 400: Bad Request (if new_username is missing).
- 401: Unauthorized (if JWT token is missing or invalid).
### `PUT /update/displayname`
- **Description:** Update the display name of the authenticated user.
- **Authentication:**
- Requires a valid JWT token.
- **Request Body:**
- JSON with the following field:
- `new_displayname` (string): New display name.
- **Response:**
- JSON indicating the success of the display name update.
- **Status Codes:**
- 200: Success.
- 400: Bad Request (if new_displayname is missing).
- 401: Unauthorized (if JWT token is missing or invalid).
### `PUT /update/email`
- **Description:** Update the email address of the authenticated user.
- **Authentication:**
- Requires a valid JWT token.
- **Request Body:**
- JSON with the following field:
- `new_email` (string): New email address.
- **Response:**
- JSON indicating the success of the email update.
- **Status Codes:**
- 200: Success.
- 400: Bad Request (if new_email is missing).
- 401: Unauthorized (if JWT token is missing or invalid).
### `PUT /update/password`
- **Description:** Update the password of the authenticated user.
- **Authentication:**
- Requires a valid JWT token.
- **Request Body:**
- JSON with the following field:
- `new_password` (string): New password.
- **Response:**
- JSON indicating the success of the password update.
- **Status Codes:**
- 200: Success.
- 400: Bad Request (if new_password is missing).
- 401: Unauthorized (if JWT token is missing or invalid).
### `DELETE /delete`
- **Description:** Delete the account of the authenticated user.
- **Authentication:**
- Requires a valid JWT token.
- **Response:**
- JSON indicating the success of the account deletion.
- **Status Codes:**
- 200: Success.
- 401: Unauthorized (if JWT token is missing or invalid).
## Cart
### `GET /`
- **Description:** Retrieve the contents of the user's shopping cart.
- **Authentication:**
- Requires a valid JWT token.
- **Response:**
- JSON containing a list of dictionaries representing the cart contents.
- **Status Codes:**
- 200: Success.
- 401: Unauthorized (if JWT token is missing or invalid).
### `PUT /add/<int:product_id>`
- **Description:** Add a specified quantity of a product to the user's shopping cart.
- **Authentication:**
- Requires a valid JWT token.
- **Parameters:**
- `count` (optional, int): Quantity of the product to add. Defaults to 1.
- **Response:**
- JSON indicating the success of adding the product to the cart.
- **Status Codes:**
- 200: Success.
- 400: Bad Request (if count is less than 1).
- 401: Unauthorized (if JWT token is missing or invalid).
### `DELETE /remove/<int:product_id>`
- **Description:** Remove a specific product from the user's shopping cart.
- **Authentication:**
- Requires a valid JWT token.
- **Response:**
- JSON indicating the success of removing the product from the cart.
- **Status Codes:**
- 200: Success.
- 401: Unauthorized (if JWT token is missing or invalid).
### `PUT /update/<int:product_id>`
- **Description:** Update the quantity of a product in the user's shopping cart.
- **Authentication:**
- Requires a valid JWT token.
- **Parameters:**
- `count` (int): New quantity of the product.
- **Response:**
- JSON indicating the success of updating the product quantity in the cart.
- **Status Codes:**
- 200: Success.
- 400: Bad Request (if count is missing or not a valid integer).
- 401: Unauthorized (if JWT token is missing or invalid).
### `GET /purchase`
- **Description:** Complete a purchase, transferring items from the user's cart to the purchase history.
- **Authentication:**
- Requires a valid JWT token.
- **Response:**
- JSON indicating the success of the purchase.
- **Status Codes:**
- 200: Success.
- 401: Unauthorized (if JWT token is missing or invalid).
## Products
### `GET /get`
- **Description:** Retrieve a paginated list of products.
- **Parameters:**
- `page` (optional, int): Page number for pagination. Defaults to 0.
- **Response:**
- JSON containing a list of products.
- **Status Codes:**
- 200: Success.
- 400: Bad Request (if the page is less than 0).
### `GET /<int:id>`
- **Description:** Retrieve information about a specific product.
- **Parameters:**
- `id` (int): Product identifier.
- `fields` (optional, string): Comma-separated list of fields to retrieve (e.g., 'name,price,image').
- **Response:**
- JSON containing information about the specified product.
- **Status Codes:**
- 200: Success.
- 400: Bad Request (if invalid fields are provided).
### `POST /create`
- **Description:** Create a new product listing.
- **Authentication:**
- Requires a valid JWT token.
- **Request Body:**
- JSON with the following fields:
- `name` (string): Product name.
- `price` (float): Product price.
- **Response:**
- JSON indicating the success of the product listing creation.
- **Status Codes:**
- 200: Success.
- 400: Bad Request (if name or price is missing or if price is not a valid float).
- 401: Unauthorized (if JWT token is missing or invalid).

View File

@ -1,10 +1,8 @@
from flask import Flask from flask import Flask
from flask_jwt_extended import JWTManager from flask_jwt_extended import JWTManager
from flask_mail import Mail
app = Flask(__name__) app = Flask(__name__)
jwt_manager = JWTManager(app) jwt_manager = JWTManager(app)
mail = Mail(app)
def create_app(): def create_app():
from app.api import bp, bp_errors, bp_product, bp_user, bp_cart from app.api import bp, bp_errors, bp_product, bp_user, bp_cart

View File

@ -2,7 +2,7 @@ from flask import Blueprint
bp_errors = Blueprint('errors', __name__) bp_errors = Blueprint('errors', __name__)
bp = Blueprint('api', __name__) bp = Blueprint('api', __name__)
bp_product = Blueprint('products', __name__, url_prefix="/products") bp_product = Blueprint('product', __name__, url_prefix="/product")
bp_user = Blueprint('user', __name__, url_prefix="/user") bp_user = Blueprint('user', __name__, url_prefix="/user")
bp_cart = Blueprint('cart', __name__, url_prefix="/cart") bp_cart = Blueprint('cart', __name__, url_prefix="/cart")

View File

@ -1,52 +1,20 @@
from flask import jsonify, abort, request from flask import jsonify, abort, request
from flask_jwt_extended import jwt_required, get_jwt_identity
from app.api import bp_product from app.api import bp_product
from app.services.product_service import ProductService from app.services.product_service import ProductService
@bp_product.route('/get', methods=['GET'])
def get_products():
page = request.args.get('page', default=0, type=int)
if page < 0:
return abort(400)
result, status_code = ProductService.get_products(page)
return result, status_code
@bp_product.route('/<int:id>', methods=['GET']) @bp_product.route('/<int:id>', methods=['GET'])
def get_product_info(id: int): def get_product_info(id: int):
fields = ['name', 'price', 'image', 'image_name', 'seller'] fields = ['name', 'price', 'image', 'image_name', 'seller']
fields_param = request.args.get('fields') fields_param = request.args.get('fields')
requested_fields = []
fields_param_list = fields_param.split(',') if fields_param else fields return abort(501)
common_fields = list(set(fields) & set(fields_param_list))
result, status_code = ProductService.get_product_info(common_fields, id)
return result, status_code
@bp_product.route('/create', methods=['POST']) @bp_product.route('/create', methods=['POST'])
@jwt_required()
def create_product_listing(): def create_product_listing():
user_id = get_jwt_identity()
name = request.json.get('name') 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 = ProductService.create_listing(user_id, name, float_price)
return result, status_code

View File

@ -20,9 +20,3 @@ class FlaskTesting:
DEBUG = True DEBUG = True
JWT_SECRET_KEY = os.environ.get('JWT_SECRET_KEY') JWT_SECRET_KEY = os.environ.get('JWT_SECRET_KEY')
SERVER_NAME = os.environ.get('HOST') + ':' + os.environ.get('PORT') SERVER_NAME = os.environ.get('HOST') + ':' + os.environ.get('PORT')
MAIL_SERVER = os.environ.get('MAIL_SERVER')
MAIL_PORT = os.environ.get('MAIL_USERNAME')
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS')

View File

@ -12,6 +12,8 @@ db_connection = mysql.connector.connect(
database=MySqlConfig.MYSQL_DATABASE, database=MySqlConfig.MYSQL_DATABASE,
) )
db_cursor = db_connection.cursor(dictionary=True)
jwt_redis_blocklist = redis.StrictRedis( jwt_redis_blocklist = redis.StrictRedis(
host=RedisConfig.REDIS_HOST, host=RedisConfig.REDIS_HOST,
port=RedisConfig.REDIS_PORT, port=RedisConfig.REDIS_PORT,

View File

@ -1,15 +0,0 @@
from flask_mail import Message
from app import mail
def send_mail(subject: str, recipient: str, body: str):
msg = Message(subject, recipients=[recipient])
msg.body = body
try:
mail.send(msg)
return True
except Exception as e:
print(f"Failed to send email. Error: {e}")
return False

View File

@ -7,17 +7,6 @@ class CartService:
@staticmethod @staticmethod
def add_to_cart(user_id: str, product_id: int, count: int) -> Tuple[Union[dict, str], int]: def add_to_cart(user_id: str, product_id: int, count: int) -> Tuple[Union[dict, str], int]:
"""
Adds a product to a user's cart.
:param user_id: User ID.
:type user_id: str
:param product_id: ID of product to be added.
:type product_id: int
:return: Tuple containing a dictionary with a token and an HTTP status code.
:rtype: Tuple[Union[dict, str], int]
"""
try: try:
with db_connection.cursor(dictionary=True) as cursor: with db_connection.cursor(dictionary=True) as cursor:
cursor.execute("select count from cart_item where cart_id = %s and product_id = %s", (user_id, product_id)) cursor.execute("select count from cart_item where cart_id = %s and product_id = %s", (user_id, product_id))
@ -37,19 +26,6 @@ class CartService:
@staticmethod @staticmethod
def update_count(user_id: str, product_id: int, count: int) -> Tuple[Union[dict, str], int]: def update_count(user_id: str, product_id: int, count: int) -> Tuple[Union[dict, str], int]:
"""
Updates count of products in user's cart
:param user_id: User ID.
:type user_id: str
:param product_id: ID of product to be updated.
:type product_id: int
:param count: New count of products
:type count: int
:return: Tuple containing a dictionary with a token and an HTTP status code.
:rtype: Tuple[Union[dict, str], int]
"""
try: try:
if count <= 0: if count <= 0:
return CartService.delete_from_cart(user_id, product_id) return CartService.delete_from_cart(user_id, product_id)
@ -65,18 +41,6 @@ class CartService:
@staticmethod @staticmethod
def delete_from_cart(user_id: str, product_id: int) -> Tuple[Union[dict, str], int]: def delete_from_cart(user_id: str, product_id: int) -> Tuple[Union[dict, str], int]:
"""
Completely deletes an item from a user's cart
:param user_id: User ID.
:type user_id: str
:param product_id: ID of product to be updated.
:type product_id: int
:return: Tuple containing a dictionary with a token and an HTTP status code.
:rtype: Tuple[Union[dict, str], int]
"""
try: try:
with db_connection.cursor() as cursor: with db_connection.cursor() as cursor:
cursor.execute("delete from cart_item where cart_id = %s and product_id = %s", (user_id, product_id)) cursor.execute("delete from cart_item where cart_id = %s and product_id = %s", (user_id, product_id))
@ -89,15 +53,6 @@ class CartService:
@staticmethod @staticmethod
def show_cart(user_id: str) -> Tuple[Union[dict, str], int]: def show_cart(user_id: str) -> Tuple[Union[dict, str], int]:
"""
Gives the user the content of their cart
:param user_id: User ID.
:type user_id: str
:return: Tuple containing a dictionary with a token and an HTTP status code.
:rtype: Tuple[Union[dict, str], int]
"""
try: try:
with db_connection.cursor(dictionary=True) as cursor: with db_connection.cursor(dictionary=True) as cursor:
cursor.execute("select product.name as product_name, count, price_subtotal, date_added from cart_item inner join product on cart_item.product_id = product.id where cart_item.cart_id = %s", (user_id,)) cursor.execute("select product.name as product_name, count, price_subtotal, date_added from cart_item inner join product on cart_item.product_id = product.id where cart_item.cart_id = %s", (user_id,))
@ -123,15 +78,6 @@ class CartService:
@staticmethod @staticmethod
def purchase(user_id: str) -> Tuple[Union[dict, str], int]: def purchase(user_id: str) -> Tuple[Union[dict, str], int]:
"""
"Purchases" the contents of user's cart
:param user_id: User ID.
:type user_id: str
:return: Tuple containing a dictionary with a token and an HTTP status code.
:rtype: Tuple[Union[dict, str], int]
"""
try: try:
with db_connection.cursor(dictionary=True) as cursor: with db_connection.cursor(dictionary=True) as cursor:
# get all cart items # get all cart items

View File

@ -3,125 +3,82 @@ import base64
from flask import abort from flask import abort
from mysql.connector import Error from mysql.connector import Error
from decimal import Decimal
from app.extensions import db_connection from app.extensions import db_connection
class ProductService: class ProductService:
@staticmethod @staticmethod
def get_products(page: int): def get_name(product_id: int):
""" db_cursor.execute(f"select name from product where product.id = {product_id}")
Fetches 10 products
:param page: Page, aka offset of fetching if db_cursor.rowcount != 1:
:type page: int return abort(404)
"""
try: result = db_cursor.fetchone()
with db_connection.cursor(dictionary=True) as cursor: return result['name']
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 {"Failed": "Failed to fetch products. You've probably selected too far with pages"}, 400
result_obj = []
for row in results:
mid_result = {
"id": row['id'],
"seller": row['seller'],
"name": row['name'],
"price": row['price_pc']
}
result_obj.append(mid_result)
return result_obj, 200
except Error as e:
return {"Failed": f"Failed to fetch products. Error: {e}"}, 500
@staticmethod @staticmethod
def get_product_info(fields: list[str], product_id: int): def get_manufacturer(product_id: int):
""" db_cursor.execute(f"select user.displayname as seller from product inner join user on product.seller_id = user.id where product.id = {product_id}")
Fetches specific product info
:param fields: array of fields that can be fetched if db_cursor.rowcount != 1:
:type fields: list[str] return abort(404)
:param product_id: ID of product to be updated.
:type product_id: int
"""
try: result = db_cursor.fetchone()
with db_connection.cursor(dictionary=True) as cursor: return result['seller']
fields_to_sql = {
"name": "product.name",
"price": "product.price_pc as price",
"image": "product.image",
"image_name": "product.image_name",
"seller": "user.displayname as seller"
}
field_sql = []
for field in fields:
field_sql.append(fields_to_sql[field])
select_params = ", ".join(field_sql)
if "seller" in fields:
cursor.execute(f"select {select_params} from product inner join user on user.id = product.seller_id where product.id = %s", (product_id,))
else:
cursor.execute(f"select {select_params} from product where id = %s", (product_id,))
result = cursor.fetchone()
if cursor.rowcount != 1:
return {"Failed": "Failed to fetch product. Product likely doesn't exist"}, 400
result_obj = {}
for field in fields:
if field == "image": # Encode image into base64
result_obj[field] = base64.b64encode(result[field]).decode('utf-8')
else:
result_obj[field] = result[field]
return result_obj, 200
except Error as e:
return {"Failed": f"Failed to fetch product info. Error: {e}"}, 500
@staticmethod @staticmethod
def create_listing(seller_id: str, name: str, price: float): def get_price(product_id: int):
""" db_cursor.execute(f"select price_pc from product where product.id = {product_id}")
Creates a new product listing
:param seller_id: User ID if db_cursor.rowcount != 1:
:type seller_id: str return abort(404)
:param name: New product's name
:type name: str
:param price: New product's price
:type price: float
"""
result = db_cursor.fetchone()
return result['price_pc']
@staticmethod
def get_image(product_id: int):
db_cursor.execute(f"select image from product where product.id = {product_id}")
if db_cursor.rowcount != 1:
return abort(404)
result = db_cursor.fetchone()
return base64.b64encode(result['image']).decode('utf-8')
@staticmethod
def get_image_name(product_id: int):
db_cursor.execute(f"select image_name from product where product.id = {product_id}")
if db_cursor.rowcount != 1:
return abort(404)
result = db_cursor.fetchone()
return result['image_name']
@staticmethod
def get_all_info(product_id: int):
db_cursor.execute(f"select name, user.displayname as seller, price_pc, image_name, image from product inner join user on product.seller_id = user.id where product.id = {product_id}")
if db_cursor.rowcount != 1:
return abort(404)
result = db_cursor.fetchone()
return {
"name": result['name'],
"seller": result['seller'],
"price": result['price_pc'],
"image_name": result['image_name'],
"image": base64.b64encode(result['image']).decode('utf-8')
}
@staticmethod
def create_listing(name: str, seller_id: str, price: float, image_name: str, image):
try: try:
with db_connection.cursor() as cursor: db_cursor.execute("insert into product(seller_id, name, price_pc, image, image_name) values (%s, %s, %s, %s, %s)", (seller_id, name, price, image, image_name))
rounded_price = round(price, 2) db_connection.commit()
cursor.execute("insert into product(seller_id, name, price_pc) values (%s, %s, %s)", (seller_id, name, Decimal(str(rounded_price))))
db_connection.commit()
except Error as e: except Error as e:
return {"Failed": f"Failed to create product. {e}"}, 400 return {"Failed": f"Failed to create product. {e}"}, 400
return {"Success": "Successfully created new product listing"}, 200 return {"Success", "Successfully created new product listing"}, 200
@staticmethod
def __is_base64_jpg(decoded_string):
try:
# Checking if the decoded data represents a valid JPEG image
image_type = imghdr.what(None, decoded_data)
return image_type == 'jpeg'
except Exception:
return False

View File

@ -6,11 +6,9 @@ from typing import Tuple, Union
from mysql.connector import Error from mysql.connector import Error
from flask_jwt_extended import create_access_token from flask_jwt_extended import create_access_token
from app.extensions import db_connection from app.extensions import db_cursor, db_connection
from app.extensions import jwt_redis_blocklist from app.extensions import jwt_redis_blocklist
from app.mail_utils import send_mail
class UserService: class UserService:
""" """
@ -40,33 +38,27 @@ class UserService:
:rtype: Tuple[Union[dict, str], int] :rtype: Tuple[Union[dict, str], int]
""" """
if not UserService.__verify_username(username):
return {"Failed": "Failed to verify username. Try another username"}, 400
if not UserService.__verify_displayname(displayname):
return {"Failed": "Failed to verify display name. Try another name"}, 400
if not UserService.__verify_email(email):
return {"Failed": "Failed to verify email. Try another email"}, 400
if not UserService.__verify_password(password):
return {"Failed": "Failed to verify password. Try another (stronger) password"}, 400
hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
try: try:
if not UserService.__verify_username(username): db_cursor.execute("insert into user (username, displayname, email, password) values (%s, %s, %s, %s)", (username, displayname, email, hashed_password))
return {"Failed": "Failed to verify username. Try another username"}, 400 db_connection.commit()
if not UserService.__verify_displayname(displayname):
return {"Failed": "Failed to verify display name. Try another name"}, 400
if not UserService.__verify_email(email):
return {"Failed": "Failed to verify email. Try another email"}, 400
if not UserService.__verify_password(password):
return {"Failed": "Failed to verify password. Try another (stronger) password"}, 400
hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
with db_connection.cursor() as cursor:
cursor.execute("insert into user (username, displayname, email, password) values (%s, %s, %s, %s)", (username, displayname, email, hashed_password))
db_connection.commit()
except Error as e: except Error as e:
print(f"Error: {e}") print(f"Error: {e}")
return {"Failed": "Failed to insert into database. Username or email are likely in use already"}, 500 return {"Failed": "Failed to insert into database. Username or email are likely in use already"}, 500
# TODO Implement mail sending
# Currently throws error - connection refused
# send_mail("Successfully registered!", email, "Congratulations! Your account has been successfully created.\nThis mail also serves as a test that the email address is correct")
return {"Success": "User created successfully"}, 200 return {"Success": "User created successfully"}, 200
@staticmethod @staticmethod
@ -81,29 +73,24 @@ class UserService:
:return: Tuple containing a dictionary with a token and an HTTP status code. :return: Tuple containing a dictionary with a token and an HTTP status code.
:rtype: Tuple[Union[dict, str], int] :rtype: Tuple[Union[dict, str], int]
""" """
try:
with db_connection.cursor(dictionary=True) as cursor:
cursor.execute("select id, password from user where username = %s", (username,)) db_cursor.execute("select id, password from user where username = %s", (username,))
result = db_cursor.fetchone() result = db_cursor.fetchone()
user_id = result['id'] user_id = result['id']
password_hash = result['password'] password_hash = result['password']
if user_id is None: if user_id is None:
return {"Failed": "Username not found"}, 400 return {"Failed": "Username not found"}, 400
if not bcrypt.checkpw(password.encode('utf-8'), password_hash.encode('utf-8')): if not bcrypt.checkpw(password.encode('utf-8'), password_hash.encode('utf-8')):
return {"Failed": "Incorrect password"}, 401 return {"Failed": "Incorrect password"}, 401
expire = datetime.timedelta(hours=1) expire = datetime.timedelta(hours=1)
token = create_access_token(identity=user_id, expires_delta=expire) token = create_access_token(identity=user_id, expires_delta=expire)
return {"token": token}, 200 return {"token": token}, 200
except Error as e:
return {"Failed": f"Failed to login. Error: {e}"}, 500
@staticmethod @staticmethod
def logout(jti, exp) -> Tuple[Union[dict, str], int]: def logout(jti, exp) -> Tuple[Union[dict, str], int]:
@ -123,19 +110,9 @@ class UserService:
@staticmethod @staticmethod
def delete_user(user_id: str) -> Tuple[Union[dict, str], int]: def delete_user(user_id: str) -> Tuple[Union[dict, str], int]:
"""
Deletes a user account.
:param user_id: User ID.
:type user_id: str
:return: Tuple containing a dictionary and an HTTP status code.
:rtype: Tuple[Union[dict, str], int]
"""
try: try:
with db_connection.cursor() as cursor: db_cursor.execute("delete from user where id = %s", (user_id,))
cursor.execute("delete from user where id = %s", (user_id,)) db_connection.commit()
db_connection.commit()
except Error as e: except Error as e:
return {"Failed": f"Failed to delete user. {e}"}, 500 return {"Failed": f"Failed to delete user. {e}"}, 500
@ -154,13 +131,12 @@ class UserService:
:rtype: Tuple[Union[dict, str], int] :rtype: Tuple[Union[dict, str], int]
""" """
try: if not UserService.__verify_email(new_email):
if not UserService.__verify_email(new_email): return {"Failed": "Failed to verify email. Try another email"}, 400
return {"Failed": "Failed to verify email. Try another email"}, 400
with db_connection.cursor() as cursor: try:
cursor.execute("update user set email = %s where id = %s", (new_email, user_id)) db_cursor.execute("update user set email = %s where id = %s", (new_email, user_id))
db_connection.commit() db_connection.commit()
except Error as e: except Error as e:
return {"Failed": f"Failed to update email. Email is likely in use already. Error: {e}"}, 500 return {"Failed": f"Failed to update email. Email is likely in use already. Error: {e}"}, 500
@ -179,13 +155,12 @@ class UserService:
:rtype: Tuple[Union[dict, str], int] :rtype: Tuple[Union[dict, str], int]
""" """
try: if not UserService.__verify_name(new_username):
if not UserService.__verify_name(new_username): return {"Failed": "Failed to verify username. Try another one"}, 400
return {"Failed": "Failed to verify username. Try another one"}, 400
with db_connection.cursor() as cursor: try:
cursor.execute("update user set username = %s where id = %s", (new_username, user_id)) db_cursor.execute("update user set username = %s where id = %s", (new_username, user_id))
db_connection.commit() db_connection.commit()
except Error as e: except Error as e:
return {"Failed": f"Failed to update username. Username is likely in use already. Error: {e}"}, 500 return {"Failed": f"Failed to update username. Username is likely in use already. Error: {e}"}, 500
@ -204,15 +179,14 @@ class UserService:
:rtype: Tuple[Union[dict, str], int] :rtype: Tuple[Union[dict, str], int]
""" """
if not UserService.__verify_password(new_password):
return {"Failed": "Failed to verify password. Try another (stronger) one"}, 400
hashed_password = bcrypt.hashpw(new_password.encode('utf-8'), bcrypt.gensalt())
try: try:
if not UserService.__verify_password(new_password): db_cursor.execute("update user set password = %s where id = %s", (new_username, user_id))
return {"Failed": "Failed to verify password. Try another (stronger) one"}, 400 db_connection.commit()
hashed_password = bcrypt.hashpw(new_password.encode('utf-8'), bcrypt.gensalt())
with db_connection.cursor() as cursor:
cursor.execute("update user set password = %s where id = %s", (new_username, user_id))
db_connection.commit()
except Error as e: except Error as e:
return {"Failed": f"Failed to update password. Error: {e}"}, 500 return {"Failed": f"Failed to update password. Error: {e}"}, 500

View File

@ -4,4 +4,3 @@ mysql-connector-python==8.3.0
python-dotenv==1.0.1 python-dotenv==1.0.1
Flask-JWT-Extended==4.5.3 Flask-JWT-Extended==4.5.3
PyJWT==2.8.0 PyJWT==2.8.0
Flask-Mail==0.9.1

View File

@ -35,7 +35,7 @@ create table product(
seller_id int not null, seller_id int not null,
name varchar(32) not null, name varchar(32) not null,
price_pc decimal(12,2) not null, price_pc decimal(12,2) not null,
image blob, image blob not null,
image_name varchar(64), image_name varchar(64),
foreign key (seller_id) references user(id) foreign key (seller_id) references user(id)
) auto_increment = 13001; ) auto_increment = 13001;