🚧 port to rust

This commit is contained in:
JMARyA 2022-11-12 00:41:51 +01:00
parent e5aa247f11
commit af3a052acb
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
24 changed files with 3125 additions and 564 deletions

71
src/pages/assets.rs Normal file
View file

@ -0,0 +1,71 @@
use actix_files::NamedFile;
use actix_web::*;
use std::io::Write;
// Bootstrap
async fn download_file(url: &str, file: &str) {
let content = reqwest::get(url).await.expect("couldn't download file");
std::fs::File::create(file)
.unwrap()
.write_all(&content.bytes().await.unwrap())
.unwrap();
}
pub(crate) async fn cache_bootstrap() {
std::fs::create_dir_all("./cache/fonts").expect("couldn't create cache dir");
download_file(
"https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css",
"./cache/bootstrap.min.css",
)
.await;
download_file(
"https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css",
"./cache/bootstrap-icons.css",
)
.await;
download_file(
"https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js",
"./cache/bootstrap.bundle.min.js",
)
.await;
download_file("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/fonts/bootstrap-icons.woff2?8d200481aa7f02a2d63a331fc782cfaf", "./cache/fonts/bootstrap-icons.woff2").await;
download_file("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/fonts/bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf", "./cache/fonts/bootstrap-icons.woff").await;
}
#[get("/bootstrap.min.css")]
pub(crate) async fn bootstrap_css() -> Result<NamedFile> {
Ok(NamedFile::open("./cache/bootstrap.min.css")?)
}
#[get("/bootstrap-icons.css")]
pub(crate) async fn bootstrap_icons() -> Result<NamedFile> {
Ok(NamedFile::open("./cache/bootstrap-icons.css")?)
}
#[get("/bootstrap.bundle.min.js")]
pub(crate) async fn bootstrap_js() -> Result<NamedFile> {
Ok(NamedFile::open("./cache/bootstrap.bundle.min.js")?)
}
#[get("/fonts/bootstrap-icons.woff2")]
pub(crate) async fn bootstrap_font1(_: HttpRequest) -> Result<NamedFile> {
Ok(NamedFile::open("./cache/fonts/bootstrap-icons.woff2")?)
}
#[get("/fonts/bootstrap-icons.woff")]
pub(crate) async fn bootstrap_font2(_: HttpRequest) -> Result<NamedFile> {
Ok(NamedFile::open("./cache/fonts/bootstrap-icons.woff")?)
}
// Assets
#[get("/assets/wall")]
pub(crate) async fn wallpaper() -> Result<NamedFile> {
Ok(NamedFile::open("/config/wall.avif")?)
}
#[get("/assets/me")]
pub(crate) async fn me_img() -> Result<NamedFile> {
Ok(NamedFile::open("/config/me.avif")?)
}

53
src/pages/func.rs Normal file
View file

@ -0,0 +1,53 @@
use reqwest::get;
pub fn is_browser(req: &actix_web::HttpRequest) -> bool {
let ua = req
.headers()
.get("user-agent")
.unwrap()
.to_str()
.unwrap()
.to_lowercase();
if ua.contains("chrome") || ua.contains("safari") || ua.contains("firefox") {
return true;
}
return false;
}
pub fn get_host(r: &actix_web::HttpRequest) -> String {
let res = r.headers().get("HOST").unwrap().to_str().unwrap();
return res.to_string();
}
pub fn get_host_address(r: &actix_web::HttpRequest) -> String {
let res = r.headers().get("HOST").unwrap().to_str().unwrap();
let res: Vec<&str> = res.split(":").collect();
let res = res.first().unwrap();
return res.to_string();
}
pub fn is_onion(r: &actix_web::HttpRequest) -> bool {
return get_host_address(r).ends_with("onion");
}
pub fn is_i2p(r: &actix_web::HttpRequest) -> bool {
return get_host_address(r).ends_with("i2p");
}
pub fn dynamic_link(
inner: &str,
normal_link: &str,
onion: Option<&str>,
i2p: Option<&str>,
req: &actix_web::HttpRequest,
) -> String {
if is_onion(req) {
let href = onion.unwrap_or(normal_link);
return format!("<a href={href}> {inner} </a>");
}
if is_i2p(req) {
let href = i2p.unwrap_or(normal_link);
return format!("<a href={href}> {inner} </a>");
}
return format!("<a href={normal_link}> {inner} </a>");
}

