diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | chan.db | bin | 0 -> 24576 bytes | |||
| -rw-r--r-- | src/api.rs | 4 | ||||
| -rw-r--r-- | src/db.rs | 57 | ||||
| -rw-r--r-- | src/main.rs | 7 | ||||
| -rw-r--r-- | src/structures.rs | 16 | ||||
| -rw-r--r-- | src/ui.rs | 51 | ||||
| -rw-r--r-- | templates/404.html.hbs | 24 | ||||
| -rw-r--r-- | templates/boards.html.hbs | 18 | ||||
| -rw-r--r-- | templates/header.html.hbs | 26 | ||||
| -rw-r--r-- | templates/index.html.hbs | 15 | ||||
| -rw-r--r-- | templates/layout.html.hbs | 28 | ||||
| -rw-r--r-- | templates/post.html.hbs | 35 | ||||
| -rw-r--r-- | templates/threads.html.hbs | 48 |
14 files changed, 252 insertions, 78 deletions
@@ -1,2 +1 @@ /target -*.db Binary files differ@@ -2,12 +2,14 @@ use rocket::request::Form; use rocket::response::Redirect; use crate::db::*; +use crate::structures::*; // POST: Create a new thread. #[post("/post", data = "<thread>")] pub fn post(thread: Form<Thread>) -> Redirect { // Pretty rudimentary error handling. match new_thread(Thread { + board: thread.board.clone(), name: thread.name.clone(), comment: thread.comment.clone(), }) { @@ -16,5 +18,5 @@ pub fn post(thread: Form<Thread>) -> Redirect { } // Redirect to all posts. - Redirect::to("/threads") + Redirect::to(format!("/board/{}", thread.board)) } @@ -1,11 +1,6 @@ use rusqlite::{params, Connection, Result, Error as SQLError}; -/// The format a valid SQLlite thread entry should have. -#[derive(FromForm, Debug, Serialize, Deserialize)] -pub struct Thread { - pub name: String, - pub comment: String -} +use crate::structures::*; /// Create a new thread in the SQLite database. pub fn new_thread(mut thread: Thread) -> Result<()> { @@ -14,29 +9,65 @@ pub fn new_thread(mut thread: Thread) -> Result<()> { // If the name field in the POST request is empty, user should be "anonymous". if thread.name == "" { - thread.name = "anonymous".to_owned(); + thread.name = "Anonymous".to_owned(); } // Add thread entry to database. db.execute( - "INSERT INTO thread (name, comment) VALUES (?1, ?2)", - params![thread.name, thread.comment] + "INSERT INTO threads (board, name, comment) VALUES (?1, ?2, ?3)", + params![thread.board, thread.name, thread.comment] )?; Ok(()) } /// Get all threads from the SQLite databse. -pub fn get_threads() -> Result<Vec<Thread>, SQLError> { +pub fn _get_all_threads() -> Result<Vec<Thread>, SQLError> { // Open SQLite database file. let db = Connection::open("chan.db")?; // Get all entries from the thread table. - let mut statement = db.prepare("SELECT name, comment FROM thread")?; + let mut statement = db.prepare("SELECT board, name, comment FROM threads")?; let threads = statement.query_map(params![], |row| { Ok(Thread { - name: row.get(0)?, - comment: row.get(1)? + board: row.get(0)?, + name: row.get(1)?, + comment: row.get(2)? + }) + })?; + threads.collect() +} + +/// Get all boards from the SQLite databse. +pub fn get_boards() -> Result<Vec<Board>, SQLError> { + // Open SQLite database file. + let db = Connection::open("chan.db")?; + + // Get all entries from the thread table. + let mut statement = db.prepare("SELECT tag, name, nsfw, disabled FROM boards")?; + let boards = statement.query_map(params![], |row| { + Ok(Board { + tag: row.get(0)?, + name: row.get(1)?, + nsfw: row.get(2)?, + disabled: row.get(3)? + }) + })?; + boards.collect() +} + +/// Get all threads from a specific board within the SQLite databse. +pub fn get_threads(board: String) -> Result<Vec<Thread>, SQLError> { + // Open SQLite database file. + let db = Connection::open("chan.db")?; + + // Get all entries from the thread table. + let mut statement = db.prepare("SELECT board, name, comment FROM threads WHERE board = (?)")?; + let threads = statement.query_map(params![board], |row| { + Ok(Thread { + board: row.get(0)?, + name: row.get(1)?, + comment: row.get(2)? }) })?; threads.collect() diff --git a/src/main.rs b/src/main.rs index f5fbd56..c9afc46 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene, decl_macro)] +#![feature(proc_macro_hygiene, decl_macro, option_result_contains)] #[macro_use] extern crate rocket; #[macro_use] extern crate serde_derive; @@ -8,6 +8,7 @@ extern crate serde_json; mod api; mod ui; mod db; +mod structures; use rocket_contrib::templates::Template; @@ -19,8 +20,8 @@ fn main() { ]) .mount("/", routes![ ui::index, - ui::make_post, - ui::threads + ui::boards, + ui::board ]) .mount("/api/v1/", routes![ api::post diff --git a/src/structures.rs b/src/structures.rs new file mode 100644 index 0000000..8d0efb3 --- /dev/null +++ b/src/structures.rs @@ -0,0 +1,16 @@ +/// The format a valid SQLlite thread entry should have. +#[derive(FromForm, Debug, Serialize, Deserialize)] +pub struct Thread { + pub board: String, + pub name: String, + pub comment: String +} + +/// The format a valid SQLlite thread entry should have. +#[derive(FromForm, Debug, Serialize, Deserialize, PartialEq)] +pub struct Board { + pub tag: String, + pub name: String, + pub nsfw: i32, + pub disabled: i32 +} @@ -1,5 +1,4 @@ use rocket_contrib::templates::Template; -use rocket::response::Redirect; use crate::db::*; @@ -10,20 +9,52 @@ pub fn index() -> Template { } #[catch(404)] -pub fn not_found() -> Redirect { - Redirect::to("/") +pub fn not_found() -> Template { + Template::render("404", &()) } // GET: Make a new thread. -#[get("/post")] -pub fn make_post() -> Template { - Template::render("post", &()) -} +// #[get("/post")] +// pub fn make_post() -> Template { +// Template::render("post", &()) +// } // GET: Check out all the threads. -#[get("/threads")] -pub fn threads() -> Template { - let context = get_threads().unwrap(); +// #[get("/threads")] +// pub fn threads() -> Template { +// let context = get_all_threads().unwrap(); +// let threads = serde_json::json!(&context); +// Template::render("threads", threads) +// } + +// GET: Check out all the boards. +#[get("/boards")] +pub fn boards() -> Template { + let context = get_boards().unwrap(); + let threads = serde_json::json!(&context); + Template::render("boards", threads) +} + +// GET: Check out all threads within a board. +#[get("/board/<board>")] +pub fn board(board: String) -> Template { + let boards = serde_json::json!(get_boards().unwrap()); + + let mut board_count: i32 = 0; + for i in boards.as_array().unwrap() { + if i.get("tag") == Some(&serde_json::json!(board)) { + board_count += 1; + } + } + if board_count == 0 { return Template::render("404", &()); } + + // Return 404 if the board is not found. + // This doesn't work, but I kept it because I might try to fix it later. + // if !boards.as_array().unwrap().contains(&serde_json::json!(board)) { + // return Template::render("404", &()); + // } + + let context = get_threads(board).unwrap(); let threads = serde_json::json!(&context); Template::render("threads", threads) } diff --git a/templates/404.html.hbs b/templates/404.html.hbs new file mode 100644 index 0000000..5940d43 --- /dev/null +++ b/templates/404.html.hbs @@ -0,0 +1,24 @@ +<title>chan - 404</title> + +{{> header }} + +<h1>Error 404: Resource not found...</h1> + +<pre> +⣿⣿⣷⡁⢆⠈⠕⢕⢂⢕⢂⢕⢂⢔⢂⢕⢄⠂⣂⠂⠆⢂⢕⢂⢕⢂⢕⢂⢕⢂ +⣿⣿⣿⡷⠊⡢⡹⣦⡑⢂⢕⢂⢕⢂⢕⢂⠕⠔⠌⠝⠛⠶⠶⢶⣦⣄⢂⢕⢂⢕ +⣿⣿⠏⣠⣾⣦⡐⢌⢿⣷⣦⣅⡑⠕⠡⠐⢿⠿⣛⠟⠛⠛⠛⠛⠡⢷⡈⢂⢕⢂ +⠟⣡⣾⣿⣿⣿⣿⣦⣑⠝⢿⣿⣿⣿⣿⣿⡵⢁⣤⣶⣶⣿⢿⢿⢿⡟⢻⣤⢑⢂ +⣾⣿⣿⡿⢟⣛⣻⣿⣿⣿⣦⣬⣙⣻⣿⣿⣷⣿⣿⢟⢝⢕⢕⢕⢕⢽⣿⣿⣷⣔ +⣿⣿⠵⠚⠉⢀⣀⣀⣈⣿⣿⣿⣿⣿⣿⣿⣿⣿⣗⢕⢕⢕⢕⢕⢕⣽⣿⣿⣿⣿ +⢷⣂⣠⣴⣾⡿⡿⡻⡻⣿⣿⣴⣿⣿⣿⣿⣿⣿⣷⣵⣵⣵⣷⣿⣿⣿⣿⣿⣿⡿ +⢌⠻⣿⡿⡫⡪⡪⡪⡪⣺⣿⣿⣿⣿⣿⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃ +⠣⡁⠹⡪⡪⡪⡪⣪⣾⣿⣿⣿⣿⠋⠐⢉⢍⢄⢌⠻⣿⣿⣿⣿⣿⣿⣿⣿⠏⠈ +⡣⡘⢄⠙⣾⣾⣾⣿⣿⣿⣿⣿⣿⡀⢐⢕⢕⢕⢕⢕⡘⣿⣿⣿⣿⣿⣿⠏⠠⠈ +⠌⢊⢂⢣⠹⣿⣿⣿⣿⣿⣿⣿⣿⣧⢐⢕⢕⢕⢕⢕⢅⣿⣿⣿⣿⡿⢋⢜⠠⠈ +⠄⠁⠕⢝⡢⠈⠻⣿⣿⣿⣿⣿⣿⣿⣷⣕⣑⣑⣑⣵⣿⣿⣿⡿⢋⢔⢕⣿⠠⠈ +⠨⡂⡀⢑⢕⡅⠂⠄⠉⠛⠻⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢋⢔⢕⢕⣿⣿⠠⠈ +⠄⠪⣂⠁⢕⠆⠄⠂⠄⠁⡀⠂⡀⠄⢈⠉⢍⢛⢛⢛⢋⢔⢕⢕⢕⣽⣿⣿⠠⠈ +</pre> + +<a href="/">Return home.</a> diff --git a/templates/boards.html.hbs b/templates/boards.html.hbs new file mode 100644 index 0000000..d86ba2b --- /dev/null +++ b/templates/boards.html.hbs @@ -0,0 +1,18 @@ +<title>chan - Boards</title> + +{{> header }} + +<h1>Boards</h1> + +<div class="col"></div> +<div class="col"> + <ul> + <p><ul>Unorganized Boards</ul></p> + {{ #each this }} + <li> + <a href="/board/{{ this.tag }}">{{ this.name }} <span class="badge badge-primary">New!</span> {{ #if this.nsfw }} <span class="badge badge-danger">NSFW!</span> {{ /if }}</p> + </li> + {{ /each }} + </ul> +</div> +<div class="col"></div> diff --git a/templates/header.html.hbs b/templates/header.html.hbs index 6057f69..28786e4 100644 --- a/templates/header.html.hbs +++ b/templates/header.html.hbs @@ -1,16 +1,16 @@ -<style> - * { - font-family: cursive; - } -</style> +{{> layout }} -<title>chan</title> +<ul class="nav justify-content-center"> + <li class="nav-item"> + <a class="nav-link" href="/"> + Home + </a> + </li> -<center> -<nav> - <p>chan</a> | - <a href="/">index</a> | - <a href="/post">post</a> | - <a href="/threads">threads</a> -</nav> + <li class="nav-item"> + <a class="nav-link" href="/boards"> + Boards + </a> + </li> +</ul> <br> diff --git a/templates/index.html.hbs b/templates/index.html.hbs index 92d2b35..ef756d0 100644 --- a/templates/index.html.hbs +++ b/templates/index.html.hbs @@ -1,5 +1,9 @@ +<title>chan - Home</title> + {{> header }} +<h1>chan</h1> + <pre> ⣿⣿⣷⡁⢆⠈⠕⢕⢂⢕⢂⢕⢂⢔⢂⢕⢄⠂⣂⠂⠆⢂⢕⢂⢕⢂⢕⢂⢕⢂ ⣿⣿⣿⡷⠊⡢⡹⣦⡑⢂⢕⢂⢕⢂⢕⢂⠕⠔⠌⠝⠛⠶⠶⢶⣦⣄⢂⢕⢂⢕ @@ -16,3 +20,14 @@ ⠨⡂⡀⢑⢕⡅⠂⠄⠉⠛⠻⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢋⢔⢕⢕⣿⣿⠠⠈ ⠄⠪⣂⠁⢕⠆⠄⠂⠄⠁⡀⠂⡀⠄⢈⠉⢍⢛⢛⢛⢋⢔⢕⢕⢕⣽⣿⣿⠠⠈ </pre> + +<div class="footer"> + <p> + Copyleft - <a href="/">chan</a> | + Made by <a href="https://github.com/fuwn">Fuwn</a> + </p> + + <p> + Fork me on <a href="https://github.com/fuwn/chan">GitHub</a>! + </p> +</div> diff --git a/templates/layout.html.hbs b/templates/layout.html.hbs new file mode 100644 index 0000000..a9c0dcc --- /dev/null +++ b/templates/layout.html.hbs @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> + <meta name="description" content="A simple `chan'-like board written in Rust."> + <meta name="author" content="Fuwn"> + + <!-- External --> + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous"> + </head> + + <body> + <style> + * { + font-family: Arial, Helvetica, sans-serif + } + + .footer { + position: absolute; + left: 0; + bottom: 0; + width: 100%; + text-align: center; + } + </style> + + <div class="container"> diff --git a/templates/post.html.hbs b/templates/post.html.hbs deleted file mode 100644 index 37c5ec6..0000000 --- a/templates/post.html.hbs +++ /dev/null @@ -1,35 +0,0 @@ -{{> header }} - -<form action="/api/v1/post" method="post"> - <table> - <tr> - <td>name</td> - <td> - <input name="name" type="text" placeholder="blank 4 `anonymous'"> - <input type="submit" value="post"> - </td> - </tr> - - <tr> - <td>comment</td> - <td> - <textarea name="comment" cols="48" rows="4" - wrap="soft" placeholder="comment" required></textarea> - </td> - </tr> - - <tr> - <td>verification</td> - <td> - <input type="checkbox" disabled> i'm not a robot - </td> - </tr> - - <tr> - <td>file</td> - <td> - <input type="file" disabled> - </td> - </tr> - </table> -</form> diff --git a/templates/threads.html.hbs b/templates/threads.html.hbs index 5a656f7..f9f1b85 100644 --- a/templates/threads.html.hbs +++ b/templates/threads.html.hbs @@ -1,13 +1,57 @@ +<title>chan - /{{ this.0.board }}/</title> + {{> header }} +<form action="/api/v1/post" method="post"> + <div class="form-group"> + <label for="name">Name <span class="badge badge-info">Optional</span></label> + <input class="form-control" name="name" type="text" + placeholder="Anonymous"> + </div> + + <div class="form-group"> + <label for="comment">Comment <span class="badge badge-warning">Required</span></label> + <textarea class="form-control" name="comment" cols="48" rows="4" + wrap="soft" placeholder="comment" required></textarea> + </div> + + <div class="form-group form-check"> + <input class="form-check-input" type="checkbox" disabled> + <label class="form-check-label">I'm not a robot. <span class="badge badge-warning">Required</span> <span class="badge badge-danger">Disabled</span></label> + </div> + + <div class="form-group"> + <label class="form-check-label">Image <span class="badge badge-info">Optional</span> <span class="badge badge-danger">Disabled</span></label> + <input class="form-control-file" type="file" disabled> + </div> + + <input type="hidden" name="board" value="{{ this.0.board }}"> + + <button type="submit" class="btn btn-primary">Post</button> +</form> +<br> + +<hr> + {{ #if this }} {{ #each this }} - <div class="post" style="width: 420px"> + <!-- <div class="post" style="width: 420px"> <h2>{{ this.name }}</h4> <p>{{ this.comment }}</p> + </div> --> + + <div class="card"> + <div class="card-body"> + <h2>{{ this.name }}</h4> + <p>{{ this.comment }}</p> + + <button class="btn btn-primary" disabled>View thread</button> + </div> </div> <br> {{ /each }} {{ else }} - <p>no posts today</p> + <div class="alert alert-primary" role="alert"> + No posts have been made today! + </div> {{ /if }} |