use axum::{
extract::{Path, State},
http::StatusCode,
middleware,
routing::{get, post},
Extension, Json, Router,
};
use serde::Deserialize;
use serde_json::{json, Value};
use dragoon_proto::models::{Task, TaskKind, TaskLimits};
use crate::{
app::{signed_request, username_for, AppState, SignedSession},
audit, tasks_repo, workers_repo,
};
pub fn router(state: AppState) -> Router {
Router::new()
.route("/v1/workers", get(list_workers))
.route("/v1/workers/:name/fetch", post(fetch))
.layer(middleware::from_fn_with_state(state.clone(), signed_request))
.with_state(state)
}
async fn list_workers(
State(state): State<AppState>,
Extension(_sess): Extension<SignedSession>,
) -> Result<Json<Vec<Value>>, StatusCode> {
let conn = state.conn.lock().unwrap();
let rows = workers_repo::list_all(&conn).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(Json(
rows.into_iter()
.map(|w| {
json!({
"name": w.name,
"status": w.status,
"current_pwd": w.current_pwd,
"current_task_id": w.current_task_id,
"last_poll_at": w.last_poll_at,
})
})
.collect(),
))
}
#[derive(Debug, Deserialize)]
struct FetchBody {
path: String,
glob: String,
}
async fn fetch(
State(state): State<AppState>,
Path(name): Path<String>,
Extension(sess): Extension<SignedSession>,
Json(body): Json<FetchBody>,
) -> Result<Json<Task>, StatusCode> {
let conn = state.conn.lock().unwrap();
if workers_repo::lookup_by_name(&conn, &name)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
.is_none()
{
return Err(StatusCode::NOT_FOUND);
}
let id = tasks_repo::new_task_id();
let username = username_for(&conn, sess.0.user_id);
let task = tasks_repo::insert_task(
&conn,
&id,
&name,
&username,
TaskKind::Fetch,
"",
&[body.glob.clone()],
&TaskLimits::default(),
Some(&body.path),
)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let _ = audit::log(
&conn,
Some(&username),
"fetch",
Some(&id),
Some(&sess.0.fingerprint),
&json!({"worker": name, "path": body.path, "glob": body.glob}),
);
Ok(Json(task))
}