56
src/pages/html_fn.rs Normal file
View file

@ -0,0 +1,56 @@
use crate::config;
use crate::config::Config;
use actix_web::web::Data;
use actix_web::*;
pub(crate) async fn build_site(
content: String,
title: &str,
disable_color: bool,
shadow: bool,
config: &Data<Config>,
) -> HttpResponse<String> {
const BOOTSTRAP: &str = r#"
<link href="/bootstrap.min.css" rel="stylesheet">
<link href="/bootstrap-icons.css" rel="stylesheet">
<link href="/bootstrap.bundle.min.js" rel="stylesheet">
"#;
let mut c_class = "bg-dark text-white justify-content-center text-center".to_string();
let mut c_style = "".to_string();
let mut g_style = "a {text-decoration: none; font-weight: bold; color: white}".to_string();
if !disable_color {
if let (Some(fg), Some(bg)) = (config.fg_color(), config.bg_color()) {
c_class = "justify-content-center text-center".to_string();
c_style = format!("background: {bg}; color: {fg};");
g_style = format!("a {{text-decoration: none; font-weight: bold; color: {fg}}}");
}
}
if std::path::Path::new("/config/wall.avif").exists() {
c_style.push_str("background-image: url('assets/wall');background-size:cover;");
}
if shadow {
c_style.push_str("text-shadow: 1px 1px 3px black;");
}
let r = format!(
"
<!DOCTYPE html>
<html>
<head>
<title> {title} </title>
<meta name=\"viewport\" content=\"user-scalable=no, width=device-width, initial-scale=1.0\">
{BOOTSTRAP}
</head>
<body style=\"{c_style}\" class=\"{c_class}\">
<style>
{g_style}
</style>
{content}
</body>
</html>
"
);
return HttpResponse::Ok().message_body(r).unwrap();
}

184
src/pages/index.rs Normal file
View file

