mirror of
https://github.com/Dangoware/confetti-box.git
synced 2025-04-19 23:32:58 -05:00
Reorganized some files for more reasonable naming
This commit is contained in:
parent
55bea3a770
commit
071b3c206c
7 changed files with 241 additions and 240 deletions
|
@ -16,7 +16,7 @@ use serde_with::{serde_as, DisplayFromStr};
|
||||||
const BINCODE_CFG: Configuration = bincode::config::standard();
|
const BINCODE_CFG: Configuration = bincode::config::standard();
|
||||||
|
|
||||||
#[derive(Debug, Clone, Decode, Encode)]
|
#[derive(Debug, Clone, Decode, Encode)]
|
||||||
pub struct Database {
|
pub struct Mochibase {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
|
|
||||||
/// Every hash in the database along with the [`Mmid`]s associated with them
|
/// Every hash in the database along with the [`Mmid`]s associated with them
|
||||||
|
@ -28,7 +28,7 @@ pub struct Database {
|
||||||
entries: HashMap<Mmid, MochiFile>,
|
entries: HashMap<Mmid, MochiFile>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Database {
|
impl Mochibase {
|
||||||
pub fn new<P: AsRef<Path>>(path: &P) -> Result<Self, io::Error> {
|
pub fn new<P: AsRef<Path>>(path: &P) -> Result<Self, io::Error> {
|
||||||
let output = Self {
|
let output = Self {
|
||||||
path: path.as_ref().to_path_buf(),
|
path: path.as_ref().to_path_buf(),
|
||||||
|
@ -225,7 +225,7 @@ impl MochiFile {
|
||||||
|
|
||||||
/// Clean the database. Removes files which are past their expiry
|
/// Clean the database. Removes files which are past their expiry
|
||||||
/// [`chrono::DateTime`]. Also removes files which no longer exist on the disk.
|
/// [`chrono::DateTime`]. Also removes files which no longer exist on the disk.
|
||||||
fn clean_database(db: &Arc<RwLock<Database>>, file_path: &Path) {
|
fn clean_database(db: &Arc<RwLock<Mochibase>>, file_path: &Path) {
|
||||||
let mut database = db.write().unwrap();
|
let mut database = db.write().unwrap();
|
||||||
|
|
||||||
// Add expired entries to the removal list
|
// Add expired entries to the removal list
|
||||||
|
@ -256,7 +256,7 @@ fn clean_database(db: &Arc<RwLock<Database>>, file_path: &Path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Cleaned database. Removed {removed_entries} expired entries. Removed {removed_files} no longer referenced files.");
|
info!("Cleaned database.\n\t| Removed {removed_entries} expired entries.\n\t| Removed {removed_files} no longer referenced files.");
|
||||||
|
|
||||||
if let Err(e) = database.save() {
|
if let Err(e) = database.save() {
|
||||||
error!("Failed to save database: {e}")
|
error!("Failed to save database: {e}")
|
||||||
|
@ -266,7 +266,7 @@ fn clean_database(db: &Arc<RwLock<Database>>, file_path: &Path) {
|
||||||
|
|
||||||
/// A loop to clean the database periodically.
|
/// A loop to clean the database periodically.
|
||||||
pub async fn clean_loop(
|
pub async fn clean_loop(
|
||||||
db: Arc<RwLock<Database>>,
|
db: Arc<RwLock<Mochibase>>,
|
||||||
file_path: PathBuf,
|
file_path: PathBuf,
|
||||||
mut shutdown_signal: Receiver<()>,
|
mut shutdown_signal: Receiver<()>,
|
||||||
interval: TimeDelta,
|
interval: TimeDelta,
|
||||||
|
|
|
@ -11,7 +11,7 @@ use rocket::{
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
database::{Database, Mmid, MochiFile},
|
database::{Mochibase, Mmid, MochiFile},
|
||||||
settings::Settings,
|
settings::Settings,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ pub fn server_info(settings: &State<Settings>) -> Json<ServerInfo> {
|
||||||
/// Get information about a file
|
/// Get information about a file
|
||||||
#[get("/info/<mmid>")]
|
#[get("/info/<mmid>")]
|
||||||
pub async fn file_info(
|
pub async fn file_info(
|
||||||
db: &State<Arc<RwLock<Database>>>,
|
db: &State<Arc<RwLock<Mochibase>>>,
|
||||||
mmid: &str,
|
mmid: &str,
|
||||||
) -> Option<Json<MochiFile>> {
|
) -> Option<Json<MochiFile>> {
|
||||||
let mmid: Mmid = mmid.try_into().ok()?;
|
let mmid: Mmid = mmid.try_into().ok()?;
|
||||||
|
@ -55,7 +55,7 @@ pub struct ServerInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/f/<mmid>")]
|
#[get("/f/<mmid>")]
|
||||||
pub async fn lookup_mmid(db: &State<Arc<RwLock<Database>>>, mmid: &str) -> Option<Redirect> {
|
pub async fn lookup_mmid(db: &State<Arc<RwLock<Mochibase>>>, mmid: &str) -> Option<Redirect> {
|
||||||
let mmid: Mmid = mmid.try_into().ok()?;
|
let mmid: Mmid = mmid.try_into().ok()?;
|
||||||
let entry = db.read().unwrap().get(&mmid).cloned()?;
|
let entry = db.read().unwrap().get(&mmid).cloned()?;
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ pub async fn lookup_mmid(db: &State<Arc<RwLock<Database>>>, mmid: &str) -> Optio
|
||||||
|
|
||||||
#[get("/f/<mmid>?noredir")]
|
#[get("/f/<mmid>?noredir")]
|
||||||
pub async fn lookup_mmid_noredir(
|
pub async fn lookup_mmid_noredir(
|
||||||
db: &State<Arc<RwLock<Database>>>,
|
db: &State<Arc<RwLock<Mochibase>>>,
|
||||||
settings: &State<Settings>,
|
settings: &State<Settings>,
|
||||||
mmid: &str,
|
mmid: &str,
|
||||||
) -> Option<(ContentType, File)> {
|
) -> Option<(ContentType, File)> {
|
||||||
|
@ -86,7 +86,7 @@ pub async fn lookup_mmid_noredir(
|
||||||
|
|
||||||
#[get("/f/<mmid>/<name>")]
|
#[get("/f/<mmid>/<name>")]
|
||||||
pub async fn lookup_mmid_name(
|
pub async fn lookup_mmid_name(
|
||||||
db: &State<Arc<RwLock<Database>>>,
|
db: &State<Arc<RwLock<Mochibase>>>,
|
||||||
settings: &State<Settings>,
|
settings: &State<Settings>,
|
||||||
mmid: &str,
|
mmid: &str,
|
||||||
name: &str,
|
name: &str,
|
||||||
|
|
174
src/lib.rs
Normal file
174
src/lib.rs
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
pub mod database;
|
||||||
|
pub mod endpoints;
|
||||||
|
pub mod settings;
|
||||||
|
pub mod strings;
|
||||||
|
pub mod utils;
|
||||||
|
pub mod pages;
|
||||||
|
pub mod resources;
|
||||||
|
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use crate::database::{Mmid, MochiFile, Mochibase};
|
||||||
|
use maud::{html, Markup, PreEscaped};
|
||||||
|
use crate::pages::{footer, head};
|
||||||
|
use rocket::{
|
||||||
|
data::ToByteUnit, form::Form, fs::TempFile, get, post, serde::{json::Json, Serialize}, FromForm, State
|
||||||
|
};
|
||||||
|
use crate::settings::Settings;
|
||||||
|
use crate::strings::{parse_time_string, to_pretty_time};
|
||||||
|
use crate::utils::hash_file;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
pub fn home(settings: &State<Settings>) -> Markup {
|
||||||
|
html! {
|
||||||
|
(head("Confetti-Box"))
|
||||||
|
script src="/resources/request.js" { }
|
||||||
|
|
||||||
|
center {
|
||||||
|
h1 { "Confetti-Box 🎉" }
|
||||||
|
h2 { "Files up to " (settings.max_filesize.bytes()) " in size are allowed!" }
|
||||||
|
hr;
|
||||||
|
button.main_file_upload #fileButton onclick="document.getElementById('fileInput').click()" {
|
||||||
|
h4 { "Upload File(s)" }
|
||||||
|
p { "Click, Paste, or Drag and Drop" }
|
||||||
|
}
|
||||||
|
h3 { "Expire after:" }
|
||||||
|
div id="durationBox" {
|
||||||
|
@for d in &settings.duration.allowed {
|
||||||
|
button.button.{@if settings.duration.default == *d { "selected" }}
|
||||||
|
data-duration-seconds=(d.num_seconds())
|
||||||
|
{
|
||||||
|
(PreEscaped(to_pretty_time(d.num_seconds() as u32)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form #uploadForm {
|
||||||
|
// It's stupid how these can't be styled so they're just hidden here...
|
||||||
|
input #fileDuration type="text" name="duration" minlength="2"
|
||||||
|
maxlength="7" value=(settings.duration.default.num_seconds().to_string() + "s") style="display:none;";
|
||||||
|
input #fileInput type="file" name="fileUpload" multiple
|
||||||
|
onchange="formSubmit(this.parentNode)" data-max-filesize=(settings.max_filesize) style="display:none;";
|
||||||
|
}
|
||||||
|
hr;
|
||||||
|
|
||||||
|
h3 { "Uploaded Files" }
|
||||||
|
div #uploadedFilesDisplay {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
hr;
|
||||||
|
(footer())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, FromForm)]
|
||||||
|
pub struct Upload<'r> {
|
||||||
|
#[field(name = "duration")]
|
||||||
|
expire_time: String,
|
||||||
|
|
||||||
|
#[field(name = "fileUpload")]
|
||||||
|
file: TempFile<'r>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle a file upload and store it
|
||||||
|
#[post("/upload", data = "<file_data>")]
|
||||||
|
pub async fn handle_upload(
|
||||||
|
mut file_data: Form<Upload<'_>>,
|
||||||
|
db: &State<Arc<RwLock<Mochibase>>>,
|
||||||
|
settings: &State<Settings>,
|
||||||
|
) -> Result<Json<ClientResponse>, std::io::Error> {
|
||||||
|
let current = Utc::now();
|
||||||
|
// Ensure the expiry time is valid, if not return an error
|
||||||
|
let expire_time = if let Ok(t) = parse_time_string(&file_data.expire_time) {
|
||||||
|
if settings.duration.restrict_to_allowed && !settings.duration.allowed.contains(&t) {
|
||||||
|
return Ok(Json(ClientResponse::failure("Duration not allowed")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if t > settings.duration.maximum {
|
||||||
|
return Ok(Json(ClientResponse::failure("Duration larger than max")));
|
||||||
|
}
|
||||||
|
|
||||||
|
t
|
||||||
|
} else {
|
||||||
|
return Ok(Json(ClientResponse::failure("Duration invalid")));
|
||||||
|
};
|
||||||
|
|
||||||
|
let raw_name = file_data
|
||||||
|
.file
|
||||||
|
.raw_name()
|
||||||
|
.unwrap()
|
||||||
|
.dangerous_unsafe_unsanitized_raw()
|
||||||
|
.as_str()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
// Get temp path for the file
|
||||||
|
let temp_filename = settings.temp_dir.join(Uuid::new_v4().to_string());
|
||||||
|
file_data.file.persist_to(&temp_filename).await?;
|
||||||
|
|
||||||
|
// Get hash and random identifier and expiry
|
||||||
|
let file_mmid = Mmid::new();
|
||||||
|
let file_hash = hash_file(&temp_filename).await?;
|
||||||
|
let expiry = current + expire_time;
|
||||||
|
|
||||||
|
// Process filetype
|
||||||
|
let file_type = file_format::FileFormat::from_file(&temp_filename)?;
|
||||||
|
|
||||||
|
let constructed_file = MochiFile::new(
|
||||||
|
file_mmid.clone(),
|
||||||
|
raw_name,
|
||||||
|
file_type.media_type().to_string(),
|
||||||
|
file_hash,
|
||||||
|
current,
|
||||||
|
expiry
|
||||||
|
);
|
||||||
|
|
||||||
|
// If the hash does not exist in the database,
|
||||||
|
// move the file to the backend, else, delete it
|
||||||
|
if db.read().unwrap().get_hash(&file_hash).is_none() {
|
||||||
|
std::fs::rename(temp_filename, settings.file_dir.join(file_hash.to_string()))?;
|
||||||
|
} else {
|
||||||
|
std::fs::remove_file(temp_filename)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
db.write().unwrap().insert(&file_mmid, constructed_file.clone());
|
||||||
|
|
||||||
|
Ok(Json(ClientResponse {
|
||||||
|
status: true,
|
||||||
|
name: constructed_file.name().clone(),
|
||||||
|
mmid: Some(constructed_file.mmid().clone()),
|
||||||
|
hash: constructed_file.hash().to_string(),
|
||||||
|
expires: Some(constructed_file.expiry()),
|
||||||
|
..Default::default()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A response to the client from the server
|
||||||
|
#[derive(Serialize, Default, Debug)]
|
||||||
|
pub struct ClientResponse {
|
||||||
|
/// Success or failure
|
||||||
|
pub status: bool,
|
||||||
|
|
||||||
|
pub response: &'static str,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "str::is_empty")]
|
||||||
|
pub name: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub mmid: Option<Mmid>,
|
||||||
|
#[serde(skip_serializing_if = "str::is_empty")]
|
||||||
|
pub hash: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub expires: Option<DateTime<Utc>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientResponse {
|
||||||
|
fn failure(response: &'static str) -> Self {
|
||||||
|
Self {
|
||||||
|
status: false,
|
||||||
|
response,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
214
src/main.rs
214
src/main.rs
|
@ -1,182 +1,9 @@
|
||||||
mod database;
|
use std::{fs, sync::{Arc, RwLock}};
|
||||||
mod endpoints;
|
|
||||||
mod settings;
|
|
||||||
mod strings;
|
|
||||||
mod utils;
|
|
||||||
mod pages;
|
|
||||||
|
|
||||||
use std::{
|
use chrono::TimeDelta;
|
||||||
fs,
|
use confetti_box::{database::{clean_loop, Mochibase}, endpoints, pages, resources, settings::Settings};
|
||||||
sync::{Arc, RwLock},
|
|
||||||
};
|
|
||||||
|
|
||||||
use chrono::{DateTime, TimeDelta, Utc};
|
|
||||||
use database::{clean_loop, Database, Mmid, MochiFile};
|
|
||||||
use endpoints::{file_info, lookup_mmid, lookup_mmid_name, lookup_mmid_noredir, server_info};
|
|
||||||
use log::info;
|
use log::info;
|
||||||
use maud::{html, Markup, PreEscaped};
|
use rocket::{data::ToByteUnit as _, routes, tokio};
|
||||||
use pages::{about, api_info, favicon, fira_code, footer, form_handler_js, head, roboto_flex, stylesheet};
|
|
||||||
use rocket::{
|
|
||||||
data::{Limits, ToByteUnit}, form::Form, fs::TempFile, get, post, routes, serde::{json::Json, Serialize}, tokio, Config, FromForm, State
|
|
||||||
};
|
|
||||||
use settings::Settings;
|
|
||||||
use strings::{parse_time_string, to_pretty_time};
|
|
||||||
use utils::hash_file;
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
#[get("/")]
|
|
||||||
fn home(settings: &State<Settings>) -> Markup {
|
|
||||||
html! {
|
|
||||||
(head("Confetti-Box"))
|
|
||||||
script src="/resources/request.js" { }
|
|
||||||
|
|
||||||
center {
|
|
||||||
h1 { "Confetti-Box 🎉" }
|
|
||||||
h2 { "Files up to " (settings.max_filesize.bytes()) " in size are allowed!" }
|
|
||||||
hr;
|
|
||||||
button.main_file_upload #fileButton onclick="document.getElementById('fileInput').click()" {
|
|
||||||
h4 { "Upload File(s)" }
|
|
||||||
p { "Click, Paste, or Drag and Drop" }
|
|
||||||
}
|
|
||||||
h3 { "Expire after:" }
|
|
||||||
div id="durationBox" {
|
|
||||||
@for d in &settings.duration.allowed {
|
|
||||||
button.button.{@if settings.duration.default == *d { "selected" }}
|
|
||||||
data-duration-seconds=(d.num_seconds())
|
|
||||||
{
|
|
||||||
(PreEscaped(to_pretty_time(d.num_seconds() as u32)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
form #uploadForm {
|
|
||||||
// It's stupid how these can't be styled so they're just hidden here...
|
|
||||||
input #fileDuration type="text" name="duration" minlength="2"
|
|
||||||
maxlength="7" value=(settings.duration.default.num_seconds().to_string() + "s") style="display:none;";
|
|
||||||
input #fileInput type="file" name="fileUpload" multiple
|
|
||||||
onchange="formSubmit(this.parentNode)" data-max-filesize=(settings.max_filesize) style="display:none;";
|
|
||||||
}
|
|
||||||
hr;
|
|
||||||
|
|
||||||
h3 { "Uploaded Files" }
|
|
||||||
div #uploadedFilesDisplay {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
hr;
|
|
||||||
(footer())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, FromForm)]
|
|
||||||
struct Upload<'r> {
|
|
||||||
#[field(name = "duration")]
|
|
||||||
expire_time: String,
|
|
||||||
|
|
||||||
#[field(name = "fileUpload")]
|
|
||||||
file: TempFile<'r>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handle a file upload and store it
|
|
||||||
#[post("/upload", data = "<file_data>")]
|
|
||||||
async fn handle_upload(
|
|
||||||
mut file_data: Form<Upload<'_>>,
|
|
||||||
db: &State<Arc<RwLock<Database>>>,
|
|
||||||
settings: &State<Settings>,
|
|
||||||
) -> Result<Json<ClientResponse>, std::io::Error> {
|
|
||||||
let current = Utc::now();
|
|
||||||
// Ensure the expiry time is valid, if not return an error
|
|
||||||
let expire_time = if let Ok(t) = parse_time_string(&file_data.expire_time) {
|
|
||||||
if settings.duration.restrict_to_allowed && !settings.duration.allowed.contains(&t) {
|
|
||||||
return Ok(Json(ClientResponse::failure("Duration not allowed")));
|
|
||||||
}
|
|
||||||
|
|
||||||
if t > settings.duration.maximum {
|
|
||||||
return Ok(Json(ClientResponse::failure("Duration larger than max")));
|
|
||||||
}
|
|
||||||
|
|
||||||
t
|
|
||||||
} else {
|
|
||||||
return Ok(Json(ClientResponse::failure("Duration invalid")));
|
|
||||||
};
|
|
||||||
|
|
||||||
let raw_name = file_data
|
|
||||||
.file
|
|
||||||
.raw_name()
|
|
||||||
.unwrap()
|
|
||||||
.dangerous_unsafe_unsanitized_raw()
|
|
||||||
.as_str()
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
// Get temp path for the file
|
|
||||||
let temp_filename = settings.temp_dir.join(Uuid::new_v4().to_string());
|
|
||||||
file_data.file.persist_to(&temp_filename).await?;
|
|
||||||
|
|
||||||
// Get hash and random identifier and expiry
|
|
||||||
let file_mmid = Mmid::new();
|
|
||||||
let file_hash = hash_file(&temp_filename).await?;
|
|
||||||
let expiry = current + expire_time;
|
|
||||||
|
|
||||||
// Process filetype
|
|
||||||
let file_type = file_format::FileFormat::from_file(&temp_filename)?;
|
|
||||||
|
|
||||||
let constructed_file = MochiFile::new(
|
|
||||||
file_mmid.clone(),
|
|
||||||
raw_name,
|
|
||||||
file_type.media_type().to_string(),
|
|
||||||
file_hash,
|
|
||||||
current,
|
|
||||||
expiry
|
|
||||||
);
|
|
||||||
|
|
||||||
// If the hash does not exist in the database,
|
|
||||||
// move the file to the backend, else, delete it
|
|
||||||
if db.read().unwrap().get_hash(&file_hash).is_none() {
|
|
||||||
std::fs::rename(temp_filename, settings.file_dir.join(file_hash.to_string()))?;
|
|
||||||
} else {
|
|
||||||
std::fs::remove_file(temp_filename)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
db.write().unwrap().insert(&file_mmid, constructed_file.clone());
|
|
||||||
|
|
||||||
Ok(Json(ClientResponse {
|
|
||||||
status: true,
|
|
||||||
name: constructed_file.name().clone(),
|
|
||||||
mmid: Some(constructed_file.mmid().clone()),
|
|
||||||
hash: constructed_file.hash().to_string(),
|
|
||||||
expires: Some(constructed_file.expiry()),
|
|
||||||
..Default::default()
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A response to the client from the server
|
|
||||||
#[derive(Serialize, Default, Debug)]
|
|
||||||
#[serde(crate = "rocket::serde")]
|
|
||||||
struct ClientResponse {
|
|
||||||
/// Success or failure
|
|
||||||
pub status: bool,
|
|
||||||
|
|
||||||
pub response: &'static str,
|
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "str::is_empty")]
|
|
||||||
pub name: String,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub mmid: Option<Mmid>,
|
|
||||||
#[serde(skip_serializing_if = "str::is_empty")]
|
|
||||||
pub hash: String,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub expires: Option<DateTime<Utc>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClientResponse {
|
|
||||||
fn failure(response: &'static str) -> Self {
|
|
||||||
Self {
|
|
||||||
status: false,
|
|
||||||
response,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rocket::main]
|
#[rocket::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
@ -192,17 +19,17 @@ async fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set rocket configuration settings
|
// Set rocket configuration settings
|
||||||
let rocket_config = Config {
|
let rocket_config = rocket::Config {
|
||||||
address: config.server.address.parse().expect("IP address invalid"),
|
address: config.server.address.parse().expect("IP address invalid"),
|
||||||
port: config.server.port,
|
port: config.server.port,
|
||||||
temp_dir: config.temp_dir.clone().into(),
|
temp_dir: config.temp_dir.clone().into(),
|
||||||
limits: Limits::default()
|
limits: rocket::data::Limits::default()
|
||||||
.limit("data-form", config.max_filesize.bytes())
|
.limit("data-form", config.max_filesize.bytes())
|
||||||
.limit("file", config.max_filesize.bytes()),
|
.limit("file", config.max_filesize.bytes()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let database = Arc::new(RwLock::new(Database::open_or_new(&config.database_path).expect("Failed to open or create database")));
|
let database = Arc::new(RwLock::new(Mochibase::open_or_new(&config.database_path).expect("Failed to open or create database")));
|
||||||
let local_db = database.clone();
|
let local_db = database.clone();
|
||||||
|
|
||||||
// Start monitoring thread, cleaning the database every 2 minutes
|
// Start monitoring thread, cleaning the database every 2 minutes
|
||||||
|
@ -217,25 +44,24 @@ async fn main() {
|
||||||
.mount(
|
.mount(
|
||||||
config.server.root_path.clone() + "/",
|
config.server.root_path.clone() + "/",
|
||||||
routes![
|
routes![
|
||||||
home,
|
confetti_box::home,
|
||||||
api_info,
|
pages::api_info,
|
||||||
about,
|
pages::about,
|
||||||
favicon,
|
resources::favicon,
|
||||||
form_handler_js,
|
resources::form_handler_js,
|
||||||
stylesheet,
|
resources::stylesheet,
|
||||||
fira_code,
|
resources::font_static,
|
||||||
roboto_flex,
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.mount(
|
.mount(
|
||||||
config.server.root_path.clone() + "/",
|
config.server.root_path.clone() + "/",
|
||||||
routes![
|
routes![
|
||||||
handle_upload,
|
confetti_box::handle_upload,
|
||||||
server_info,
|
endpoints::server_info,
|
||||||
file_info,
|
endpoints::file_info,
|
||||||
lookup_mmid,
|
endpoints::lookup_mmid,
|
||||||
lookup_mmid_noredir,
|
endpoints::lookup_mmid_noredir,
|
||||||
lookup_mmid_name,
|
endpoints::lookup_mmid_name,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.manage(database)
|
.manage(database)
|
||||||
|
|
28
src/pages.rs
28
src/pages.rs
|
@ -26,32 +26,6 @@ pub fn footer() -> Markup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stylesheet
|
|
||||||
#[get("/resources/main.css")]
|
|
||||||
pub fn stylesheet() -> RawCss<&'static str> {
|
|
||||||
RawCss(include_str!("../web/main.css"))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Upload handler javascript
|
|
||||||
#[get("/resources/request.js")]
|
|
||||||
pub fn form_handler_js() -> RawJavaScript<&'static str> {
|
|
||||||
RawJavaScript(include_str!("../web/request.js"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/resources/favicon.svg")]
|
|
||||||
pub fn favicon() -> (ContentType, &'static str) {
|
|
||||||
(ContentType::SVG, include_str!("../web/favicon.svg"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/resources/Roboto.woff2")]
|
|
||||||
pub fn roboto_flex() -> (ContentType, &'static [u8]) {
|
|
||||||
(ContentType::WOFF2, include_bytes!("../web/fonts/roboto.woff2"))
|
|
||||||
}
|
|
||||||
#[get("/resources/FiraCode.woff2")]
|
|
||||||
pub fn fira_code() -> (ContentType, &'static [u8]) {
|
|
||||||
(ContentType::WOFF2, include_bytes!("../web/fonts/fira-code.woff2"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/api")]
|
#[get("/api")]
|
||||||
pub fn api_info(settings: &State<Settings>) -> Markup {
|
pub fn api_info(settings: &State<Settings>) -> Markup {
|
||||||
let domain = &settings.server.domain;
|
let domain = &settings.server.domain;
|
||||||
|
@ -156,7 +130,7 @@ pub fn api_info(settings: &State<Settings>) -> Markup {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/about")]
|
#[get("/about")]
|
||||||
pub fn about(settings: &State<Settings>) -> Markup {
|
pub fn about() -> Markup {
|
||||||
html! {
|
html! {
|
||||||
(head("Confetti-Box | About"))
|
(head("Confetti-Box | About"))
|
||||||
|
|
||||||
|
|
27
src/resources.rs
Normal file
27
src/resources.rs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
use rocket::{get, http::ContentType, response::content::{RawCss, RawJavaScript}};
|
||||||
|
|
||||||
|
#[get("/resources/fonts/<font>")]
|
||||||
|
pub fn font_static(font: &str) -> Option<(ContentType, &'static [u8])> {
|
||||||
|
match font {
|
||||||
|
"Roboto.woff2" => Some((ContentType::WOFF2, include_bytes!("../web/fonts/roboto.woff2"))),
|
||||||
|
"FiraCode.woff2" => Some((ContentType::WOFF2, include_bytes!("../web/fonts/fira-code.woff2"))),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stylesheet
|
||||||
|
#[get("/resources/main.css")]
|
||||||
|
pub fn stylesheet() -> RawCss<&'static str> {
|
||||||
|
RawCss(include_str!("../web/main.css"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Upload handler javascript
|
||||||
|
#[get("/resources/request.js")]
|
||||||
|
pub fn form_handler_js() -> RawJavaScript<&'static str> {
|
||||||
|
RawJavaScript(include_str!("../web/request.js"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/resources/favicon.svg")]
|
||||||
|
pub fn favicon() -> (ContentType, &'static str) {
|
||||||
|
(ContentType::SVG, include_str!("../web/favicon.svg"))
|
||||||
|
}
|
10
web/main.css
10
web/main.css
|
@ -1,20 +1,20 @@
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Roboto";
|
font-family: "Roboto";
|
||||||
src:
|
src:
|
||||||
/*local("Roboto"),*/
|
local("Roboto"),
|
||||||
url("/resources/Roboto.woff2");
|
url("/resources/fonts/Roboto.woff2");
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Fira Code";
|
font-family: "Fira Code";
|
||||||
src:
|
src:
|
||||||
/*local("Fira Code"),*/
|
local("Fira Code"),
|
||||||
url("/resources/FiraCode.woff2");
|
url("/resources/fonts/FiraCode.woff2");
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: "Roboto", sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
font-size: 14pt;
|
font-size: 12pt;
|
||||||
font-optical-sizing: auto;
|
font-optical-sizing: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue