Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds ability to upload zipped server as instance #406

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 170 additions & 12 deletions core/src/handlers/global_fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

use axum::{
body::{Bytes, StreamBody},
extract::{Multipart, Path},
extract::{DefaultBodyLimit, Multipart, Path},
http,
routing::{delete, get, put},
Json, Router,
Expand Down Expand Up @@ -103,6 +103,12 @@
}
}

#[derive(Deserialize)]
struct UploadFilePathParams {
base64_absolute_path: String,

Check warning on line 108 in core/src/handlers/global_fs.rs

View workflow job for this annotation

GitHub Actions / clippy

fields `base64_absolute_path` and `overwrite` are never read

warning: fields `base64_absolute_path` and `overwrite` are never read --> core/src/handlers/global_fs.rs:108:5 | 107 | struct UploadFilePathParams { | -------------------- fields in this struct 108 | base64_absolute_path: String, | ^^^^^^^^^^^^^^^^^^^^ 109 | overwrite: Option<String>, | ^^^^^^^^^
overwrite: Option<String>,
}

async fn list_files(
axum::extract::State(state): axum::extract::State<AppState>,
Path(base64_absolute_path): Path<String>,
Expand All @@ -119,7 +125,10 @@
source: eyre!("Token error"),
})?;

requester.try_action(&UserAction::ReadGlobalFile, state.global_settings.lock().await.safe_mode())?;
requester.try_action(
&UserAction::ReadGlobalFile,
state.global_settings.lock().await.safe_mode(),
)?;

let path = PathBuf::from(absolute_path);
let caused_by = CausedBy::User {
Expand Down Expand Up @@ -158,7 +167,10 @@
kind: ErrorKind::Unauthorized,
source: eyre!("Token error"),
})?;
requester.try_action(&UserAction::ReadGlobalFile, state.global_settings.lock().await.safe_mode())?;
requester.try_action(
&UserAction::ReadGlobalFile,
state.global_settings.lock().await.safe_mode(),
)?;

let path = PathBuf::from(absolute_path);
let ret = tokio::fs::read_to_string(&path).await.context(
Expand All @@ -178,6 +190,19 @@
Ok(ret)
}

async fn get_tmp_path(
axum::extract::State(state): axum::extract::State<AppState>,

Check warning on line 194 in core/src/handlers/global_fs.rs

View workflow job for this annotation

GitHub Actions / clippy

unused variable: `state`

warning: unused variable: `state` --> core/src/handlers/global_fs.rs:194:26 | 194 | axum::extract::State(state): axum::extract::State<AppState>, | ^^^^^ help: if this is intentional, prefix it with an underscore: `_state` | = note: `#[warn(unused_variables)]` on by default
AuthBearer(token): AuthBearer,

Check warning on line 195 in core/src/handlers/global_fs.rs

View workflow job for this annotation

GitHub Actions / clippy

unused variable: `token`

warning: unused variable: `token` --> core/src/handlers/global_fs.rs:195:16 | 195 | AuthBearer(token): AuthBearer, | ^^^^^ help: if this is intentional, prefix it with an underscore: `_token`
) -> Result<Json<String>, Error> {
let tmp_path = path_to_tmp()
.clone()
.into_os_string()
.into_string()
.expect("Failed to get path to tmp directory");

Ok(Json(tmp_path))
}

async fn write_file(
axum::extract::State(state): axum::extract::State<AppState>,
Path(base64_absolute_path): Path<String>,
Expand All @@ -195,7 +220,10 @@
kind: ErrorKind::Unauthorized,
source: eyre!("Token error"),
})?;
requester.try_action(&UserAction::WriteGlobalFile, state.global_settings.lock().await.safe_mode())?;
requester.try_action(
&UserAction::WriteGlobalFile,
state.global_settings.lock().await.safe_mode(),
)?;

let path = PathBuf::from(absolute_path);

Expand Down Expand Up @@ -231,7 +259,10 @@
kind: ErrorKind::Unauthorized,
source: eyre!("Token error"),
})?;
requester.try_action(&UserAction::WriteGlobalFile, state.global_settings.lock().await.safe_mode())?;
requester.try_action(
&UserAction::WriteGlobalFile,
state.global_settings.lock().await.safe_mode(),
)?;

let path = PathBuf::from(absolute_path);
tokio::fs::create_dir(&path).await.context(format!(
Expand Down Expand Up @@ -271,7 +302,10 @@
source: eyre!("Token error"),
})?;

requester.try_action(&UserAction::WriteGlobalFile, state.global_settings.lock().await.safe_mode())?;
requester.try_action(
&UserAction::WriteGlobalFile,
state.global_settings.lock().await.safe_mode(),
)?;

crate::util::fs::rename(&path_source, &path_dest).await?;

Expand Down Expand Up @@ -306,7 +340,10 @@
kind: ErrorKind::Unauthorized,
source: eyre!("Token error"),
})?;
requester.try_action(&UserAction::WriteGlobalFile, state.global_settings.lock().await.safe_mode())?;
requester.try_action(
&UserAction::WriteGlobalFile,
state.global_settings.lock().await.safe_mode(),
)?;

let path = PathBuf::from(absolute_path);

