๐Ÿ“๐Ÿ–ผ๏ธ๐Ÿน A small thing where I can upload a file and get a link back. https://media.strawmelonjuice.com/
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

add /api/files endpoint to rust server. still need to implement the file size checks

ollie 9d417669 6cef33af

+222 -8
+26
.sqlx/query-04c6e15fe956565cc574b5fd9250fce7c3bc9289c542918b8d445333e52436d8.json
··· 1 + { 2 + "db_name": "SQLite", 3 + "query": "SELECT id as 'id: Uuid', filename FROM file_mapping WHERE owner_uid = ?1", 4 + "describe": { 5 + "columns": [ 6 + { 7 + "name": "id: Uuid", 8 + "ordinal": 0, 9 + "type_info": "Blob" 10 + }, 11 + { 12 + "name": "filename", 13 + "ordinal": 1, 14 + "type_info": "Text" 15 + } 16 + ], 17 + "parameters": { 18 + "Right": 1 19 + }, 20 + "nullable": [ 21 + false, 22 + false 23 + ] 24 + }, 25 + "hash": "04c6e15fe956565cc574b5fd9250fce7c3bc9289c542918b8d445333e52436d8" 26 + }
+32
.sqlx/query-55cdc86205af3f03b3cff9dbc25158f02c53dd14f9ad98f00102a2d715e202f8.json
··· 1 + { 2 + "db_name": "SQLite", 3 + "query": "SELECT id as 'id: Uuid', filename, username FROM file_mapping LEFT JOIN users ON users.uid = file_mapping.owner_uid", 4 + "describe": { 5 + "columns": [ 6 + { 7 + "name": "id: Uuid", 8 + "ordinal": 0, 9 + "type_info": "Blob" 10 + }, 11 + { 12 + "name": "filename", 13 + "ordinal": 1, 14 + "type_info": "Text" 15 + }, 16 + { 17 + "name": "username", 18 + "ordinal": 2, 19 + "type_info": "Text" 20 + } 21 + ], 22 + "parameters": { 23 + "Right": 0 24 + }, 25 + "nullable": [ 26 + false, 27 + false, 28 + true 29 + ] 30 + }, 31 + "hash": "55cdc86205af3f03b3cff9dbc25158f02c53dd14f9ad98f00102a2d715e202f8" 32 + }
+38
.sqlx/query-ace5da0bc6eabaf15af75d667a6bb32110d8e17f65a41d87d63ab3138788a889.json
··· 1 + { 2 + "db_name": "SQLite", 3 + "query": "SELECT username, max_megabytes, used_megabytes, is_admin FROM users WHERE uid = ?1", 4 + "describe": { 5 + "columns": [ 6 + { 7 + "name": "username", 8 + "ordinal": 0, 9 + "type_info": "Text" 10 + }, 11 + { 12 + "name": "max_megabytes", 13 + "ordinal": 1, 14 + "type_info": "Integer" 15 + }, 16 + { 17 + "name": "used_megabytes", 18 + "ordinal": 2, 19 + "type_info": "Float" 20 + }, 21 + { 22 + "name": "is_admin", 23 + "ordinal": 3, 24 + "type_info": "Bool" 25 + } 26 + ], 27 + "parameters": { 28 + "Right": 1 29 + }, 30 + "nullable": [ 31 + false, 32 + false, 33 + false, 34 + false 35 + ] 36 + }, 37 + "hash": "ace5da0bc6eabaf15af75d667a6bb32110d8e17f65a41d87d63ab3138788a889" 38 + }
+3 -3
README.md
··· 22 22 ### โŒ **Missing from Rust:** 23 23 24 24 1. **`/api/files` - File Listing** 25 - - Lists all files for authenticated users 26 - - Admins see all files, regular users see only their own 27 - - Returns file sizes in MB 25 + - [x] Lists all files for authenticated users 26 + - [x] Admins see all files, regular users see only their own 27 + - [ ] Returns file sizes in MB 28 28 29 29 2. **`/api/user/fetch` - User Info Retrieval** 30 30 - Returns current user details (username, quotas, admin status)
+1 -2
server-rs/src/app.rs
··· 104 104 )?; 105 105 } 106 106 { 107 - // Always create the strawmelonjuice logo as id 9962109f-2d98-4c51-a8ac-8f71d45a41ab. 108 - 107 + // Always create the strawmelonjuice logo as id 9962109f-2d98-4c51-a8ac-8f71d45a41ab. 109 108 } 110 109 }; 111 110
+3
server-rs/src/main.rs
··· 42 42 routes::api::destroy_session, 43 43 // /api/upload -> upload file using simple upload method (base64 in body) 44 44 routes::api::upload_simple, 45 + // /api/files -> lists all files for authenticated users. 46 + // (admins see all, users just their own) 47 + routes::api::list_files, 45 48 ], 46 49 ) 47 50 .manage(app)
+119 -3
server-rs/src/routes/api.rs
··· 1 - use crate::App; 1 + use crate::{App, Database, DbError}; 2 2 use rocket::State; 3 3 use rocket::http::Status; 4 4 use rocket::response::content::RawJson; 5 5 use rocket::serde::json::Json; 6 - use serde::Deserialize; 6 + use serde::{Deserialize, Serialize}; 7 7 use sqlx::query; 8 8 use uuid::Uuid; 9 9 ··· 52 52 .fetch_one(&app.db) 53 53 .await 54 54 .map_err(|_| (Status::Unauthorized, "Invalid username or password"))?; 55 - let (user_uuid, stored_hash): (Uuid, String) = { (user_credentials.uid, user_credentials.password_hash) }; 55 + let (user_uuid, stored_hash): (Uuid, String) = 56 + { (user_credentials.uid, user_credentials.password_hash) }; 56 57 57 58 if bcrypt::verify(password.clone(), &stored_hash) 58 59 .map_err(|_| (Status::InternalServerError, "Internal Server Error"))? ··· 159 160 }, 160 161 } 161 162 } 163 + 164 + #[post("/files", data = "<single_identify>")] 165 + pub(crate) async fn list_files( 166 + single_identify: Json<SingleIdentify>, 167 + app: &State<App>, 168 + ) -> Result<Json<Vec<File>>, (Status, &'static str)> { 169 + // Validate the session 170 + let sid = match Uuid::parse_str(&single_identify.session) { 171 + Ok(v) => v, 172 + Err(_) => return Err((Status::BadRequest, "Invalid session ID format.")), 173 + }; 174 + let user_id = match app.validate_session(sid).await { 175 + Ok(v) => v, 176 + Err(_) => return Err((Status::Unauthorized, "Invalid session token.")), 177 + }; 178 + 179 + // get the user data from the id 180 + let user = match user_from_id(&app.db, user_id).await { 181 + Ok(v) => v, 182 + Err(_) => return Err((Status::NotFound, "Couldn't find the user for session id")), 183 + }; 184 + 185 + // get the relevant files for this user 186 + let files = match files_for_user(&app.db, user).await { 187 + Ok(v) => v, 188 + Err(_) => { 189 + return Err(( 190 + Status::InternalServerError, 191 + "Failed to fetch files for user", 192 + )); 193 + } 194 + }; 195 + 196 + // i hope this is how thats done o-o 197 + return Ok(Json(files)); 198 + } 199 + 200 + /// Get files for a given user. 201 + /// Admin -> All files 202 + /// Non-Admin -> Their own files 203 + async fn files_for_user(conn: &Database, user: User) -> Result<Vec<File>, DbError> { 204 + if user.is_admin { 205 + let files = query!( 206 + "SELECT id as 'id: Uuid', filename, username FROM file_mapping LEFT JOIN users ON users.uid = file_mapping.owner_uid" 207 + ) 208 + .fetch_all(conn) 209 + .await?; 210 + 211 + let files: Vec<File> = files 212 + .iter() 213 + .map(|file| File { 214 + id: file.id, 215 + filename: file.filename.clone(), 216 + owner: file.username.clone(), 217 + size_mb: 0, 218 + }) 219 + .collect(); 220 + 221 + return Ok(files); 222 + } 223 + 224 + let files = query!( 225 + "SELECT id as 'id: Uuid', filename FROM file_mapping WHERE owner_uid = ?1", 226 + user.id 227 + ) 228 + .fetch_all(conn) 229 + .await?; 230 + 231 + let files: Vec<File> = files 232 + .iter() 233 + .map(|file| File { 234 + id: file.id, 235 + filename: file.filename.clone(), 236 + owner: Some(user.username.clone()), 237 + size_mb: 0, 238 + }) 239 + .collect(); 240 + 241 + return Ok(files); 242 + } 243 + 244 + /// Gets a User from the database using it's uuid 245 + async fn user_from_id(conn: &Database, user_id: Uuid) -> Result<User, DbError> { 246 + let user = query!( 247 + "SELECT username, max_megabytes, used_megabytes, is_admin FROM users WHERE uid = ?1", 248 + user_id 249 + ) 250 + .fetch_one(conn) 251 + .await?; 252 + 253 + return Ok(User { 254 + id: user_id, 255 + username: user.username, 256 + max_megabytes: user.max_megabytes, 257 + user_megabytes: user.used_megabytes, 258 + is_admin: user.is_admin, 259 + }); 260 + } 261 + 262 + struct User { 263 + id: Uuid, 264 + username: String, 265 + max_megabytes: i64, 266 + user_megabytes: f64, 267 + is_admin: bool, 268 + } 269 + 270 + #[derive(Serialize)] 271 + #[serde(crate = "rocket::serde")] 272 + pub(crate) struct File { 273 + id: Uuid, 274 + owner: Option<String>, 275 + filename: String, 276 + size_mb: i32, 277 + }