files.rs
⎇
Raw
1use std::sync::Arc;
2
3use axum::extract::{Path, State};
4use axum::Json;
5
6use crate::api::common::AuthUser;
7use crate::error::{ApiError, AppState};
8use crate::fs;
9
10/// GET /api/files/{root_id}/{*path}
11///
12/// List a directory inside one of the user's roots. The wildcard part is the
13/// path relative to that root (percent-decoded by axum).
14pub async fn list(
15 State(state): State<Arc<AppState>>,
16 auth: AuthUser,
17 path: Path<(i64, String)>,
18) -> Result<Json<serde_json::Value>, ApiError> {
19 let (root_id, req_rel) = path.0;
20 list_inner(state, auth, root_id, req_rel).await
21}
22
23/// GET /api/files/{root_id} — list the root directory itself.
24pub async fn list_root(
25 State(state): State<Arc<AppState>>,
26 auth: AuthUser,
27 path: Path<i64>,
28) -> Result<Json<serde_json::Value>, ApiError> {
29 list_inner(state, auth, path.0, String::new()).await
30}
31
32async fn list_inner(
33 state: Arc<AppState>,
34 auth: AuthUser,
35 root_id: i64,
36 req_rel: String,
37) -> Result<Json<serde_json::Value>, ApiError> {
38 let root = auth
39 .roots
40 .iter()
41 .find(|r| r.id == root_id)
42 .ok_or_else(|| {
43 crate::error::ApiError::new(
44 axum::http::StatusCode::FORBIDDEN,
45 "no such folder",
46 )
47 })?;
48
49 let server_root = state.root.clone();
50 let root_rel = root.path.clone();
51 let full = tokio::task::spawn_blocking(move || fs::resolve_path(&server_root, &root_rel, &req_rel))
52 .await
53 .map_err(|_| ApiError::new(axum::http::StatusCode::INTERNAL_SERVER_ERROR, "internal error"))??;
54
55 let entries = tokio::task::spawn_blocking(move || fs::list_dir(&full))
56 .await
57 .map_err(|_| ApiError::new(axum::http::StatusCode::INTERNAL_SERVER_ERROR, "internal error"))??;
58
59 Ok(Json(serde_json::json!({ "entries": entries })))
60}
61