Read text from TOML config file

This commit is contained in:
~erin 2022-04-01 00:23:59 -04:00
parent b4856487b3
commit 4d0fb59e33
No known key found for this signature in database
GPG Key ID: DA70E064A8C70F44
6 changed files with 363 additions and 36 deletions

1
Cargo.lock generated
View File

@ -1406,6 +1406,7 @@ dependencies = [
"serde",
"sled",
"tera",
"toml",
"uuid",
]

40
config.toml Normal file
View File

@ -0,0 +1,40 @@
title = "Catgirl Pharmacy"
description = "Nyaa~"
# label - displayed in navigation bar
# title - displayed on page
[index]
label = "Home"
href = "/"
title = "The best source for certified gay HRT & supplies"
[about]
label = "About"
href = "/about/"
title = "About Us"
[contact]
label = "Contact"
href = "/contact/"
title = "Contact Us"
[products]
label = "Products"
href = "/products/"
title = "Our Products"
[products.estrogens]
label = "Estrogens"
title = "Estrogens"
href = "/products/#estrogens"
[products.anti_androgens]
label = "Anti-Androgens"
title = "Anti-Androgens"
href = "/products/#aa"
[products.progestogens]
label = "Progestogens"
title = "Progestogens"
href = "/products/#progestogens"

View File

