Update
This commit is contained in:
parent
94fb270008
commit
584c07ff23
22 changed files with 539 additions and 187 deletions
|
@ -1,3 +1,4 @@
|
|||
use diesel::QueryDsl;
|
||||
#[cfg(not(target_env = "msvc"))]
|
||||
use tikv_jemallocator::Jemalloc;
|
||||
|
||||
|
@ -5,42 +6,105 @@ use tikv_jemallocator::Jemalloc;
|
|||
#[global_allocator]
|
||||
static GLOBAL: Jemalloc = Jemalloc;
|
||||
|
||||
use actix_web::{
|
||||
http::header,
|
||||
middleware,
|
||||
web::{self, Data},
|
||||
App, Error, HttpResponse, HttpServer,
|
||||
};
|
||||
use clap::{Parser, Subcommand};
|
||||
use actix_web::{get, http::StatusCode, middleware, web, App, HttpResponse, HttpServer};
|
||||
use actix_web_static_files::ResourceFiles;
|
||||
use askama_actix::{Template, TemplateToResponse};
|
||||
use diesel::prelude::*;
|
||||
|
||||
use backend::*;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct Cli {
|
||||
#[clap(subcommand)]
|
||||
commands: Commands,
|
||||
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||
|
||||
mod filters {
|
||||
pub fn cmark<T: std::fmt::Display>(s: T) -> ::askama::Result<String> {
|
||||
let s = s.to_string();
|
||||
|
||||
let options = pulldown_cmark::Options::empty();
|
||||
let parser = pulldown_cmark::Parser::new_ext(&s, options);
|
||||
|
||||
let mut html_output = String::with_capacity(s.len() * 3 / 2);
|
||||
pulldown_cmark::html::push_html(&mut html_output, parser);
|
||||
|
||||
Ok(html_output)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum Commands {
|
||||
#[clap(about = "Starts webserver")]
|
||||
Run,
|
||||
#[derive(Template)]
|
||||
#[template(path = "status_code.html")]
|
||||
struct StatusCodeTemplate {
|
||||
status_code: StatusCode,
|
||||
message: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "posts/{slug}.html")]
|
||||
struct PostBySlugTemplate {
|
||||
post: db::models::Post,
|
||||
}
|
||||
|
||||
async fn not_found() -> HttpResponse {
|
||||
StatusCodeTemplate {
|
||||
status_code: StatusCode::NOT_FOUND,
|
||||
message: None,
|
||||
}
|
||||
.to_response()
|
||||
}
|
||||
|
||||
#[get("/posts/{slug}")]
|
||||
async fn post_by_slug(db_pool: web::Data<db::DbPool>, path: web::Path<String>) -> HttpResponse {
|
||||
let slug = path.into_inner();
|
||||
|
||||
let conn = &mut match db_pool.get() {
|
||||
Ok(x) => x,
|
||||
Err(e) => return HttpResponse::InternalServerError().body(format!("{:?}", e)),
|
||||
};
|
||||
|
||||
let post = match db::schema::posts::table
|
||||
.filter(db::schema::posts::slug.eq(&slug))
|
||||
.filter(db::schema::posts::active)
|
||||
.first::<db::models::Post>(conn)
|
||||
.optional()
|
||||
{
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
dbg!(&e);
|
||||
return HttpResponse::InternalServerError().body(format!("{:?}", e));
|
||||
}
|
||||
};
|
||||
|
||||
match post {
|
||||
Some(x) => PostBySlugTemplate { post: x }.to_response(),
|
||||
None => {
|
||||
let mut resp = StatusCodeTemplate {
|
||||
status_code: StatusCode::NOT_FOUND,
|
||||
message: None,
|
||||
}
|
||||
.to_response();
|
||||
*resp.status_mut() = StatusCode::NOT_FOUND;
|
||||
|
||||
resp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
match Cli::parse().commands {
|
||||
Commands::Run => {
|
||||
std::env::set_var("RUST_LOG", "info");
|
||||
env_logger::init();
|
||||
env_logger::init();
|
||||
log::info!("Listening to requests at http://0.0.0.0:80");
|
||||
|
||||
let server = HttpServer::new(move || {
|
||||
App::new()
|
||||
.wrap(middleware::Compress::default())
|
||||
.wrap(middleware::Logger::default())
|
||||
});
|
||||
server.bind("0.0.0.0:80").unwrap().run().await
|
||||
}
|
||||
}
|
||||
HttpServer::new(move || {
|
||||
let generated = generate();
|
||||
|
||||
App::new()
|
||||
.wrap(middleware::Compress::default())
|
||||
.wrap(middleware::Logger::default())
|
||||
.app_data(web::Data::new(db::pool().unwrap()))
|
||||
.service(post_by_slug)
|
||||
.service(ResourceFiles::new("/static", generated))
|
||||
.default_service(web::route().to(not_found))
|
||||
})
|
||||
.bind("0.0.0.0:80")
|
||||
.unwrap()
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
|
|
|
@ -10,8 +10,9 @@ use chrono::prelude::*;
|
|||
use clap::{Parser, Subcommand};
|
||||
use diesel::prelude::*;
|
||||
use scan_dir::ScanDir;
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use backend::*;
|
||||
|
||||
|
@ -28,23 +29,9 @@ enum Commands {
|
|||
Import,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct Blog {
|
||||
name: String,
|
||||
description: String,
|
||||
copyright: String,
|
||||
owner: BlogOwner,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct BlogOwner {
|
||||
name: String,
|
||||
email: String,
|
||||
website: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
struct PostFrontmatter {
|
||||
id: Option<i32>,
|
||||
name: String,
|
||||
slug: String,
|
||||
description: String,
|
||||
|
@ -55,6 +42,7 @@ struct PostFrontmatter {
|
|||
|
||||
#[derive(Debug)]
|
||||
struct Post {
|
||||
path: PathBuf,
|
||||
frontmatter: PostFrontmatter,
|
||||
content: String,
|
||||
}
|
||||
|
@ -64,61 +52,86 @@ fn main() -> Result<()> {
|
|||
Commands::Import => {
|
||||
let conn = &mut db::establish_connection()?;
|
||||
|
||||
let blog: Blog = serde_yaml::from_str(&fs::read_to_string("/blog/blog.yml")?)?;
|
||||
|
||||
diesel::delete(db::schema::configs::table)
|
||||
.filter(db::schema::configs::active.eq(true))
|
||||
.execute(conn)?;
|
||||
diesel::insert_into(db::schema::configs::table)
|
||||
.values(db::models::NewConfig {
|
||||
active: true,
|
||||
name: &blog.name,
|
||||
description: &blog.description,
|
||||
copyright: &blog.copyright,
|
||||
owner_name: &blog.owner.name,
|
||||
owner_email: &blog.owner.email,
|
||||
owner_website: blog.owner.website.as_deref(),
|
||||
})
|
||||
.execute(conn)?;
|
||||
|
||||
let posts = ScanDir::dirs().read("/blog/posts", |iter| {
|
||||
iter.map(|(entry, name)| {
|
||||
dbg!(&entry, &name);
|
||||
let src = fs::read_to_string(entry.path().join("post.md"))?;
|
||||
for post in ScanDir::dirs().read("/blog", |iter| {
|
||||
iter.map(|(entry, _)| {
|
||||
let path = entry.path().join("post.md");
|
||||
let src = fs::read_to_string(&path)?;
|
||||
let frontmatter = match fronma::parser::parse::<PostFrontmatter>(&src) {
|
||||
Ok(x) => x,
|
||||
Err(x) => bail!("Error parsing frontmatter: {:?}", x),
|
||||
};
|
||||
|
||||
Ok(Post {
|
||||
frontmatter: PostFrontmatter {
|
||||
name: frontmatter.headers.name.trim().to_string(),
|
||||
slug: frontmatter.headers.slug.trim().to_string(),
|
||||
description: frontmatter.headers.description.trim().to_string(),
|
||||
..frontmatter.headers
|
||||
},
|
||||
content: frontmatter.body.trim().to_string(),
|
||||
path,
|
||||
frontmatter: frontmatter.headers,
|
||||
content: frontmatter.body.to_string(),
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()
|
||||
})??;
|
||||
dbg!(&posts);
|
||||
})?? {
|
||||
let content = post.content.trim();
|
||||
|
||||
for post in posts {
|
||||
diesel::delete(db::schema::posts::table)
|
||||
.filter(db::schema::posts::slug.eq(&post.frontmatter.slug))
|
||||
.execute(conn)?;
|
||||
diesel::insert_into(db::schema::posts::table)
|
||||
.values(db::models::NewPost {
|
||||
name: &post.frontmatter.name,
|
||||
slug: &post.frontmatter.slug,
|
||||
description: &post.frontmatter.description,
|
||||
content: &post.content,
|
||||
published_at: post.frontmatter.published_at,
|
||||
edited_at: post.frontmatter.edited_at,
|
||||
active: post.frontmatter.active,
|
||||
})
|
||||
.execute(conn)?;
|
||||
if let Some(id) = post.frontmatter.id {
|
||||
diesel::update(db::schema::posts::table)
|
||||
.filter(db::schema::posts::id.eq(id))
|
||||
.set(db::models::UpdatePost {
|
||||
name: Some(&post.frontmatter.name),
|
||||
slug: Some(&post.frontmatter.slug),
|
||||
description: Some(&post.frontmatter.description),
|
||||
content: Some(content),
|
||||
published_at: Some(post.frontmatter.published_at),
|
||||
edited_at: Some(post.frontmatter.edited_at),
|
||||
active: Some(post.frontmatter.active),
|
||||
})
|
||||
.execute(conn)?;
|
||||
} else {
|
||||
let id = if let Some(id) = db::schema::posts::table
|
||||
.select(db::schema::posts::id)
|
||||
.filter(db::schema::posts::slug.eq(&post.frontmatter.slug))
|
||||
.first::<i32>(conn)
|
||||
.optional()?
|
||||
{
|
||||
diesel::update(db::schema::posts::table)
|
||||
.filter(db::schema::posts::id.eq(id))
|
||||
.set(db::models::UpdatePost {
|
||||
name: Some(&post.frontmatter.name),
|
||||
slug: None,
|
||||
description: Some(&post.frontmatter.description),
|
||||
content: Some(content),
|
||||
published_at: Some(post.frontmatter.published_at),
|
||||
edited_at: Some(post.frontmatter.edited_at),
|
||||
active: Some(post.frontmatter.active),
|
||||
})
|
||||
.execute(conn)?;
|
||||
|
||||
id
|
||||
} else {
|
||||
diesel::insert_into(db::schema::posts::table)
|
||||
.values(db::models::NewPost {
|
||||
name: &post.frontmatter.name,
|
||||
slug: &post.frontmatter.slug,
|
||||
description: &post.frontmatter.description,
|
||||
content: content,
|
||||
published_at: post.frontmatter.published_at,
|
||||
edited_at: post.frontmatter.edited_at,
|
||||
active: post.frontmatter.active,
|
||||
})
|
||||
.returning(db::schema::posts::id)
|
||||
.get_result::<i32>(conn)?
|
||||
};
|
||||
|
||||
fs::write(
|
||||
post.path,
|
||||
format!(
|
||||
"---\n{}---\n{}",
|
||||
serde_yaml::to_string(&PostFrontmatter {
|
||||
id: Some(id),
|
||||
..post.frontmatter
|
||||
})?,
|
||||
post.content
|
||||
),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -3,31 +3,6 @@ use diesel::prelude::*;
|
|||
|
||||
use crate::db::schema;
|
||||
|
||||
#[derive(Identifiable, Queryable, Debug)]
|
||||
#[diesel(table_name = schema::configs)]
|
||||
pub struct Config {
|
||||
pub id: i32,
|
||||
pub active: bool,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub copyright: String,
|
||||
pub owner_name: String,
|
||||
pub owner_email: String,
|
||||
pub owner_website: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Insertable, Debug)]
|
||||
#[diesel(table_name = schema::configs)]
|
||||
pub struct NewConfig<'a> {
|
||||
pub active: bool,
|
||||
pub name: &'a str,
|
||||
pub description: &'a str,
|
||||
pub copyright: &'a str,
|
||||
pub owner_name: &'a str,
|
||||
pub owner_email: &'a str,
|
||||
pub owner_website: Option<&'a str>,
|
||||
}
|
||||
|
||||
#[derive(Identifiable, Queryable, Debug)]
|
||||
#[diesel(table_name = schema::tags)]
|
||||
pub struct Tag {
|
||||
|
@ -65,3 +40,15 @@ pub struct NewPost<'a> {
|
|||
pub edited_at: Option<NaiveDate>,
|
||||
pub active: bool,
|
||||
}
|
||||
|
||||
#[derive(AsChangeset, Debug)]
|
||||
#[diesel(table_name = schema::posts)]
|
||||
pub struct UpdatePost<'a> {
|
||||
pub name: Option<&'a str>,
|
||||
pub slug: Option<&'a str>,
|
||||
pub description: Option<&'a str>,
|
||||
pub content: Option<&'a str>,
|
||||
pub published_at: Option<NaiveDate>,
|
||||
pub edited_at: Option<Option<NaiveDate>>,
|
||||
pub active: Option<bool>,
|
||||
}
|
||||
|
|
|
@ -1,16 +1,3 @@
|
|||
diesel::table! {
|
||||
configs {
|
||||
id -> Integer,
|
||||
active -> Bool,
|
||||
name -> Text,
|
||||
description -> Text,
|
||||
copyright -> Text,
|
||||
owner_name -> Text,
|
||||
owner_email -> Text,
|
||||
owner_website -> Nullable<Text>,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
tags {
|
||||
id -> Integer,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue