1use std::fs;
2use std::path::PathBuf;
3use std::str::FromStr;
4use rocket::{get, http::Status, post};
5use rocket::data::Data;
6use rocket::http::ContentType;
7use rocket_multipart_form_data::{MultipartFormData, MultipartFormDataField, MultipartFormDataOptions};
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug,Serialize,Deserialize)]
11pub struct DeployPermissions {
12 max_file_count: u64
13}
14impl Default for DeployPermissions {
15 fn default() -> Self {
16 Self { max_file_count: 4500 }
17 }
18}
19#[get("/deploy/permissions")]
20pub fn deploy_permissions() -> Result<rocket::serde::json::Json<DeployPermissions>,Status> {
21
22 Ok(rocket::serde::json::Json(DeployPermissions::default()))
23}
24
25#[post("/app/<app_id>/build", data = "<data>")]
26pub async fn build<'a>(app_id: String, content_type: &ContentType, data: Data<'a>) -> Result<Status,Status> {
27 println!("Starting deploy handler");
28 println!("Content-Type: {:?}", content_type);
29 println!("Build started for app: {:#?}", app_id);
30
31 let mut options = MultipartFormDataOptions::new();
32
33 options
35 .allowed_fields
36 .push(MultipartFormDataField::file("media").size_limit(5 * 1024 * 1024 * 1024));
37 options
38 .allowed_fields
39 .push(MultipartFormDataField::file("file").size_limit(5 * 1024 * 1024 * 1024));
40 options
41 .allowed_fields
42 .push(MultipartFormDataField::file("upload").size_limit(5 * 1024 * 1024 * 1024));
43
44 let form_data = match MultipartFormData::parse(content_type, data, options).await {
46 Ok(form) => {
47 println!("Successfully parsed form data");
48 form
49 }
50 Err(e) => {
51 println!("Error parsing form data: {:?}", e);
52 return Err(Status::new(400))
53 }
54 };
55
56 println!("Available fields in form_data:");
58 println!("Raw fields: {:#?}", form_data.raw);
59 println!("Text fields: {:#?}", form_data.texts);
60 println!("Files: {:#?}", form_data.files);
61
62 for field_name in ["media", "file", "upload"] {
64 if let Some(files) = form_data.files.get(field_name) {
65 println!("Found files in field '{}': {:?}", field_name, files);
66
67 if let Some(file) = files.first() {
68 println!("Processing file:");
69 println!(" Path: {:?}", file.path);
70 println!(" Filename: {:?}", file.file_name);
71 println!(" Content-Type: {:?}", file.content_type);
72
73 match fs::create_dir_all("./App") {
75 Ok(_) => {
76 let dir = std::path::PathBuf::from_str("./App").unwrap();
77 let canon_dir = dir.canonicalize().unwrap();
78 log::info!("Created Directory at {}",canon_dir.display())
79 },
80 Err(_) => {
81 return Err::<Status,Status>(Status::new(500));
82 },
83 }
84
85
86 let source_size = fs::metadata(&file.path)
88 .map_err(|_| return Err::<Status,Status>(Status::new(500))).unwrap()
89 .len();
90
91 println!("Source file size: {} bytes", source_size);
92
93 match fs::copy(&file.path, "./App/app.tar.gz") {
94 Ok(bytes_written) => {
95 println!("Successfully wrote {} bytes", bytes_written);
96 if bytes_written == source_size {
97 let file_path = PathBuf::from_str("./App/app.tar.gz")
98 .expect("Failed to get app zip");
99 let tar_gz = fs::File::open(&file_path).expect("Failed to open tar");
100 let tar = flate2::read::GzDecoder::new(tar_gz);
101 let mut archive = tar::Archive::new(tar);
102
103 archive.unpack("./App").unwrap();
104
105 fs::remove_file(&file_path).expect("Fail");
107 return Ok(Status::new(200));
108
109 } else {
110 return Err(Status::new(500))
111 }
112 }
113 Err(e) => {
114 println!("Error copying file: {:?}", e);
115 return Err(Status::new(500))
116 }
117 }
118 } else {
119 println!("No valid file found in request");
120 return Err(Status::new(500))
121 }
122 }
123 }
124 return Ok::<Status,Status>(Status::new(200));
125}