Initial support to Gemini protocol

master
kirbylife 2024-06-25 20:48:36 -06:00
parent 42b4285ae9
commit 90513d4007
9 changed files with 314 additions and 7 deletions

View File

@ -16,3 +16,7 @@ comrak = "0.24.1"
tera = "1.19.1"
isbot = "0.1.3"
once_cell = "1.19.0"
windmark = { version = "0.3.10", features = ["response-macros", "logger"] }
tokio = { version = "1.26.0", features = ["full"] }
md2gemtext = "0.1.0"
glob = "0.3.1"

31
cert.pem 100644
View File

@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIUfrrLPPEvujX8T/aKVSwJKr1E00gwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDA2MjUwMTQyNThaFw0yNDA3
MjUwMTQyNThaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQDYa/XTMXG6dG+LBlQMvc9qk6YG0DfwW//na1DYdS5e
XA2jjhPNJ/Fzro51D8zctUuN6RoiZS1w53REBs+3pSdX0Orfu3grdwSXbGz+pkJ1
pqaaseSKH7xp0bYFefWG8mhwdOxSA5h/O2Y62YJ1/2UGNmM6zyJo99Be05HnAc7c
RK6LcS1DtzsNQ/DDNmvQvHEx88AuF5F99JqzYoioaavDzzzIcRvXl8gvykPw74+i
T4jZ/hKS53y2v17ArQRz/eQO5EiDk2b8fj6BUuTUkjvOyHY31FjVlicMkOM0l2tE
cE/rj1HIONkzjBwOlVBobNovyVZTTXHa+NN83QbEy9rbo8na3IonQiUARzTabfR8
ZuHOLcgnH4JZI20IeLIsvcHBH762UXbFBS9vHVvY49MnclPudNQV4PhAqBGkgzos
4jLec7jxqfNYQ6IHZDKnJcFPGMgQwxXpuk3x6jGnsDbb39E+Dqfw+645GpsSxBiz
zRS3A+q8I3jZaIiUbJC5YY0cTMUUV16QjfvUnln/JWZAc9IwDkr/AA6oeVZztD5w
PQ6RSVLPKhYCFpHTtYY2dyuGZvLqmfjuTWl1DLI8Aehmr603r3Hvmry00UeDHmDB
AzLtFX9UoTpfRwQFSZUfZHD7NrHOg4C4PGYyYy5YOImi0LTfyv5gREu98Oj/FxoG
6QIDAQABo1MwUTAdBgNVHQ4EFgQUcFc2bBA7ElhkULl2k/1P7EUas6owHwYDVR0j
BBgwFoAUcFc2bBA7ElhkULl2k/1P7EUas6owDwYDVR0TAQH/BAUwAwEB/zANBgkq
hkiG9w0BAQsFAAOCAgEAbaaC55Ru2kbMmzCNvbxU2VndbOUsr+mrEnIFPWnvWQ2q
1h7k5jAKEziTl+UvnBMkHYa9b8idhneevxp53jH8rfmxBmWSO4cIP424xAE1ThhR
S72sXELRwLUuplWfwdYlyPzt6QDv/GH5pA8qbrzFBmmE7p2ECj3xa5M+yQI6pSl7
KtUY/0nB4C69xIEJadukwSQFZUxARlhT7bXUDJAZnfhzmWZBij/iPtNQeAjy1sDm
rVzAQGCUAltIDa1CMhOyrmpFMbSNKsbX7tiWHhEKoDe71QmxE1ccuappDhWUusp7
mISFunE7H258lrXl6VHvkBVwvUlppWOEtP+TxoxZuxpZplV0HQUArEAAtC0+JCkG
up0nrNElgu+7qgX3Qm5ufU/p1lqEozPt3mvUV5CsZqKm2CjmtzrFyxEHC/UpnEhX
dzxOTZ6ZBjoBwwBsG2PvAO+wu438EeBcfwzKlP20cX1s2Bq/CjzZNlxAeK8iCvf2
TbfuyojwIcU7ceLIXxZjBt5UmaR2w5nYCRxKWpNHxGoPGZFfSET2XbvZy4WET6bv
a7WNXgSiE866qMrw0u57tiI1Zmp16sV5oovdSdpZjjzHNO7EtmryVANIkmNvQgLl
WyvFTgr5Q/GISBS4cayyiIwMn+oMfKaSW/kVspT8+QQi+crfr/Qv7s7If0yTKuI=
-----END CERTIFICATE-----