Expand Down Expand Up @@ -341,7 +378,10 @@
kind: ErrorKind::Unauthorized,
source: eyre!("Token error"),
})?;
requester.try_action(&UserAction::WriteGlobalFile, state.global_settings.lock().await.safe_mode())?;
requester.try_action(
&UserAction::WriteGlobalFile,
state.global_settings.lock().await.safe_mode(),
)?;

let path = PathBuf::from(absolute_path);

Expand Down Expand Up @@ -377,7 +417,10 @@
kind: ErrorKind::Unauthorized,
source: eyre!("Token error"),
})?;
requester.try_action(&UserAction::WriteGlobalFile, state.global_settings.lock().await.safe_mode())?;
requester.try_action(
&UserAction::WriteGlobalFile,
state.global_settings.lock().await.safe_mode(),
)?;

let path = PathBuf::from(absolute_path);

Expand Down Expand Up @@ -413,7 +456,10 @@
kind: ErrorKind::Unauthorized,
source: eyre!("Token error"),
})?;
requester.try_action(&UserAction::ReadGlobalFile, state.global_settings.lock().await.safe_mode())?;
requester.try_action(
&UserAction::ReadGlobalFile,
state.global_settings.lock().await.safe_mode(),
)?;
let path = PathBuf::from(absolute_path);
let downloadable_file_path: PathBuf;
let downloadable_file = if fs::metadata(path.clone()).unwrap().is_dir() {
Expand Down Expand Up @@ -450,6 +496,111 @@
Ok(key)
}

async fn upload_tmp_file(
axum::extract::State(state): axum::extract::State<AppState>,
headers: HeaderMap,
AuthBearer(token): AuthBearer,
mut multipart: Multipart,
) -> Result<Json<()>, Error> {
let requester = state
.users_manager
.read()
.await
.try_auth(&token)
.ok_or_else(|| Error {
kind: ErrorKind::Unauthorized,
source: eyre!("Token error"),
})?;

requester.try_action(
&UserAction::WriteGlobalFile,
state.global_settings.lock().await.safe_mode(),
)?;

let tmp_path = path_to_tmp().clone();

let total = headers
.get(CONTENT_LENGTH)
.and_then(|v| v.to_str().ok())
.and_then(|v| v.parse::<f64>().ok());

let (progression_start_event, event_id) = Event::new_progression_event_start(
"Uploading file(s)",
total,
None,
CausedBy::User {
user_id: requester.uid.clone(),
user_name: requester.username.clone(),
},
);
state.event_broadcaster.send(progression_start_event);

while let Ok(Some(mut field)) = multipart.next_field().await {
let name = field
.file_name()
.ok_or_else(|| Error {
kind: ErrorKind::BadRequest,
source: eyre!("Missing file name"),
})?
.to_owned();
let path = tmp_path.join(&name);
let mut file = tokio::fs::File::create(&path)
.await
.context(format!("Failed to create file {}", path.display()))?;

while let Some(chunk) = match field.chunk().await {
Ok(v) => v,
Err(e) => {
tokio::fs::remove_file(&path).await.ok();
state
.event_broadcaster
.send(Event::new_progression_event_end(
event_id,
false,
Some(&e.to_string()),
None,
));
return Err(Error {
kind: ErrorKind::BadRequest,
source: eyre!("Failed to read chunk: {}", e),
});
}
} {
state
.event_broadcaster
.send(Event::new_progression_event_update(
&event_id,
format!("Uploading {name}"),
chunk.len() as f64,
));
file.write_all(&chunk).await.map_err(|_| {
std::fs::remove_file(&path).ok();
eyre!("Failed to write chunk")
})?;
}

let caused_by = CausedBy::User {
user_id: requester.uid.clone(),
user_name: requester.username.clone(),
};
state.event_broadcaster.send(new_fs_event(
FSOperation::Upload,
FSTarget::File(path),
caused_by,
));
}
state
.event_broadcaster
.send(Event::new_progression_event_end(
event_id,
true,
Some("Upload complete: "),
None,
));

Ok(Json(()))
}

async fn upload_file(
axum::extract::State(state): axum::extract::State<AppState>,
Path(base64_absolute_path): Path<String>,
Expand All @@ -468,7 +619,10 @@
source: eyre!("Token error"),
})?;

requester.try_action(&UserAction::WriteGlobalFile, state.global_settings.lock().await.safe_mode())?;
requester.try_action(
&UserAction::WriteGlobalFile,
state.global_settings.lock().await.safe_mode(),
)?;

let path_to_dir = PathBuf::from(absolute_path);

Expand Down Expand Up @@ -574,7 +728,7 @@
.send(Event::new_progression_event_end(
event_id,
true,
Some("Upload complete"),
Some("Upload complete: "),
None,
));

Expand Down Expand Up @@ -651,6 +805,10 @@
.route("/fs/:base64_absolute_path/new", put(new_file))
.route("/fs/:base64_absolute_path/download", get(download_file))
.route("/fs/:base64_absolute_path/upload", put(upload_file))
.layer(DefaultBodyLimit::disable())
.route("/fs/upload_tmp", put(upload_tmp_file))
.layer(DefaultBodyLimit::disable())
.route("/fs/tmp_path", get(get_tmp_path))
.route("/file/:key", get(download))
.with_state(state)
}
Loading
Loading