|
|
|
@ -6,15 +6,11 @@ use crate::errors::{HandlingError, RequestError};
|
|
|
|
|
use crate::http::{Form, Request, Response, Status};
|
|
|
|
|
use crate::post::{Post, Thread};
|
|
|
|
|
|
|
|
|
|
use std::error::Error;
|
|
|
|
|
use std::io;
|
|
|
|
|
use std::io::{BufRead, BufReader, Read, Write};
|
|
|
|
|
use std::net::{SocketAddr, TcpListener, TcpStream};
|
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
use std::str;
|
|
|
|
|
use std::str::from_utf8;
|
|
|
|
|
use std::sync::{Arc, RwLock};
|
|
|
|
|
use std::thread::JoinHandle;
|
|
|
|
|
|
|
|
|
|
use bincode::{deserialize, serialize};
|
|
|
|
|
use errors::DatabaseError;
|
|
|
|
@ -25,14 +21,13 @@ use log::*;
|
|
|
|
|
use simplelog::*;
|
|
|
|
|
use structopt::StructOpt;
|
|
|
|
|
use threadpool::ThreadPool;
|
|
|
|
|
use urlencoding::decode;
|
|
|
|
|
|
|
|
|
|
// use this instead of hard-coding
|
|
|
|
|
type ID_TYPE = u32;
|
|
|
|
|
pub const INDEX: &'static str = include_str!("www/index.html");
|
|
|
|
|
pub const FAVICON: &'static [u8] = include_bytes!("www/favicon.ico");
|
|
|
|
|
pub const STYLE: &'static str = include_str!("www/style.css");
|
|
|
|
|
pub const FAQ: &'static str = include_str!("www/faq.html");
|
|
|
|
|
//type ID_TYPE = u32;
|
|
|
|
|
pub const INDEX: &str = include_str!("www/index.html");
|
|
|
|
|
pub const FAVICON: &[u8] = include_bytes!("www/favicon.ico");
|
|
|
|
|
pub const STYLE: &str = include_str!("www/style.css");
|
|
|
|
|
pub const FAQ: &str = include_str!("www/faq.html");
|
|
|
|
|
|
|
|
|
|
// represents command line arguments
|
|
|
|
|
#[derive(StructOpt, Debug)]
|
|
|
|
@ -40,9 +35,6 @@ struct Opt {
|
|
|
|
|
#[structopt(short, long, default_value = "8000")]
|
|
|
|
|
port: u16,
|
|
|
|
|
|
|
|
|
|
#[structopt(short, long)]
|
|
|
|
|
admin_hash: Option<String>,
|
|
|
|
|
|
|
|
|
|
#[structopt(short, long, default_value = "data")]
|
|
|
|
|
database: PathBuf,
|
|
|
|
|
}
|
|
|
|
@ -87,6 +79,7 @@ fn get_thread(id: u32) -> Result<Vec<Post>, DatabaseError> {
|
|
|
|
|
.and_then(|x| deserialize::<Thread>(&x).map_err(|_| DatabaseError::SerError))?
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|x| get_post(x))
|
|
|
|
|
.filter(|x| x.is_ok())
|
|
|
|
|
.collect()
|
|
|
|
|
// let thread = THREADS.get(id.to_be_bytes()).and_then()
|
|
|
|
|
}
|
|
|
|
@ -104,14 +97,6 @@ fn next_id() -> Result<u32, DatabaseError> {
|
|
|
|
|
.map_err(|e| DatabaseError::from(e))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// returns current id
|
|
|
|
|
/// not used
|
|
|
|
|
fn get_id() -> Result<u32, DatabaseError> {
|
|
|
|
|
let s = DB.get(b"id")?.unwrap();
|
|
|
|
|
let buf: [u8; 4] = (*s).try_into().unwrap();
|
|
|
|
|
Ok(u32::from_be_bytes(buf))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// lists all threads
|
|
|
|
|
fn list_threads() -> Result<Vec<Post>, DatabaseError> {
|
|
|
|
|
THREADS
|
|
|
|
@ -155,6 +140,15 @@ fn add_post(mut post: Post, thread_id: u32) -> Result<(), DatabaseError> {
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn delete_post(id: u32) -> Result<(), DatabaseError> {
|
|
|
|
|
POSTS.remove(id.to_be_bytes())?;
|
|
|
|
|
if THREADS.contains_key(id.to_be_bytes())? {
|
|
|
|
|
THREADS.remove(id.to_be_bytes())?;
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn add_thread(mut post: Post) -> Result<(), DatabaseError> {
|
|
|
|
|
let id = next_id()?;
|
|
|
|
|
let thread = Thread::new(id);
|
|
|
|
@ -205,9 +199,7 @@ fn handle(mut reader: BufReader<&mut TcpStream>) -> Result<Response, HandlingErr
|
|
|
|
|
Method::Get => get(&request.uri),
|
|
|
|
|
Method::Post => post(request),
|
|
|
|
|
// TODO check admin hash
|
|
|
|
|
_ => Ok(Response::from(HandlingError::from(
|
|
|
|
|
RequestError::BadRequest,
|
|
|
|
|
))),
|
|
|
|
|
Method::Delete => delete(&request.uri),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -269,12 +261,7 @@ fn post(request: Request) -> Result<Response, HandlingError> {
|
|
|
|
|
|
|
|
|
|
let form = Form::try_from(&request)?;
|
|
|
|
|
|
|
|
|
|
let mut post = Post::new(
|
|
|
|
|
0,
|
|
|
|
|
dbg!(encode_text(form.content.trim()))
|
|
|
|
|
.replace("\r\n", "<br>")
|
|
|
|
|
.to_string(),
|
|
|
|
|
);
|
|
|
|
|
let mut post = Post::new(0, encode_text(form.content.trim()).replace("\r\n", "<br>"));
|
|
|
|
|
post.img = form.image;
|
|
|
|
|
|
|
|
|
|
match request.uri.as_str() {
|
|
|
|
@ -294,8 +281,8 @@ fn post(request: Request) -> Result<Response, HandlingError> {
|
|
|
|
|
// means we wish to post in specific thread
|
|
|
|
|
s => {
|
|
|
|
|
let id = s
|
|
|
|
|
.trim_start_matches("/")
|
|
|
|
|
.split("/")
|
|
|
|
|
.trim_start_matches('/')
|
|
|
|
|
.split('/')
|
|
|
|
|
.next()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.parse::<u32>()
|
|
|
|
@ -314,8 +301,17 @@ fn post(request: Request) -> Result<Response, HandlingError> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn delete(path: &str) -> Response {
|
|
|
|
|
todo!();
|
|
|
|
|
fn delete(path: &str) -> Result<Response, HandlingError> {
|
|
|
|
|
let id = path
|
|
|
|
|
.trim_start_matches("/")
|
|
|
|
|
.parse::<u32>()
|
|
|
|
|
.map_err(|_| RequestError::NotFound)?;
|
|
|
|
|
delete_post(id)?;
|
|
|
|
|
Ok(Response::new(
|
|
|
|
|
Status::SeeOther,
|
|
|
|
|
vec![("location", "/")],
|
|
|
|
|
vec![],
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn content(name: &str, main: &str) -> String {
|
|
|
|
@ -323,11 +319,6 @@ fn content(name: &str, main: &str) -> String {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
// bind listener to local adress and port
|
|
|
|
|
let listener =
|
|
|
|
|
TcpListener::bind(("127.0.0.1", OPT.port)).expect("Cannot bind to specified port.");
|
|
|
|
|
// create threadpool for incoming requests
|
|
|
|
|
let pool = ThreadPool::new(num_cpus::get());
|
|
|
|
|
// setup logger
|
|
|
|
|
TermLogger::init(
|
|
|
|
|
LevelFilter::Info,
|
|
|
|
@ -337,6 +328,15 @@ fn main() {
|
|
|
|
|
)
|
|
|
|
|
.expect("failed to setup logger");
|
|
|
|
|
|
|
|
|
|
// bind listener to local adress and port
|
|
|
|
|
let listener =
|
|
|
|
|
TcpListener::bind(("127.0.0.1", OPT.port)).expect("Cannot bind to specified port.");
|
|
|
|
|
|
|
|
|
|
info!("listening on port {}", OPT.port);
|
|
|
|
|
|
|
|
|
|
// create threadpool for incoming requests
|
|
|
|
|
let pool = ThreadPool::new(num_cpus::get());
|
|
|
|
|
|
|
|
|
|
// wait for requests
|
|
|
|
|
for stream in listener.incoming() {
|
|
|
|
|
let mut stream = match stream {
|
|
|
|
@ -352,11 +352,14 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
let response = match handle(reader) {
|
|
|
|
|
Ok(s) => s,
|
|
|
|
|
Err(e) => Response::from(dbg!(e)),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
warn!("{e}");
|
|
|
|
|
Response::from(e)
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// handle request
|
|
|
|
|
stream.write(&response.respond()).unwrap();
|
|
|
|
|
stream.write_all(&response.respond()).unwrap();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|