Multiple enhancements and bugfixes
!Breaking change! - The updated version will not be able to read your old database file Major improvements: - Added editable pastas - Added private pastas - Added line numbers - Added support for wide mode (1080p instead of 720p) - Added syntax highlighting support - Added read-only mode - Added built-in help page - Added option to remove logo, change title and footer text Minor improvements: - Improved looks in pure html mode - Removed link to GitHub repo from navbar - Broke up 7km long main.rs file into smaller modules - Moved water.css into a template instead of serving it as an external resource - Made Save button a bit bigger - Updated README.MD Bugfixes: - Fixed a bug where an incorrect animal ID in a request would cause a crash - Fixed a bug where an empty or corrupt JSON database would cause a crash
This commit is contained in:
parent
1c873d23b5
commit
4cc737731a
27 changed files with 1343 additions and 494 deletions
53
src/util/animalnumbers.rs
Normal file
53
src/util/animalnumbers.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
const ANIMAL_NAMES: &[&str] = &[
|
||||
"ant", "eel", "mole", "sloth", "ape", "emu", "monkey", "snail", "bat", "falcon", "mouse",
|
||||
"snake", "bear", "fish", "otter", "spider", "bee", "fly", "parrot", "squid", "bird", "fox",
|
||||
"panda", "swan", "bison", "frog", "pig", "tiger", "camel", "gecko", "pigeon", "toad", "cat",
|
||||
"goat", "pony", "turkey", "cobra", "goose", "pug", "turtle", "crow", "hawk", "rabbit", "viper",
|
||||
"deer", "horse", "rat", "wasp", "dog", "jaguar", "raven", "whale", "dove", "koala", "seal",
|
||||
"wolf", "duck", "lion", "shark", "worm", "eagle", "lizard", "sheep", "zebra",
|
||||
];
|
||||
|
||||
pub fn to_animal_names(mut number: u64) -> String {
|
||||
let mut result: Vec<&str> = Vec::new();
|
||||
|
||||
if number == 0 {
|
||||
return ANIMAL_NAMES[0].parse().unwrap();
|
||||
}
|
||||
|
||||
let mut power = 6;
|
||||
|
||||
loop {
|
||||
let digit = number / ANIMAL_NAMES.len().pow(power) as u64;
|
||||
if !(result.is_empty() && digit == 0) {
|
||||
result.push(ANIMAL_NAMES[digit as usize]);
|
||||
}
|
||||
number -= digit * ANIMAL_NAMES.len().pow(power) as u64;
|
||||
if power > 0 {
|
||||
power -= 1;
|
||||
} else if power <= 0 || number == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result.join("-")
|
||||
}
|
||||
|
||||
pub fn to_u64(animal_names: &str) -> Result<u64, &str> {
|
||||
let mut result: u64 = 0;
|
||||
|
||||
let animals: Vec<&str> = animal_names.split("-").collect();
|
||||
|
||||
let mut pow = animals.len();
|
||||
for i in 0..animals.len() {
|
||||
pow -= 1;
|
||||
let animal_index = ANIMAL_NAMES.iter().position(|&r| r == animals[i]);
|
||||
match animal_index {
|
||||
None => return Err("Failed to convert animal name to u64!"),
|
||||
Some(_) => {
|
||||
result += (animal_index.unwrap() * ANIMAL_NAMES.len().pow(pow as u32)) as u64
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
29
src/util/auth.rs
Normal file
29
src/util/auth.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
use actix_web::dev::ServiceRequest;
|
||||
use actix_web::{error, Error};
|
||||
use actix_web_httpauth::extractors::basic::BasicAuth;
|
||||
|
||||
use crate::args::ARGS;
|
||||
|
||||
pub async fn auth_validator(
|
||||
req: ServiceRequest,
|
||||
credentials: BasicAuth,
|
||||
) -> Result<ServiceRequest, Error> {
|
||||
// check if username matches
|
||||
if credentials.user_id().as_ref() == ARGS.auth_username.as_ref().unwrap() {
|
||||
return match ARGS.auth_password.as_ref() {
|
||||
Some(cred_pass) => match credentials.password() {
|
||||
None => Err(error::ErrorBadRequest("Invalid login details.")),
|
||||
Some(arg_pass) => {
|
||||
if arg_pass == cred_pass {
|
||||
Ok(req)
|
||||
} else {
|
||||
Err(error::ErrorBadRequest("Invalid login details."))
|
||||
}
|
||||
}
|
||||
},
|
||||
None => Ok(req),
|
||||
};
|
||||
} else {
|
||||
Err(error::ErrorBadRequest("Invalid login details."))
|
||||
}
|
||||
}
|
56
src/util/dbio.rs
Normal file
56
src/util/dbio.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::{BufReader, BufWriter};
|
||||
|
||||
use crate::Pasta;
|
||||
|
||||
static DATABASE_PATH: &'static str = "pasta_data/database.json";
|
||||
|
||||
pub fn save_to_file(pasta_data: &Vec<Pasta>) {
|
||||
let mut file = File::create(DATABASE_PATH);
|
||||
match file {
|
||||
Ok(_) => {
|
||||
let writer = BufWriter::new(file.unwrap());
|
||||
serde_json::to_writer(writer, &pasta_data).expect("Failed to create JSON writer");
|
||||
}
|
||||
Err(_) => {
|
||||
log::info!("Database file {} not found!", DATABASE_PATH);
|
||||
file = File::create(DATABASE_PATH);
|
||||
match file {
|
||||
Ok(_) => {
|
||||
log::info!("Database file {} created.", DATABASE_PATH);
|
||||
save_to_file(pasta_data);
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!(
|
||||
"Failed to create database file {}: {}!",
|
||||
&DATABASE_PATH,
|
||||
&err
|
||||
);
|
||||
panic!("Failed to create database file {}: {}!", DATABASE_PATH, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_from_file() -> io::Result<Vec<Pasta>> {
|
||||
let file = File::open(DATABASE_PATH);
|
||||
match file {
|
||||
Ok(_) => {
|
||||
let reader = BufReader::new(file.unwrap());
|
||||
let data: Vec<Pasta> = match serde_json::from_reader(reader) {
|
||||
Ok(t) => t,
|
||||
_ => Vec::new(),
|
||||
};
|
||||
Ok(data)
|
||||
}
|
||||
Err(_) => {
|
||||
log::info!("Database file {} not found!", DATABASE_PATH);
|
||||
save_to_file(&Vec::<Pasta>::new());
|
||||
|
||||
log::info!("Database file {} created.", DATABASE_PATH);
|
||||
load_from_file()
|
||||
}
|
||||
}
|
||||
}
|
42
src/util/misc.rs
Normal file
42
src/util/misc.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use linkify::{LinkFinder, LinkKind};
|
||||
use std::fs;
|
||||
|
||||
use crate::Pasta;
|
||||
|
||||
pub fn remove_expired(pastas: &mut Vec<Pasta>) {
|
||||
// get current time - this will be needed to check which pastas have expired
|
||||
let timenow: i64 = match SystemTime::now().duration_since(UNIX_EPOCH) {
|
||||
Ok(n) => n.as_secs(),
|
||||
Err(_) => {
|
||||
log::error!("SystemTime before UNIX EPOCH!");
|
||||
0
|
||||
}
|
||||
} as i64;
|
||||
|
||||
pastas.retain(|p| {
|
||||
// expiration is `never` or not reached
|
||||
if p.expiration == 0 || p.expiration > timenow {
|
||||
// keep
|
||||
true
|
||||
} else {
|
||||
// remove the file itself
|
||||
fs::remove_file(format!("./pasta_data/{}/{}", p.id_as_animals(), p.file))
|
||||
.expect(&*format!("Failed to delete file {}!", p.file));
|
||||
// and remove the containing directory
|
||||
fs::remove_dir(format!("./pasta_data/{}/", p.id_as_animals())).expect(&*format!(
|
||||
"Failed to delete directory {}!",
|
||||
p.id_as_animals()
|
||||
));
|
||||
// remove
|
||||
false
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn is_valid_url(url: &str) -> bool {
|
||||
let finder = LinkFinder::new();
|
||||
let spans: Vec<_> = finder.spans(url).collect();
|
||||
spans[0].as_str() == url && Some(&LinkKind::Url) == spans[0].kind()
|
||||
}
|
37
src/util/syntaxhighlighter.rs
Normal file
37
src/util/syntaxhighlighter.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use syntect::easy::HighlightLines;
|
||||
use syntect::highlighting::{Style, ThemeSet};
|
||||
use syntect::html::append_highlighted_html_for_styled_line;
|
||||
use syntect::html::IncludeBackground::No;
|
||||
use syntect::parsing::SyntaxSet;
|
||||
use syntect::util::LinesWithEndings;
|
||||
|
||||
pub fn html_highlight(text: &str, extension: &str) -> String {
|
||||
let ps = SyntaxSet::load_defaults_newlines();
|
||||
let ts = ThemeSet::load_defaults();
|
||||
|
||||
let syntax = ps
|
||||
.find_syntax_by_extension(extension)
|
||||
.or(Option::from(ps.find_syntax_plain_text()))
|
||||
.unwrap();
|
||||
let mut h = HighlightLines::new(syntax, &ts.themes["InspiredGitHub"]);
|
||||
|
||||
let mut highlighted_content: String = String::from("");
|
||||
|
||||
for line in LinesWithEndings::from(text) {
|
||||
let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps).unwrap();
|
||||
append_highlighted_html_for_styled_line(&ranges[..], No, &mut highlighted_content)
|
||||
.expect("Failed to append highlighted line!");
|
||||
}
|
||||
|
||||
let mut highlighted_content2: String = String::from("");
|
||||
for line in highlighted_content.lines() {
|
||||
highlighted_content2 += &*format!("<code-line>{}</code-line>\n", line);
|
||||
}
|
||||
|
||||
// Rewrite colours to ones that are compatible with water.css and both light/dark modes
|
||||
highlighted_content2 = highlighted_content2.replace("style=\"color:#323232;\"", "");
|
||||
highlighted_content2 =
|
||||
highlighted_content2.replace("style=\"color:#183691;\"", "style=\"color:blue;\"");
|
||||
|
||||
return highlighted_content2;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue