fix: stuff

This commit is contained in:
pavel 2026-02-11 00:56:33 +01:00
commit 39143e1998
5 changed files with 69 additions and 12 deletions

View file

@ -13,7 +13,7 @@ reqwest = { version = "0.12", features = ["json"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
axum = "0.7" 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 = { version = "1.1", features = ["sqlx-postgres", "runtime-tokio-rustls", "macros"] }
sea-orm-migration = "1.1" sea-orm-migration = "1.1"
uuid = { version = "1.8", features = ["v4", "serde"] } uuid = { version = "1.8", features = ["v4", "serde"] }

View file

@ -130,7 +130,7 @@ async function fetchTasks() {
const task = state.tasks.find((t) => t.id === state.selectedTaskId); const task = state.tasks.find((t) => t.id === state.selectedTaskId);
if (task) { if (task) {
if (shouldFollowLatest && task.runs && task.runs.length > 0) { 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); renderRunHistory(task);
@ -200,8 +200,8 @@ function selectTask(id, runId = null) {
if (runId) { if (runId) {
state.selectedRunId = runId; state.selectedRunId = runId;
} else if (task.runs && task.runs.length > 0) { } else if (task.runs && task.runs.length > 0) {
// Default to latest run if not specified // Default to latest run if not specified (index 0 is newest)
state.selectedRunId = task.runs[task.runs.length - 1].id; state.selectedRunId = task.runs[0].id;
} else { } else {
state.selectedRunId = null; 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; let isPolling = false;
async function startAutoRefresh() { async function startAutoRefresh() {
setInterval(async () => { setInterval(async () => {
@ -540,11 +554,16 @@ logoutBtn.addEventListener('click', () => {
}); });
async function initializeApp() { async function initializeApp() {
const hasSession = await checkSession();
if (hasSession) {
appEl.classList.remove('hidden'); appEl.classList.remove('hidden');
loginOverlay.classList.add('hidden'); loginOverlay.classList.add('hidden');
await fetchTasks(); await fetchTasks();
startAutoRefresh(); startAutoRefresh();
} else {
showLogin();
}
} }
// Check for callback on load // Check for callback on load

View file

@ -71,8 +71,16 @@ impl Agent {
pub async fn run(&mut self) -> Result<(String, Option<String>), Box<dyn std::error::Error>> { pub async fn run(&mut self) -> Result<(String, Option<String>), Box<dyn std::error::Error>> {
let mut finished = false; let mut finished = false;
let start_time = Instant::now(); let start_time = Instant::now();
let max_duration = Duration::from_secs(120); let max_duration_secs = std::env::var("AGENT_MAX_DURATION_SECS")
let max_turns = 20; .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; let mut turns = 0;
while !finished { while !finished {

View file

@ -100,7 +100,11 @@ impl Scheduler {
tokio::spawn(async move { tokio::spawn(async move {
let (logs, answer, status) = match agent.run().await { let (logs, answer, status) = match agent.run().await {
Ok((logs, answer)) => (logs, answer, "completed".to_string()), 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 { let run_complete = task_run::ActiveModel {
@ -110,7 +114,12 @@ impl Scheduler {
answer: Set(answer), answer: Set(answer),
..Default::default() ..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(()) Ok(())

View file

@ -128,10 +128,24 @@ pub async fn start(db_url: &str) -> Result<(), Box<dyn std::error::Error>> {
.route("/api/tasks/:id", get(get_task).put(update_task)) .route("/api/tasks/:id", get(get_task).put(update_task))
.route("/api/tasks/:id/runs", post(rerun_task)) .route("/api/tasks/:id/runs", post(rerun_task))
.route("/api/runs/recent", get(get_recent_runs)) .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/callback", get(auth_callback))
.route("/api/auth/refresh", post(auth_refresh)) .route("/api/auth/refresh", post(auth_refresh))
.route("/api/auth/logout", post(auth_logout)) .route("/api/auth/logout", post(auth_logout))
.layer(cors) .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); .with_state(state);
let port = std::env::var("PORT").unwrap_or_else(|_| "3000".to_string()); 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) (jar, StatusCode::NO_CONTENT)
} }
async fn auth_session(user: AuthenticatedUser) -> Json<serde_json::Value> {
Json(serde_json::json!({
"authenticated": true,
"user": user.0
}))
}
fn secure() -> bool { fn secure() -> bool {
std::env::var("COOKIE_SECURE") std::env::var("COOKIE_SECURE")
.map(|value| value == "true") .map(|value| value == "true")