@ -1,8 +1,9 @@
use serde::{Deserialize, Serialize};
use std::io::Write;
use std::io::{Read, Write};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Mutex;
use tera::{Context, Tera};
use toml::Value;
#[derive(Serialize)]
struct NavLink {
@ -11,7 +12,41 @@ struct NavLink {
active: AtomicBool,
}
#[derive(Serialize, Deserialize)]
struct Config {
title: String,
description: String,
index: Page,
about: Page,
contact: Page,
products: Products,
}
#[derive(Serialize, Deserialize)]
struct Products {
label: String,
href: String,
title: String,
estrogens: Page,
anti_androgens: Page,
progestogens: Page,
}
#[derive(Serialize, Deserialize)]
struct Page {
label: String,
href: String,
title: String,
}
pub fn render_pages() {
// Read in TOML
let mut config_file = std::fs::File::open("config.toml").unwrap();
let mut contents = String::new();
config_file.read_to_string(&mut contents).unwrap();
let config: Config = toml::from_str(&contents).unwrap();
println!("{}", config.index.href);
// Use globbing
let tera = match Tera::new("templates/**/*.html") {
Ok(t) => t,
@ -23,79 +58,107 @@ pub fn render_pages() {
// Setup basic elements
let nav_home = NavLink {
href: "/".to_string(),
text: "Home".to_string(),
href: config.index.href,
text: config.index.label.clone(),
active: AtomicBool::new(true),
};
let nav_about = NavLink {
href: "/about/".to_string(),
text: "About".to_string(),
href: config.about.href,
text: config.about.label.clone(),
active: AtomicBool::new(false),
};
let nav_products = NavLink {
href: "/products/".to_string(),
text: "Products".to_string(),
href: config.products.href,
text: config.products.label.clone(),
active: AtomicBool::new(false),
};
let nav_contact = NavLink {
href: "/contact/".to_string(),
text: "Contact".to_string(),
href: config.contact.href,
text: config.contact.label.clone(),
active: AtomicBool::new(false),
};
let nav_elements = vec![nav_home, nav_about, nav_products, nav_contact];
let estrogens = NavLink {
href: "/products/#estrogens".to_string(),
text: "Estrogens".to_string(),
href: config.products.estrogens.href,
text: config.products.estrogens.label.clone(),
active: AtomicBool::new(false),
};
let aa = NavLink {
href: "/products/#aa".to_string(),
text: "Anti-Androgens".to_string(),
href: config.products.anti_androgens.href,
text: config.products.anti_androgens.label.clone(),
active: AtomicBool::new(false),
};
let progestogens = NavLink {
href: "/products/#progestogens".to_string(),
text: "Progestogens".to_string(),
href: config.products.progestogens.href,
text: config.products.progestogens.label.clone(),
active: AtomicBool::new(false),
};
let product_classes = vec![estrogens, aa, progestogens];
// Render pages
let index_render = RenderObject {
url: "index.html".to_string(),
tera: tera.clone(),
subtitle: &config.index.title,
active_page: &config.index.label,
title: &config.title,
description: &config.description,
};
render_page(
// Home
"index.html".to_string(),
tera.clone(),
"The best source for certified gay HRT & supplies".to_string(),
"Home".to_string(),
index_render,
&nav_elements,
&product_classes,
);
let about_render = RenderObject {
url: "about/index.html".to_string(),
tera: tera.clone(),
subtitle: &config.about.title,
active_page: &config.about.label,
title: &config.title,
description: &config.description,
};
render_page(
// About
"about/index.html".to_string(),
tera.clone(),
"About Us".to_string(),
"About".to_string(),
about_render,
&nav_elements,
&product_classes,
);
let contact_render = RenderObject {
url: "contact/index.html".to_string(),
tera: tera.clone(),
subtitle: &config.contact.title,
active_page: &config.contact.label,
title: &config.title,
description: &config.description,
};
render_page(
// About
contact_render,
&nav_elements,
&product_classes,
);
}
fn render_page(
struct RenderObject<'a> {
url: String,
tera: Tera,
subtitle: String,
active_page: String,
nav_elements: &Vec<NavLink>,
product_classes: &Vec<NavLink>,
) {
let mut output_file = std::fs::File::create(format!("static/{}", url)).expect("create failed");
subtitle: &'a str,
active_page: &'a str,
title: &'a str,
description: &'a str,
}
fn render_page(data: RenderObject, nav_elements: &[NavLink], product_classes: &[NavLink]) {
let mut output_file =
std::fs::File::create(format!("static/{}", data.url)).expect("create failed");
for i in nav_elements {
if i.text == active_page {
if i.text == data.active_page {
i.active.store(true, Ordering::Relaxed);
} else {
i.active.store(false, Ordering::Relaxed);
@ -103,14 +166,20 @@ fn render_page(
}
let mut context = Context::new();
context.insert("title", "Catgirl Pharmacy");
context.insert("description", "Meow");
context.insert("subtitle", &subtitle);
context.insert("title", data.title);
context.insert("description", data.description);
context.insert("subtitle", &data.subtitle);
for i in nav_elements {
context.insert(&i.text.to_ascii_lowercase(), &i);
}
context.insert("product_classes", &product_classes);
if data.active_page == "Contact" {
context.insert("is_contact", &true);
} else {
context.insert("is_contact", &false);
}
output_file
.write_all(tera.render("index.html", &context).unwrap().as_bytes())
.write_all(data.tera.render(&data.url, &context).unwrap().as_bytes())
.expect("write failed");
}

View File

@ -0,0 +1,82 @@
<!DOCTYPE html>
<html lang="en">
<head>
{# head content #}
<title>{{ title }}</title>
<!-- css style -->
<link rel="preload" href="/style.css" as="style">
<link rel="stylesheet" href="/style.css">
<!-- metadata -->
<meta charset="UTF-8">
<meta name="description" content="{{ description }}">
<!-- link preview card -->
<meta name="og:title" content="{{ title }}">
<meta name="twitter:title" content="{{ title }}">
<meta name="og:description" content="{{ description }}">
<meta name="twitter:description" content="{{ description }}">
<!-- display settings & favicon -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="/assets/pill.svg">
</head>
<body>
<h1>{{ title }}</h1>
{# Navigation #}
<nav class="menu">
<ul>
<li><a {% if home.active %}class="active"{% endif %} href="{{ home.href }}">{{ home.text }}</a></li>
<li><a {% if about.active %}class="active"{% endif %} href="{{ about.href }}">{{ about.text }}</a></li>
<li class="dropdown">
<a {% if products.active %}class="active"{% endif %} href="{{ products.href }}" class="dropbtn">{{ products.text }}</a>
<div class="dropdown-content">
{% for product in product_classes %}
<a href="{{product.href}}">{{product.text}}</a>
{% endfor %}
</div>
</li>
<li><a {% if contact.active %}class="active"{% endif %} href="{{ contact.href }}">{{ contact.text }}</a></li>
</ul>
</nav>
<h2>{{ subtitle }}</h2>
{# articles #}
<p>We're a queer, anarcho-communist, collectively-owned pharmacy.<br>
We produce & distribute high-quality and cheap HRT, medical equipment, and other drugs.<br>
Please go to our <a class="inline" href="/contact">contact</a> page if you have any questions, or would like to place an order.</p>
<p>By purchasing our products, you're helping us manufacture and distribute HRT to people.</b>
We need to pay for raw ingredients, tools, and other supplies.<br>
We are not a large pharmaceutical company, just a small group of queers who enjoy doing this.</p>
<p>This website is optimized for speed & privacy.<br>
We will never share your information with any third-parties.<br>
We only keep what details are necessary.<br>
We encourage the use of encryption to secure communications.<br>
This site can be used completely without javascript.
There are only some optional scripts on product pages to automatically fetch price/stock info.<br>
It only makes one GET request per page, on the same domain of this site.
If you are disabling javascript, buttons will appear so that you can manually fetch the info.<br>
A single private cookie is used on the contact page to store the CAPTCHA answer.</p>
<h3>Reviews</h3>
<p>Trust is important for any sort of online market, however we don't, and do not plan to, have any on-site review/comment system.<br>
Not only would this require lot's of moderation to prevent abuse, but there's nothing stopping us from creating fake reviews.<br>
We'd reccomend checking reviews on other sites, or getting info from a trusted person. This way the review can be trusted more.<br>
We encourage you to create reviews of us though! It will help our reputation, and help spread us to help more people.</p>
<h3>Ordering</h3>
<p>You can place an order by email, XMPP, or through our website.<br>
For payment, we only accept Monero (XMR) at the moment.</p>
<p>In your message, please specify the products & amounts you wish to purchase.<br>
We will respond with the exact amount to pay, as well as the address to send payment to.</p>
<p>If you cannot afford it, contact us and we can work something out. We will try our best to make HRT affordable and available to everyone who needs it<br>
We should also be able to make custom dosages/amounts.</p>
</html>

View File

@ -0,0 +1,112 @@
<!DOCTYPE html>
<html lang="en">
<head>
{# head content #}
<title>{{ title }}</title>
<!-- css style -->
<link rel="preload" href="/style.css" as="style">
<link rel="stylesheet" href="/style.css">
<!-- metadata -->
<meta charset="UTF-8">
<meta name="description" content="{{ description }}">
<!-- link preview card -->
<meta name="og:title" content="{{ title }}">
<meta name="twitter:title" content="{{ title }}">
<meta name="og:description" content="{{ description }}">
<meta name="twitter:description" content="{{ description }}">
<!-- display settings & favicon -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="/assets/pill.svg">
</head>
<body>
<h1>{{ title }}</h1>
{# Navigation #}
<nav class="menu">
<ul>
<li><a {% if home.active %}class="active"{% endif %} href="{{ home.href }}">{{ home.text }}</a></li>
<li><a {% if about.active %}class="active"{% endif %} href="{{ about.href }}">{{ about.text }}</a></li>
<li class="dropdown">
<a {% if products.active %}class="active"{% endif %} href="{{ products.href }}" class="dropbtn">{{ products.text }}</a>
<div class="dropdown-content">
{% for product in product_classes %}
<a href="{{product.href}}">{{product.text}}</a>
{% endfor %}
</div>
</li>
<li><a {% if contact.active %}class="active"{% endif %} href="{{ contact.href }}">{{ contact.text }}</a></li>
</ul>
</nav>
<h2>{{ subtitle }}</h2>
{# articles #}
<p>Please use our <a class="inline" href="/pgp.txt">PGP key</a> if contacting us through email, or OMEMO for XMPP.<br>
We reccomend using the <a class="inline" href="https://gajim.org/">Gajim</a> XMPP client. You will need to configure it to go through tor.<br>
<p>We currently only have an XMPP account on our locally ran server, so if you want to contact us there your server must be able to connect to onion services, or you can request to register on our server.<br>
You can also use <a class="inline" href="https://github.com/FiloSottile/age">age</a> to encrypt messages if you want.<br>
Our age key is <code>age13ukug0w2drc3k7x35r88rzjwuwt3dtrrqx0mvzk4uet35w5a3vysdud69t</code><br>
Our PGP fingerprint is <code>28EE 9DAC B217 1959 E8A4 0321 5533 5B9E BFC6 535B</code><br>
Our OMEMO fingerprint is<br>
<code>
7C7D9438 DDCC620E EE3A858F 7D926374
F65DBE83 8EF48DE1 40D6A12E 3A97D62A
</code>
</p>
<a href="mailto:hrt@[REDACTED]">hrt@[REDACTED]</a>
<a href="xmpp:pharma@zwiybhyhqdamyjnjzmn34q6u2lnbikvlv73gfxeiks7ymzrzccy6qgyd.onion">hrt@zwiybhyhqdamyjnjzmn34q6u2lnbikvlv73gfxeiks7ymzrzccy6qgyd.onion</a>
<h3>Web Order</h3>
<p>Use whatever name you'd prefer for us to refer to you by.<br>
Your email is needed so we can send you confirmation/info of your order. (XMPP address also accepted)<br>
Feel free to link to somewhere we can get your PGP key if you have one as well.<br>
Required fields are followed by <strong><abbr title="required">*</abbr></strong>.</p>
{# For Contact Page #}
{% if is_contact %}
<form action="/api/order" method="post" class="order-form">
<div class="order-form">
<label for="name">Name: </label>
<input type="text" name="name" id="name">
</div>
<div class="order-form">
<label for="email">Email/XMPP: <abbr title="required" aria-label="required">*</abbr></label>
<input type="email" name="email" id="email">
</div>
<div class="order-form">
<label for="msg">Message: <abbr title="required" aria-label="required">*</abbr></label>
<textarea id="msg" name="user_message">Enter your order here, as well as any notes. Make sure to specify dosages & amounts.</textarea>
</div>
<div class="order-form">
<label for="key">Encryption Key: </label>
<textarea id="key" name="encryption_key">Enter your PGP or age key here.</textarea>
</div>
<div class="order-form">
<label for="type">Payment Type: </label>
<select id="type" name="payment_type">
<option selected value="xmr">Monero (XMR)</option>
<option disabled value="btc">Bitcoin (BTC)</option>
<option disabled value="eth">Ethereum (ETH)</option>
</select>
</div>
<div class="order-form">
<img class="captcha" src="/api/captcha" width="200" height="100">
</div>
<div class="order-form">
<label for="captcha">Captcha Text: <abbr title="required" aria-label="required">*</abbr></label>
<input type="text" id="captcha" name="captcha">
</div>
<div class="order-form">
<button class="order-form" type="submit">Order</button>
</div>
</form>
{% endif %}
</html>

View File

@ -46,4 +46,27 @@
<h2>{{ subtitle }}</h2>
{# articles #}
<h3>Stay Safe!</h3>
<p>Check what URL you're on! The only valid URL's are <a class="inline" href="/mirrors.txt">here</a> according to the dark.fail <a class="inline" href="https://dark.fail/spec/omg.txt">Onion Mirror Guidelines</a> <a class="inline" href="http://darkfailenbsdla5mal2mxn2uz66od5vtzd5qozslagrfzachha3f3id.onion/spec/omg.txt">(mirror)</a>.<br>
That message will be signed by our <a class="inline" href="/pgp.txt">PGP</a> key, along with a <a class="inline" href="/canary.txt">canary</a>, which you can verify <a class="inline" href="https://dark.fail/pgp">here</a> <a class="inline" href="http://darkfailenbsdla5mal2mxn2uz66od5vtzd5qozslagrfzachha3f3id.onion/pgp">(mirror)</a>.</p>
<p>Monero is the reccomended cryptocurrency for privacy. <a class="inline" href="http://j6s7ohobyvhy3jnhocw5agl7iwcmgi5zm3d6isjl62wflufibox4exid.onion/book-club/cryptonote.pdf">(cryptonote)</a><br>
Only use the address we send to you directly, to ensure you're not being scammed.<br>
</p>
<p>Please use PGP for sending/recieving messages to ensure their authenticity, and to protect the contents.<br>
Age can also be used for encryption, but cannot be used for signing.<br>
While email is insecure, it is the easist communication platform, and most people have it, which is why we use it.<br>
And with PGP, while still not the best, is still good enough for our purposes.<br>
However if you would like, you can contact us over XMPP using OMEMO for more security.</p>
<h3>Privacy Policy</h3>
<p><b>Note: This is not a legal document!</b></p>
<p>IP's on clearnet site are stored for maximum 1 week before deletion, for anti-spam purposes.<br>
Contact details, messages, etc. are stored in plaintext. You can request this info to be deleted.<br>
Messages sent over email/xmpp will be subject to whatever security/privacy policies the servers used have.<br>
Use encryption to prevent the site admins from reading messages.<br>
Order details and addresses are stored only for as long as needed.<br>
Information is not accessible to anyone other that the site admins.</p>
</html>