This commit is contained in:
IMK 2024-09-30 22:00:53 +02:00
commit 0f81091c0a
16 changed files with 2473 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

2049
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

16
Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "blog"
version = "0.1.0"
edition = "2021"
[dependencies]
actix-files = "0.6.6"
actix-web = "4.9.0"
env_logger = "0.11.5"
ignore = "0.4.23"
lazy_static = "1.5.0"
pulldown-cmark = "0.12.1"
serde = {version = "1.0.210", features = ["derive"]}
serde_json = "1.0.128"
tera = "1.20.0"
toml = "0.8.19"

View File

@ -0,0 +1,21 @@
# WELCOME TO MY BLOG!!!
So uh the plan is to update this blog as often as possible as I learn more about rust :3
Don't expect me to word posts professionally as this is intended to be more of something where I can turn off my mind and write my thoughts down.
## I aim to post daily though so that's cool
also this blog was made with a random tutorial from The Medium so yeahhhhhhh I can barely understand the codebase or whatever the fancy term for the source code is
so uhh have fun and stuff :3
P.S. I go by IMK, IMKJET on most places (and IMKTHECOOL[x] rarely), It's like capitalized usually but I honestly don't give a fuckkkkkk
1. number
2. list
3. test
- unsorted
- yeahhh

View File

@ -0,0 +1,8 @@
title = 'Welcommeeee :3'
file_name = 'my-first-article'
description = 'about this blog and stuff'
tags = ["Casual"]
posted = '22/08/2022'
estimated_reading_time = 1
author = 'IMK' # feel free to swap with your own name
order = 1

View File

@ -0,0 +1,77 @@
use std::{fs, io::Error};
use ignore::WalkBuilder;
use actix_web::{get, web, HttpResponse, Responder};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct Frontmatter {
title: String,
file_name: String,
description: String,
posted: String,
tags: Vec<String>,
author: String,
estimated_reading_time: u32,
order: u32,
}
#[get("/")]
pub async fn index(templates: web::Data<tera::Tera>) -> impl Responder {
let mut context = tera::Context::new();
let mut frontmatters = match find_all_frontmatters(){
Ok(fm) => fm,
Err(e) => {
println!("{:?}", e);
return HttpResponse::InternalServerError()
.content_type("text/html")
.body("SOMETHING WENT WRONG SHOOT IMK THANK YOUUUUUU :3");
}
};
frontmatters.sort_by(|a,b| b.order.cmp(&a.order));
context.insert("posts", &frontmatters);
match templates.render("home.html", &context) {
Ok(s) => HttpResponse::Ok().content_type("text/html").body(s),
Err(e) => {
println!("{:?}",e);
HttpResponse::InternalServerError()
.content_type("text/html")
.body("<p>Something is fucked up :33333 tell IMK(jet) to fix this if refreshing doesn't works or to shoot himself :3c</p>")
}
}
}
fn find_all_frontmatters() -> Result<Vec<Frontmatter>, std::io::Error> {
let mut t = ignore::types::TypesBuilder::new();
t.add_defaults();
let toml = match t.select("toml").build() {
Ok(t)=> t,
Err(e)=> {
println!("{:}", e);
return Err(Error::new(std::io::ErrorKind::Other,
"could not build toml file type matcher"))
}
};
let file_walker = WalkBuilder::new("./posts").types(toml).build();
let mut frontmatters = Vec::new();
for frontmatter in file_walker {
match frontmatter {
Ok(fm) => {
if fm.path().is_file() {
let fm_content = fs::read_to_string(fm.path())?;
let frontmatter: Frontmatter = toml::from_str::<Frontmatter>(&fm_content).unwrap();
frontmatters.push(frontmatter);
}
}
Err(e) => {
println!("{:}", e); // we're just going to print the error for now
return Err(Error::new(std::io::ErrorKind::NotFound, "could not locate frontmatter"))
}
}
}
Ok(frontmatters)
}

7
src/handlers/mod.rs Normal file
View File

@ -0,0 +1,7 @@
mod home_handler;
mod post_handler;
pub use home_handler::index;
pub use post_handler::post;

View File

@ -0,0 +1,80 @@
use std::{io::Error, fs};
use super::home_handler::Frontmatter;
use actix_web::{web, get, Responder, HttpResponse};
use pulldown_cmark::{Options, Parser, html};
#[get("/posts/{post_name}")]
pub async fn post(
tmpl: web::Data<tera::Tera>,
post_name: web::Path<String>,
) -> impl Responder {
let mut context = tera::Context::new();
let options = Options::empty(); // used as part of pulldown_cmark for setting flags to enable extra features - we're not going to use any of those, hence the `empty();`
let markdown_input = match extract_markdown(&post_name) {
Ok(s) => s,
Err(e) => {
println!("{:?}", e);
return HttpResponse::NotFound()
.content_type("text/html")
.body("<p>Could not find post - sorry!</p>")
}
};
let frontmatter = match extract_frontmatter(&post_name) {
Ok(s) => s,
Err(e) => {
println!("{:?}", e);
return HttpResponse::NotFound()
.content_type("text/html")
.body("<p>Could not find post - sorry!</p>")
}
};
let parser = Parser::new_ext(&markdown_input, options);
let mut html_output = String::new();
html::push_html(&mut html_output, parser);
context.insert("post", &html_output);
context.insert("meta_data", &frontmatter);
match tmpl.render("post.html", &context) {
Ok(s) => HttpResponse::Ok().content_type("text/html").body(s),
Err(e) => {
println!("{:?}", e);
return HttpResponse::NotFound()
.content_type("text/html")
.body("<p>Could not find post - sorry!</p>")
}
}
}
fn extract_markdown(post_name: &str) -> Result<String, Error> {
let markdown = match fs::read_to_string(format!("./posts/{}/post.md", post_name)) {
Ok(markdown) => markdown,
Err(e) => {
println!("{:?}", e);
return Err(e)
}
};
Ok(markdown)
}
fn extract_frontmatter(post_name: &str) -> Result<Frontmatter, Error> {
let frontmatter_input = match fs::read_to_string(format!("./posts/{}/post_frontmatter.toml", post_name)) {
Ok(s) => s,
Err(e) => {
println!("{:?}", e);
return Err(e)
}
};
let frontmatter = match toml::from_str(&frontmatter_input) {
Ok(fm) => fm,
Err(e) => {
println!("{:?}", e);
return Err(Error::new(std::io::ErrorKind::Other, "could not find post frontmatter KILL IMKKKKKKKKKKK :3"))
}
};
Ok(frontmatter)
}

39
src/lib.rs Normal file
View File

@ -0,0 +1,39 @@
use actix_files::Files;
use std::net::TcpListener;
use actix_web::{dev::Server, web, App, HttpResponse, HttpServer, middleware};
use tera::Tera;
pub mod handlers;
#[macro_use]
extern crate lazy_static;
lazy_static! {
pub static ref TEMPLATES: Tera = {
let mut tera = match Tera::new("templates/**/*.html") {
Ok(t) => t,
Err(e) => {
println!("Parsing error(s): {}",e);
::std::process::exit(1);
}
};
tera.autoescape_on(vec![".html",".sql"]);
tera
};
}
pub fn start_blog(listener: TcpListener) -> Result<Server, std::io::Error>{
let srv = HttpServer::new(move || {
App::new()
.app_data(web::Data::new(TEMPLATES.clone()))
.wrap(middleware::Logger::default())
.service(Files::new("/static","static/").use_last_modified(true))
.route("/health", web::get().to(HttpResponse::Ok))
.service(handlers::index)
.service(handlers::post)
})
.listen(listener)?
.run();
Ok(srv)
}

12
src/main.rs Normal file
View File

