algohub_server/routes/
asset.rs1use rocket::{
2 form::Form,
3 fs::NamedFile,
4 serde::json::Json,
5 tokio::fs::{create_dir_all, File},
6 State,
7};
8use surrealdb::{engine::remote::ws::Client, Surreal};
9
10use crate::{
11 models::{
12 asset::{AssetFile, CreateAsset, UserContent},
13 error::Error,
14 response::{Empty, Response},
15 Credentials,
16 },
17 utils::{asset, session},
18 Result,
19};
20
21#[put("/upload", format = "multipart/form-data", data = "<data>")]
22pub async fn upload(
23 db: &State<Surreal<Client>>,
24 data: Form<CreateAsset<'_>>,
25) -> Result<UserContent> {
26 if !session::verify(db, data.auth.id, data.auth.token).await {
27 return Err(Error::Unauthorized(Json("Invalid credentials".into())));
28 }
29
30 let file_extension = data
31 .file
32 .content_type()
33 .and_then(|ext| ext.extension().map(ToString::to_string))
34 .ok_or_else(|| Error::BadRequest(Json("Invalid file type".into())))?;
35
36 let user_path = std::env::current_dir()
37 .unwrap()
38 .join("content")
39 .join(data.auth.id);
40
41 if !user_path.exists() {
42 create_dir_all(&user_path).await?;
43 }
44 let file_name = format!("{}.{}", uuid::Uuid::new_v4(), file_extension);
45 let file_path = user_path.join(&file_name);
46
47 let mut file = data
48 .file
49 .open()
50 .await
51 .map_err(|e| Error::ServerError(Json(format!("Failed to open file: {}", e).into())))?;
52 let mut output_file = File::create(&file_path)
53 .await
54 .map_err(|e| Error::ServerError(Json(format!("Failed to create file: {}", e).into())))?;
55
56 rocket::tokio::io::copy(&mut file, &mut output_file)
57 .await
58 .map_err(|e| Error::ServerError(Json(format!("Failed to save file: {}", e).into())))?;
59
60 let asset_name = data
61 .file
62 .name()
63 .map(|name| name.to_string())
64 .unwrap_or(uuid::Uuid::new_v4().to_string());
65
66 let asset = asset::create(db, data.owner.clone(), &asset_name, file_path)
67 .await?
68 .ok_or(Error::ServerError(Json("Failed to create asset".into())))?;
69
70 Ok(Json(Response {
71 success: true,
72 message: "Content updated successfully".into(),
73 data: Some(UserContent {
74 id: asset.id.unwrap().id.to_string(),
75 name: asset_name,
76 }),
77 }))
78}
79
80#[get("/<id>")]
81pub async fn get(db: &State<Surreal<Client>>, id: &str) -> Option<AssetFile> {
82 let asset = asset::get_by_id(db, id).await.ok()??;
83
84 Some(AssetFile(NamedFile::open(&asset.path).await.ok()?))
85}
86
87#[delete("/delete/<id>", data = "<auth>")]
88pub async fn delete(
89 db: &State<Surreal<Client>>,
90 id: &str,
91 auth: Json<Credentials<'_>>,
92) -> Result<Empty> {
93 if !session::verify(db, auth.id, auth.token).await {
94 return Err(Error::Unauthorized(Json("Invalid credentials".into())));
95 }
96
97 let asset = asset::get_by_id(db, id)
98 .await?
99 .ok_or(Error::NotFound(Json("Asset not found".into())))?;
100
101 let _ = rocket::tokio::fs::remove_file(&asset.path).await;
102
103 asset::delete(db, id).await?;
104
105 Ok(Json(Response {
106 success: true,
107 message: "Content deleted successfully".into(),
108 data: None,
109 }))
110}
111
112pub fn routes() -> Vec<rocket::Route> {
113 use rocket::routes;
114 routes![upload, get, delete]
115}