maj/site/src/main.rs

170 lines
4.4 KiB
Rust
Raw Normal View History

2020-07-27 23:49:39 +00:00
use async_std::task;
2020-08-01 20:47:34 +00:00
use dnd_dice_roller::{dice::Dice, error::DiceError};
2020-07-28 00:50:42 +00:00
use maj::{
2020-07-31 17:42:47 +00:00
gemini::{Builder, Node},
2020-07-28 00:50:42 +00:00
route, seg,
server::{Error, Handler as MajHandler, Request},
split, Response,
};
use percent_encoding::percent_decode_str;
2020-07-28 01:03:50 +00:00
use rustls::{
2020-07-31 17:42:47 +00:00
internal::pemfile::{certs, rsa_private_keys},
2020-07-28 01:03:50 +00:00
AllowAnyAnonymousOrAuthenticatedClient, Certificate, PrivateKey, RootCertStore, ServerConfig,
};
2020-08-01 20:47:34 +00:00
use std::{
fs::File,
io::{self, BufReader},
path::{Path, PathBuf},
str::FromStr,
sync::Arc,
};
2020-07-26 00:16:07 +00:00
use structopt::StructOpt;
#[derive(StructOpt, Debug)]
struct Options {
/// host to listen on
#[structopt(short = "H", long, env = "HOST", default_value = "0.0.0.0")]
host: String,
/// port to listen on
#[structopt(short = "p", long, env = "PORT", default_value = "1965")]
port: u16,
/// cert file
#[structopt(short = "c", long = "cert", env = "CERT_FILE")]
cert: PathBuf,
/// key file
#[structopt(short = "k", long = "key", env = "KEY_FILE")]
key: PathBuf,
2020-07-26 12:21:02 +00:00
/// static path
2020-07-31 17:38:33 +00:00
#[structopt(short = "s", long, env = "STATIC_PATH", default_value = "./static")]
static_path: PathBuf,
2020-07-26 12:21:02 +00:00
/// server hostname
#[structopt(
long = "hostname",
env = "SERVER_HOSTNAME",
2020-08-01 20:47:34 +00:00
default_value = "cetacean.club"
2020-07-26 12:21:02 +00:00
)]
hostname: String,
2020-07-26 00:16:07 +00:00
}
fn load_certs(path: &Path) -> io::Result<Vec<Certificate>> {
certs(&mut BufReader::new(File::open(path)?))
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert"))
}
fn load_keys(path: &Path) -> io::Result<Vec<PrivateKey>> {
rsa_private_keys(&mut BufReader::new(File::open(path)?))
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid key"))
}
2020-07-27 23:49:39 +00:00
fn main() -> Result<(), maj::server::Error> {
env_logger::init();
2020-07-26 00:16:07 +00:00
let opts = Options::from_args();
let certs = load_certs(&opts.cert)?;
let mut keys = load_keys(&opts.key)?;
2020-07-28 00:50:42 +00:00
log::info!(
"serving gemini://{} on {}:{}",
opts.hostname,
opts.host,
opts.port
);
2020-07-26 12:21:02 +00:00
2020-07-28 01:03:50 +00:00
let mut config = ServerConfig::new(AllowAnyAnonymousOrAuthenticatedClient::new(
RootCertStore::empty(),
));
2020-07-26 00:16:07 +00:00
config
.set_single_cert(certs, keys.remove(0))
.map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?;
2020-07-27 23:49:39 +00:00
task::block_on(maj::server::serve(
Arc::new(Handler {
2020-07-26 12:21:02 +00:00
hostname: opts.hostname,
files: maj::server::files::Handler::new(opts.static_path),
2020-07-27 23:49:39 +00:00
}),
2020-07-26 12:21:02 +00:00
config,
opts.host,
opts.port,
2020-07-27 23:49:39 +00:00
))?;
2020-07-26 00:16:07 +00:00
Ok(())
}
2020-07-26 12:21:02 +00:00
struct Handler {
hostname: String,
files: maj::server::files::Handler,
2020-07-26 12:21:02 +00:00
}
2020-07-26 00:16:07 +00:00
2020-08-01 20:47:34 +00:00
async fn dice(req: Request) -> Result<Response, Error> {
fn dice_roll<T: Into<String>>(roll: T) -> Result<String, DiceError> {
let mut dice = Dice::from_str(&roll.into())?;
2020-07-26 00:16:07 +00:00
2020-08-01 20:47:34 +00:00
if dice.number_of_dice_to_roll > 100 {
dice.number_of_dice_to_roll = 100;
}
2020-07-26 12:21:02 +00:00
2020-08-01 20:47:34 +00:00
if dice.sides > 100 {
dice.sides = 100
}
if dice.sides == 0 {
dice.sides = 6;
}
let res = dice.roll_dice();
let reply = format!(
"{}{} = {}\n",
res.dice_results,
match dice.modifier {
Some(amt) => format!(" + {}", amt),
None => "".into(),
},
res.final_result[0]
);
Ok(reply)
2020-07-28 01:21:24 +00:00
}
2020-07-28 01:03:50 +00:00
match req.url.query() {
2020-08-01 20:47:34 +00:00
None => Ok(Response::input(
"What do you want to roll? [n]dn[+n] [adv|dadv]",
)),
Some(q) => Ok({
2020-08-01 20:47:34 +00:00
let dice = percent_decode_str(q).decode_utf8()?;
let b = Builder::new()
.heading(1, "Dice Results")
.text("")
.text(format!("You rolled {} and you got:", dice))
.text("")
.preformatted(format!("{}", dice_roll(dice)?));
Response::render(b.build())
}),
}
}
2020-07-26 00:16:07 +00:00
#[async_trait::async_trait]
2020-07-28 00:50:42 +00:00
impl MajHandler for Handler {
async fn handle(&self, req: Request) -> Result<Response, Error> {
if req.url.has_host() && req.url.host_str().unwrap().to_string() != self.hostname {
return Ok(Response::no_proxy());
2020-07-26 12:21:02 +00:00
}
2020-07-28 00:50:42 +00:00
if req.url.path() == "" {
return Ok(Response::perm_redirect(format!(
"gemini://{}/",
self.hostname
)));
2020-07-26 00:16:07 +00:00
}
2020-07-28 00:50:42 +00:00
route!(req.url.path(), {
2020-08-01 20:47:34 +00:00
(/"dice") => dice(req).await;
2020-07-28 00:50:42 +00:00
});
2020-07-31 17:38:33 +00:00
self.files.handle(req).await
2020-07-26 00:16:07 +00:00
}
}