54
key.pem 100644
View File

@ -0,0 +1,54 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
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
mwFIH5TpX3CFJXdlZybNcA==
-----END ENCRYPTED PRIVATE KEY-----

86
src/bin/gemini.rs 100644
View File

@ -0,0 +1,86 @@
use codigocomentado::*;
use controllers::posts;
use dotenv::dotenv;
use misc::{gen_title, replace_tables};
use tera::Tera;
use tokio::sync::OnceCell;
static TERA: OnceCell<Tera> = OnceCell::const_new();
async fn render<S: AsRef<str>>(name: S, context: &tera::Context) -> Result<String, ()> {
let tera = TERA
.get_or_init(|| async {
let mut tera = Tera::default();
for path in glob::glob("templates/gemini/*").unwrap().flatten() {
let raw_path = path.clone();
let filename = raw_path
.file_name()
.unwrap() // This should be safe (?
.to_str()
.unwrap() // ._.?
.split('.')
.next();
tera.add_template_file(path, filename).unwrap()
}
tera
})
.await;
tera.render(name.as_ref(), context).map_err(|_| ())
}
#[windmark::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenv().ok();
windmark::router::Router::new()
.set_private_key_file("key.pem")
.set_certificate_file("cert.pem")
.set_languages(["es"])
.enable_default_logger(true)
.set_fix_path(true)
.mount("/", move |_| {
let (posts, _) = posts::get_posts(None);
let mut context = tera::Context::new();
context.insert("title", &gen_title());
context.insert("posts", &posts);
async move {
windmark::response::Response::success(render("index", &context).await.unwrap())
}
})
.mount("/post/:title", |request| {
let title_splited: Vec<&str> = request
.parameters
.get("title")
// This should be safe because its requires the parameter to enter to the route
.unwrap()
.split('-')
.collect();
let id = title_splited.last().unwrap().parse().unwrap_or(-1);
async move {
let result = match posts::get_post(id) {
Ok(post) => {
let mut context = tera::Context::new();
let content = replace_tables(post.content)
.replace("<pre>", "```")
.replace("</pre>", "```");
context.insert("header", &gen_title());
context.insert("title", &post.title);
context.insert("created_at", &post.created_at);
context.insert("content", &content);
render("post", &context).await.unwrap()
}
Err(_) => "No se encontró ningún Post :c".into(),
};
windmark::response::Response::success(result)
}
})
.set_error_handler(|_| {
windmark::response::Response::permanent_failure("This route does not exist!")
})
.run()
.await
}

12
src/lib.rs 100644
View File

@ -0,0 +1,12 @@
pub mod admin;
pub mod contexts;
pub mod controllers;
pub mod misc;
pub mod models;
mod schema;
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate rocket;

View File

@ -1,15 +1,12 @@
pub mod admin;
pub mod contexts;
pub mod controllers;
pub mod misc;
pub mod models;
mod schema;
#[macro_use]
extern crate rocket;
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate rocket;
use codigocomentado::*;
use comrak::{markdown_to_html, Options};
use controllers::posts;
use dotenv::dotenv;

View File

