Spaces:
Runtime error
Runtime error
da03
commited on
Commit
·
4555c1c
1
Parent(s):
b9e6b75
- dispatcher.py +40 -13
- static/index.html +21 -21
dispatcher.py
CHANGED
@@ -329,6 +329,7 @@ class UserSession:
|
|
329 |
worker_id: Optional[str] = None
|
330 |
last_activity: Optional[float] = None
|
331 |
max_session_time: Optional[float] = None
|
|
|
332 |
user_has_interacted: bool = False
|
333 |
ip_address: Optional[str] = None
|
334 |
interaction_count: int = 0
|
@@ -411,6 +412,7 @@ class SessionManager:
|
|
411 |
if session and session.max_session_time is None: # Currently unlimited
|
412 |
# Give them 60 seconds from now
|
413 |
session.max_session_time = 60.0
|
|
|
414 |
session.last_activity = current_time # Reset activity timer to start 60s countdown
|
415 |
session.session_warning_sent = False # Reset warning flag
|
416 |
affected_sessions += 1
|
@@ -418,8 +420,7 @@ class SessionManager:
|
|
418 |
# Notify the user about the new time limit
|
419 |
try:
|
420 |
queue_size = len(self.session_queue)
|
421 |
-
|
422 |
-
message = f"{queue_size} other {user_text} waiting. You have 60 seconds to finish."
|
423 |
|
424 |
await session.websocket.send_json({
|
425 |
"type": "queue_limit_applied",
|
@@ -464,6 +465,7 @@ class SessionManager:
|
|
464 |
# Set session time limit based on queue status
|
465 |
if len(self.session_queue) > 0:
|
466 |
session.max_session_time = self.MAX_SESSION_TIME_WITH_QUEUE
|
|
|
467 |
|
468 |
worker.is_available = False
|
469 |
worker.current_session = session_id
|
@@ -520,9 +522,15 @@ class SessionManager:
|
|
520 |
while session.status == SessionStatus.ACTIVE:
|
521 |
current_time = time.time()
|
522 |
|
523 |
-
# Check
|
|
|
|
|
|
|
|
|
524 |
if session.max_session_time:
|
525 |
-
|
|
|
|
|
526 |
remaining = session.max_session_time - elapsed
|
527 |
|
528 |
# Send warning at 15 seconds before timeout (only once)
|
@@ -540,6 +548,7 @@ class SessionManager:
|
|
540 |
# Check if queue is empty - if so, extend session
|
541 |
if len(self.session_queue) == 0:
|
542 |
session.max_session_time = None # Remove time limit
|
|
|
543 |
session.session_warning_sent = False # Reset warning since limit removed
|
544 |
await session.websocket.send_json({
|
545 |
"type": "time_limit_removed",
|
@@ -553,24 +562,31 @@ class SessionManager:
|
|
553 |
"queue_size": len(self.session_queue)
|
554 |
})
|
555 |
|
556 |
-
#
|
557 |
elif remaining <= 0:
|
558 |
-
|
559 |
-
return
|
560 |
|
561 |
-
# Check idle timeout when
|
562 |
-
|
563 |
idle_time = current_time - session.last_activity
|
564 |
if idle_time >= self.IDLE_TIMEOUT:
|
565 |
-
|
566 |
-
return
|
567 |
elif idle_time >= self.QUEUE_WARNING_TIME and not session.idle_warning_sent:
|
|
|
568 |
await session.websocket.send_json({
|
569 |
"type": "idle_warning",
|
570 |
"time_remaining": self.IDLE_TIMEOUT - idle_time
|
571 |
})
|
572 |
session.idle_warning_sent = True
|
573 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
574 |
|
575 |
await asyncio.sleep(1) # Check every second
|
576 |
|
@@ -679,9 +695,17 @@ class SessionManager:
|
|
679 |
session = self.sessions.get(session_id)
|
680 |
if session:
|
681 |
old_time = session.last_activity
|
682 |
-
session.last_activity = time.time()
|
683 |
session.interaction_count += 1
|
684 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
685 |
# Reset warning flags if user activity detected after warnings
|
686 |
warning_reset = False
|
687 |
if session.idle_warning_sent:
|
@@ -832,6 +856,9 @@ async def websocket_endpoint(websocket: WebSocket):
|
|
832 |
|
833 |
# Update activity only for real user inputs, not auto inputs
|
834 |
if not data.get("is_auto_input", False):
|
|
|
|
|
|
|
835 |
await session_manager.handle_user_activity(session_id)
|
836 |
|
837 |
# Handle different message types
|
|
|
329 |
worker_id: Optional[str] = None
|
330 |
last_activity: Optional[float] = None
|
331 |
max_session_time: Optional[float] = None
|
332 |
+
session_limit_start_time: Optional[float] = None # When the time limit was first applied
|
333 |
user_has_interacted: bool = False
|
334 |
ip_address: Optional[str] = None
|
335 |
interaction_count: int = 0
|
|
|
412 |
if session and session.max_session_time is None: # Currently unlimited
|
413 |
# Give them 60 seconds from now
|
414 |
session.max_session_time = 60.0
|
415 |
+
session.session_limit_start_time = current_time # Track when limit started
|
416 |
session.last_activity = current_time # Reset activity timer to start 60s countdown
|
417 |
session.session_warning_sent = False # Reset warning flag
|
418 |
affected_sessions += 1
|
|
|
420 |
# Notify the user about the new time limit
|
421 |
try:
|
422 |
queue_size = len(self.session_queue)
|
423 |
+
message = f"Other users waiting. Time remaining: 60 seconds."
|
|
|
424 |
|
425 |
await session.websocket.send_json({
|
426 |
"type": "queue_limit_applied",
|
|
|
465 |
# Set session time limit based on queue status
|
466 |
if len(self.session_queue) > 0:
|
467 |
session.max_session_time = self.MAX_SESSION_TIME_WITH_QUEUE
|
468 |
+
session.session_limit_start_time = time.time() # Track when limit started
|
469 |
|
470 |
worker.is_available = False
|
471 |
worker.current_session = session_id
|
|
|
522 |
while session.status == SessionStatus.ACTIVE:
|
523 |
current_time = time.time()
|
524 |
|
525 |
+
# Check timeouts - both session limit AND idle timeout can apply
|
526 |
+
session_timeout = False
|
527 |
+
idle_timeout = False
|
528 |
+
|
529 |
+
# Check session time limit (when queue exists)
|
530 |
if session.max_session_time:
|
531 |
+
# Use session_limit_start_time for absolute timeout, fall back to last_activity if not set
|
532 |
+
start_time = session.session_limit_start_time if session.session_limit_start_time else session.last_activity
|
533 |
+
elapsed = current_time - start_time if start_time else 0
|
534 |
remaining = session.max_session_time - elapsed
|
535 |
|
536 |
# Send warning at 15 seconds before timeout (only once)
|
|
|
548 |
# Check if queue is empty - if so, extend session
|
549 |
if len(self.session_queue) == 0:
|
550 |
session.max_session_time = None # Remove time limit
|
551 |
+
session.session_limit_start_time = None # Clear time limit start time
|
552 |
session.session_warning_sent = False # Reset warning since limit removed
|
553 |
await session.websocket.send_json({
|
554 |
"type": "time_limit_removed",
|
|
|
562 |
"queue_size": len(self.session_queue)
|
563 |
})
|
564 |
|
565 |
+
# Session timeout
|
566 |
elif remaining <= 0:
|
567 |
+
session_timeout = True
|
|
|
568 |
|
569 |
+
# Check idle timeout (always check when user has interacted)
|
570 |
+
if session.last_activity:
|
571 |
idle_time = current_time - session.last_activity
|
572 |
if idle_time >= self.IDLE_TIMEOUT:
|
573 |
+
idle_timeout = True
|
|
|
574 |
elif idle_time >= self.QUEUE_WARNING_TIME and not session.idle_warning_sent:
|
575 |
+
# Send idle warning (even when session limit exists)
|
576 |
await session.websocket.send_json({
|
577 |
"type": "idle_warning",
|
578 |
"time_remaining": self.IDLE_TIMEOUT - idle_time
|
579 |
})
|
580 |
session.idle_warning_sent = True
|
581 |
+
reason = "with session limit" if session.max_session_time else "no session limit"
|
582 |
+
logger.info(f"Idle warning sent to {session_id}, time remaining: {self.IDLE_TIMEOUT - idle_time:.1f}s ({reason})")
|
583 |
+
|
584 |
+
# End session if either timeout triggered
|
585 |
+
if session_timeout or idle_timeout:
|
586 |
+
timeout_reason = "session_limit" if session_timeout else "idle"
|
587 |
+
logger.info(f"Ending session {session_id} due to {timeout_reason} timeout")
|
588 |
+
await self.end_session(session_id, SessionStatus.TIMEOUT)
|
589 |
+
return
|
590 |
|
591 |
await asyncio.sleep(1) # Check every second
|
592 |
|
|
|
695 |
session = self.sessions.get(session_id)
|
696 |
if session:
|
697 |
old_time = session.last_activity
|
|
|
698 |
session.interaction_count += 1
|
699 |
|
700 |
+
# Always update last_activity for idle detection
|
701 |
+
# Session time limits are now tracked separately via session_limit_start_time
|
702 |
+
session.last_activity = time.time()
|
703 |
+
|
704 |
+
if session.max_session_time is not None:
|
705 |
+
logger.info(f"Activity detected for session {session_id} - idle timer reset (session limit still active)")
|
706 |
+
else:
|
707 |
+
logger.info(f"Activity detected for session {session_id} - idle timer reset (no session limit)")
|
708 |
+
|
709 |
# Reset warning flags if user activity detected after warnings
|
710 |
warning_reset = False
|
711 |
if session.idle_warning_sent:
|
|
|
856 |
|
857 |
# Update activity only for real user inputs, not auto inputs
|
858 |
if not data.get("is_auto_input", False):
|
859 |
+
# Log stay-connected attempts for monitoring
|
860 |
+
if data.get("is_stay_connected", False):
|
861 |
+
logger.info(f"Stay-connected ping from session {session_id} - idle timer will be reset")
|
862 |
await session_manager.handle_user_activity(session_id)
|
863 |
|
864 |
# Handle different message types
|
static/index.html
CHANGED
@@ -275,7 +275,7 @@
|
|
275 |
} else if (data.type === "timeout_warning") {
|
276 |
console.log(`Received timeout warning: ${data.timeout_in} seconds remaining`);
|
277 |
setTimeoutMessage(`No activity detected. Connection will be dropped in <span id="timeoutCountdown">${data.timeout_in}</span> seconds and page will refresh automatically.`);
|
278 |
-
startTimeoutCountdown(data.timeout_in);
|
279 |
} else if (data.type === "activity_reset") {
|
280 |
console.log("Server detected user activity, resetting timeout");
|
281 |
stopTimeoutCountdown();
|
@@ -288,16 +288,8 @@
|
|
288 |
waitText = "Starting soon...";
|
289 |
} else if (waitSeconds === 1) {
|
290 |
waitText = "1 second max";
|
291 |
-
} else if (waitSeconds < 60) {
|
292 |
-
waitText = `${waitSeconds} seconds max`;
|
293 |
} else {
|
294 |
-
|
295 |
-
const seconds = waitSeconds % 60;
|
296 |
-
if (seconds === 0) {
|
297 |
-
waitText = `${minutes} minutes max`;
|
298 |
-
} else {
|
299 |
-
waitText = `${minutes}m ${seconds}s max`;
|
300 |
-
}
|
301 |
}
|
302 |
|
303 |
const statusText = data.available_workers > 0 ?
|
@@ -314,23 +306,23 @@
|
|
314 |
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
315 |
} else if (data.type === "session_warning") {
|
316 |
console.log(`Session time warning: ${data.time_remaining} seconds remaining`);
|
317 |
-
setTimeoutMessage(`
|
318 |
-
startTimeoutCountdown(Math.ceil(data.time_remaining));
|
319 |
} else if (data.type === "idle_warning") {
|
320 |
console.log(`Idle warning: ${data.time_remaining} seconds until timeout`);
|
321 |
setTimeoutMessage(`No activity detected. Connection will be dropped in <span id="timeoutCountdown">${Math.ceil(data.time_remaining)}</span> seconds and page will refresh automatically.`);
|
322 |
-
startTimeoutCountdown(Math.ceil(data.time_remaining));
|
323 |
} else if (data.type === "grace_period") {
|
324 |
console.log(`Grace period: ${data.time_remaining} seconds remaining`);
|
325 |
-
setTimeoutMessage(`
|
326 |
-
startTimeoutCountdown(Math.ceil(data.time_remaining));
|
327 |
} else if (data.type === "time_limit_removed") {
|
328 |
console.log("Time limit removed - queue became empty");
|
329 |
stopTimeoutCountdown();
|
330 |
} else if (data.type === "queue_limit_applied") {
|
331 |
console.log(`Queue limit applied: ${data.message}, ${data.time_remaining} seconds remaining`);
|
332 |
-
setTimeoutMessage(`⏰ ${data.message}
|
333 |
-
startTimeoutCountdown(Math.ceil(data.time_remaining));
|
334 |
}
|
335 |
};
|
336 |
}
|
@@ -463,7 +455,7 @@
|
|
463 |
}
|
464 |
}
|
465 |
|
466 |
-
function startTimeoutCountdown(initialTime = 10) {
|
467 |
if (timeoutCountdownInterval) {
|
468 |
clearInterval(timeoutCountdownInterval);
|
469 |
}
|
@@ -477,6 +469,12 @@
|
|
477 |
warning.style.display = 'block';
|
478 |
}
|
479 |
|
|
|
|
|
|
|
|
|
|
|
|
|
480 |
// Update initial display
|
481 |
const countdownElement = document.getElementById('timeoutCountdown');
|
482 |
if (countdownElement) {
|
@@ -532,7 +530,7 @@
|
|
532 |
}
|
533 |
|
534 |
function resetTimeout() {
|
535 |
-
// Send a
|
536 |
if (socket && socket.readyState === WebSocket.OPEN && lastSentPosition) {
|
537 |
try {
|
538 |
socket.send(JSON.stringify({
|
@@ -543,11 +541,13 @@
|
|
543 |
"keys_down": [],
|
544 |
"keys_up": [],
|
545 |
"wheel_delta_x": 0,
|
546 |
-
"wheel_delta_y": 0
|
|
|
547 |
}));
|
548 |
updateLastUserInputTime(); // Update for auto-input mechanism
|
|
|
549 |
} catch (error) {
|
550 |
-
console.error("Error sending
|
551 |
}
|
552 |
}
|
553 |
stopTimeoutCountdown();
|
|
|
275 |
} else if (data.type === "timeout_warning") {
|
276 |
console.log(`Received timeout warning: ${data.timeout_in} seconds remaining`);
|
277 |
setTimeoutMessage(`No activity detected. Connection will be dropped in <span id="timeoutCountdown">${data.timeout_in}</span> seconds and page will refresh automatically.`);
|
278 |
+
startTimeoutCountdown(data.timeout_in, false); // false = show stay connected button
|
279 |
} else if (data.type === "activity_reset") {
|
280 |
console.log("Server detected user activity, resetting timeout");
|
281 |
stopTimeoutCountdown();
|
|
|
288 |
waitText = "Starting soon...";
|
289 |
} else if (waitSeconds === 1) {
|
290 |
waitText = "1 second max";
|
|
|
|
|
291 |
} else {
|
292 |
+
waitText = `${waitSeconds} seconds max`;
|
|
|
|
|
|
|
|
|
|
|
|
|
293 |
}
|
294 |
|
295 |
const statusText = data.available_workers > 0 ?
|
|
|
306 |
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
307 |
} else if (data.type === "session_warning") {
|
308 |
console.log(`Session time warning: ${data.time_remaining} seconds remaining`);
|
309 |
+
setTimeoutMessage(`Other users waiting. Time remaining: <span id="timeoutCountdown">${Math.ceil(data.time_remaining)}</span> seconds.`);
|
310 |
+
startTimeoutCountdown(Math.ceil(data.time_remaining), true); // true = hide stay connected button
|
311 |
} else if (data.type === "idle_warning") {
|
312 |
console.log(`Idle warning: ${data.time_remaining} seconds until timeout`);
|
313 |
setTimeoutMessage(`No activity detected. Connection will be dropped in <span id="timeoutCountdown">${Math.ceil(data.time_remaining)}</span> seconds and page will refresh automatically.`);
|
314 |
+
startTimeoutCountdown(Math.ceil(data.time_remaining), false); // false = show stay connected button
|
315 |
} else if (data.type === "grace_period") {
|
316 |
console.log(`Grace period: ${data.time_remaining} seconds remaining`);
|
317 |
+
setTimeoutMessage(`Other users waiting. Time remaining: <span id="timeoutCountdown">${Math.ceil(data.time_remaining)}</span> seconds.`);
|
318 |
+
startTimeoutCountdown(Math.ceil(data.time_remaining), true); // true = hide stay connected button
|
319 |
} else if (data.type === "time_limit_removed") {
|
320 |
console.log("Time limit removed - queue became empty");
|
321 |
stopTimeoutCountdown();
|
322 |
} else if (data.type === "queue_limit_applied") {
|
323 |
console.log(`Queue limit applied: ${data.message}, ${data.time_remaining} seconds remaining`);
|
324 |
+
setTimeoutMessage(`⏰ ${data.message}`);
|
325 |
+
startTimeoutCountdown(Math.ceil(data.time_remaining), true); // true = hide stay connected button
|
326 |
}
|
327 |
};
|
328 |
}
|
|
|
455 |
}
|
456 |
}
|
457 |
|
458 |
+
function startTimeoutCountdown(initialTime = 10, hideStayConnectedButton = false) {
|
459 |
if (timeoutCountdownInterval) {
|
460 |
clearInterval(timeoutCountdownInterval);
|
461 |
}
|
|
|
469 |
warning.style.display = 'block';
|
470 |
}
|
471 |
|
472 |
+
// Show/hide Stay Connected button based on context
|
473 |
+
const stayConnectedButton = warning.querySelector('button');
|
474 |
+
if (stayConnectedButton) {
|
475 |
+
stayConnectedButton.style.display = hideStayConnectedButton ? 'none' : 'inline-block';
|
476 |
+
}
|
477 |
+
|
478 |
// Update initial display
|
479 |
const countdownElement = document.getElementById('timeoutCountdown');
|
480 |
if (countdownElement) {
|
|
|
530 |
}
|
531 |
|
532 |
function resetTimeout() {
|
533 |
+
// Send a stay-connected ping to reset the server's timeout
|
534 |
if (socket && socket.readyState === WebSocket.OPEN && lastSentPosition) {
|
535 |
try {
|
536 |
socket.send(JSON.stringify({
|
|
|
541 |
"keys_down": [],
|
542 |
"keys_up": [],
|
543 |
"wheel_delta_x": 0,
|
544 |
+
"wheel_delta_y": 0,
|
545 |
+
"is_stay_connected": true // Mark this as a stay-connected ping
|
546 |
}));
|
547 |
updateLastUserInputTime(); // Update for auto-input mechanism
|
548 |
+
console.log("Stay connected ping sent to server");
|
549 |
} catch (error) {
|
550 |
+
console.error("Error sending stay connected ping:", error);
|
551 |
}
|
552 |
}
|
553 |
stopTimeoutCountdown();
|