Improve shortlink behavior

This commit is contained in:
G2-Games 2024-10-27 17:33:43 -05:00
parent 769bd72542
commit ac41059f0b
3 changed files with 64 additions and 47 deletions

View file

@ -1,5 +1,5 @@
use std::{ use std::{
collections::{hash_map::Values, HashMap, HashSet}, fs::{self, File}, path::{Path, PathBuf}, sync::{Arc, RwLock} collections::{hash_map::Values, HashMap, HashSet}, ffi::OsStr, fs::{self, File}, path::{Path, PathBuf}, sync::{Arc, RwLock}
}; };
use bincode::{config::Configuration, decode_from_std_read, encode_into_std_write, Decode, Encode}; use bincode::{config::Configuration, decode_from_std_read, encode_into_std_write, Decode, Encode};
@ -308,3 +308,38 @@ impl TryFrom<&str> for Mmid {
Ok(Self(value.to_owned())) Ok(Self(value.to_owned()))
} }
} }
impl TryFrom<&Path> for Mmid {
type Error = ();
fn try_from(value: &Path) -> Result<Self, Self::Error> {
value.as_os_str().try_into()
}
}
impl TryFrom<&OsStr> for Mmid {
type Error = ();
fn try_from(value: &OsStr) -> Result<Self, Self::Error> {
let string = match value.to_str() {
Some(p) => p,
None => return Err(()),
};
if string.len() != 8 {
return Err(())
}
if string.chars().any(|c| !c.is_ascii_alphanumeric()) {
return Err(())
}
Ok(Self(string.to_owned()))
}
}
impl std::fmt::Display for Mmid {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}

View file

@ -1,7 +1,7 @@
use std::sync::{Arc, RwLock}; use std::{path::PathBuf, sync::{Arc, RwLock}};
use rocket::{ use rocket::{
fs::NamedFile, get, http::ContentType, serde::{self, json::Json}, tokio::fs::File, State get, http::ContentType, response::Redirect, serde::{self, json::Json}, tokio::fs::File, uri, State
}; };
use serde::Serialize; use serde::Serialize;
@ -36,56 +36,36 @@ pub struct ServerInfo {
/// Look up the [`Mmid`] of a file to find it. /// Look up the [`Mmid`] of a file to find it.
#[get("/f/<mmid>")] #[get("/f/<mmid>")]
pub async fn lookup( pub async fn lookup_mmid(
db: &State<Arc<RwLock<Database>>>, db: &State<Arc<RwLock<Database>>>,
settings: &State<Settings>, mmid: &str,
mmid: &str ) -> Option<Redirect> {
) -> Option<(ContentType, NamedFile)> { let mmid: Mmid = mmid.try_into().ok()?;
let mmid: Mmid = match mmid.try_into() { let entry = db.read().unwrap().get(&mmid).cloned()?;
Ok(v) => v,
Err(_) => return None,
};
let entry = if let Some(e) = db.read().unwrap().get(&mmid).cloned() { Some(
e Redirect::to(uri!(lookup_mmid_name(mmid.to_string(), entry.name())))
} else { )
return None
};
let file = NamedFile::open(settings.file_dir.join(entry.hash().to_string())).await.ok()?;
Some((
ContentType::from_extension(entry.extension()).unwrap_or(ContentType::Binary),
file
))
} }
/// Look up the [`Mmid`] of a file to find it.
#[get("/f/<mmid>/<filename>")] #[get("/f/<mmid>/<name>")]
pub async fn lookup_filename( pub async fn lookup_mmid_name(
db: &State<Arc<RwLock<Database>>>, db: &State<Arc<RwLock<Database>>>,
settings: &State<Settings>, settings: &State<Settings>,
mmid: &str, mmid: &str,
filename: &str, name: &str,
) -> Option<(ContentType, NamedFile)> { ) -> Option<(ContentType, File)> {
let mmid: Mmid = match mmid.try_into() { let mmid: Mmid = mmid.try_into().ok()?;
Ok(v) => v,
Err(_) => return None,
};
let entry = if let Some(e) = db.read().unwrap().get(&mmid).cloned() { let entry = db.read().unwrap().get(&mmid).cloned()?;
e
} else {
return None
};
dbg!(entry.name()); // If the name does not match, then this is invalid
dbg!(filename); if name != entry.name() {
if entry.name() != filename {
return None return None
} }
let file = NamedFile::open(settings.file_dir.join(entry.hash().to_string())).await.ok()?; let file = File::open(settings.file_dir.join(entry.hash().to_string())).await.ok()?;
Some(( Some((
ContentType::from_extension(entry.extension()).unwrap_or(ContentType::Binary), ContentType::from_extension(entry.extension()).unwrap_or(ContentType::Binary),

View file

@ -11,7 +11,7 @@ use std::{
use chrono::{DateTime, TimeDelta, Utc}; use chrono::{DateTime, TimeDelta, Utc};
use database::{clean_loop, Database, Mmid, MochiFile}; use database::{clean_loop, Database, Mmid, MochiFile};
use endpoints::{lookup, lookup_filename, server_info}; use endpoints::{lookup_mmid, lookup_mmid_name, server_info};
use log::info; use log::info;
use maud::{html, Markup, PreEscaped, DOCTYPE}; use maud::{html, Markup, PreEscaped, DOCTYPE};
use rocket::{ use rocket::{
@ -102,7 +102,7 @@ fn home(settings: &State<Settings>) -> Markup {
footer { footer {
p {a href="https://github.com/G2-Games/confetti-box" {"Source"}} p {a href="https://github.com/G2-Games/confetti-box" {"Source"}}
p {a href="https://g2games.dev/" {"My Website"}} p {a href="https://g2games.dev/" {"My Website"}}
p {a href="#" {"Links"}} p {a href="api" {"API Info"}}
p {a href="#" {"Go"}} p {a href="#" {"Go"}}
p {a href="#" {"Here"}} p {a href="#" {"Here"}}
} }
@ -264,8 +264,8 @@ async fn main() {
stylesheet, stylesheet,
server_info, server_info,
favicon, favicon,
lookup, lookup_mmid,
lookup_filename, lookup_mmid_name,
], ],
) )
.mount( .mount(
@ -284,12 +284,14 @@ async fn main() {
// Ensure the server gracefully shuts down // Ensure the server gracefully shuts down
rocket.expect("Server failed to shutdown gracefully"); rocket.expect("Server failed to shutdown gracefully");
info!("Stopping database cleaning thread"); info!("Stopping database cleaning thread...");
shutdown shutdown
.send(()) .send(())
.await .await
.expect("Failed to stop cleaner thread"); .expect("Failed to stop cleaner thread.");
info!("Stopping database cleaning thread completed successfully.");
info!("Saving database on shutdown..."); info!("Saving database on shutdown...");
local_db.write().unwrap().save(); local_db.write().unwrap().save();
info!("Saving database completed successfully.");
} }