@ -20,3 +20,95 @@ pub fn gen_title() -> String {
let title = str::replace(title_fmt, "{}", "CódigoComentado");
title.to_string()
}
pub fn ascii_table(raw_table: String) -> String {
let raw_table = raw_table.trim();
let mut new_table: Vec<Vec<Vec<String>>> = Vec::new();
let mut max_width: Option<Vec<usize>> = None;
let mut result = String::new();
for line in raw_table.lines() {
let items: Vec<String> = line.split('|').map(|s| s.to_string()).collect();
let mut new_items: Vec<Vec<String>> = vec![Vec::new(); items.len()];
if max_width.is_none() {
max_width = Some(vec![0; items.len()]);
}
for (n, item) in items.iter().enumerate() {
let item = item
.replace("<b>", "")
.replace("</b>", "")
.replace("<pre>", "")
.replace("</pre>", "")
.replace("\\", "");
let split_items: Vec<String> = item.split("<br>").map(|s| s.to_string()).collect();
new_items[n].clone_from(&split_items);
let max_local = split_items.iter().map(|s| s.len()).max().unwrap_or(0);
if let Some(ref mut mw) = max_width {
mw[n] = std::cmp::max(mw[n], max_local);
}
}
new_table.push(new_items);
}
for row in new_table {
let max_height = row.iter().map(|col| col.len()).max().unwrap_or(0);
for index in 0..(row.len() * max_height) {
let index_subrow = index / row.len();
let index_col = index % row.len();
let text = if let Some(col) = row.get(index_col) {
if let Some(&ref text) = col.get(index_subrow) {
text
} else {
&String::from("")
}
} else {
&String::from("")
};
let text = text.trim_end();
if let Some(ref mw) = max_width {
result.push_str(&format!(
"{:width$}{}",
text,
if index_col < row.len() - 1 { "|" } else { "\n" },
width = mw[index_col]
));
}
}
}
format!("```\n{}\n```\n", result)
}
pub fn replace_tables(text: String) -> String {
let mut result = String::new();
let mut table: Option<String> = None;
let mut is_code = false;
for line in text.lines() {
if line.starts_with('|') & !is_code {
if table.is_none() {
table = Some(String::new());
}
if let Some(ref mut tab) = table {
tab.push_str(&format!("{}\n", line));
}
} else {
let temp = match table {
Some(ref tab) => {
println!("raw:\n{}", tab);
let new_table = ascii_table(tab.to_string());
println!("formated:\n{}", new_table);
table = None;
new_table
}
None => line.to_string(),
};
result.push_str(&temp);
result.push('\n');
}
}
result
}

View File

@ -0,0 +1,24 @@
# {{ title }}
¡Hola!, acabas de aterrizar en mi cápsula, puedes tomar asiento donde gustes.
Me presento, en el mundo de los internets decidí que mi apodo sería "kirbylife" y de vez en cuando me gusta escribir sobre cosas de programación y todo lo relacionado con las computadoras en general, y para hacerlo me creé un blog en la web de los hipertextos y por fin decidí a traerla a este otro espacio de internet, espero les guste lo que leen y, con un poco de suerte, le encuentren utilidad.
## Artículos
{% for post in posts %}
=> /post/{{ post.title | slugify }}-{{ post.id }} {{ post.title }}
{{ post.content | split(pat=" ") | slice(end=30) | join(sep=" ") | split(pat="#") | slice(end=1) | join(sep="") | replace(from="\n", to="") | trim }}...
{% endfor %}
## Info
Este blog está escrito en el lenguaje de programación Rust, utilizando el framework Windmark, con el editor Emacs y cuenta con feeds RSS/Atom.
=> https://blog.kirbylife.dev/feed.xml RSS
=> https://blog.kirbylife.dev/atom.xml Atom
## Contacto
En caso de que quieran contactarme por la razón que sea, pueden hacerlo por cualquiera de estos medios:
Correo electrónico
* hola(at)kirbylife.dev
Mastodon
* @kirbylife@mstdn.mx
Telegram
* @kirbylife

View File

@ -0,0 +1,7 @@
# {{ header }}
=> / Volver al inicio
# {{ title }}
Publicado el: {{ created_at | date(format="%Y-%m-%d") }}
{{ content | striptags }}