@ -0,0 +1,12 @@
use std::net::TcpListener;
use blog::start_blog;
#[actix_web::main]
async fn main() -> std::io::Result<()>{
std::env::set_var("RUST_LOG","actix_web=info");
env_logger::init();
let listener = TcpListener::bind("0.0.0.0:8080");
start_blog(listener.unwrap())?.await?;
Ok(())
}

9
static/assets/bullet.svg Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 29 25" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
<g transform="matrix(1,0,0,1,0.0528936,-1.41031)">
<g transform="matrix(1,0,0,1,-5.93425,-19.6645)">
<path d="M11.898,26.509L28.769,26.509L11.316,39.774" style="fill:white;stroke:white;stroke-width:10.87px;"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 729 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 139 KiB

62
static/css/index.css Normal file
View File

@ -0,0 +1,62 @@
body {
background-color: #000;
font-family: "Zilla Slab";
color: #FFFF;
}
a {
text-decoration: none;
}
a:link{
color: white;
}
a:visited{
color: white;
}
a.return{
font-size: 2.5vw;
font-weight:bold;
}
hr
{
border-top: 2px dashed white;
}
p.title{
font-size: 3vw;
font-weight: bold;
}
article h1{
font-size: 3vw;
}
article p{
font-size: 1.5vw;
}
article h2{
font-size: 1.5vw;
}
article li {
font-size: 1.5vw;
}
article ul {
list-style-image: url("/static/assets/bullet.svg")
}
div.post {
border: 1px solid #fff;
}
h1.htitle{
text-align: center;
text-size: 6vw;
}

30
templates/base.html Normal file
View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
{% block head %}
<title>{% block title %}{% endblock title %}</title>
{% endblock head %}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Zilla+Slab:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="/static/css/index.css">
</head>
<body class="flex flex-col justify-between min-h-screen font-sans leading-normal tracking-normal">
<div class="h-16">{% block header %}{% endblock header %}</div>
<main class="container flex-1 w-full md:max-w-3xl mx-auto overflow-x-hidden">
{% block content %}{% endblock content %}
</main>
{% block footer %}
<div class="flex w-full h-20 justify-center items-center">
<hr>
<img src="/static/assets/signature.svg" alt="IMK (signature)" style="max-width:5%;height:auto;">
</div>
{% endblock footer %}
</body>
<script src="/static/js/highlight.min.js"></script>
<script>hljs.highlightAll();</script>
</script>
</html>

21
templates/home.html Normal file
View File

@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block title %}ALEPH 1 | IMK'S BLOG{% endblock title %}
{% block content %}
<div>
<h1 class="htitle">ℵ₁ | IMK'S BLOG</h1>
<hr>
{% for fm in posts %}
<a class="mb-3" href="/posts/{{fm.file_name}}">
<div class="post">
<h2 class="text-2xl font-semibold hover:underline">{{fm.title}}</h2>
<hr>
<ul>
<li>On: {{fm.posted}}</li>
<li>By: {{fm.author}}</li>
<li>Reading time:{{fm.estimated_reading_time}} min</li>
</ul>
</div>
</a>
{% endfor %}
</div>
{% endblock content %}

26
templates/post.html Normal file
View File

@ -0,0 +1,26 @@
{% extends "base.html" %}
{% block title %}{{ meta_data.title }} | by {{ meta_data.author }} {% endblock title %}
{% block header %}
<div class="relative mt-2 ml-4 flex w-full justify-center">
<a class="return" href="/">ℵ₁ | IMK's BLOG</a>
<p class="title">{{ meta_data.title }}</p>
<hr>
</div>
{% endblock header %}
{% block content %}
<article
id="article-content"
>
{{ post | safe }}
</article>
{% endblock content %}
{% block footer %}
<hr>
<div class="flex w-full h-full justify-center items-center">
<img src="/static/assets/signature.svg" alt="IMK (signature)" style="max-width:2%;height:auto;">
</div>
{% endblock footer %}