Major restructure

This commit is contained in:
Imbus 2023-10-20 06:06:55 +02:00
parent 5b8d1cbdb1
commit 27386d5d18
9 changed files with 87 additions and 110 deletions

View file

@ -2,6 +2,8 @@ CREATE TABLE IF NOT EXISTS posts (
id SERIAL PRIMARY KEY,
user_id SERIAL 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,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id)

25
server/src/db.rs Normal file
View 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!();
}

View file

@ -4,12 +4,13 @@ use actix_web::web::Data;
use actix_web::{web::scope, App, HttpServer};
use log::info;
mod db;
mod jwt;
mod routes;
mod state;
mod types;
use routes::{get_posts, login, new_post, register, test};
use routes::{get_posts, login, new_post, register};
use state::ServerState;
#[actix_web::main]
@ -28,7 +29,6 @@ async fn main() -> std::io::Result<()> {
.service(get_posts)
.service(new_post)
.service(routes::vote)
.service(test)
.service(login)
.service(register)
.app_data(Data::new(data.clone())),

7
server/src/routes/mod.rs Normal file
View 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
View 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!")
}

View file

@ -14,95 +14,6 @@ use log::*;
use serde::{Deserialize, Serialize};
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)]
pub struct RegisterData {
username: String,

16
server/src/routes/vote.rs Normal file
View 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")
}

View file

@ -9,7 +9,7 @@ use uuid::Uuid;
#[derive(Clone)]
pub struct ServerState {
pub posts: Arc<Mutex<BTreeMap<Uuid, Post>>>,
// pub posts: Arc<Mutex<BTreeMap<Uuid, Post>>>,
pub pool: Pool<Sqlite>,
}
@ -24,7 +24,7 @@ impl ServerState {
sqlx::migrate!("./migrations").run(&pool).await.unwrap();
Self {
posts: Arc::new(Mutex::new(BTreeMap::new())),
// posts: Arc::new(Mutex::new(BTreeMap::new())),
pool: pool,
}
}

View file

@ -4,8 +4,8 @@ use uuid::Uuid;
// The post as it is received from the client
#[derive(Debug, Serialize, Deserialize)]
pub struct NewPost {
content: String,
token: String,
pub content: String,
pub token: String,
}
// The post as it is stored in the database
@ -13,7 +13,8 @@ pub struct NewPost {
pub struct Post {
pub uuid: Uuid,
pub content: String,
pub votes: VoteCount,
pub upvotes: i32,
pub downvotes: i32,
}
impl From<NewPost> for Post {
@ -21,20 +22,8 @@ impl From<NewPost> for Post {
Self {
uuid: Uuid::new_v4(),
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 }
}
}