yeah
This commit is contained in:
commit
0f81091c0a
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
2049
Cargo.lock
generated
Normal file
2049
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
16
Cargo.toml
Normal file
16
Cargo.toml
Normal 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"
|
21
posts/my-first-article/post.md
Normal file
21
posts/my-first-article/post.md
Normal 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
|
||||
|
8
posts/my-first-article/post_frontmatter.toml
Normal file
8
posts/my-first-article/post_frontmatter.toml
Normal 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
|
77
src/handlers/home_handler.rs
Normal file
77
src/handlers/home_handler.rs
Normal 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
7
src/handlers/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
mod home_handler;
|
||||
mod post_handler;
|
||||
|
||||
|
||||
pub use home_handler::index;
|
||||
pub use post_handler::post;
|
||||
|
80
src/handlers/post_handler.rs
Normal file
80
src/handlers/post_handler.rs
Normal 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
39
src/lib.rs
Normal 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
12
src/main.rs
Normal 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
9
static/assets/bullet.svg
Normal 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 |
15
static/assets/signature.svg
Normal file
15
static/assets/signature.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 139 KiB |
62
static/css/index.css
Normal file
62
static/css/index.css
Normal 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
30
templates/base.html
Normal 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
21
templates/home.html
Normal 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
26
templates/post.html
Normal 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 %}
|
Loading…
Reference in New Issue
Block a user