initial commit
This commit is contained in:
commit
f9f2a75e89
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/target
|
||||||
|
/.idea
|
||||||
|
Cargo.lock
|
||||||
|
*.db*
|
17
Cargo.toml
Normal file
17
Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "hermit"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-web = "*"
|
||||||
|
sqlx = { version = "*", features = [ "runtime-actix-rustls", "sqlite" ] }
|
||||||
|
serde = { version = "*", features = [ "derive" ] }
|
||||||
|
ammonia = "*"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = "thin"
|
||||||
|
# Tell `rustc` to optimize for small code size.
|
||||||
|
opt-level = "z"
|
8
migrations/20221024052654_posts.sql
Normal file
8
migrations/20221024052654_posts.sql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS posts
|
||||||
|
(
|
||||||
|
id INTEGER PRIMARY KEY NOT NULL,
|
||||||
|
parent_id INTEGER,
|
||||||
|
author TEXT NOT NULL,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
content TEXT NOT NULL
|
||||||
|
);
|
173
src/main.rs
Normal file
173
src/main.rs
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
use actix_web::{http::header, web, App, HttpResponse, HttpServer, Responder};
|
||||||
|
use ammonia::clean;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use sqlx::{
|
||||||
|
sqlite::{Sqlite, SqlitePool},
|
||||||
|
Pool,
|
||||||
|
};
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
struct HermitState {
|
||||||
|
pool: Pool<Sqlite>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Post {
|
||||||
|
id: i64,
|
||||||
|
parent_id: Option<i64>,
|
||||||
|
author: String,
|
||||||
|
title: String,
|
||||||
|
content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct PostForm {
|
||||||
|
author: String,
|
||||||
|
title: String,
|
||||||
|
content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(posts: Vec<Post>, details: bool) -> String {
|
||||||
|
let mut page = r#"
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>booru</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a href="/">Home</a>
|
||||||
|
<form method="POST">
|
||||||
|
<input type="text" placeholder="op" name="author"><br />
|
||||||
|
<input type="text" placeholder="title" name="title"><br />
|
||||||
|
<textarea placeholder="your post goes here!" name="content"></textarea><br />
|
||||||
|
<input type="submit" value="Post!">
|
||||||
|
</form>"#
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
|
for post in posts {
|
||||||
|
page.push_str(&format!(
|
||||||
|
"<a href=\"/{}\">💬{}</a> - {}<br />\n",
|
||||||
|
post.id, post.title, post.author
|
||||||
|
));
|
||||||
|
if details {
|
||||||
|
page.push_str(&format!("<p>{}</p>", post.content))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
page.push_str(
|
||||||
|
r#"
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
page
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get(state: web::Data<HermitState>, post_id: Option<web::Path<u32>>) -> impl Responder {
|
||||||
|
match post_id {
|
||||||
|
None => {
|
||||||
|
let posts = sqlx::query_as!(
|
||||||
|
Post,
|
||||||
|
r#"
|
||||||
|
SELECT * FROM posts
|
||||||
|
WHERE parent_id IS NULL
|
||||||
|
ORDER BY id DESC
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
.fetch_all(&state.pool)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
HttpResponse::Ok().body(render(posts, false))
|
||||||
|
}
|
||||||
|
Some(id) => {
|
||||||
|
let post_id = id.into_inner();
|
||||||
|
let posts = sqlx::query_as!(
|
||||||
|
Post,
|
||||||
|
r#"
|
||||||
|
SELECT * FROM posts
|
||||||
|
WHERE id = ?1 OR parent_id = ?1
|
||||||
|
ORDER BY id ASC
|
||||||
|
"#,
|
||||||
|
post_id
|
||||||
|
)
|
||||||
|
.fetch_all(&state.pool)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
HttpResponse::Ok().body(render(posts, true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn post(
|
||||||
|
state: web::Data<HermitState>,
|
||||||
|
post_id: Option<web::Path<u32>>,
|
||||||
|
form: web::Form<PostForm>,
|
||||||
|
) -> impl Responder {
|
||||||
|
let author = clean(form.author.as_str());
|
||||||
|
if author.is_empty() {
|
||||||
|
return HttpResponse::BadRequest().body("Author required");
|
||||||
|
}
|
||||||
|
let title = clean(form.title.as_str());
|
||||||
|
let content = clean(form.content.as_str());
|
||||||
|
|
||||||
|
let id = match post_id {
|
||||||
|
None => {
|
||||||
|
if title.is_empty() {
|
||||||
|
return HttpResponse::BadRequest().body("Title required for top posts");
|
||||||
|
}
|
||||||
|
sqlx::query!(
|
||||||
|
r#"
|
||||||
|
INSERT INTO posts (author, title, content)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
"#,
|
||||||
|
author,
|
||||||
|
title,
|
||||||
|
content
|
||||||
|
)
|
||||||
|
.execute(&state.pool)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.last_insert_rowid()
|
||||||
|
}
|
||||||
|
Some(id) => {
|
||||||
|
let post_id = id.into_inner();
|
||||||
|
sqlx::query!(
|
||||||
|
r#"
|
||||||
|
INSERT INTO posts (parent_id, author, title, content)
|
||||||
|
VALUES (?, ?, ?, ?)
|
||||||
|
"#,
|
||||||
|
post_id,
|
||||||
|
author,
|
||||||
|
title,
|
||||||
|
content
|
||||||
|
)
|
||||||
|
.execute(&state.pool)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
post_id as i64
|
||||||
|
}
|
||||||
|
};
|
||||||
|
HttpResponse::Found()
|
||||||
|
.insert_header((header::LOCATION, format!("/{}", id)))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_web::main]
|
||||||
|
async fn main() -> Result<(), std::io::Error> {
|
||||||
|
let hermit = web::Data::new(HermitState {
|
||||||
|
pool: SqlitePool::connect(&env::var("DATABASE_URL").expect("Please set DATABASE_URL."))
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
});
|
||||||
|
|
||||||
|
HttpServer::new(move || {
|
||||||
|
App::new().app_data(hermit.clone()).service(
|
||||||
|
web::resource(["/", "/{post_id}"])
|
||||||
|
.route(web::get().to(get))
|
||||||
|
.route(web::post().to(post)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.bind(("0.0.0.0", 8069))?
|
||||||
|
.run()
|
||||||
|
.await
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user