From a2ddb6c724b339764c8691a175da6ec63a02f893 Mon Sep 17 00:00:00 2001 From: EvanLin3141 Date: Thu, 6 Feb 2025 17:18:11 +0000 Subject: [PATCH 1/5] FIX: Auth token for Reset Password --- frontend/src/App.tsx | 2 +- frontend/src/components/Auth/PasswordResetForm.tsx | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 653c2bb..b3f86d4 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -42,7 +42,7 @@ function App() { } /> } /> - }> + }> }> } /> diff --git a/frontend/src/components/Auth/PasswordResetForm.tsx b/frontend/src/components/Auth/PasswordResetForm.tsx index 9931abc..a1dab67 100644 --- a/frontend/src/components/Auth/PasswordResetForm.tsx +++ b/frontend/src/components/Auth/PasswordResetForm.tsx @@ -48,7 +48,9 @@ const PasswordResetForm: React.FC = ({ onSubmit, token }) => { newErrors[key as keyof ResetPasswordErrors] = "Confirm your password"; } }); - + if (resetData.newPassword.length < 8) { + newErrors.newPasswordError = "Password must be at least 8 characters long"; + } if (resetData.newPassword !== resetData.confirmNewPassword) { newErrors.confirmNewPasswordError = "Passwords do not match"; } @@ -64,7 +66,7 @@ const PasswordResetForm: React.FC = ({ onSubmit, token }) => { if (validateResetForm()) { try { - const response = await fetch("/user/reset_password/ = ({ onSubmit, token }) => { if (!response.ok) { const data = await response.json(); throw new Error(data.message || "An error has occurred while resetting"); + } else { + confirmPasswordReset(); } } catch (error: any) { console.error("Password reset error:", error.message); @@ -84,7 +88,6 @@ const PasswordResetForm: React.FC = ({ onSubmit, token }) => { general: error.message || "An unexpected error occurred.", })); - confirmPasswordReset(); } } }; From c0ddca85bab1753fe810c183cc807361043af378 Mon Sep 17 00:00:00 2001 From: ThisBirchWood Date: Thu, 6 Feb 2025 17:25:25 +0000 Subject: [PATCH 2/5] PATCH: Fixed a load of broken user routes --- web_server/blueprints/user.py | 18 ++++++++++----- web_server/database/app.db | Bin 94208 -> 94208 bytes web_server/database/testing_data.sql | 6 ++--- web_server/requirements.txt | 3 ++- web_server/utils/user_utils.py | 32 +++++++++++++-------------- 5 files changed, 33 insertions(+), 26 deletions(-) diff --git a/web_server/blueprints/user.py b/web_server/blueprints/user.py index 6508b57..1b3be85 100644 --- a/web_server/blueprints/user.py +++ b/web_server/blueprints/user.py @@ -52,23 +52,28 @@ def user_following(user_id: int, followed_id: int): @login_required @user_bp.route('/user/follow/') -def follow(username): +def follow_user(username): """ Follows a user """ user_id = session.get("user_id") following_id = get_user_id(username) - follow(user_id, following_id) + if follow(user_id, following_id): + return jsonify({"success": True, + "already_following": False}) + return jsonify({"success": True, + "already_following": True}) @login_required @user_bp.route('/user/unfollow/') -def user_unfollow(followed_username): +def unfollow_user(username): """ Unfollows a user """ user_id = session.get("user_id") - followed_id = get_user_id(followed_username) + followed_id = get_user_id(username) unfollow(user_id, followed_id) + return jsonify({"success": True}) @login_required @user_bp.route('/user/following') @@ -88,7 +93,10 @@ def get_login_status(): Returns whether the user is logged in or not """ username = session.get("username") - return jsonify({'status': username is not None, 'username': username}) + user_id = session.get("user_id") + return jsonify({'status': username is not None, + 'username': username, + 'user_id': user_id}) @user_bp.route('/user/forgot_password/', methods=['GET','POST']) def user_forgot_password(email): diff --git a/web_server/database/app.db b/web_server/database/app.db index df6d35b8a60a578cdf18adb95750ff3674d3f3b4..af0b7442295dfe9c8a1c16e17c4cfd7299bf47c3 100644 GIT binary patch delta 522 zcmYk3O=uHA6vyXvW@mSrt`jYG)kH}SMM_&tOqyb-#t$q)?YVer6G&+xb=%$8*rZCk zqvEk)!2u~Ycn~itcEJUqCxr@ChSTdbVxl3lxzwp*f{tc_sB+|in4)O2&Gk?!t z^QT<$d%VbV-oeNf?&ek=W5(A!wYF;>rxGzGium)k-qttTniAD2^e0_s6I4>^p9lh0 zh+_%Ui!uuW+BXzajZg^gUXnZg(1{bmk{>dVAWm7#cv*EGVA}nnWz=4Aof;QH@?!>$ z93D3d1+!$2#+yq(|A8viT8TI#!uIOQ2oPsnOnK|AlK=%j9)@~*mpXs~cpEW6$5f>3 zkgs_Az#Qw-H{0^611YqO5Zu|DZZv55%P~mcFw2m4O}=6_SIFB%E1#XR?ZuLqb2;9H zoBp!_&;UyE)mfM!i(zp?4)?($WtJMlC--QNN|u5!VH;@Btt%IaI3;d-zx5Y@heSnw zIs-%MWl^NYwZ#5ya_}5<=?Jwv-2YiqLm5beuDv0RL%^98xBio6$*W2Dg*(K2wf2Ar OEXsa&8U#E!53xUPl$Tln delta 370 zcmZp8z}oPDb%K--^H&B225}&U0;W$Bb&Qypziv#J&(9dUNuVR1!@$7G#Kg+jbn=9J z9R%k{z6qSOsena6iksh#f&V@KUH&utyZBe|Pv>vrFX2z(58$`kEU2KsuOh(B!pNw| z&T3>}WTI0*sScrvEi%Oy1nWTF$Zkw=5%@kS3cd126M=2L4lg`F!fUn|W4puj5kZ zyvb?LrpM~Z`jYuPGdE)kqZz}T$%+C++vgZC8Z)yrl{IHipDo6yvwf-!qcU%s3C8m2QFe^-o6Q*2*_Jsl8UO%wj$d>D diff --git a/web_server/database/testing_data.sql b/web_server/database/testing_data.sql index bd015ed..212b889 100644 --- a/web_server/database/testing_data.sql +++ b/web_server/database/testing_data.sql @@ -124,9 +124,7 @@ LIMIT 10; INSERT INTO follows (user_id, followed_id, since) VALUES (7, 1, '2024-08-30'), (7, 2, '2024-08-30'), -(7, 3, '2024-08-30'), -(7, 4, '2024-08-30'), -(7, 5, '2024-08-30'); +(7, 3, '2024-08-30'); INSERT INTO followed_categories (user_id, category_id) VALUES (7, 1), @@ -134,5 +132,5 @@ INSERT INTO followed_categories (user_id, category_id) VALUES (7, 3); INSERT INTO subscribes (user_id, subscribed_id, since, expires) VALUES -(7, 1, '2024-08-30', '2025-02-28'), +(7, 1, '2024-08-30', '2025-02-28 12:00:00'), (7, 2, '2024-08-30', '2025-02-15'); \ No newline at end of file diff --git a/web_server/requirements.txt b/web_server/requirements.txt index 5cdc2ce..349268f 100644 --- a/web_server/requirements.txt +++ b/web_server/requirements.txt @@ -26,4 +26,5 @@ gevent>=22.10.2 gevent-websocket flask-oauthlib==0.9.6 celery==5.2.3 -redis==5.2.1 \ No newline at end of file +redis==5.2.1 +python-dateutil \ No newline at end of file diff --git a/web_server/utils/user_utils.py b/web_server/utils/user_utils.py index fffbefe..edc1781 100644 --- a/web_server/utils/user_utils.py +++ b/web_server/utils/user_utils.py @@ -4,6 +4,7 @@ from datetime import datetime from itsdangerous import URLSafeTimedSerializer from os import getenv from werkzeug.security import generate_password_hash, check_password_hash +from dateutil import parser from dotenv import load_dotenv load_dotenv() @@ -91,29 +92,28 @@ def follow(user_id: int, following_id: int): """ Follows following_id user from user_id user """ - with Database() as db: - data = db.execute(""" - SELECT * FROM follows - WHERE user_id = ? - AND followed_id = ? - """, (user_id, following_id)) - - if not data: + if not is_following(user_id, following_id): + with Database() as db: db.execute(""" INSERT INTO follows (user_id, followed_id) - VALUES(?,?) + VALUES(?,?); """, (user_id, following_id)) + return True + return False def unfollow(user_id: int, followed_id: int): """ Unfollows follow_id user from user_id user """ - with Database() as db: - db.execute(""" - DELETE FROM follows - WHERE user_id = ? - AND followed_id = ? - """, (user_id, followed_id)) + if is_following(user_id, followed_id): + with Database() as db: + db.execute(""" + DELETE FROM follows + WHERE user_id = ? + AND followed_id = ? + """, (user_id, followed_id)) + return True + return False def subscription_expiration(user_id: int, subscribed_id: int) -> int: @@ -131,7 +131,7 @@ def subscription_expiration(user_id: int, subscribed_id: int) -> int: if data: expiration_date = data["expires"] - remaining_time = (expiration_date - datetime.now()).seconds + remaining_time = (parser.parse(expiration_date) - datetime.now()).seconds return remaining_time return 0 From 4efef9d0c92252b8f7b9ca6781d1f9f350f8c5e9 Mon Sep 17 00:00:00 2001 From: white <122345776@umail.ucc.ie> Date: Thu, 6 Feb 2025 18:33:38 +0000 Subject: [PATCH 3/5] FEAT: Added a search bar feature (to be integrated with database UPDATE: Removed html pages, as they were no longer being used --- web_server/blueprints/search_bar.py | 99 ++++++++++++++++++++++ web_server/blueprints/templates/chat.html | 79 ----------------- web_server/blueprints/templates/video.html | 27 ------ 3 files changed, 99 insertions(+), 106 deletions(-) create mode 100644 web_server/blueprints/search_bar.py delete mode 100644 web_server/blueprints/templates/chat.html delete mode 100644 web_server/blueprints/templates/video.html diff --git a/web_server/blueprints/search_bar.py b/web_server/blueprints/search_bar.py new file mode 100644 index 0000000..1cb2ade --- /dev/null +++ b/web_server/blueprints/search_bar.py @@ -0,0 +1,99 @@ +from flask import Blueprint, jsonify +from database.database import Database + +search_bp = Blueprint("search", __name__) + +@search_bp.route("/search/", methods=["GET", "POST"]) +def search_results(query: str): + """ + Return the most similar search results + + This is the main route that displays a subsection of each search topic + """ + # Create the connection to the database + db = Database() + db.create_connection() + + # Get the most accurate search results + # 3 categories + categories = db.fetchall(""" + SELECT bm25(category_fts), rank, f.category_id, f.category_name + FROM categorys AS c + INNER JOIN category_fts AS f ON c.category_id = f.category_id + WHERE category_fts MATCH ? + LIMIT 3; + """, (query,)) + + # 3 users + users = db.fetchall(""" + SELECT bm25(user_fts), rank, f.user_id, f.username, f.is_live + FROM users u + INNER JOIN user_fts f ON u.user_id = f.user_id + WHERE user_fts MATCH ? + LIMIT 3; + """, (query,)) + + # 3 streams + streams = db.fetchall(""" + SELECT bm25(stream_fts), rank, f.user_id, f.title, f.num_viewers, f.category_id + FROM streams s + INNER JOIN stream_fts f ON s.user_id = f.user_id + WHERE user_fts MATCH ? + LIMIT 3; + """, (query,)) + + db.close_connection() + + return jsonify({"categories": categories, "users": users, "streams": streams}) + +@search_bp.route("/search/categories/", methods=["GET", "POST"]) +def search_categories(query: str): + # Create the connection to the database + db = Database() + db.create_connection() + + categories = db.fetchall(""" + SELECT bm25(category_fts), rank, f.category_id, f.category_name + FROM categorys AS c + INNER JOIN category_fts AS f ON c.category_id = f.category_id + WHERE category_fts MATCH ?; + """, (query,)) + + db.close_connection() + + return jsonify({"categories": categories}) + +@search_bp.route("/search/users/", methods=["GET", "POST"]) +def search_users(query: str): + # Create the connection to the database + db = Database() + db.create_connection() + + users = db.fetchall(""" + SELECT bm25(user_fts), rank, f.user_id, f.username, f.is_live + FROM users u + INNER JOIN user_fts f ON u.user_id = f.user_id + WHERE user_fts MATCH ?; + """, (query,)) + + db.close_connection() + + return jsonify({"users": users}) + + +@search_bp.route("/search/streams/", methods=["GET", "POST"]) +def search_streams(query: str): + # Create the connection to the database + db = Database() + db.create_connection() + + streams = db.fetchall(""" + SELECT bm25(stream_fts), rank, f.user_id, f.title, f.num_viewers, f.category_id + FROM streams s + INNER JOIN stream_fts f ON s.user_id = f.user_id + WHERE user_fts MATCH ?; + """, (query,)) + + db.close_connection() + + return jsonify({"streams": streams}) \ No newline at end of file diff --git a/web_server/blueprints/templates/chat.html b/web_server/blueprints/templates/chat.html deleted file mode 100644 index 3629a14..0000000 --- a/web_server/blueprints/templates/chat.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - Chat Interface - - - - - - -

Chat for Stream #{{ stream_id }}

-
- - - - diff --git a/web_server/blueprints/templates/video.html b/web_server/blueprints/templates/video.html deleted file mode 100644 index 093fc5d..0000000 --- a/web_server/blueprints/templates/video.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - \ No newline at end of file From acc38cc73e1a84fbda36416d259133d46d4026fd Mon Sep 17 00:00:00 2001 From: EvanLin3141 Date: Thu, 6 Feb 2025 18:56:33 +0000 Subject: [PATCH 4/5] MISC: Styles change --- frontend/src/components/Layout/ListRow.tsx | 14 ++++++++------ frontend/src/pages/HomePage.tsx | 3 +++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/Layout/ListRow.tsx b/frontend/src/components/Layout/ListRow.tsx index 3c50446..8a227ef 100644 --- a/frontend/src/components/Layout/ListRow.tsx +++ b/frontend/src/components/Layout/ListRow.tsx @@ -15,6 +15,7 @@ interface ListRowProps { title: string; description: string; items: ListItemProps[]; + extraClasses?: string; onClick: (itemId: number, itemName: string) => void; } @@ -43,10 +44,10 @@ const ListItem: React.FC = ({
)}
-
+

{title}

- {type === "stream" &&

{streamer}

} -

{viewers} viewers

+ {type === "stream" &&

{streamer}

} +

{viewers} viewers

); @@ -58,12 +59,13 @@ const ListRow: React.FC = ({ description, items, onClick, + extraClasses="", }) => { return ( -
+
-

{title}

-

{description}

+

{title}

+

{description}

{items.map((item) => ( diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx index c2b6dfc..3ba9e2f 100644 --- a/frontend/src/pages/HomePage.tsx +++ b/frontend/src/pages/HomePage.tsx @@ -43,6 +43,7 @@ const HomePage: React.FC = ({ variant = "default" }) => { } items={featuredStreams} onClick={handleStreamClick} + extraClasses="border border-gray-700 bg-[#FF7F50]/80" /> {/* If Personalised_HomePage, display Categories the logged-in user follows. Else, trending categories. */} @@ -60,6 +61,8 @@ const HomePage: React.FC = ({ variant = "default" }) => { } items={featuredCategories} onClick={handleCategoryClick} + extraClasses="border border-gray-700 bg-[#5AFF75]/80" + />
); From 1841e08a09d41d5374f3ceccfb2a0e612abd4c6c Mon Sep 17 00:00:00 2001 From: white <122345776@umail.ucc.ie> Date: Thu, 6 Feb 2025 18:57:21 +0000 Subject: [PATCH 5/5] UPDATE: Added database functionality for search bar, still need to integrate with webapp --- web_server/blueprints/search_bar.py | 8 +- web_server/database/app.db | Bin 94208 -> 159744 bytes web_server/database/text_search.sql | 114 ++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 web_server/database/text_search.sql diff --git a/web_server/blueprints/search_bar.py b/web_server/blueprints/search_bar.py index 1cb2ade..1ec5272 100644 --- a/web_server/blueprints/search_bar.py +++ b/web_server/blueprints/search_bar.py @@ -18,7 +18,7 @@ def search_results(query: str): # 3 categories categories = db.fetchall(""" SELECT bm25(category_fts), rank, f.category_id, f.category_name - FROM categorys AS c + FROM categories AS c INNER JOIN category_fts AS f ON c.category_id = f.category_id WHERE category_fts MATCH ? LIMIT 3; @@ -38,7 +38,7 @@ def search_results(query: str): SELECT bm25(stream_fts), rank, f.user_id, f.title, f.num_viewers, f.category_id FROM streams s INNER JOIN stream_fts f ON s.user_id = f.user_id - WHERE user_fts MATCH ? + WHERE stream_fts MATCH ? LIMIT 3; """, (query,)) @@ -54,7 +54,7 @@ def search_categories(query: str): categories = db.fetchall(""" SELECT bm25(category_fts), rank, f.category_id, f.category_name - FROM categorys AS c + FROM categories AS c INNER JOIN category_fts AS f ON c.category_id = f.category_id WHERE category_fts MATCH ?; """, (query,)) @@ -91,7 +91,7 @@ def search_streams(query: str): SELECT bm25(stream_fts), rank, f.user_id, f.title, f.num_viewers, f.category_id FROM streams s INNER JOIN stream_fts f ON s.user_id = f.user_id - WHERE user_fts MATCH ?; + WHERE stream_fts MATCH ?; """, (query,)) db.close_connection() diff --git a/web_server/database/app.db b/web_server/database/app.db index af0b7442295dfe9c8a1c16e17c4cfd7299bf47c3..a86836263d15a81407cb908fe31760672147fb5c 100644 GIT binary patch delta 7026 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 U>_`S9upq=a2&&i&%Dpk-zxF!vr~m)} delta 339 zcmZp8z}fJCb%L~@0s{ksI1obtGw(zlV^IYLJ)Hnnpb#VfFOY-?|1Yk?8%rAad6H8# z@)C1_LPE{OO#;&d7+It^Srr-h*YW6b9p>1}s>o5z_LuEAX9nAJw&@d_3a0l7Fy>A( zWwhRG#`v0fyP_!Ld1l7y=^rH+^(UV9U}*|(F5Y~BHJow#VJSu{W+ukXoB#8EW!(Nn zo{?8Tlih}aoB25dXER#@I|J(*mPgFb`6@Y0dGGV2vNtiBG0Xuvumwa_n$z`!&M6aWlORn3K)6WOa6S%7v5Z&u{w WXWY(c$5_R5 diff --git a/web_server/database/text_search.sql b/web_server/database/text_search.sql new file mode 100644 index 0000000..569684d --- /dev/null +++ b/web_server/database/text_search.sql @@ -0,0 +1,114 @@ +/* Full text search queries for categories */ +DROP TABLE IF EXISTS category_fts; +CREATE VIRTUAL TABLE category_fts +USING fts5 (category_id, category_name); + +INSERT INTO category_fts (category_id, category_name) +SELECT category_id, category_name +FROM categories; + +-- Triggers that inserts new titles into category_fts +DROP TRIGGER IF EXISTS insert_category_fts; +CREATE TRIGGER insert_category_fts +AFTER INSERT ON categories +BEGIN + INSERT INTO category_fts(category_id, category_name) + VALUES (NEW.category_id, NEW.category_name); +END; + +DROP TRIGGER IF EXISTS update_category_fts; +CREATE TRIGGER update_category_fts +AFTER UPDATE ON categories +BEGIN + UPDATE category_fts + SET + category_id = NEW.category_id, + category_name = NEW.category_name + WHERE category_id = NEW.category_id; +END; + +DROP TRIGGER IF EXISTS delete_category_fts; +CREATE TRIGGER delete_category_fts +AFTER DELETE ON categories +BEGIN + DELETE FROM category_fts + WHERE category_id = OLD.category_id; +END; + +/* Full text search queries for users */ +DROP TABLE IF EXISTS user_fts; +CREATE VIRTUAL TABLE user_fts +USING fts5 (user_id, username, is_live); + +INSERT INTO user_fts (user_id, username, is_live) +SELECT user_id, username, is_live +FROM users; + +-- Triggers that inserts new titles into user_fts +DROP TRIGGER IF EXISTS insert_user_fts; +CREATE TRIGGER insert_user_fts +AFTER INSERT ON users +BEGIN + INSERT INTO user_fts(user_id, username, is_live) + VALUES (NEW.user_id, NEW.username, NEW.is_live); +END; + +DROP TRIGGER IF EXISTS update_user_fts; +CREATE TRIGGER update_user_fts +AFTER UPDATE ON users +BEGIN + UPDATE user_fts + SET + user_id = NEW.user_id, + username = NEW.username, + is_live = NEW.is_live + WHERE user_id = NEW.user_id; +END; + +DROP TRIGGER IF EXISTS delete_user_fts; +CREATE TRIGGER delete_user_fts +AFTER DELETE ON users +BEGIN + DELETE FROM user_fts + WHERE user_id = OLD.user_id; +END; + + +/* Full text search queries for users */ +DROP TABLE IF EXISTS stream_fts; +CREATE VIRTUAL TABLE stream_fts +USING fts5 (user_id, title, num_viewers, category_id); + +INSERT INTO stream_fts (user_id, title, num_viewers, category_id) +SELECT user_id, title, num_viewers, category_id +FROM streams; + +-- Triggers that inserts new titles into stream_fts +DROP TRIGGER IF EXISTS insert_stream_fts; +CREATE TRIGGER insert_stream_fts +AFTER INSERT ON streams +BEGIN + INSERT INTO stream_fts(user_id, title, num_viewers, category_id) + VALUES (NEW.user_id, NEW.title, NEW.num_viewers, NEW.category_id); +END; + +DROP TRIGGER IF EXISTS update_stream_fts; +CREATE TRIGGER update_stream_fts +AFTER UPDATE ON streams +BEGIN + UPDATE stream_fts + SET + user_id = NEW.user_id, + title = NEW.title, + num_viewers = NEW.num_viewers, + category_id = NEW.category_id + WHERE user_id = NEW.user_id; +END; + +DROP TRIGGER IF EXISTS delete_stream_fts; +CREATE TRIGGER delete_stream_fts +AFTER DELETE ON streams +BEGIN + DELETE FROM stream_fts + WHERE user_id = OLD.user_id; +END; \ No newline at end of file