use std::sync::Arc; use axum::extract::{Path, State}; use axum::Json; use crate::api::common::AuthUser; use crate::error::{ApiError, AppState}; use crate::fs; /// GET /api/files/{root_id}/{*path} /// /// List a directory inside one of the user's roots. The wildcard part is the /// path relative to that root (percent-decoded by axum). pub async fn list( State(state): State>, auth: AuthUser, path: Path<(i64, String)>, ) -> Result, ApiError> { let (root_id, req_rel) = path.0; list_inner(state, auth, root_id, req_rel).await } /// GET /api/files/{root_id} — list the root directory itself. pub async fn list_root( State(state): State>, auth: AuthUser, path: Path, ) -> Result, ApiError> { list_inner(state, auth, path.0, String::new()).await } async fn list_inner( state: Arc, auth: AuthUser, root_id: i64, req_rel: String, ) -> Result, ApiError> { let root = auth .roots .iter() .find(|r| r.id == root_id) .ok_or_else(|| { crate::error::ApiError::new( axum::http::StatusCode::FORBIDDEN, "no such folder", ) })?; let server_root = state.root.clone(); let root_rel = root.path.clone(); let full = tokio::task::spawn_blocking(move || fs::resolve_path(&server_root, &root_rel, &req_rel)) .await .map_err(|_| ApiError::new(axum::http::StatusCode::INTERNAL_SERVER_ERROR, "internal error"))??; let entries = tokio::task::spawn_blocking(move || fs::list_dir(&full)) .await .map_err(|_| ApiError::new(axum::http::StatusCode::INTERNAL_SERVER_ERROR, "internal error"))??; Ok(Json(serde_json::json!({ "entries": entries }))) }