Compare commits

...

12 Commits

3 changed files with 122 additions and 26 deletions

View File

@@ -9,19 +9,21 @@ const QUEUE_THRESHOLD = parseInt(process.env.QUEUE_THRESHOLD || "100", 10)
const CONCURRENT_ACTIVE = parseInt(process.env.CONCURRENT_ACTIVE || "50", 10) const CONCURRENT_ACTIVE = parseInt(process.env.CONCURRENT_ACTIVE || "50", 10)
const TOKEN_TTL_SECONDS = parseInt(process.env.TOKEN_TTL_SECONDS || `${15 * 60}`, 10) const TOKEN_TTL_SECONDS = parseInt(process.env.TOKEN_TTL_SECONDS || `${15 * 60}`, 10)
// MySQL kapcsolat // MySQL kapcsolat - minden híváskor új connection
let db = null
async function getDbConnection() { async function getDbConnection() {
if (!db && process.env.MYSQL_HOST) { if (!process.env.MYSQL_HOST) return null
db = await mysql.createConnection({ try {
const connection = await mysql.createConnection({
host: process.env.MYSQL_HOST, host: process.env.MYSQL_HOST,
user: process.env.MYSQL_USER, user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD, password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE, database: process.env.MYSQL_DATABASE,
}) })
return connection
} catch (error) {
console.error('MySQL connection error:', error)
return null
} }
return db
} }
function ensureEvent(eventId) { function ensureEvent(eventId) {
@@ -61,12 +63,17 @@ async function evaluateQueue(eventId, io) {
const next = ev.queue.shift() const next = ev.queue.shift()
if (!next) break if (!next) break
// sign token // Get database time for consistency
const expiresAt = new Date(Date.now() + TOKEN_TTL_SECONDS * 1000) const [timeRows] = await connection.execute('SELECT NOW() as db_time')
const dbTime = new Date(timeRows[0].db_time)
const expiresAt = new Date(dbTime.getTime() + TOKEN_TTL_SECONDS * 1000)
const token = jwt.sign({ sid: next, eventId }, process.env.JWT_SECRET || "dev-secret", { const token = jwt.sign({ sid: next, eventId }, process.env.JWT_SECRET || "dev-secret", {
expiresIn: TOKEN_TTL_SECONDS, expiresIn: TOKEN_TTL_SECONDS,
}) })
console.log(`Creating queued token for ${next.substring(0, 8)}: DB time ${dbTime.toISOString()}, expires at ${expiresAt.toISOString()}, TTL: ${TOKEN_TTL_SECONDS}s`)
ev.active.add(next) ev.active.add(next)
// Save to database // Save to database
@@ -87,14 +94,32 @@ async function evaluateQueue(eventId, io) {
// Check for expired tokens in database and notify clients // Check for expired tokens in database and notify clients
if (connection) { if (connection) {
try { try {
// Debug: check current time vs stored times
const [allSessions] = await connection.execute(
'SELECT socket_id, expires_at, NOW() as server_time FROM active_sessions WHERE event_id = ?',
[eventId]
)
console.log(`Event ${eventId} - Current sessions:`, allSessions.map(s => ({
socket: s.socket_id.substring(0, 8),
expires: s.expires_at,
server_time: s.server_time,
expired: new Date(s.expires_at) < new Date(s.server_time)
})))
const [expiredSessions] = await connection.execute( const [expiredSessions] = await connection.execute(
'SELECT socket_id FROM active_sessions WHERE event_id = ? AND expires_at < NOW()', 'SELECT socket_id FROM active_sessions WHERE event_id = ? AND expires_at < NOW()',
[eventId] [eventId]
) )
if (expiredSessions.length > 0) {
console.log(`Found ${expiredSessions.length} expired sessions for event ${eventId}`)
}
for (const session of expiredSessions) { for (const session of expiredSessions) {
const sid = session.socket_id const sid = session.socket_id
if (ev.active.has(sid)) { if (ev.active.has(sid)) {
console.log(`Sending token_expired to ${sid.substring(0, 8)}`)
ev.active.delete(sid) ev.active.delete(sid)
io.to(sid).emit("token_expired") io.to(sid).emit("token_expired")
} }
@@ -102,6 +127,7 @@ async function evaluateQueue(eventId, io) {
// Clean up expired sessions from database // Clean up expired sessions from database
if (expiredSessions.length > 0) { if (expiredSessions.length > 0) {
console.log(`Cleaning up ${expiredSessions.length} expired sessions for event ${eventId}`);
await connection.execute( await connection.execute(
'DELETE FROM active_sessions WHERE event_id = ? AND expires_at < NOW()', 'DELETE FROM active_sessions WHERE event_id = ? AND expires_at < NOW()',
[eventId] [eventId]
@@ -136,6 +162,15 @@ async function evaluateQueue(eventId, io) {
// If queue no longer needed, clear it // If queue no longer needed, clear it
if (ev.sockets.size < QUEUE_THRESHOLD) ev.queueOn = false if (ev.sockets.size < QUEUE_THRESHOLD) ev.queueOn = false
// Close database connection
if (connection) {
try {
await connection.end()
} catch (error) {
console.error('Error closing DB connection:', error)
}
}
// broadcast // broadcast
broadcastUpdate(eventId, io) broadcastUpdate(eventId, io)
} }
@@ -163,6 +198,18 @@ export async function GET(req) {
global.io = io global.io = io
// Clean up all expired sessions on server start
const startupConnection = await getDbConnection()
if (startupConnection) {
try {
const [result] = await startupConnection.execute('DELETE FROM active_sessions WHERE expires_at < NOW()')
console.log(`Server startup: Cleaned ${result.affectedRows} expired sessions`)
await startupConnection.end()
} catch (error) {
console.error('Cleanup error on startup:', error)
}
}
// Periodikus token ellenőrzés minden 5 másodpercben // Periodikus token ellenőrzés minden 5 másodpercben
setInterval(async () => { setInterval(async () => {
for (const eventId of Object.keys(events)) { for (const eventId of Object.keys(events)) {
@@ -230,8 +277,15 @@ export async function GET(req) {
// If queue is NOT active and user doesn't have access, grant it immediately // If queue is NOT active and user doesn't have access, grant it immediately
if (!ev.queueOn && !ev.active.has(socket.id)) { if (!ev.queueOn && !ev.active.has(socket.id)) {
console.log(`Granting immediate access to ${socket.id} (under threshold)`) console.log(`Granting immediate access to ${socket.id.substring(0, 8)} (under threshold)`)
const expiresAt = new Date(Date.now() + TOKEN_TTL_SECONDS * 1000)
// Get server time from database to ensure consistency
const [timeRows] = await connection.execute('SELECT NOW() as db_time')
const dbTime = new Date(timeRows[0].db_time)
const expiresAt = new Date(dbTime.getTime() + TOKEN_TTL_SECONDS * 1000)
console.log(`DB time: ${dbTime.toISOString()}, Token expires: ${expiresAt.toISOString()}, TTL: ${TOKEN_TTL_SECONDS}s`)
const token = jwt.sign({ sid: socket.id, eventId }, process.env.JWT_SECRET || "dev-secret", { const token = jwt.sign({ sid: socket.id, eventId }, process.env.JWT_SECRET || "dev-secret", {
expiresIn: TOKEN_TTL_SECONDS, expiresIn: TOKEN_TTL_SECONDS,
}) })
@@ -263,6 +317,15 @@ export async function GET(req) {
position: pos === -1 ? null : pos + 1, position: pos === -1 ? null : pos + 1,
estimatedWait: pos === -1 ? null : (ev.active.size + pos) * TOKEN_TTL_SECONDS, estimatedWait: pos === -1 ? null : (ev.active.size + pos) * TOKEN_TTL_SECONDS,
}) })
// Close database connection
if (connection) {
try {
await connection.end()
} catch (error) {
console.error('Error closing DB connection:', error)
}
}
}) })
socket.on("disconnect", () => { socket.on("disconnect", () => {

View File

@@ -15,6 +15,21 @@ export default function EventPage() {
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`; return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
}; };
// Function to clear all token and queue state
const clearAllTokenState = () => {
console.log("Clearing all token state");
setHasAccess(false);
setTokenExpiry(null);
setPosition(null);
setEstimatedWait(null);
try {
localStorage.removeItem("event_token");
} catch (e) {}
if (socketRef.current) {
socketRef.current.disconnect();
}
};
const [connected, setConnected] = useState(false); const [connected, setConnected] = useState(false);
const [position, setPosition] = useState<number | null>(null); const [position, setPosition] = useState<number | null>(null);
const [estimatedWait, setEstimatedWait] = useState<number | null>(null); const [estimatedWait, setEstimatedWait] = useState<number | null>(null);
@@ -35,7 +50,9 @@ export default function EventPage() {
fetch(`/api/events?id=${eventId}`) fetch(`/api/events?id=${eventId}`)
.then(res => res.json()) .then(res => res.json())
.then(data => { .then(data => {
console.log('Event API response:', data);
if (data.error) { if (data.error) {
console.log('Event API error, redirecting:', data.error);
router.push('/'); router.push('/');
return; return;
} }
@@ -65,7 +82,8 @@ export default function EventPage() {
.then(({ io }) => { .then(({ io }) => {
if (!mounted) return; if (!mounted) return;
const socketPort = process.env.NEXT_PUBLIC_SOCKET_PORT || "4000"; const socketPort = process.env.NEXT_PUBLIC_SOCKET_PORT || "4000";
const socket = io(`http://localhost:${socketPort}`, { const socketHost = process.env.NEXT_PUBLIC_SOCKET_HOST || window.location.hostname;
const socket = io(`http://${socketHost}:${socketPort}`, {
autoConnect: true autoConnect: true
}); });
socketRef.current = socket; socketRef.current = socket;
@@ -89,8 +107,18 @@ export default function EventPage() {
}); });
socket.on("granted", (data: any) => { socket.on("granted", (data: any) => {
console.log("Granted event received:", data);
const expiryTime = data.expiresAt ? Date.parse(data.expiresAt) : Date.now() + 15 * 60 * 1000;
console.log("Setting token expiry:", {
raw: data.expiresAt,
parsed: new Date(expiryTime),
now: new Date(),
diffMs: expiryTime - Date.now(),
diffMin: Math.round((expiryTime - Date.now()) / 60000)
});
setHasAccess(true); setHasAccess(true);
setTokenExpiry(data.expiresAt ? Date.parse(data.expiresAt) : Date.now() + 15 * 60 * 1000); setTokenExpiry(expiryTime);
try { try {
localStorage.setItem("event_token", data.token); localStorage.setItem("event_token", data.token);
} catch (e) {} } catch (e) {}
@@ -103,11 +131,11 @@ export default function EventPage() {
}); });
socket.on("token_expired", () => { socket.on("token_expired", () => {
setHasAccess(false); console.log("Token expired received from server");
setTokenExpiry(null); clearAllTokenState();
localStorage.removeItem("event_token"); setTimeout(() => {
// Redirect to homepage window.location.href = "/";
window.location.href = "/"; }, 100);
}); });
}) })
.catch(error => { .catch(error => {
@@ -120,20 +148,25 @@ export default function EventPage() {
}; };
}, [eventId, loading]); }, [eventId, loading]);
// Check for existing token on page load and clear everything
useEffect(() => {
console.log('Page loaded, clearing any existing token state');
clearAllTokenState();
}, []);
// Token expiry timer - ellenőrzés minden másodpercben // Token expiry timer - ellenőrzés minden másodpercben
useEffect(() => { useEffect(() => {
if (!tokenExpiry) return; if (!tokenExpiry) return;
console.log('Setting token expiry timer for:', new Date(tokenExpiry));
const id = setInterval(() => { const id = setInterval(() => {
const msLeft = tokenExpiry - Date.now(); const msLeft = tokenExpiry - Date.now();
console.log('Token check - ms left:', msLeft);
if (msLeft <= 0) { if (msLeft <= 0) {
setHasAccess(false); console.log('Token expired locally, redirecting to homepage');
setTokenExpiry(null); clearAllTokenState();
localStorage.removeItem("event_token"); setTimeout(() => {
// Disconnect socket and redirect to homepage window.location.href = "/";
if (socketRef.current) { }, 100);
socketRef.current.disconnect();
}
window.location.href = "/";
} }
}, 1000); }, 1000);
return () => clearInterval(id); return () => clearInterval(id);

View File

@@ -1,7 +1,7 @@
import type { NextConfig } from "next"; import type { NextConfig } from "next";
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
/* config options here */ allowedDevOrigins: ["10.20.0.188"]
}; };
export default nextConfig; export default nextConfig;