aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFuwn <[email protected]>2020-11-19 23:53:52 +0000
committerFuwn <[email protected]>2020-11-19 23:53:52 +0000
commit26b647ca3f006662676690e6b99bfbfb32167738 (patch)
tree1815e651090e928341b7f88be6781028a400b19a
parentfeat: limitations in readme (diff)
downloadchan-26b647ca3f006662676690e6b99bfbfb32167738.tar.xz
chan-26b647ca3f006662676690e6b99bfbfb32167738.zip
feat, chore (desc)
feat: - add example database - ADD BOARDS - 404 page - better ui (bootstrap) chore: - move structures to strucutres file - various capitalizations
-rw-r--r--.gitignore1
-rw-r--r--chan.dbbin0 -> 24576 bytes
-rw-r--r--src/api.rs4
-rw-r--r--src/db.rs57
-rw-r--r--src/main.rs7
-rw-r--r--src/structures.rs16
-rw-r--r--src/ui.rs51
-rw-r--r--templates/404.html.hbs24
-rw-r--r--templates/boards.html.hbs18
-rw-r--r--templates/header.html.hbs26
-rw-r--r--templates/index.html.hbs15
-rw-r--r--templates/layout.html.hbs28
-rw-r--r--templates/post.html.hbs35
-rw-r--r--templates/threads.html.hbs48
14 files changed, 252 insertions, 78 deletions
diff --git a/.gitignore b/.gitignore
index 2c4918c..ea8c4bf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1 @@
/target
-*.db
diff --git a/chan.db b/chan.db
new file mode 100644
index 0000000..bd7412e
--- /dev/null
+++ b/chan.db
Binary files differ
diff --git a/src/api.rs b/src/api.rs
index 8d8d2f2..8fd61c5 100644
--- a/src/api.rs
+++ b/src/api.rs
@@ -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))
}
diff --git a/src/db.rs b/src/db.rs
index 7a5a85d..4eea4c7 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -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
+}
diff --git a/src/ui.rs b/src/ui.rs
index 0a054e7..def8d76 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -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 }}