···2222### โ **Missing from Rust:**
232324241. **`/api/files` - File Listing**
2525- - Lists all files for authenticated users
2626- - Admins see all files, regular users see only their own
2727- - Returns file sizes in MB
2525+ - [x] Lists all files for authenticated users
2626+ - [x] Admins see all files, regular users see only their own
2727+ - [ ] Returns file sizes in MB
282829292. **`/api/user/fetch` - User Info Retrieval**
3030 - Returns current user details (username, quotas, admin status)
+1-2
server-rs/src/app.rs
···104104 )?;
105105 }
106106 {
107107- // Always create the strawmelonjuice logo as id 9962109f-2d98-4c51-a8ac-8f71d45a41ab.
108108-107107+ // Always create the strawmelonjuice logo as id 9962109f-2d98-4c51-a8ac-8f71d45a41ab.
109108 }
110109 };
111110
+3
server-rs/src/main.rs
···4242 routes::api::destroy_session,
4343 // /api/upload -> upload file using simple upload method (base64 in body)
4444 routes::api::upload_simple,
4545+ // /api/files -> lists all files for authenticated users.
4646+ // (admins see all, users just their own)
4747+ routes::api::list_files,
4548 ],
4649 )
4750 .manage(app)
+119-3
server-rs/src/routes/api.rs
···11-use crate::App;
11+use crate::{App, Database, DbError};
22use rocket::State;
33use rocket::http::Status;
44use rocket::response::content::RawJson;
55use rocket::serde::json::Json;
66-use serde::Deserialize;
66+use serde::{Deserialize, Serialize};
77use sqlx::query;
88use uuid::Uuid;
99···5252 .fetch_one(&app.db)
5353 .await
5454 .map_err(|_| (Status::Unauthorized, "Invalid username or password"))?;
5555- let (user_uuid, stored_hash): (Uuid, String) = { (user_credentials.uid, user_credentials.password_hash) };
5555+ let (user_uuid, stored_hash): (Uuid, String) =
5656+ { (user_credentials.uid, user_credentials.password_hash) };
56575758 if bcrypt::verify(password.clone(), &stored_hash)
5859 .map_err(|_| (Status::InternalServerError, "Internal Server Error"))?
···159160 },
160161 }
161162}
163163+164164+#[post("/files", data = "<single_identify>")]
165165+pub(crate) async fn list_files(
166166+ single_identify: Json<SingleIdentify>,
167167+ app: &State<App>,
168168+) -> Result<Json<Vec<File>>, (Status, &'static str)> {
169169+ // Validate the session
170170+ let sid = match Uuid::parse_str(&single_identify.session) {
171171+ Ok(v) => v,
172172+ Err(_) => return Err((Status::BadRequest, "Invalid session ID format.")),
173173+ };
174174+ let user_id = match app.validate_session(sid).await {
175175+ Ok(v) => v,
176176+ Err(_) => return Err((Status::Unauthorized, "Invalid session token.")),
177177+ };
178178+179179+ // get the user data from the id
180180+ let user = match user_from_id(&app.db, user_id).await {
181181+ Ok(v) => v,
182182+ Err(_) => return Err((Status::NotFound, "Couldn't find the user for session id")),
183183+ };
184184+185185+ // get the relevant files for this user
186186+ let files = match files_for_user(&app.db, user).await {
187187+ Ok(v) => v,
188188+ Err(_) => {
189189+ return Err((
190190+ Status::InternalServerError,
191191+ "Failed to fetch files for user",
192192+ ));
193193+ }
194194+ };
195195+196196+ // i hope this is how thats done o-o
197197+ return Ok(Json(files));
198198+}
199199+200200+/// Get files for a given user.
201201+/// Admin -> All files
202202+/// Non-Admin -> Their own files
203203+async fn files_for_user(conn: &Database, user: User) -> Result<Vec<File>, DbError> {
204204+ if user.is_admin {
205205+ let files = query!(
206206+ "SELECT id as 'id: Uuid', filename, username FROM file_mapping LEFT JOIN users ON users.uid = file_mapping.owner_uid"
207207+ )
208208+ .fetch_all(conn)
209209+ .await?;
210210+211211+ let files: Vec<File> = files
212212+ .iter()
213213+ .map(|file| File {
214214+ id: file.id,
215215+ filename: file.filename.clone(),
216216+ owner: file.username.clone(),
217217+ size_mb: 0,
218218+ })
219219+ .collect();
220220+221221+ return Ok(files);
222222+ }
223223+224224+ let files = query!(
225225+ "SELECT id as 'id: Uuid', filename FROM file_mapping WHERE owner_uid = ?1",
226226+ user.id
227227+ )
228228+ .fetch_all(conn)
229229+ .await?;
230230+231231+ let files: Vec<File> = files
232232+ .iter()
233233+ .map(|file| File {
234234+ id: file.id,
235235+ filename: file.filename.clone(),
236236+ owner: Some(user.username.clone()),
237237+ size_mb: 0,
238238+ })
239239+ .collect();
240240+241241+ return Ok(files);
242242+}
243243+244244+/// Gets a User from the database using it's uuid
245245+async fn user_from_id(conn: &Database, user_id: Uuid) -> Result<User, DbError> {
246246+ let user = query!(
247247+ "SELECT username, max_megabytes, used_megabytes, is_admin FROM users WHERE uid = ?1",
248248+ user_id
249249+ )
250250+ .fetch_one(conn)
251251+ .await?;
252252+253253+ return Ok(User {
254254+ id: user_id,
255255+ username: user.username,
256256+ max_megabytes: user.max_megabytes,
257257+ user_megabytes: user.used_megabytes,
258258+ is_admin: user.is_admin,
259259+ });
260260+}
261261+262262+struct User {
263263+ id: Uuid,
264264+ username: String,
265265+ max_megabytes: i64,
266266+ user_megabytes: f64,
267267+ is_admin: bool,
268268+}
269269+270270+#[derive(Serialize)]
271271+#[serde(crate = "rocket::serde")]
272272+pub(crate) struct File {
273273+ id: Uuid,
274274+ owner: Option<String>,
275275+ filename: String,
276276+ size_mb: i32,
277277+}