REFACTOR: Update to handling sending and receiving stream data in frontend and backend, making it more secure
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.',
|
||||||
|
|||||||
Reference in New Issue
Block a user