algohub_server/routes/
asset.rs

1use 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}