From d42a361e9518971119fc9397254e0c31ea9d8e64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Szab=C3=B3?= <25702868+szabodanika@users.noreply.github.com> Date: Sun, 10 Apr 2022 23:21:45 +0100 Subject: [PATCH] Initial commit --- Cargo.toml | 13 ++++ src/animalnumbers.rs | 61 +++++++++++++++++ src/main.rs | 137 +++++++++++++++++++++++++++++++++++++++ src/pasta.rs | 57 ++++++++++++++++ templates/footer.html | 7 ++ templates/header.html | 19 ++++++ templates/index.html | 23 +++++++ templates/pasta.html | 6 ++ templates/pastalist.html | 40 ++++++++++++ 9 files changed, 363 insertions(+) create mode 100644 Cargo.toml create mode 100644 src/animalnumbers.rs create mode 100644 src/main.rs create mode 100644 src/pasta.rs create mode 100644 templates/footer.html create mode 100644 templates/header.html create mode 100644 templates/index.html create mode 100644 templates/pasta.html create mode 100644 templates/pastalist.html diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b87b1b8 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "microbin" +version = "0.1.0" +edition = "2021" + +[dependencies] +actix-web = "4" +actix-files = "0.6.0" +serde = { version = "1.0", features = ["derive"] } +askama = "0.10" +askama-filters = { version = "0.1.3", features = ["chrono"] } +chrono = "0.4.19" +rand = "0.8.5" \ No newline at end of file diff --git a/src/animalnumbers.rs b/src/animalnumbers.rs new file mode 100644 index 0000000..a2ef104 --- /dev/null +++ b/src/animalnumbers.rs @@ -0,0 +1,61 @@ +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 n: u64) -> String { + let mut result: Vec<&str> = Vec::new(); + + if n == 0 { + return animal_names[0].parse().unwrap(); + } else if n == 1 { + return animal_names[1].parse().unwrap(); + } + + // max 4 animals so 6 * 6 = 64 bits + let mut power = 6; + loop { + let d = n / animal_names.len().pow(power) as u64; + + if !(result.is_empty() && d == 0) { + result.push(animal_names[d as usize]); + } + + n -= d * animal_names.len().pow(power) as u64; + + if power > 0 { + power -= 1; + } else { break; } + } + + result.join("-") +} + +pub fn to_u64(n: &str) -> u64 { + let mut result: u64 = 0; + + let mut animals: Vec<&str> = n.split("-").collect(); + + let mut pow = animals.len(); + for i in 0..animals.len() { + pow -= 1; + result += (animal_names.iter().position(|&r| r == animals[i]).unwrap() * animal_names.len().pow(pow as u32)) as u64; + } + + result +} + diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..9ac8cb3 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,137 @@ +extern crate core; + +use std::path::PathBuf; +use std::sync::Mutex; +use std::time::{SystemTime, UNIX_EPOCH}; + +use actix_files::NamedFile; +use actix_web::{App, get, HttpRequest, HttpResponse, HttpServer, post, Responder, Result, web}; +use actix_web::web::Data; +use askama::Template; +use rand::Rng; + +use crate::animalnumbers::{to_animal_names, to_u64}; +use crate::pasta::{Pasta, PastaFormData}; + +mod pasta; +mod animalnumbers; + +struct AppState { + pastas: Mutex>, +} + +#[derive(Template)] +#[template(path = "index.html")] +struct IndexTemplate {} + +#[derive(Template)] +#[template(path = "pasta.html")] +struct PastaTemplate<'a> { + pasta: &'a Pasta, +} + +#[derive(Template)] +#[template(path = "pastalist.html")] +struct PastaListTemplate<'a> { + pastas: &'a Vec, +} + +#[get("/")] +async fn index() -> impl Responder { + HttpResponse::Found().content_type("text/html").body(IndexTemplate {}.render().unwrap()) +} + +#[post("/create")] +async fn create(data: web::Data, pasta: web::Form) -> impl Responder { + let mut pastas = data.pastas.lock().unwrap(); + + let mut innerPasta = pasta.into_inner(); + + let timenow: i64 = match SystemTime::now().duration_since(UNIX_EPOCH) { + Ok(n) => n.as_secs(), + Err(_) => panic!("SystemTime before UNIX EPOCH!"), + } as i64; + + let expiration = match innerPasta.expiration.as_str() { + "firstread" => 1, + "10min" => timenow + 60 * 10, + "1hour" => timenow + 60 * 60, + "24hour" => timenow + 60 * 60 * 24, + "1week" => timenow + 60 * 60 * 24 * 7, + "never" => 0, + _ => panic!("Unexpected expiration time!") + }; + + let mut newPasta = Pasta { + id: rand::thread_rng().gen::() as u64, + content: innerPasta.content, + created: timenow, + expiration, + }; + + let id = newPasta.id; + + pastas.push(newPasta); + + HttpResponse::Found().append_header(("Location", format!("/pasta/{}", to_animal_names(id)))).finish() +} + +#[get("/pasta/{id}")] +async fn getpasta(data: web::Data, id: web::Path) -> HttpResponse { + let pastas = data.pastas.lock().unwrap(); + let id = to_u64(&*id.into_inner()); + + for pasta in pastas.iter() { + if pasta.id == id { + return HttpResponse::Found().content_type("text/html").body(PastaTemplate { pasta }.render().unwrap()); + } + } + + HttpResponse::Found().body("Pasta not found! :-(") +} + +#[get("/rawpasta/{id}")] +async fn getrawpasta(data: web::Data, id: web::Path) -> String { + let pastas = data.pastas.lock().unwrap(); + let id = to_u64(&*id.into_inner()); + + for pasta in pastas.iter() { + if pasta.id == id { + return pasta.content.to_owned(); + } + } + + String::from("Pasta not found! :-(") +} + +#[get("/remove/{id}")] +async fn remove(data: web::Data, id: web::Path) -> HttpResponse { + let mut pastas = data.pastas.lock().unwrap(); + let id = to_u64(&*id.into_inner()); + + for (i, pasta) in pastas.iter().enumerate() { + if pasta.id == id { + pastas.remove(i); + return HttpResponse::Found().append_header(("Location", "/pastalist")).finish(); + } + } + + HttpResponse::Found().body("Pasta not found! :-(") +} + +#[get("/pastalist")] +async fn list(data: web::Data) -> HttpResponse { + let mut pastas = data.pastas.lock().unwrap(); + + HttpResponse::Found().content_type("text/html").body(PastaListTemplate { pastas: &pastas }.render().unwrap()) +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + let data = web::Data::new(AppState { + pastas: Mutex::new(Vec::new()), + }); + + HttpServer::new(move || App::new().app_data(data.clone()).service(index).service(create).service(getpasta).service(getrawpasta).service(remove).service(list) + ).bind("127.0.0.1:8080")?.run().await +} diff --git a/src/pasta.rs b/src/pasta.rs new file mode 100644 index 0000000..9df8921 --- /dev/null +++ b/src/pasta.rs @@ -0,0 +1,57 @@ +use std::fmt; +use actix_web::cookie::time::macros::format_description; +use chrono::{Datelike, DateTime, NaiveDateTime, Timelike, Utc}; +use serde::Deserialize; +use crate::to_animal_names; + +pub struct Pasta { + pub id: u64, + pub content: String, + pub created: i64, + pub expiration: i64 +} + +#[derive(Deserialize)] +pub struct PastaFormData { + pub content: String, + pub expiration: String +} + +impl Pasta { + + pub fn idAsAnimals(&self) -> String { + to_animal_names(self.id) + } + + pub fn createdAsString(&self) -> String { + let date = DateTime::::from_utc(NaiveDateTime::from_timestamp(self.created, 0), Utc); + format!( + "{}-{:02}-{:02} {}:{}", + date.year(), + date.month(), + date.day(), + date.hour(), + date.minute(), + ) + } + + pub fn expirationAsString(&self) -> String { + let date = DateTime::::from_utc(NaiveDateTime::from_timestamp(self.expiration, 0), Utc); + format!( + "{}-{:02}-{:02} {}:{}", + date.year(), + date.month(), + date.day(), + date.hour(), + date.minute(), + ) + } + +} + + +impl fmt::Display for Pasta { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.content) + } +} diff --git a/templates/footer.html b/templates/footer.html new file mode 100644 index 0000000..067f487 --- /dev/null +++ b/templates/footer.html @@ -0,0 +1,7 @@ +
+

+ MicroBin by Daniel Szabo. Fork me on GitHub! + Let's keep the Web compact, accessible and humane! +

+ + \ No newline at end of file diff --git a/templates/header.html b/templates/header.html new file mode 100644 index 0000000..1a093e3 --- /dev/null +++ b/templates/header.html @@ -0,0 +1,19 @@ + + + MicroBin + + + +
+ + + MicroBin + +| +New Pasta +| +Pasta List +| +GitHub + +
\ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..eda0fdb --- /dev/null +++ b/templates/index.html @@ -0,0 +1,23 @@ +{% include "header.html" %} +
+ +
+ +
+ +
+ +
+
+ +
+{% include "footer.html" %} \ No newline at end of file diff --git a/templates/pasta.html b/templates/pasta.html new file mode 100644 index 0000000..29e37fe --- /dev/null +++ b/templates/pasta.html @@ -0,0 +1,6 @@ +{% include "header.html" %} +Raw Pasta +
+{{pasta}}
+
+{% include "footer.html" %} \ No newline at end of file diff --git a/templates/pastalist.html b/templates/pastalist.html new file mode 100644 index 0000000..d943656 --- /dev/null +++ b/templates/pastalist.html @@ -0,0 +1,40 @@ +{% include "header.html" %} + + + + + + + + {% for pasta in pastas %} + + + + + + + {% endfor %} +
+ Key + + Created + + Expiration + + +
+ {{pasta.idAsAnimals()}} + + {{pasta.createdAsString()}} + + {{pasta.expirationAsString()}} + + Remove +
+ +{% if pastas.is_empty() %} +

+ No Pastas :-( +

+{%- endif %} +{% include "footer.html" %} \ No newline at end of file