REFACTOR: Update to handling sending and receiving stream data in frontend and backend, making it more secure

This commit is contained in:
Chris-1010
2025-02-28 15:49:22 +00:00
parent 077530b6e6
commit 98f27c0478
4 changed files with 94 additions and 67 deletions

View File

@@ -206,13 +206,13 @@ const StreamDashboardPage: React.FC = () => {
const handlePublishStream = async () => { const handlePublishStream = async () => {
console.log("Starting stream with data:", streamData); console.log("Starting stream with data:", streamData);
const formData = new FormData();
formData.append("data", JSON.stringify(streamData));
try { try {
const response = await fetch("/api/publish_stream", { const response = await fetch("/api/publish_stream", {
method: "POST", method: "POST",
body: formData, headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(streamData),
}); });
if (response.ok) { if (response.ok) {
@@ -231,18 +231,18 @@ const StreamDashboardPage: React.FC = () => {
const handleUpdateStream = async () => { const handleUpdateStream = async () => {
console.log("Updating stream with data:", streamData); console.log("Updating stream with data:", streamData);
const formData = new FormData();
formData.append("key", streamData.stream_key);
formData.append("title", streamData.title);
formData.append("category_name", streamData.category_name);
if (thumbnail) {
formData.append("thumbnail", thumbnail);
}
try { try {
const response = await fetch("/api/update_stream", { const response = await fetch("/api/update_stream", {
method: "POST", method: "POST",
body: formData, headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
key: streamData.stream_key,
title: streamData.title,
category_name: streamData.category_name,
thumbnail: thumbnail,
}),
}); });
if (response.ok) { if (response.ok) {
@@ -259,13 +259,13 @@ const StreamDashboardPage: React.FC = () => {
const handleEndStream = async () => { const handleEndStream = async () => {
console.log("Ending stream..."); console.log("Ending stream...");
const formData = new FormData();
formData.append("key", streamData.stream_key);
try { try {
const response = await fetch("/api/end_stream", { const response = await fetch("/api/end_stream", {
method: "POST", method: "POST",
body: formData, headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ key: streamData.stream_key }),
}); });
if (response.ok) { if (response.ok) {

View File

@@ -95,28 +95,28 @@ http {
#! Unused right now so the following are inaccurate locations #! Unused right now so the following are inaccurate locations
# The thumbnails location # The thumbnails location
# location ~ ^/stream/(.+)/thumbnails/(.+\.jpg)$ { location ~ ^/stream/(.+)/thumbnails/(.+\.jpg)$ {
# alias /stream_data/$1/thumbnails/$2; alias /stream_data/$1/thumbnails/$2;
# # The thumbnails should not be cacheable # The thumbnails should not be cacheable
# expires -1d; expires -1d;
# } }
# # The vods location # The vods location
# location ~ ^/stream/(.+)/vods/(.+\.mp4)$ { location ~ ^/stream/(.+)/vods/(.+\.mp4)$ {
# alias /stream_data/$1/vods/$2; alias /stream_data/$1/vods/$2;
# # The vods should not be cacheable # The vods should not be cacheable
# expires -1d; expires -1d;
# } }
# location ~ ^/\?token=.*$ { location ~ ^/\?token=.*$ {
# proxy_pass http://frontend:5173; proxy_pass http://frontend:5173;
# proxy_http_version 1.1; proxy_http_version 1.1;
# proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "upgrade"; proxy_set_header Connection "upgrade";
# proxy_set_header Host $host; proxy_set_header Host $host;
# } }
location / { location / {
proxy_pass http://frontend:5173; proxy_pass http://frontend:5173;

View File

@@ -213,31 +213,37 @@ def publish_stream():
except KeyError as ex: except KeyError as ex:
print(f"Error: {ex}") print(f"Error: {ex}")
stream_key = data.get("stream_key")
stream_title = data.get("title")
stream_category = data.get("category_name")
user_id = None
username = None
with Database() as db: with Database() as db:
user_info = db.fetchone("""SELECT user_id, username, is_live user_info = db.fetchone("""SELECT user_id, username, is_live
FROM users FROM users
WHERE stream_key = ?""", (data['stream_key'],)) WHERE stream_key = ?""", (stream_key,))
if not user_info or user_info["is_live"]: if not user_info or user_info.get("is_live"):
print( print(
"Unauthorized. No user found from Stream key or user is already streaming.", flush=True) "Unauthorized. No user found from Stream key or user is already streaming.", flush=True)
return "Unauthorized", 403 return "Unauthorized", 403
user_id = user_info.get("user_id")
username = user_info.get("username")
# Insert stream into database # Insert stream into database
db.execute("""INSERT INTO streams (user_id, title, start_time, num_viewers, category_id) db.execute("""INSERT INTO streams (user_id, title, start_time, num_viewers, category_id)
VALUES (?, ?, ?, ?, ?)""", (user_info["user_id"], VALUES (?, ?, ?, ?, ?)""", (user_id,
data["title"], stream_title,
datetime.now(), datetime.now(),
0, 0,
get_category_id(data['category_name']))) get_category_id(stream_category)))
# Set user as streaming # Set user as streaming
db.execute("""UPDATE users SET is_live = 1 WHERE user_id = ?""", db.execute("""UPDATE users SET is_live = 1 WHERE user_id = ?""",
(user_info["user_id"],)) (user_id,))
username = user_info["username"]
user_id = user_info["user_id"]
# Update thumbnail periodically # Update thumbnail periodically
update_thumbnail.delay(user_id, update_thumbnail.delay(user_id,
@@ -255,23 +261,32 @@ def update_stream():
""" """
# TODO: Add thumbnails (paths) to table, allow user to update thumbnail # TODO: Add thumbnails (paths) to table, allow user to update thumbnail
stream_key = request.form.get("key") print("Updating stream info", flush=True)
title = request.form.get("title")
category_name = request.form.get("category_name") data = request.get_json()
stream_key = data.get("key")
stream_title = data.get("title")
stream_category = data.get("category_name")
# TODO stream_thumbnail = data.get("thumbnail")
user_id = None
with Database() as db: with Database() as db:
user_info = db.fetchone("""SELECT user_id, username, is_live user_info = db.fetchone("""SELECT user_id, username, is_live
FROM users FROM users
WHERE stream_key = ?""", (stream_key,)) WHERE stream_key = ?""", (stream_key,))
if not user_info or not user_info["is_live"]: if not user_info or not user_info.get("is_live"):
print( print(
"Unauthorized - No user found from stream key or user is not streaming", flush=True) "Unauthorized - No user found from stream key or user is not streaming", flush=True)
return "Unauthorized", 403 return "Unauthorized", 403
user_id = user_info.get("user_id")
# TODO: Add update to thumbnail here
db.execute("""UPDATE streams db.execute("""UPDATE streams
SET title = ?, category_id = ? SET title = ?, category_id = ?
WHERE user_id = ?""", (title, get_category_id(category_name), user_info["user_id"])) WHERE user_id = ?""", (stream_title, get_category_id(stream_category), user_id))
return "Stream updated", 200 return "Stream updated", 200
@@ -290,8 +305,14 @@ def end_stream():
end thumbnail generation end thumbnail generation
""" """
stream_key = request.form.get("key") print("Ending stream", flush=True)
if stream_key is None:
stream_key = request.get_json().get("key")
user_id = None
username = None
if not stream_key:
# Try getting stream_key from form data (for nginx in the case that the stream is ended on OBS's end)
stream_key = request.form.get("name") stream_key = request.form.get("name")
if stream_key is None: if stream_key is None:
@@ -300,6 +321,9 @@ def end_stream():
# Open database connection # Open database connection
with Database() as db: with Database() as db:
initial_streams = db.fetchall("""SELECT title FROM streams""")
print("Initial streams:", initial_streams, flush=True)
# Get user info from stream key # Get user info from stream key
user_info = db.fetchone("""SELECT * user_info = db.fetchone("""SELECT *
FROM users FROM users
@@ -307,13 +331,14 @@ def end_stream():
stream_info = db.fetchone("""SELECT * stream_info = db.fetchone("""SELECT *
FROM streams FROM streams
WHERE user_id = ?""", (user_info["user_id"],)) WHERE user_id = ?""", (user_id,))
print("Got stream_info", stream_info, flush=True)
# If stream key is invalid, return unauthorized # If stream key is invalid, return unauthorized
if not user_info: if not user_info:
print("Unauthorized - No user found from stream key", flush=True) print("Unauthorized - No user found from stream key", flush=True)
return "Unauthorized", 403 return "Unauthorized", 403
# If stream never published, return # If stream never published, return
if not stream_info: if not stream_info:
print(f"Stream for stream key: {stream_key} never began", flush=True) print(f"Stream for stream key: {stream_key} never began", flush=True)
@@ -321,17 +346,19 @@ def end_stream():
# Remove stream from database # Remove stream from database
db.execute("""DELETE FROM streams db.execute("""DELETE FROM streams
WHERE user_id = ?""", (user_info["user_id"],)) WHERE user_id = ?""", (user_id,))
# Move stream to vod table # Move stream to vod table
stream_length = int( stream_length = int(
(datetime.now() - parser.parse(stream_info["start_time"])).total_seconds()) (datetime.now() - parser.parse(stream_info.get("start_time"))).total_seconds())
db.execute("""INSERT INTO vods (user_id, title, datetime, category_id, length, views) db.execute("""INSERT INTO vods (user_id, title, datetime, category_id, length, views)
VALUES (?, ?, ?, ?, ?, ?)""", (user_info["user_id"], VALUES (?, ?, ?, ?, ?, ?)""", (user_id,
stream_info["title"], stream_info.get("title"),
stream_info["start_time"], stream_info.get(
stream_info["category_id"], "start_time"),
stream_info.get(
"category_id"),
stream_length, stream_length,
0)) 0))
@@ -340,12 +367,12 @@ def end_stream():
# Set user as not streaming # Set user as not streaming
db.execute("""UPDATE users db.execute("""UPDATE users
SET is_live = 0 SET is_live = 0
WHERE user_id = ?""", (user_info["user_id"],)) WHERE user_id = ?""", (user_id,))
# Get username current_streams = db.fetchall("""SELECT title FROM streams""")
username = user_info["username"]
combine_ts_stream.delay(path_manager.get_stream_path( combine_ts_stream.delay(path_manager.get_stream_path(
username), path_manager.get_vods_path(username), vod_id) username), path_manager.get_vods_path(username), vod_id)
print("Stream ended. Current streams now:", current_streams, flush=True)
return "Stream ended", 200 return "Stream ended", 200

View File

@@ -6,7 +6,7 @@ CREATE TABLE users
password VARCHAR(256), password VARCHAR(256),
email VARCHAR(128) NOT NULL, email VARCHAR(128) NOT NULL,
num_followers INTEGER NOT NULL DEFAULT 0, num_followers INTEGER NOT NULL DEFAULT 0,
stream_key VARCHAR(60) NOT NULL, stream_key VARCHAR(64) NOT NULL,
is_partnered BOOLEAN NOT NULL DEFAULT 0, is_partnered BOOLEAN NOT NULL DEFAULT 0,
is_live BOOLEAN NOT NULL DEFAULT 0, is_live BOOLEAN NOT NULL DEFAULT 0,
bio VARCHAR(1024) DEFAULT 'This user does not have a Bio.', bio VARCHAR(1024) DEFAULT 'This user does not have a Bio.',