fix: Lock verschwindet nicht mehr - Token-Refresh + laengerer Timeout

Problem: Lock verschwand nach 5 Minuten weil:
1. JWT-Token nach 15 Min ablief -> Heartbeat schlug still fehl
2. Server gab Lock nach 5 Min ohne Heartbeat frei

Fix Client:
- Token-Refresh alle 10 Minuten (vor dem 15-Min-Ablauf)
- Aktualisiert den Token in der shared API-Instanz
- Heartbeat nutzt immer den aktuellen Token

Fix Backend:
- Lock-Timeout von 5 auf 15 Minuten erhoeht
- Genug Puffer fuer Netzwerk-Probleme oder kurze Unterbrechungen

Timeline:
  0s    -> Lock + Heartbeat alle 10s
  600s  -> Token-Refresh
  900s  -> Lock wuerde erst jetzt ablaufen (15 Min ohne Heartbeat)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Hacker 2026-04-12 01:45:52 +02:00
parent b937351556
commit e9638cc6ed
2 changed files with 21 additions and 4 deletions

View File

@ -2,8 +2,9 @@ from datetime import datetime, timezone, timedelta
from app.extensions import db
# Lock expires after 5 minutes without heartbeat
LOCK_TIMEOUT_MINUTES = 5
# Lock expires after 15 minutes without heartbeat
# Client sends heartbeat every 10 seconds and refreshes JWT every 10 minutes
LOCK_TIMEOUT_MINUTES = 15
class FileLock(db.Model):

View File

@ -481,19 +481,35 @@ fn start_background_sync(
}
});
// Heartbeat every 60s + check if opened files are still in use
// Heartbeat + token refresh + check if opened files still in use
let app_hb = app.clone();
let api_hb = api.clone();
std::thread::spawn(move || {
let rt = tokio::runtime::Runtime::new().unwrap();
let mut api_mut = api_hb.clone();
let mut token_refresh_counter = 0u32;
loop {
std::thread::sleep(Duration::from_secs(10));
let state = app_hb.state::<AppState>();
// Refresh JWT token every 10 minutes (before 15 min expiry)
token_refresh_counter += 10;
if token_refresh_counter >= 600 {
token_refresh_counter = 0;
if let Ok(new_token) = rt.block_on(api_mut.refresh_token()) {
// Update the shared API instance with new token
if let Some(ref mut api) = *state.api.lock().unwrap() {
api.access_token = new_token;
}
eprintln!("[Auth] Token refreshed");
}
}
// Heartbeat for locked files
let locked = state.locked_files.lock().unwrap().clone();
for file_id in &locked {
let _ = rt.block_on(api_hb.heartbeat(*file_id));
let _ = rt.block_on(api_mut.heartbeat(*file_id));
}
// Check if opened files are still in use by another process