-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
136 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,3 +36,4 @@ yarn-error.log* | |
next-env.d.ts | ||
|
||
local | ||
uploads |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,82 +1,102 @@ | ||
use crate::app_state::AppState; | ||
use crate::auth::AuthUser; | ||
use crate::core::{Layer, Workspace, WorkspaceRole}; | ||
use crate::data::Database; | ||
use axum::{extract::Multipart, extract::State, http::StatusCode, response::IntoResponse}; | ||
use crate::{app_state::AppState, core::CreateLayer}; | ||
use axum::{ | ||
extract::{Extension, Multipart, State}, | ||
http::StatusCode, | ||
response::IntoResponse, | ||
Json, | ||
}; | ||
use duckdb_postgis::duckdb_load::launch_process_file; | ||
use std::path::Path; | ||
use std::sync::Arc; | ||
use tokio::{fs::File, io::AsyncWriteExt}; | ||
use uuid::Uuid; | ||
use tokio::{ | ||
fs::{self, File}, | ||
io::AsyncWriteExt, | ||
}; | ||
|
||
pub async fn upload_layer<D: Database>( | ||
State(state): State<Arc<AppState<D>>>, | ||
Extension(auth_user): Extension<AuthUser>, | ||
mut multipart: Multipart, | ||
) -> impl IntoResponse { | ||
const MAX_FILE_SIZE: usize = 1024 * 1024 * 100; // 100 MB limit | ||
let upload_dir = "uploads"; | ||
) -> Result<impl IntoResponse, StatusCode> { | ||
let mut file_data = Vec::new(); | ||
let mut layer_info: Option<CreateLayer> = None; | ||
|
||
// Ensure the upload directory exists | ||
if let Err(err) = tokio::fs::create_dir_all(upload_dir).await { | ||
return ( | ||
StatusCode::INTERNAL_SERVER_ERROR, | ||
format!("Failed to create upload directory: {}", err), | ||
) | ||
.into_response(); | ||
while let Some(field) = multipart | ||
.next_field() | ||
.await | ||
.map_err(|_| StatusCode::BAD_REQUEST)? | ||
{ | ||
let name = field.name().ok_or(StatusCode::BAD_REQUEST)?.to_string(); | ||
match name.as_str() { | ||
"file" => { | ||
println!("FILE"); | ||
file_data = field | ||
.bytes() | ||
.await | ||
.map_err(|_| StatusCode::BAD_REQUEST)? | ||
.to_vec(); | ||
println!("File data size: {} bytes", file_data.len()); | ||
} | ||
"layer_info" => { | ||
let json_str = String::from_utf8( | ||
field | ||
.bytes() | ||
.await | ||
.map_err(|_| StatusCode::BAD_REQUEST)? | ||
.to_vec(), | ||
) | ||
.map_err(|_| StatusCode::BAD_REQUEST)?; | ||
layer_info = | ||
Some(serde_json::from_str(&json_str).map_err(|_| StatusCode::BAD_REQUEST)?); | ||
} | ||
_ => {} | ||
} | ||
} | ||
|
||
while let Some(mut field) = multipart.next_field().await.unwrap_or(None) { | ||
let file_name = match field.file_name() { | ||
Some(name) => name.to_string(), | ||
None => { | ||
return (StatusCode::BAD_REQUEST, "File name is required".to_string()) | ||
.into_response() | ||
} | ||
}; | ||
let layer_info = layer_info.ok_or(StatusCode::BAD_REQUEST)?; | ||
let layer = Layer::from_req(layer_info, auth_user.user.clone().unwrap()); | ||
let workspace = Workspace::from_id(state.app_data.clone(), &layer.workspace_id) | ||
.await | ||
.unwrap(); | ||
let member = workspace | ||
.get_member(state.app_data.clone(), auth_user.user.unwrap()) | ||
.await | ||
.unwrap(); | ||
|
||
let file_uuid = Uuid::new_v4(); | ||
let file_extension = Path::new(&file_name) | ||
.extension() | ||
.and_then(|ext| ext.to_str()) | ||
.unwrap_or("bin"); | ||
let unique_filename = format!("{}.{}", file_uuid, file_extension); | ||
let file_path = Path::new(upload_dir).join(&unique_filename); | ||
// Check requesting user workspace permissions | ||
if member.role == WorkspaceRole::Read { | ||
return Ok((StatusCode::FORBIDDEN, format!(""))); | ||
} | ||
|
||
let mut file = match File::create(&file_path).await { | ||
Ok(file) => file, | ||
Err(err) => { | ||
return ( | ||
StatusCode::INTERNAL_SERVER_ERROR, | ||
format!("Failed to create file: {}", err), | ||
) | ||
.into_response() | ||
} | ||
}; | ||
// Save the file data locally | ||
let file_name = format!("{}", layer.id); | ||
let dir_path = Path::new("uploads"); | ||
let file_path = dir_path.join(&file_name); | ||
|
||
let mut total_bytes = 0; | ||
fs::create_dir_all(dir_path) | ||
.await | ||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; | ||
|
||
while let Some(chunk) = field.chunk().await.unwrap_or(None) { | ||
total_bytes += chunk.len(); | ||
if total_bytes > MAX_FILE_SIZE { | ||
return (StatusCode::BAD_REQUEST, "File too large".to_string()).into_response(); | ||
} | ||
let mut file = File::create(&file_path) | ||
.await | ||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; | ||
|
||
if let Err(err) = file.write_all(&chunk).await { | ||
return ( | ||
StatusCode::INTERNAL_SERVER_ERROR, | ||
format!("Failed to write to file: {}", err), | ||
) | ||
.into_response(); | ||
} | ||
} | ||
file.write_all(&file_data) | ||
.await | ||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; | ||
|
||
// Here you might want to add the file information to your database | ||
// state.db.add_file_record(unique_filename, total_bytes).await?; | ||
let postgis_uri = "postgresql://admin:password@localhost:5432/gridwalk"; | ||
let _processed_file = launch_process_file(file_path.to_str().unwrap(), &layer.id, postgis_uri); | ||
println!("Uploaded to POSTGIS!"); | ||
|
||
return ( | ||
StatusCode::OK, | ||
format!("File uploaded successfully: {}", unique_filename), | ||
) | ||
.into_response(); | ||
match layer.clone().write_record(state.app_data.clone()).await { | ||
Ok(_) => { | ||
let json_response = serde_json::to_value(&layer).unwrap(); | ||
Ok((StatusCode::OK, Json(json_response).to_string())) | ||
} | ||
Err(_) => Ok((StatusCode::INTERNAL_SERVER_ERROR, "".to_string())), | ||
} | ||
|
||
(StatusCode::BAD_REQUEST, "No file provided".to_string()).into_response() | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"type": "FeatureCollection", | ||
"features": [ | ||
{ | ||
"type": "Feature", | ||
"properties": { | ||
"name": "Center of London", | ||
"description": "A point representing the approximate center of London" | ||
}, | ||
"geometry": { | ||
"type": "Point", | ||
"coordinates": [-0.1276, 51.5074] | ||
} | ||
} | ||
] | ||
} |