@ -0,0 +1,184 @@
use crate::pages::html_fn::build_site;
use crate::{config, pages};
use actix_web::http::header;
use actix_web::web::{Form, Json};
use actix_web::*;
use serde::{Deserialize, Serialize};
use std::io::Read;
#[derive(Serialize, Deserialize, Debug)]
pub struct MessageForm {
msg_name: String,
message: String,
}
#[post("/message")]
pub async fn message_post(r: HttpRequest, f: Form<MessageForm>) -> impl Responder {
let config: &web::Data<config::Config> = r.app_data().unwrap();
let cipher = crate::msg::encrypt(f.message.to_string());
crate::msg::save_msg(cipher, &f.msg_name.to_string());
crate::notification::notify(
&format!("New Message from {}", f.msg_name.to_string()),
"New Message",
config.clone(),
);
return HttpResponse::Found()
.header("Location", "/message")
.finish();
}
#[get("/message")]
pub async fn message_page(r: HttpRequest) -> impl Responder {
let config: &web::Data<config::Config> = r.app_data().unwrap();
let host = pages::func::get_host(&r);
let resp = format!(
r#"
<div class="container" style="margin-top: 25px"><h1>Message</h1>
<br>
<form action="http://{host}/message" method="post" autocomplete="off">
<input value="" type="text" required name="msg_name" placeholder="Name" class="form-control bg-dark text-white" style="margin-bottom: 15px">
<textarea placeholder="Message" required name="message" cols="10" rows="10" class="form-control bg-dark text-white" style="margin-bottom: 15px;">
</textarea>
<input value="Send Message" type="submit" required name="submit" class="btn btn-danger text-white text-decoration-none">
</form>
</div>
"#
);
return pages::html_fn::build_site(resp, "Message", false, true, config).await;
}
#[get("/mirrors.txt")]
pub async fn mirrors(r: HttpRequest) -> impl Responder {
let config: &web::Data<config::Config> = r.app_data().unwrap();
if let Ok(mirror_file) = std::fs::File::open("/config/mirrors.txt") {
let content = std::io::read_to_string(mirror_file).unwrap();
if pages::func::is_browser(&r) {
let resp = format!(
r#"
<div style="margin: 25px;">
<pre> {content} </pre>
</div>
"#
);
return pages::html_fn::build_site(resp, "Mirrors", false, true, config).await;
}
let res: HttpResponse<String> = HttpResponse::Ok().message_body(content).unwrap();
return res;
}
let res: HttpResponse<String> = HttpResponse::NotFound()
.message_body("".to_string())
.unwrap();
return res;
}
#[get("/public_key")]
pub async fn public_key(r: HttpRequest) -> impl Responder {
if pages::func::is_browser(&r) {
let config: &web::Data<config::Config> = r.app_data().unwrap();
let host = format!("http://{}", pages::func::get_host(&r));
let key = std::io::read_to_string(std::fs::File::open("/config/pub.key").unwrap())
.unwrap()
.replace("\n", "<br>");
let resp = format!(
r#"
<div class="container" style="margin-top: 25px">
<div class="alert alert-info">
<b>To Import: </b>
<span style="display: block;font-family: monospace,monospace;margin-top: 10px; font-size: 20px;overflow-wrap: break-word;">
curl -sL "{host}/public_key"|gpg --import</span>
</div></div>
<div class="container card bg-primary"><p> {key} </p></div>
"#
);
return pages::html_fn::build_site(resp, "Public Key", true, false, config).await;
}
if let Ok(key_f) = std::fs::File::open("/config/pub.key") {
if let Ok(key_data) = std::io::read_to_string(key_f) {
let res: HttpResponse<String> = HttpResponse::Ok()
.insert_header(header::ContentType::plaintext())
.message_body(key_data)
.unwrap();
return res;
}
}
let res: HttpResponse<String> = HttpResponse::NotFound()
.message_body("".to_string())
.unwrap();
return res;
}
fn build_information_block(conf: &web::Data<config::Config>) -> String {
let name = conf.name().unwrap();
format!(
r#"
<div class="container border-dark" style="margin-top: 20px">
<img src="/assets/me" height=200 width=200 alt="Me" class="rounded">
<br><br>
<h1> {name} </h1>
<hr>
</div>
"#
)
}
fn build_contact_block(conf: &web::Data<config::Config>) -> String {
if let Some(email) = conf.email() {
let pgp_key_message = match std::path::Path::new("/config/pub.key").exists() {
true => {
r#"
<a href="/public_key"> My PGP Key </a>
<br>
<a href="/message"> Write a message </a>
<br><br>
"#
}
false => "",
};
return format!(
r#"
<div class="container border-dark">
<h1> <span class="bi bi-person-lines-fill" style="vertical-align: middle;"> </span> Contact </h1>
<tr>
{pgp_key_message}
<a href="mailto:{email}"> {email} </a>
<hr>
</div>
"#
);
} else {
return "".to_string();
}
}
fn build_donation_block(conf: &web::Data<config::Config>) -> String {
if let Some(xmr_addr) = conf.xmr_address() {
format!(
r#"
<div class="container" style="margin-top: 20px">
<h1> <span class="bi bi-cash-coin"> </span> Donation </h1>
<tr>
<p> <b> Monero: </b> <span style="color: orange;overflow-wrap: break-word;"> {xmr_addr} </span> </p>
</div>
"#
)
} else {
return "".to_string();
}
}
#[get("/")]
pub(crate) async fn index(conf: web::Data<config::Config>) -> impl Responder {
let information_block = build_information_block(&conf);
let contact_block = build_contact_block(&conf);
let donation_block = build_donation_block(&conf);
let content = format!(
"
{information_block}
{contact_block}
{donation_block}
"
);
let r = crate::pages::html_fn::build_site(content, "About Me", false, true, &conf).await;
return r;
}

4
src/pages/mod.rs Normal file
View file

@ -0,0 +1,4 @@
pub mod assets;
pub mod func;
pub mod html_fn;
pub mod index;