diff options
| author | Fuwn <[email protected]> | 2022-03-30 03:04:48 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2022-03-30 03:04:48 -0700 |
| commit | 0495bd1837087a91a36036dbaab71c83c3acc197 (patch) | |
| tree | 6f1744db3bf9a7eb07da70971f0b2aa39741a0b0 /src | |
| download | locus-0495bd1837087a91a36036dbaab71c83c3acc197.tar.xz locus-0495bd1837087a91a36036dbaab71c83c3acc197.zip | |
feat: 0.0.0 :star:
Diffstat (limited to 'src')
| -rw-r--r-- | src/constants.rs | 55 | ||||
| -rw-r--r-- | src/macros.rs | 68 | ||||
| -rw-r--r-- | src/main.rs | 155 | ||||
| -rw-r--r-- | src/modules.rs | 177 |
4 files changed, 455 insertions, 0 deletions
diff --git a/src/constants.rs b/src/constants.rs new file mode 100644 index 0000000..27c80bd --- /dev/null +++ b/src/constants.rs @@ -0,0 +1,55 @@ +// This file is part of Locus <https://github.com/gemrest/locus>. +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +pub const QUOTES: &[&str] = &[ + "That brain of mine is something more than merely mortal; as time will \ + show. - Ada Lovelace", + "Forget this world and all its troubles and if possible its multitudinous \ + Charlatans — everything in short but the Enchantress of Numbers. - Ada \ + Lovelace", + "If you can't give me poetry, can't you give me poetical science? - Ada \ + Lovelace", + "What I cannot create, I do not understand. - Richard P. Feynman", + "Know how to solve every problem that has been solved. - Richard P. Feynman", + "Teach principles, not formulas. - Richard P. Feynman", + "I learned very early the difference between knowing the name of something \ + and knowing something. - Richard P. Feynman", + "Scientists are explorers. Philosophers are tourists. - Richard P. Feynman", + "If you thought that science was certain — Well, that is just an error on \ + your part. - Richard P. Feynman", + "Talk is cheap, Show me the code. - Linus Torvalds", + "I like offending people, because I think people who get offended should be \ + offended. - Linus Torvalds", + "Only wimps use tape backup. REAL men just upload their important stuff on \ + ftp and let the rest of the world mirror it. - Linus Torvalds", + "Intelligence is the ability to avoid doing work, yet getting the work \ + done. - Linus Torvalds", + "Programming is not a science. Programming is a craft. - Richard Stallman", + "I did write some code in Java once, but that was the island in Indonesia. \ + - Richard Stallman", + "Free software' is a matter of liberty, not price. To understand the \ + concept, you should think of 'free' as in 'free speech,' not as in 'free \ + beer'. - Richard Stallman", + "You can use any editor you want, but remember that vi vi vi is the text \ + editor of the beast. - Richard Stallman", + "Free software is software that respects your freedom and the social \ + solidarity of your community. So it's free as in freedom. - Richard \ + Stallman", + "Sharing is good, and with digital technology, sharing is easy. - Richard \ + Stallman", +]; diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..9104f39 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,68 @@ +// This file is part of Locus <https://github.com/gemrest/locus>. +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +#[macro_export] +macro_rules! mount_page { + ($router:ident, $at:literal, $file:literal) => { + ($router).mount( + $at, + Box::new(|context| { + Response::Success( + Main { + body: include_str!(concat!("../content/pages/", $file, ".gmi")) + .into(), + hits: &crate::hits_from_route(context.url.path()), + quote: QUOTES.choose(&mut rand::thread_rng()).unwrap(), + commit: &format!("/tree/{}", env!("VERGEN_GIT_SHA")), + } + .to_string(), + ) + }), + ); + }; +} + +#[macro_export] +macro_rules! mount_file { + ($router:ident, $at:literal, $file:literal) => { + ($router).mount( + $at, + Box::new(|_| { + Response::SuccessFile(include_bytes!(concat!( + "../content/meta/", + $file + ))) + }), + ); + }; +} + +#[macro_export] +macro_rules! success { + ($body:expr, $context:ident) => { + Response::Success( + Main { + body: &$body, + hits: &crate::hits_from_route($context.url.path()), + quote: QUOTES.choose(&mut rand::thread_rng()).unwrap(), + commit: &format!("/tree/{}", env!("VERGEN_GIT_SHA")), + } + .to_string(), + ) + }; +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e88fd9b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,155 @@ +// This file is part of Locus <https://github.com/gemrest/locus>. +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +#![feature(once_cell)] +#![deny( + warnings, + nonstandard_style, + unused, + future_incompatible, + rust_2018_idioms, + unsafe_code +)] +#![deny(clippy::all, clippy::nursery, clippy::pedantic)] +#![recursion_limit = "128"] +#![allow(clippy::cast_precision_loss)] + +mod constants; +mod macros; +mod modules; + +#[macro_use] +extern crate log; + +use std::{lazy::SyncLazy, sync::Mutex}; + +use constants::QUOTES; +use pickledb::PickleDb; +use rand::seq::SliceRandom; +use tokio::time::Instant; +use windmark::{Response, Router}; +use yarte::Template; + +static DATABASE: SyncLazy<Mutex<PickleDb>> = SyncLazy::new(|| { + Mutex::new({ + if std::fs::File::open("locus.db").is_ok() { + PickleDb::load_json("locus.db", pickledb::PickleDbDumpPolicy::AutoDump) + .unwrap() + } else { + PickleDb::new_json("locus.db", pickledb::PickleDbDumpPolicy::AutoDump) + } + }) +}); + +#[derive(Template)] +#[template(path = "main")] +struct Main<'a> { + body: &'a str, + hits: &'a i32, + quote: &'a str, + commit: &'a str, +} + +fn hits_from_route(route: &str) -> i32 { + (*DATABASE.lock().unwrap()).get::<i32>(route).unwrap() +} + +#[windmark::main] +async fn main() -> Result<(), Box<dyn std::error::Error>> { + std::env::set_var("RUST_LOG", "windmark,locus=trace"); + pretty_env_logger::init(); + + let mut time_mount = Instant::now(); + let mut router = Router::new(); + let uptime = Instant::now(); + + router.set_private_key_file(".locus/locus_private.pem"); + router.set_certificate_chain_file(".locus/locus_pair.pem"); + router.set_pre_route_callback(Box::new(|stream, url, _| { + info!( + "accepted connection from {} to {}", + stream.peer_addr().unwrap().ip(), + url.to_string(), + ); + + let previous_database = (*DATABASE.lock().unwrap()).get::<i32>(url.path()); + + match previous_database { + None => { + (*DATABASE.lock().unwrap()) + .set::<i32>(url.path(), &0) + .unwrap(); + } + Some(_) => {} + } + + let new_database = (*DATABASE.lock().unwrap()).get::<i32>(url.path()); + + (*DATABASE.lock().unwrap()) + .set(url.path(), &(new_database.unwrap() + 1)) + .unwrap(); + })); + router.set_error_handler(Box::new(|_| { + Response::NotFound( + "The requested resource could not be found at this time. You can try \ + refreshing the page, if that doesn't change anything; contact Fuwn! \ + ([email protected])" + .into(), + ) + })); + router.mount( + "/uptime", + Box::new(move |context| { + success!( + &format!("# UPTIME\n\n{} seconds", uptime.elapsed().as_secs()), + context + ) + }), + ); + + info!( + "preliminary mounts took {}ms", + time_mount.elapsed().as_nanos() as f64 / 1_000_000.0 + ); + time_mount = Instant::now(); + + router.attach(modules::multi_blog); + + info!( + "blog mounts took {}ms", + time_mount.elapsed().as_nanos() as f64 / 1_000_000.0 + ); + time_mount = Instant::now(); + + mount_file!(router, "/robots.txt", "robots.txt"); + mount_file!(router, "/favicon.txt", "favicon.txt"); + mount_page!(router, "/", "index"); + mount_page!(router, "/contact", "contact"); + mount_page!(router, "/donate", "donate"); + mount_page!(router, "/gemini", "gemini"); + mount_page!(router, "/gopher", "gopher"); + mount_page!(router, "/interests", "interests"); + mount_page!(router, "/skills", "skills"); + + info!( + "static mounts took {}ms", + time_mount.elapsed().as_nanos() as f64 / 1_000_000.0 + ); + + router.run().await +} diff --git a/src/modules.rs b/src/modules.rs new file mode 100644 index 0000000..0067266 --- /dev/null +++ b/src/modules.rs @@ -0,0 +1,177 @@ +// This file is part of Locus <https://github.com/gemrest/locus>. +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +use std::{ + collections::HashMap, + fs::{self, read_dir}, + io::Read, +}; + +use rand::seq::SliceRandom; +use windmark::Response; + +use crate::{success, Main, QUOTES}; + +#[allow(clippy::too_many_lines)] +pub fn multi_blog(router: &mut windmark::Router) { + let paths = read_dir("content/pages/blog").unwrap(); + let mut blogs: HashMap<String, HashMap<_, _>> = HashMap::new(); + + for path in paths { + let blog = path.unwrap().path().display().to_string(); + let blog_paths = read_dir(&blog).unwrap(); + let mut entries: HashMap<_, String> = HashMap::new(); + + blog_paths + .map(|e| e.unwrap().path().display().to_string()) + .for_each(|file| { + let mut contents = String::new(); + + fs::File::open(&file) + .unwrap() + .read_to_string(&mut contents) + .unwrap(); + + entries.insert( + file.replace(&blog, "").replace(".gmi", "").replace( + { + #[cfg(windows)] + { + '\\' + } + + #[cfg(unix)] + { + '/' + } + }, + "", + ), + contents, + ); + }); + + blogs.insert( + blog + .replace( + { + #[cfg(windows)] + { + "content/pages/blog\\" + } + + #[cfg(unix)] + { + "content/pages/blog/" + } + }, + "", + ) + .split('_') + .map(|s| { + // https://gist.github.com/jpastuszek/2704f3c5a3864b05c48ee688d0fd21d7 + let mut c = s.chars(); + + match c.next() { + None => String::new(), + Some(f) => + f.to_uppercase() + .chain(c.flat_map(char::to_lowercase)) + .collect(), + } + }) + .collect::<Vec<_>>() + .join(" "), + entries, + ); + } + + let blog_clone = blogs.clone(); + + router.mount( + "/blog", + Box::new(move |context| { + success!( + &format!( + "# BLOGS ({})\n\n{}", + blog_clone.len(), + blog_clone + .iter() + .map(|(title, _)| title.clone()) + .collect::<Vec<_>>() + .into_iter() + .map(|i| { + format!( + "=> {} {}", + format_args!("/blog/{}", i.replace(' ', "_").to_lowercase(),), + i + ) + }) + .collect::<Vec<_>>() + .join("\n") + ), + context + ) + }), + ); + + for (blog, entries) in blogs { + let fixed_blog_name = blog.replace(' ', "_").to_lowercase(); + let entries_clone = entries.clone(); + let fixed_blog_name_clone = fixed_blog_name.clone(); + + router.mount( + &format!("/blog/{}", fixed_blog_name), + Box::new(move |context| { + success!( + &format!( + "# {} ({})\n\n{}", + blog.to_uppercase(), + entries_clone.len(), + entries_clone + .iter() + .map(|(title, _)| title.clone()) + .collect::<Vec<_>>() + .into_iter() + .map(|i| { + format!( + "=> {} {}", + format_args!( + "/blog/{}/{}", + fixed_blog_name_clone, + i.to_lowercase() + ), + i + ) + }) + .collect::<Vec<_>>() + .join("\n") + ), + context + ) + }), + ); + + for (title, contents) in entries { + router.mount( + &format!("/blog/{}/{}", fixed_blog_name, title.to_lowercase()), + Box::new(move |context| success!(contents, context)), + ); + } + } +} |