From 39143e1998b1d19f51c117052b7321daea766dde Mon Sep 17 00:00:00 2001 From: pavel Date: Wed, 11 Feb 2026 00:56:33 +0100 Subject: [PATCH] fix: stuff --- Cargo.toml | 2 +- frontend/src/main.js | 33 ++++++++++++++++++++++++++------- src/agent.rs | 12 ++++++++++-- src/scheduler.rs | 13 +++++++++++-- src/server.rs | 21 +++++++++++++++++++++ 5 files changed, 69 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 27633df..d0b8b6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ reqwest = { version = "0.12", features = ["json"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" axum = "0.7" -tower-http = { version = "0.5", features = ["cors"] } +tower-http = { version = "0.5", features = ["cors", "set-header", "limit"] } sea-orm = { version = "1.1", features = ["sqlx-postgres", "runtime-tokio-rustls", "macros"] } sea-orm-migration = "1.1" uuid = { version = "1.8", features = ["v4", "serde"] } diff --git a/frontend/src/main.js b/frontend/src/main.js index a1a4c66..2150495 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -130,7 +130,7 @@ async function fetchTasks() { const task = state.tasks.find((t) => t.id === state.selectedTaskId); if (task) { if (shouldFollowLatest && task.runs && task.runs.length > 0) { - state.selectedRunId = task.runs[task.runs.length - 1].id; + state.selectedRunId = task.runs[0].id; } renderRunHistory(task); @@ -200,8 +200,8 @@ function selectTask(id, runId = null) { if (runId) { state.selectedRunId = runId; } else if (task.runs && task.runs.length > 0) { - // Default to latest run if not specified - state.selectedRunId = task.runs[task.runs.length - 1].id; + // Default to latest run if not specified (index 0 is newest) + state.selectedRunId = task.runs[0].id; } else { state.selectedRunId = null; } @@ -472,6 +472,20 @@ newTaskForm.addEventListener('submit', async (e) => { } }); +async function checkSession() { + try { + const response = await fetch(`${API_URL}/auth/session`, { credentials: 'include' }); + if (response.ok) { + state.isAuthenticated = true; + return true; + } + } catch (error) { + console.error('Session check failed:', error); + } + state.isAuthenticated = false; + return false; +} + let isPolling = false; async function startAutoRefresh() { setInterval(async () => { @@ -540,11 +554,16 @@ logoutBtn.addEventListener('click', () => { }); async function initializeApp() { - appEl.classList.remove('hidden'); - loginOverlay.classList.add('hidden'); + const hasSession = await checkSession(); - await fetchTasks(); - startAutoRefresh(); + if (hasSession) { + appEl.classList.remove('hidden'); + loginOverlay.classList.add('hidden'); + await fetchTasks(); + startAutoRefresh(); + } else { + showLogin(); + } } // Check for callback on load diff --git a/src/agent.rs b/src/agent.rs index 0aebd4e..ca02946 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -71,8 +71,16 @@ impl Agent { pub async fn run(&mut self) -> Result<(String, Option), Box> { let mut finished = false; let start_time = Instant::now(); - let max_duration = Duration::from_secs(120); - let max_turns = 20; + let max_duration_secs = std::env::var("AGENT_MAX_DURATION_SECS") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(120); + let max_duration = Duration::from_secs(max_duration_secs); + + let max_turns = std::env::var("AGENT_MAX_TURNS") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(20); let mut turns = 0; while !finished { diff --git a/src/scheduler.rs b/src/scheduler.rs index 6f3632d..d2c8d47 100644 --- a/src/scheduler.rs +++ b/src/scheduler.rs @@ -100,7 +100,11 @@ impl Scheduler { tokio::spawn(async move { let (logs, answer, status) = match agent.run().await { Ok((logs, answer)) => (logs, answer, "completed".to_string()), - Err(e) => (format!("Scheduled run failed: {}", e), None, "failed".to_string()), + Err(e) => ( + format!("Scheduled run failed: {}", e), + None, + "failed".to_string(), + ), }; let run_complete = task_run::ActiveModel { @@ -110,7 +114,12 @@ impl Scheduler { answer: Set(answer), ..Default::default() }; - let _ = run_complete.update(&db).await; + if let Err(e) = run_complete.update(&db).await { + eprintln!( + "Failed to update scheduled run status for task {}: {}", + task_id, e + ); + } }); Ok(()) diff --git a/src/server.rs b/src/server.rs index db9fac3..a7e97b1 100644 --- a/src/server.rs +++ b/src/server.rs @@ -128,10 +128,24 @@ pub async fn start(db_url: &str) -> Result<(), Box> { .route("/api/tasks/:id", get(get_task).put(update_task)) .route("/api/tasks/:id/runs", post(rerun_task)) .route("/api/runs/recent", get(get_recent_runs)) + .route("/api/auth/session", get(auth_session)) .route("/api/auth/callback", get(auth_callback)) .route("/api/auth/refresh", post(auth_refresh)) .route("/api/auth/logout", post(auth_logout)) .layer(cors) + .layer(tower_http::set_header::SetResponseHeaderLayer::overriding( + axum::http::header::CONTENT_SECURITY_POLICY, + HeaderValue::from_static("default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;"), + )) + .layer(tower_http::set_header::SetResponseHeaderLayer::overriding( + axum::http::header::X_CONTENT_TYPE_OPTIONS, + HeaderValue::from_static("nosniff"), + )) + .layer(tower_http::set_header::SetResponseHeaderLayer::overriding( + axum::http::header::REFERRER_POLICY, + HeaderValue::from_static("strict-origin-when-cross-origin"), + )) + .layer(tower_http::limit::RequestBodyLimitLayer::new(1024 * 1024)) // 1MB limit .with_state(state); let port = std::env::var("PORT").unwrap_or_else(|_| "3000".to_string()); @@ -453,6 +467,13 @@ async fn auth_logout(jar: CookieJar) -> impl IntoResponse { (jar, StatusCode::NO_CONTENT) } +async fn auth_session(user: AuthenticatedUser) -> Json { + Json(serde_json::json!({ + "authenticated": true, + "user": user.0 + })) +} + fn secure() -> bool { std::env::var("COOKIE_SECURE") .map(|value| value == "true")