MULTI-UPDATE: Big Error Cleanup:
Enhanced Docker and Nginx configurations - Can now run frontend either on local dev version OR the docker version; Improved socket connection handling; Refactored stream data fetching in VideoPage to properly display stream data; Chat-Visibility Button moved to ChatPanel so that chat's socket persists when hiding/showing chat;
This commit is contained in:
@@ -7,6 +7,7 @@ services:
|
|||||||
- "1935:1935" # RTMP
|
- "1935:1935" # RTMP
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
depends_on:
|
depends_on:
|
||||||
|
- frontend
|
||||||
- web_server
|
- web_server
|
||||||
networks:
|
networks:
|
||||||
- app_network
|
- app_network
|
||||||
@@ -35,6 +36,8 @@ services:
|
|||||||
- "5173"
|
- "5173"
|
||||||
networks:
|
networks:
|
||||||
- app_network
|
- app_network
|
||||||
|
environment:
|
||||||
|
- VITE_API_URL=/api
|
||||||
depends_on:
|
depends_on:
|
||||||
- web_server
|
- web_server
|
||||||
|
|
||||||
|
|||||||
@@ -13,4 +13,4 @@ COPY . .
|
|||||||
|
|
||||||
EXPOSE 5173
|
EXPOSE 5173
|
||||||
|
|
||||||
CMD ["npm", "run", "dev", "--", "--host"]
|
CMD ["npm", "run", "docker-dev", "--", "--host", "--strictPort"]
|
||||||
@@ -4,7 +4,8 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite --config vite.config.dev.ts",
|
||||||
|
"docker-dev": "vite",
|
||||||
"build": "tsc -b && vite build",
|
"build": "tsc -b && vite build",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
@@ -35,6 +36,6 @@
|
|||||||
"tailwindcss": "^3.4.17",
|
"tailwindcss": "^3.4.17",
|
||||||
"typescript": "~5.6.2",
|
"typescript": "~5.6.2",
|
||||||
"typescript-eslint": "^8.18.2",
|
"typescript-eslint": "^8.18.2",
|
||||||
"vite": "^6.0.5"
|
"vite": "latest"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from "react";
|
|||||||
import Input from "../Layout/Input";
|
import Input from "../Layout/Input";
|
||||||
import { useAuth } from "../../context/AuthContext";
|
import { useAuth } from "../../context/AuthContext";
|
||||||
import { useSocket } from "../../context/SocketContext";
|
import { useSocket } from "../../context/SocketContext";
|
||||||
import Button from "../Layout/Button";
|
import Button, { ToggleButton } from "../Layout/Button";
|
||||||
import AuthModal from "../Auth/AuthModal";
|
import AuthModal from "../Auth/AuthModal";
|
||||||
|
|
||||||
interface ChatMessage {
|
interface ChatMessage {
|
||||||
@@ -21,6 +21,7 @@ const ChatPanel: React.FC<ChatPanelProps> = ({ streamId }) => {
|
|||||||
const [inputMessage, setInputMessage] = useState("");
|
const [inputMessage, setInputMessage] = useState("");
|
||||||
const chatContainerRef = useRef<HTMLDivElement>(null);
|
const chatContainerRef = useRef<HTMLDivElement>(null);
|
||||||
const { isLoggedIn, username } = useAuth();
|
const { isLoggedIn, username } = useAuth();
|
||||||
|
const [isChatVisible, setIsChatVisible] = useState(false);
|
||||||
|
|
||||||
// Join chat room when component mounts
|
// Join chat room when component mounts
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -92,6 +93,10 @@ const ChatPanel: React.FC<ChatPanelProps> = ({ streamId }) => {
|
|||||||
setInputMessage("");
|
setInputMessage("");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleChat = () => {
|
||||||
|
setIsChatVisible((prev) => !prev);
|
||||||
|
};
|
||||||
|
|
||||||
const handleKeyPress = (e: React.KeyboardEvent) => {
|
const handleKeyPress = (e: React.KeyboardEvent) => {
|
||||||
if (e.key === "Enter" && !e.shiftKey) {
|
if (e.key === "Enter" && !e.shiftKey) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -115,72 +120,83 @@ const ChatPanel: React.FC<ChatPanelProps> = ({ streamId }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div id="chat-panel" className="h-full flex flex-col rounded-lg p-4">
|
<ToggleButton
|
||||||
<h2 className="text-xl font-bold mb-4 text-white">Stream Chat</h2>
|
onClick={toggleChat}
|
||||||
|
toggled={isChatVisible}
|
||||||
|
extraClasses="z-5"
|
||||||
|
>
|
||||||
|
{isChatVisible ? "Hide Chat" : "Show Chat"}
|
||||||
|
</ToggleButton>
|
||||||
|
{isChatVisible && (
|
||||||
|
<div id="chat-panel" className="h-full flex flex-col rounded-lg p-4">
|
||||||
|
<h2 className="text-xl font-bold mb-4 text-white">Stream Chat</h2>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
ref={chatContainerRef}
|
ref={chatContainerRef}
|
||||||
id="chat-message-list"
|
id="chat-message-list"
|
||||||
className="flex-grow w-full max-h-[50vh] overflow-y-auto mb-4 space-y-2"
|
className="flex-grow w-full max-h-[50vh] overflow-y-auto mb-4 space-y-2"
|
||||||
>
|
>
|
||||||
{messages.map((msg, index) => (
|
{messages.map((msg, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="grid grid-cols-[8%_minmax(15%,_100px)_1fr] items-center bg-gray-700 rounded p-2 text-white"
|
className="grid grid-cols-[8%_minmax(15%,_100px)_1fr] items-center bg-gray-700 rounded p-2 text-white"
|
||||||
>
|
|
||||||
<span className="text-gray-400 text-sm">
|
|
||||||
{new Date(msg.time_sent).toLocaleTimeString()}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
className={`font-bold ${
|
|
||||||
msg.chatter_username === username
|
|
||||||
? "text-blue-400"
|
|
||||||
: "text-green-400"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{" "}
|
<span className="text-gray-400 text-sm">
|
||||||
{msg.chatter_username}:{" "}
|
{new Date(msg.time_sent).toLocaleTimeString()}
|
||||||
</span>
|
</span>
|
||||||
<span>{msg.message}</span>
|
<span
|
||||||
</div>
|
className={`font-bold ${
|
||||||
))}
|
msg.chatter_username === username
|
||||||
</div>
|
? "text-blue-400"
|
||||||
|
: "text-green-400"
|
||||||
<div className="flex justify-center gap-2">
|
}`}
|
||||||
{isLoggedIn && (
|
>
|
||||||
<>
|
{" "}
|
||||||
<Input
|
{msg.chatter_username}:{" "}
|
||||||
type="text"
|
</span>
|
||||||
value={inputMessage}
|
<span>{msg.message}</span>
|
||||||
onChange={(e) => setInputMessage(e.target.value)}
|
</div>
|
||||||
onKeyDown={handleKeyPress}
|
))}
|
||||||
placeholder={isLoggedIn ? "Type a message..." : "Login to chat"}
|
|
||||||
disabled={!isLoggedIn}
|
|
||||||
extraClasses="flex-grow"
|
|
||||||
onClick={() => !isLoggedIn && setShowAuthModal(true)}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
onClick={sendChat}
|
|
||||||
className="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700"
|
|
||||||
>
|
|
||||||
Send
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!isLoggedIn && (
|
|
||||||
<Button
|
|
||||||
extraClasses="absolute top-[20px] left-[20px] text-[1rem] flex items-center flex-nowrap z-[999]"
|
|
||||||
onClick={() => setShowAuthModal(true)}
|
|
||||||
></Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{showAuthModal && (
|
|
||||||
<div className="fixed inset-0 z-[9999] flex items-center justify-center">
|
|
||||||
<AuthModal onClose={() => setShowAuthModal(false)} />
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</div>
|
<div className="flex justify-center gap-2">
|
||||||
|
{isLoggedIn && (
|
||||||
|
<>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
value={inputMessage}
|
||||||
|
onChange={(e) => setInputMessage(e.target.value)}
|
||||||
|
onKeyDown={handleKeyPress}
|
||||||
|
placeholder={
|
||||||
|
isLoggedIn ? "Type a message..." : "Login to chat"
|
||||||
|
}
|
||||||
|
disabled={!isLoggedIn}
|
||||||
|
extraClasses="flex-grow"
|
||||||
|
onClick={() => !isLoggedIn && setShowAuthModal(true)}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={sendChat}
|
||||||
|
className="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700"
|
||||||
|
>
|
||||||
|
Send
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isLoggedIn && (
|
||||||
|
<Button
|
||||||
|
extraClasses="absolute top-[20px] left-[20px] text-[1rem] flex items-center flex-nowrap z-[999]"
|
||||||
|
onClick={() => setShowAuthModal(true)}
|
||||||
|
></Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{showAuthModal && (
|
||||||
|
<div className="fixed inset-0 z-[9999] flex items-center justify-center">
|
||||||
|
<AuthModal onClose={() => setShowAuthModal(false)} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
import React, { createContext, useContext, useEffect, useRef, useState } from "react";
|
import React, {
|
||||||
|
createContext,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
import { io, Socket } from "socket.io-client";
|
import { io, Socket } from "socket.io-client";
|
||||||
|
|
||||||
interface SocketContextType {
|
interface SocketContextType {
|
||||||
@@ -26,14 +32,16 @@ export const SocketProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log("Creating new socket connection");
|
console.log("Creating new socket connection");
|
||||||
const newSocket = io('http://localhost:8080', {
|
const newSocket = io("http://localhost:8080", {
|
||||||
path: '/socket.io/',
|
path: "/socket.io/",
|
||||||
transports: ['websocket'],
|
transports: ["websocket"],
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
reconnectionDelay: 1000,
|
reconnectionDelay: 1000,
|
||||||
reconnectionDelayMax: 5000,
|
reconnectionDelayMax: 5000,
|
||||||
reconnectionAttempts: 5,
|
reconnectionAttempts: 5,
|
||||||
timeout: 5000
|
timeout: 5000,
|
||||||
|
autoConnect: true,
|
||||||
|
forceNew: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
socketRef.current = newSocket;
|
socketRef.current = newSocket;
|
||||||
@@ -65,15 +73,11 @@ export const SocketProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||||||
console.error("Socket connection error:", error);
|
console.error("Socket connection error:", error);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
if (newSocket) newSocket.disconnect();
|
if (newSocket) newSocket.disconnect();
|
||||||
newSocket.connect();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
newSocket.on("disconnect", (reason) => {
|
newSocket.on("disconnect", (reason) => {
|
||||||
console.log(
|
console.log("Socket disconnected! Reason: " + reason);
|
||||||
"Socket disconnected! Reason: " + reason + " - Attempting reconnect..."
|
|
||||||
);
|
|
||||||
setIsConnected(false);
|
setIsConnected(false);
|
||||||
newSocket.connect();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import Navbar from "../components/Layout/Navbar";
|
import Navbar from "../components/Layout/Navbar";
|
||||||
import Button, { ToggleButton } from "../components/Layout/Button";
|
import Button from "../components/Layout/Button";
|
||||||
import ChatPanel from "../components/Video/ChatPanel";
|
import ChatPanel from "../components/Video/ChatPanel";
|
||||||
// import CheckoutForm, { Return } from "../components/Checkout/CheckoutForm";
|
// import CheckoutForm, { Return } from "../components/Checkout/CheckoutForm";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
@@ -19,7 +19,7 @@ interface StreamDataProps {
|
|||||||
streamerId: number;
|
streamerId: number;
|
||||||
startTime: string;
|
startTime: string;
|
||||||
viewerCount: number;
|
viewerCount: number;
|
||||||
categoryId: number;
|
categoryName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
|
const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
|
||||||
@@ -29,11 +29,6 @@ const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
|
|||||||
// const [showCheckout, setShowCheckout] = useState(false);
|
// const [showCheckout, setShowCheckout] = useState(false);
|
||||||
// const showReturn = window.location.search.includes("session_id");
|
// const showReturn = window.location.search.includes("session_id");
|
||||||
// const navigate = useNavigate();
|
// const navigate = useNavigate();
|
||||||
const [isChatVisible, setIsChatVisible] = useState(false);
|
|
||||||
|
|
||||||
const toggleChat = () => {
|
|
||||||
setIsChatVisible((prev) => !prev);
|
|
||||||
};
|
|
||||||
|
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// // Prevent scrolling when checkout is open
|
// // Prevent scrolling when checkout is open
|
||||||
@@ -57,19 +52,24 @@ const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
|
|||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
console.error("Failed to load stream data:", res.statusText);
|
console.error("Failed to load stream data:", res.statusText);
|
||||||
}
|
}
|
||||||
res.json().then((data) => {
|
res
|
||||||
// if (!data.validStream) navigate(`/`);
|
.json()
|
||||||
console.log(`Loading stream data for ${streamerName}`);
|
.then((data) => {
|
||||||
setStreamData({
|
// Transform snake_case to camelCase
|
||||||
streamId: data.streamId,
|
const transformedData: StreamDataProps = {
|
||||||
streamTitle: data.streamTitle,
|
streamId: streamId,
|
||||||
streamerName: data.streamerName,
|
streamerName: data.username,
|
||||||
streamerId: data.streamerId,
|
streamerId: data.user_id,
|
||||||
startTime: data.startTime,
|
streamTitle: data.title,
|
||||||
viewerCount: data.viewerCount,
|
startTime: data.start_time,
|
||||||
categoryId: data.categoryId,
|
viewerCount: data.num_viewers,
|
||||||
|
categoryName: data.category_name,
|
||||||
|
};
|
||||||
|
setStreamData(transformedData);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Error fetching stream data:", error);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}, [streamId, streamerName]);
|
}, [streamId, streamerName]);
|
||||||
|
|
||||||
@@ -79,35 +79,48 @@ const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
|
|||||||
<Navbar />
|
<Navbar />
|
||||||
<div id="container" className="bg-gray-900">
|
<div id="container" className="bg-gray-900">
|
||||||
<VideoPlayer streamId={streamId} />
|
<VideoPlayer streamId={streamId} />
|
||||||
<ToggleButton
|
<div
|
||||||
onClick={toggleChat}
|
id="chat"
|
||||||
toggled={isChatVisible}
|
className="relative top-0 right-0 bg-gray-800 bg-opacity-75 text-white p-4 w-1/3 h-full z-10 overflow-y-auto"
|
||||||
extraClasses="z-5"
|
|
||||||
>
|
>
|
||||||
{isChatVisible ? "Hide Chat" : "Show Chat"}
|
<ChatPanel streamId={streamId} />
|
||||||
</ToggleButton>
|
</div>
|
||||||
{isChatVisible && (
|
|
||||||
<div
|
|
||||||
id="chat"
|
|
||||||
className="relative top-0 right-0 bg-gray-800 bg-opacity-75 text-white p-4 w-1/3 h-full z-10 overflow-y-auto"
|
|
||||||
>
|
|
||||||
<ChatPanel streamId={streamId} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div
|
<div
|
||||||
id="stream-info"
|
id="stream-info"
|
||||||
className="flex"
|
className="flex flex-col gap-2 p-4 text-white"
|
||||||
style={{ gridArea: "3 / 1 / 4 / 2" }}
|
style={{ gridArea: "3 / 1 / 4 / 2" }}
|
||||||
>
|
>
|
||||||
<h1>{streamData?.streamTitle}</h1>
|
<h1 className="text-2xl font-bold">
|
||||||
<h2>{streamData?.streamerName}</h2>
|
{streamData ? streamData.streamTitle : "Loading..."}
|
||||||
|
</h1>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="font-semibold">Streamer:</span>
|
||||||
|
<span>
|
||||||
|
{streamData ? streamData.streamerName : "Loading..."}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="font-semibold">Viewer Count:</span>
|
||||||
|
<span>{streamData ? streamData.viewerCount : "0"}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="font-semibold">Started At:</span>
|
||||||
|
<span>
|
||||||
|
{streamData
|
||||||
|
? new Date(streamData.startTime).toLocaleString()
|
||||||
|
: "Loading..."}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="font-semibold">Category ID:</span>
|
||||||
|
<span>
|
||||||
|
{streamData ? streamData.categoryName : "Loading..."}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{isLoggedIn && (
|
{isLoggedIn && (
|
||||||
<Button
|
<Button extraClasses="mx-auto mb-4">Payment Screen Test</Button>
|
||||||
// onClick={() => setShowCheckout(true)}
|
|
||||||
extraClasses="mx-auto mb-4"
|
|
||||||
>
|
|
||||||
Payment Screen Test
|
|
||||||
</Button>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
34
frontend/vite.config.dev.ts
Normal file
34
frontend/vite.config.dev.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react({
|
||||||
|
// Add development-specific React plugin options
|
||||||
|
jsxRuntime: 'automatic'
|
||||||
|
})],
|
||||||
|
server: {
|
||||||
|
port: 5173,
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'http://localhost:8080',
|
||||||
|
changeOrigin: true,
|
||||||
|
secure: false
|
||||||
|
},
|
||||||
|
'/socket.io': {
|
||||||
|
target: 'http://localhost:8080',
|
||||||
|
changeOrigin: true,
|
||||||
|
ws: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
sourcemap: true,
|
||||||
|
outDir: 'dist'
|
||||||
|
},
|
||||||
|
optimizeDeps: {
|
||||||
|
exclude: ['@vite/client', '@vite/env']
|
||||||
|
},
|
||||||
|
esbuild: {
|
||||||
|
logOverride: { 'this-is-undefined-in-esm': 'silent' }
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -12,6 +12,12 @@ export default defineConfig({
|
|||||||
target: 'http://web_server:5000',
|
target: 'http://web_server:5000',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
hmr: {
|
||||||
|
protocol: 'ws',
|
||||||
|
host: '127.0.0.1',
|
||||||
|
clientPort: 8080,
|
||||||
|
port: 5173
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
@@ -46,6 +46,10 @@ http {
|
|||||||
location /api/ {
|
location /api/ {
|
||||||
rewrite ^/api/(.*)$ /$1 break;
|
rewrite ^/api/(.*)$ /$1 break;
|
||||||
proxy_pass http://web_server:5000; # flask-app is the name of the Flask container in docker-compose
|
proxy_pass http://web_server:5000; # flask-app is the name of the Flask container in docker-compose
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /socket.io/ {
|
location /socket.io/ {
|
||||||
@@ -57,6 +61,9 @@ http {
|
|||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_cache_bypass $http_upgrade;
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
proxy_buffers 8 32k;
|
||||||
|
proxy_buffer_size 64k;
|
||||||
|
proxy_read_timeout 86400;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /hmr/ {
|
location /hmr/ {
|
||||||
@@ -91,8 +98,20 @@ http {
|
|||||||
expires -1d;
|
expires -1d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location ~ ^/\?token=.*$ {
|
||||||
|
proxy_pass http://frontend:5173;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_pass http://frontend:5173; # frontend is the name of the React container in docker-compose
|
proxy_pass http://frontend:5173;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
from flask_socketio import SocketIO
|
from flask_socketio import SocketIO
|
||||||
|
|
||||||
socketio = SocketIO(cors_allowed_origins="*", async_mode='gevent', logger=True, engineio_logger=True)
|
socketio = SocketIO(
|
||||||
|
cors_allowed_origins="*",
|
||||||
|
async_mode='gevent',
|
||||||
|
logger=False, # Reduce logging
|
||||||
|
engineio_logger=False, # Reduce logging
|
||||||
|
ping_timeout=5000,
|
||||||
|
ping_interval=25000
|
||||||
|
)
|
||||||
@@ -74,9 +74,12 @@ def user_stream(user_id: int, stream_id: int) -> dict:
|
|||||||
"""
|
"""
|
||||||
with Database() as db:
|
with Database() as db:
|
||||||
stream = db.fetchone("""
|
stream = db.fetchone("""
|
||||||
SELECT * FROM streams
|
SELECT u.username, s.user_id, s.title, s.start_time, s.num_viewers, c.category_name
|
||||||
WHERE user_id = ?
|
FROM streams AS s
|
||||||
AND stream_id = ?
|
JOIN categories AS c ON s.category_id = c.category_id
|
||||||
|
JOIN users AS u ON s.user_id = u.user_id
|
||||||
|
WHERE u.user_id = ?
|
||||||
|
AND s.stream_id = ?
|
||||||
""", (user_id, stream_id))
|
""", (user_id, stream_id))
|
||||||
return stream
|
return stream
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user