PATCH: fixed issues with authentication

This commit is contained in:
white
2025-01-29 11:31:43 +00:00
parent cd1a246483
commit e2070be9f9
9 changed files with 86 additions and 107 deletions

View File

@@ -26,7 +26,7 @@ def signup():
# Validation - ensure all fields exist, users cannot have an empty field # Validation - ensure all fields exist, users cannot have an empty field
if not all([username, email, password]): if not all([username, email, password]):
error_fields = get_error_fields([username, email, password]), #!←← find the error_fields, to highlight them in red to the user on the frontend error_fields = get_error_fields([username, email, password]) #!←← find the error_fields, to highlight them in red to the user on the frontend
return jsonify({ return jsonify({
"account_created": False, "account_created": False,
"error_fields": error_fields, "error_fields": error_fields,
@@ -48,25 +48,25 @@ def signup():
# Create a connection to the database # Create a connection to the database
db = Database() db = Database()
cursor = db.create_connection() db.create_connection()
try: try:
# Check for duplicate email/username, no two users can have the same # Check for duplicate email/username, no two users can have the same
dup_email = cursor.execute( dup_email = db.fetchone(
"SELECT * FROM users WHERE email = ?", "SELECT * FROM users WHERE email = ?",
(email,) (email,)
).fetchone() )
dup_username = cursor.execute( dup_username = db.fetchone(
"SELECT * FROM users WHERE username = ?", "SELECT * FROM users WHERE username = ?",
(username,) (username,)
).fetchone() )
if dup_email is not None: if dup_email is not None:
return jsonify({ return jsonify({
"account_created": False, "account_created": False,
"error_fields": ["email"], "error_fields": ["email"],
"message": "Email already taken" "message": f"Email already taken: {email}"
}), 400 }), 400
if dup_username is not None: if dup_username is not None:
@@ -77,7 +77,7 @@ def signup():
}), 400 }), 400
# Create new user once input is validated # Create new user once input is validated
cursor.execute( db.execute(
"""INSERT INTO users """INSERT INTO users
(username, password, email, num_followers, stream_key, is_partnered, bio, current_stream_title, current_selected_category_id) (username, password, email, num_followers, stream_key, is_partnered, bio, current_stream_title, current_selected_category_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""", VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
@@ -151,14 +151,14 @@ def login():
# Create a connection to the database # Create a connection to the database
db = Database() db = Database()
cursor = db.create_connection() db.create_connection()
try: try:
# Check if user exists, only existing users can be logged in # Check if user exists, only existing users can be logged in
user = cursor.execute( user = db.fetchone(
"SELECT * FROM users WHERE username = ?", "SELECT * FROM users WHERE username = ?",
(username,) (username,)
).fetchone() )
if not user: if not user:
return jsonify({ return jsonify({
@@ -210,7 +210,4 @@ def logout() -> dict:
def get_error_fields(values: list): def get_error_fields(values: list):
fields = ["username", "email", "password"] fields = ["username", "email", "password"]
for x in fields: return [fields[i] for i, v in enumerate(values) if not v]
if not values[fields.index(x)]:
fields.remove(x)
return fields

View File

@@ -51,10 +51,10 @@ def get_past_chat(stream_id: int):
# Connect to the database # Connect to the database
db = Database() db = Database()
cursor = db.create_connection() db.create_connection()
# fetched in format: [(chatter_id, message, time_sent)] # fetched in format: [(chatter_id, message, time_sent)]
all_chats = cursor.execute(""" all_chats = db.fetchall("""
SELECT * SELECT *
FROM ( FROM (
SELECT chatter_id, message, time_sent SELECT chatter_id, message, time_sent
@@ -63,7 +63,7 @@ def get_past_chat(stream_id: int):
ORDER BY time_sent DESC ORDER BY time_sent DESC
LIMIT 50 LIMIT 50
) )
ORDER BY time_sent ASC;""", (stream_id,)).fetchall() ORDER BY time_sent ASC;""", (stream_id,))
db.close_connection() db.close_connection()
# Create JSON output of chat_history to pass through NGINX proxy # Create JSON output of chat_history to pass through NGINX proxy
@@ -103,8 +103,8 @@ def send_chat(data) -> None:
def save_chat(chatter_id, stream_id, message): def save_chat(chatter_id, stream_id, message):
"""Save the chat to the database""" """Save the chat to the database"""
db = Database() db = Database()
cursor = db.create_connection() db.create_connection()
cursor.execute(""" db.execute("""
INSERT INTO chat (chatter_id, stream_id, message) INSERT INTO chat (chatter_id, stream_id, message)
VALUES (?, ?, ?);""", (chatter_id, stream_id, message)) VALUES (?, ?, ?);""", (chatter_id, stream_id, message))
db.commit_data() db.commit_data()

View File

@@ -34,7 +34,7 @@ def get_recommended_streams() -> list[dict]:
Queries DB to get a list of recommended streams using an algorithm Queries DB to get a list of recommended streams using an algorithm
""" """
user_id = session.get("user_id") user_id = session.get("username")
category = user_recommendation_category(user_id) category = user_recommendation_category(user_id)
streams = recommendations_based_on_category(category) streams = recommendations_based_on_category(category)
return jsonify(streams) return jsonify(streams)
@@ -180,7 +180,7 @@ def publish_stream():
1, 1,
datetime.now(), datetime.now(),
1)) 1))
db.commit_data()
return redirect(f"/{user_info['username']}") return redirect(f"/{user_info['username']}")
@@ -198,5 +198,6 @@ def end_stream():
# Set stream to not live # Set stream to not live
db.execute("""UPDATE streams SET isLive = 0 WHERE user_id = ? AND isLive = 1""", (user_info["user_id"],)) db.execute("""UPDATE streams SET isLive = 0 WHERE user_id = ? AND isLive = 1""", (user_info["user_id"],))
db.commit_data()
return "Stream ended", 200 return "Stream ended", 200

View File

@@ -4,69 +4,50 @@ import os
class Database: class Database:
def __init__(self) -> None: def __init__(self) -> None:
self._db = os.path.join(os.path.abspath(os.path.dirname(__file__)), "app.db") self._db = os.path.join(os.path.abspath(os.path.dirname(__file__)), "app.db")
self._conn = None
self.cursor = None self.cursor = None
def create_connection(self) -> sqlite3.Cursor: def create_connection(self) -> None:
conn = sqlite3.connect(self._db) """Create a database connection if not already established."""
conn.row_factory = sqlite3.Row if self._conn is None:
self._conn = conn self._conn = sqlite3.connect(self._db)
self.cursor = conn.cursor() self._conn.row_factory = sqlite3.Row
return self.cursor self.cursor = self._conn.cursor()
def fetchall(self, query: str, parameters=None) -> list[dict]:
if parameters:
self.cursor.execute(query, parameters)
else:
self.cursor.execute(query)
def close_connection(self) -> None:
"""Close the database connection."""
if self._conn:
self._conn.close()
self._conn = None
self.cursor = None
def fetchall(self, query: str, parameters=None) -> list[dict]:
"""Fetch all records from the database."""
self.create_connection()
self.cursor.execute(query, parameters or ())
result = self.cursor.fetchall() result = self.cursor.fetchall()
return self.convert_to_list_dict(result) return self.convert_to_list_dict(result)
def fetchone(self, query: str, parameters=None) -> list[dict]:
if parameters:
self.cursor.execute(query, parameters)
else:
self.cursor.execute(query)
def fetchone(self, query: str, parameters=None) -> dict | None:
"""Fetch one record from the database."""
self.create_connection()
self.cursor.execute(query, parameters or ())
result = self.cursor.fetchone() result = self.cursor.fetchone()
return self.convert_to_list_dict(result) return self.convert_to_list_dict(result) if result else None
def execute(self, query: str, parameters=None) -> None: def execute(self, query: str, parameters=None) -> None:
""" """Execute an INSERT, UPDATE, or DELETE command and commit changes."""
Executes a command (e.g., INSERT, UPDATE, DELETE) and commits the changes. self.create_connection()
"""
try: try:
if parameters: self.cursor.execute(query, parameters or ())
self.cursor.execute(query, parameters) self._conn.commit()
else: except sqlite3.DatabaseError as e:
self.cursor.execute(query) print(f"Database error: {e}")
self.commit_data()
except Exception as e:
print(f"Error executing command: {e}")
raise raise
def convert_to_list_dict(self, result): def convert_to_list_dict(self, result):
""" """Convert query result to a list of dictionaries."""
Converts a query result to a list of dictionaries
"""
# Get the column names from the cursor
columns = [description[0] for description in self.cursor.description]
if not result: if not result:
# for empty result
return [] return []
elif isinstance(result, sqlite3.Row): columns = [desc[0] for desc in self.cursor.description]
# for fetchone return [dict(zip(columns, row)) for row in result] if isinstance(result, list) else dict(zip(columns, result))
return dict(zip(columns, result))
else:
# for fetchall or fetchmany
return [dict(zip(columns, row)) for row in result]
def commit_data(self):
try:
self._conn.commit()
except Exception as e:
print(e)
def close_connection(self) -> None:
self._conn.close()

View File

@@ -48,4 +48,4 @@ CREATE TABLE streams
category_id NOT NULL, category_id NOT NULL,
FOREIGN KEY (category_id) REFERENCES categories(category_id) ON DELETE CASCADE, FOREIGN KEY (category_id) REFERENCES categories(category_id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
); );

View File

@@ -6,10 +6,10 @@ def user_recommendation_category(user_id: int) -> Optional[int]:
Queries user_preferences database to find users favourite streaming category and returns the category Queries user_preferences database to find users favourite streaming category and returns the category
""" """
db = Database() db = Database()
cursor = db.create_connection() db.create_connection()
data = cursor.execute( data = db.fetchone(
"SELECT category_id FROM user_preferences WHERE user_id = ? ORDER BY favourability DESC LIMIT 1", (user_id,)).fetchone() "SELECT category_id FROM user_preferences WHERE user_id = ? ORDER BY favourability DESC LIMIT 1", (user_id,))
return data[0] return data[0]
def followed_categories_recommendations(user_id: int): def followed_categories_recommendations(user_id: int):

View File

@@ -8,15 +8,15 @@ def streamer_live_status(user_id: int) -> bool:
Returns boolean on whether the given streamer is live Returns boolean on whether the given streamer is live
""" """
db = Database() db = Database()
cursor = db.create_connection() db.create_connection()
return bool(cursor.execute("SELECT 1 FROM streams WHERE user_id = ? AND isLive = 1 ORDER BY stream_id DESC", (user_id,)).fetchone()) return bool(db.fetchone("SELECT 1 FROM streams WHERE user_id = ? AND isLive = 1 ORDER BY stream_id DESC", (user_id,)))
def followed_live_streams(user_id: int) -> list[dict]: def followed_live_streams(user_id: int) -> list[dict]:
""" """
Searches for streamers who the user followed which are currently live Searches for streamers who the user followed which are currently live
""" """
db = Database() db = Database()
cursor = db.create_connection() db.create_connection()
live_streams = db.fetchall(""" live_streams = db.fetchall("""
SELECT user_id, stream_id, title, num_viewers SELECT user_id, stream_id, title, num_viewers
@@ -60,7 +60,7 @@ def user_stream(user_id: int, stream_id: int) -> dict:
Returns data of a streamers selected stream Returns data of a streamers selected stream
""" """
db = Database() db = Database()
cursor = db.create_connection() db.create_connection()
stream = db.fetchone("SELECT * FROM streams WHERE user_id = ? AND stream_id = ?", (user_id,stream_id)) stream = db.fetchone("SELECT * FROM streams WHERE user_id = ? AND stream_id = ?", (user_id,stream_id))
return stream return stream

View File

@@ -14,13 +14,13 @@ def get_user_id(username: str) -> Optional[int]:
Returns user_id associated with given username Returns user_id associated with given username
""" """
db = Database() db = Database()
cursor = db.create_connection() db.create_connection()
try: try:
data = cursor.execute( data = db.fetchone(
"SELECT user_id FROM users WHERE username = ?", "SELECT user_id FROM users WHERE username = ?",
(username,) (username,)
).fetchone() )
return data[0] if data else None return data[0] if data else None
except Exception as e: except Exception as e:
print(f"Error: {e}") print(f"Error: {e}")
@@ -31,13 +31,13 @@ def get_username(user_id: str) -> Optional[str]:
Returns username associated with given user_id Returns username associated with given user_id
""" """
db = Database() db = Database()
cursor = db.create_connection() db.create_connection()
try: try:
data = cursor.execute( data = db.fetchone(
"SELECT username FROM user WHERE user_id = ?", "SELECT username FROM user WHERE user_id = ?",
(user_id,) (user_id,)
).fetchone() )
return data[0] if data else None return data[0] if data else None
except Exception as e: except Exception as e:
print(f"Error: {e}") print(f"Error: {e}")
@@ -48,13 +48,13 @@ def is_user_partner(user_id: int) -> bool:
Returns True if user is a partner, else False Returns True if user is a partner, else False
""" """
db = Database() db = Database()
cursor = db.create_connection() db.create_connection()
try: try:
data = cursor.execute( data = db.fetchone(
"SELECT is_partnered FROM users WHERE user_id = ?", "SELECT is_partnered FROM users WHERE user_id = ?",
(user_id,) (user_id,)
).fetchone() )
return bool(data) return bool(data)
except Exception as e: except Exception as e:
print(f"Error: {e}") print(f"Error: {e}")
@@ -65,13 +65,13 @@ def is_subscribed(user_id: int, streamer_id: int) -> bool:
Returns True if user is subscribed to a streamer, else False Returns True if user is subscribed to a streamer, else False
""" """
db = Database() db = Database()
cursor = db.create_connection() db.create_connection()
try: try:
result = cursor.execute( result = db.fetchone(
"SELECT 1 FROM subscribes WHERE user_id = ? AND streamer_id = ? AND expires > ?", "SELECT 1 FROM subscribes WHERE user_id = ? AND streamer_id = ? AND expires > ?",
(user_id, streamer_id, datetime.now()) (user_id, streamer_id, datetime.now())
).fetchone() )
return bool(result) return bool(result)
except Exception as e: except Exception as e:
print(f"Error: {e}") print(f"Error: {e}")
@@ -79,13 +79,13 @@ def is_subscribed(user_id: int, streamer_id: int) -> bool:
def is_following(user_id: int, followed_id: int) -> bool: def is_following(user_id: int, followed_id: int) -> bool:
db = Database() db = Database()
cursor = db.create_connection() db.create_connection()
try: try:
result = cursor.execute( result = db.fetchone(
"SELECT 1 FROM follows WHERE user_id = ? AND followed_id = ?", "SELECT 1 FROM follows WHERE user_id = ? AND followed_id = ?",
(user_id, followed_id) (user_id, followed_id)
).fetchone() )
return bool(result) return bool(result)
except Exception as e: except Exception as e:
print(f"Error: {e}") print(f"Error: {e}")
@@ -96,11 +96,11 @@ def subscription_expiration(user_id: int, subscribed_id: int) -> int:
Returns the amount of time left until user subscription to a streamer ends Returns the amount of time left until user subscription to a streamer ends
""" """
db = Database() db = Database()
cursor = db.create_connection() db.create_connection()
remaining_time = 0 remaining_time = 0
try: try:
data = cursor.execute( data = db.fetchone(
"SELECT expires from subscriptions WHERE user_id = ? AND subscribed_id = ? AND expires > since", (user_id,subscribed_id)).fetchone() "SELECT expires from subscriptions WHERE user_id = ? AND subscribed_id = ? AND expires > since", (user_id,subscribed_id))
if data: if data:
expiration_date = data[0] expiration_date = data[0]
@@ -130,7 +130,7 @@ def reset_password(new_password: str, email: str):
try: try:
db.execute("UPDATE users SET password = ? WHERE email = ?", (generate_password_hash(new_password), email)) db.execute("UPDATE users SET password = ? WHERE email = ?", (generate_password_hash(new_password), email))
db.commit() db.commit_data()
return True return True
except Exception as e: except Exception as e:
print(f"Error: {e}") print(f"Error: {e}")

View File

@@ -5,8 +5,8 @@ def categories():
Returns all possible streaming categories Returns all possible streaming categories
""" """
db = Database() db = Database()
cursor = db.create_connection() db.create_connection()
all_categories = cursor.execute("SELECT * FROM categories").fetchall() all_categories = db.fetchall("SELECT * FROM categories")
return all_categories return all_categories
def tags(): def tags():
@@ -14,8 +14,8 @@ def tags():
Returns all possible streaming tags Returns all possible streaming tags
""" """
db = Database() db = Database()
cursor = db.create_connection() db.create_connection()
all_tags = cursor.execute("SELECT * FROM tags").fetchall() all_tags = db.fetchall("SELECT * FROM tags")
return all_tags return all_tags
def most_popular_category(): def most_popular_category():
@@ -23,9 +23,9 @@ def most_popular_category():
Returns the most popular category based on live stream viewers Returns the most popular category based on live stream viewers
""" """
db = Database() db = Database()
cursor = db.create_connection() db.create_connection()
category = cursor.execute(""" category = db.fetchone("""
SELECT categories.category_id, categories.category_name SELECT categories.category_id, categories.category_name
FROM streams FROM streams
JOIN categories ON streams.category_id = categories.category_id JOIN categories ON streams.category_id = categories.category_id
@@ -33,7 +33,7 @@ def most_popular_category():
GROUP BY categories.category_name GROUP BY categories.category_name
ORDER BY SUM(streams.num_viewers) DESC ORDER BY SUM(streams.num_viewers) DESC
LIMIT 1; LIMIT 1;
""").fetchone() """)
return category return category