Major restructure
This commit is contained in:
parent
5b8d1cbdb1
commit
27386d5d18
9 changed files with 87 additions and 110 deletions
|
@ -2,6 +2,8 @@ CREATE TABLE IF NOT EXISTS posts (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
user_id SERIAL NOT NULL,
|
user_id SERIAL NOT NULL,
|
||||||
content TEXT NOT NULL,
|
content TEXT NOT NULL,
|
||||||
|
upvotes INT NOT NULL DEFAULT 0,
|
||||||
|
downvotes INT NOT NULL DEFAULT 0,
|
||||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (user_id) REFERENCES users (id)
|
FOREIGN KEY (user_id) REFERENCES users (id)
|
||||||
|
|
25
server/src/db.rs
Normal file
25
server/src/db.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
use crate::types::{NewPost, Post};
|
||||||
|
use sqlx::{Any, AnyPool, Row};
|
||||||
|
|
||||||
|
fn db_get_user() -> String {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn db_get_posts() -> Vec<Post> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn db_new_post(post: NewPost, pool: &AnyPool) -> i32 {
|
||||||
|
let q = "INSERT INTO posts (content) VALUES (?)";
|
||||||
|
let query = sqlx::query(q).bind(post.content);
|
||||||
|
let result = query.fetch_one(pool).await.ok();
|
||||||
|
if let Some(row) = result {
|
||||||
|
row.get::<i32, _>("id")
|
||||||
|
} else {
|
||||||
|
panic!("Failed to insert post into database");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn db_vote(post_id: i32, user_id: i32, upvote: bool) {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
|
@ -4,12 +4,13 @@ use actix_web::web::Data;
|
||||||
use actix_web::{web::scope, App, HttpServer};
|
use actix_web::{web::scope, App, HttpServer};
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
|
mod db;
|
||||||
mod jwt;
|
mod jwt;
|
||||||
mod routes;
|
mod routes;
|
||||||
mod state;
|
mod state;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
use routes::{get_posts, login, new_post, register, test};
|
use routes::{get_posts, login, new_post, register};
|
||||||
use state::ServerState;
|
use state::ServerState;
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
|
@ -28,7 +29,6 @@ async fn main() -> std::io::Result<()> {
|
||||||
.service(get_posts)
|
.service(get_posts)
|
||||||
.service(new_post)
|
.service(new_post)
|
||||||
.service(routes::vote)
|
.service(routes::vote)
|
||||||
.service(test)
|
|
||||||
.service(login)
|
.service(login)
|
||||||
.service(register)
|
.service(register)
|
||||||
.app_data(Data::new(data.clone())),
|
.app_data(Data::new(data.clone())),
|
||||||
|
|
7
server/src/routes/mod.rs
Normal file
7
server/src/routes/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
mod post;
|
||||||
|
mod users;
|
||||||
|
mod vote;
|
||||||
|
|
||||||
|
pub use post::*;
|
||||||
|
pub use users::*;
|
||||||
|
pub use vote::*;
|
27
server/src/routes/post.rs
Executable file
27
server/src/routes/post.rs
Executable file
|
@ -0,0 +1,27 @@
|
||||||
|
use crate::jwt::token_factory;
|
||||||
|
use crate::types::{NewPost, Post};
|
||||||
|
use crate::ServerState;
|
||||||
|
|
||||||
|
use actix_web::web::{Data, Path};
|
||||||
|
use actix_web::{get, post, web::Json, HttpResponse, Responder, Result};
|
||||||
|
use argon2::password_hash::rand_core::OsRng;
|
||||||
|
use argon2::password_hash::SaltString;
|
||||||
|
use argon2::password_hash::*;
|
||||||
|
use argon2::Argon2;
|
||||||
|
use argon2::PasswordHasher;
|
||||||
|
use argon2::PasswordVerifier;
|
||||||
|
use log::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[get("/posts")]
|
||||||
|
pub async fn get_posts(data: Data<ServerState>) -> impl Responder {
|
||||||
|
HttpResponse::InternalServerError().body("Unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/posts")]
|
||||||
|
pub async fn new_post(new_post: Json<NewPost>, data: Data<ServerState>) -> impl Responder {
|
||||||
|
let post = Post::from(new_post.into_inner());
|
||||||
|
info!("Created post {:?}", post.uuid);
|
||||||
|
HttpResponse::Ok().json("Post added!")
|
||||||
|
}
|
|
@ -14,95 +14,6 @@ use log::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[get("/")]
|
|
||||||
pub async fn get_posts(data: Data<ServerState>) -> impl Responder {
|
|
||||||
match data.posts.lock() {
|
|
||||||
Ok(posts) => {
|
|
||||||
let posts: Vec<Post> = posts.values().cloned().collect();
|
|
||||||
HttpResponse::Ok().json(posts)
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Error: {:?}", e);
|
|
||||||
HttpResponse::InternalServerError().body("Error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/")]
|
|
||||||
pub async fn new_post(new_post: Json<NewPost>, data: Data<ServerState>) -> impl Responder {
|
|
||||||
let post = Post::from(new_post.into_inner());
|
|
||||||
info!("Created post {:?}", post.uuid);
|
|
||||||
|
|
||||||
// let q = "INSERT INTO posts (uuid, content, upvotes, downvotes) VALUES (?, ?, ?, ?)";
|
|
||||||
// let query = sqlx::query(q)
|
|
||||||
// .bind(post.uuid)
|
|
||||||
// .bind(post.content)
|
|
||||||
// .bind(post.votes.up)
|
|
||||||
// .bind(post.votes.down);
|
|
||||||
|
|
||||||
match data.posts.lock() {
|
|
||||||
Ok(mut posts) => {
|
|
||||||
posts.insert(post.uuid, post);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Error: {:?}", e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
HttpResponse::Ok().json("Post added!")
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a test route, returns "Hello, world!"
|
|
||||||
#[get("/test")]
|
|
||||||
pub async fn test(data: Data<ServerState>) -> impl Responder {
|
|
||||||
match data.posts.lock() {
|
|
||||||
Ok(posts) => {
|
|
||||||
let posts: Vec<Post> = posts.values().cloned().collect();
|
|
||||||
HttpResponse::Ok().body(format!("Hello, world! {:?}", posts))
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Error: {:?}", e);
|
|
||||||
HttpResponse::InternalServerError().body("Error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum VoteDirection {
|
|
||||||
Up,
|
|
||||||
Down,
|
|
||||||
Unupvote,
|
|
||||||
Undownvote,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("vote/{uuid}/{direction}")]
|
|
||||||
pub async fn vote(params: Path<(Uuid, VoteDirection)>, data: Data<ServerState>) -> impl Responder {
|
|
||||||
let (uuid, direction) = params.into_inner();
|
|
||||||
println!("Voting {:?} on post {:?}", direction, uuid);
|
|
||||||
|
|
||||||
match data.posts.lock() {
|
|
||||||
Ok(mut posts) => {
|
|
||||||
let uuid = uuid;
|
|
||||||
if let Some(post) = posts.get_mut(&uuid) {
|
|
||||||
match direction {
|
|
||||||
VoteDirection::Up => post.votes.up += 1,
|
|
||||||
VoteDirection::Unupvote => post.votes.up -= 1,
|
|
||||||
VoteDirection::Down => post.votes.down += 1,
|
|
||||||
VoteDirection::Undownvote => post.votes.down -= 1,
|
|
||||||
}
|
|
||||||
HttpResponse::Ok().body("Downvoted!")
|
|
||||||
} else {
|
|
||||||
HttpResponse::NotFound().body("Post not found!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Error: {:?}", e);
|
|
||||||
HttpResponse::InternalServerError().body("Error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct RegisterData {
|
pub struct RegisterData {
|
||||||
username: String,
|
username: String,
|
16
server/src/routes/vote.rs
Normal file
16
server/src/routes/vote.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
use crate::state::ServerState;
|
||||||
|
use actix_web::web::{Data, Path};
|
||||||
|
use actix_web::{get, post, web::Json, HttpResponse, Responder, Result};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
enum VoteDirection {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("vote/{uuid}")]
|
||||||
|
pub async fn vote(params: Path<(Uuid, VoteDirection)>, data: Data<ServerState>) -> impl Responder {
|
||||||
|
HttpResponse::InternalServerError().body("Unimplemented")
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ServerState {
|
pub struct ServerState {
|
||||||
pub posts: Arc<Mutex<BTreeMap<Uuid, Post>>>,
|
// pub posts: Arc<Mutex<BTreeMap<Uuid, Post>>>,
|
||||||
pub pool: Pool<Sqlite>,
|
pub pool: Pool<Sqlite>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ impl ServerState {
|
||||||
sqlx::migrate!("./migrations").run(&pool).await.unwrap();
|
sqlx::migrate!("./migrations").run(&pool).await.unwrap();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
posts: Arc::new(Mutex::new(BTreeMap::new())),
|
// posts: Arc::new(Mutex::new(BTreeMap::new())),
|
||||||
pool: pool,
|
pool: pool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ use uuid::Uuid;
|
||||||
// The post as it is received from the client
|
// The post as it is received from the client
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct NewPost {
|
pub struct NewPost {
|
||||||
content: String,
|
pub content: String,
|
||||||
token: String,
|
pub token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
// The post as it is stored in the database
|
// The post as it is stored in the database
|
||||||
|
@ -13,7 +13,8 @@ pub struct NewPost {
|
||||||
pub struct Post {
|
pub struct Post {
|
||||||
pub uuid: Uuid,
|
pub uuid: Uuid,
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub votes: VoteCount,
|
pub upvotes: i32,
|
||||||
|
pub downvotes: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<NewPost> for Post {
|
impl From<NewPost> for Post {
|
||||||
|
@ -21,20 +22,8 @@ impl From<NewPost> for Post {
|
||||||
Self {
|
Self {
|
||||||
uuid: Uuid::new_v4(),
|
uuid: Uuid::new_v4(),
|
||||||
content: post.content,
|
content: post.content,
|
||||||
votes: VoteCount::new(),
|
upvotes: 0,
|
||||||
|
downvotes: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Part of the post struct
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
||||||
pub struct VoteCount {
|
|
||||||
pub up: u32,
|
|
||||||
pub down: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VoteCount {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self { up: 0, down: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue