Database integration WIP

This commit is contained in:
Imbus 2023-10-10 00:03:04 +02:00
parent f3e5cd62b1
commit 2bcda34f6a
13 changed files with 225 additions and 37 deletions

View file

@ -1,21 +1,31 @@
use actix_web::web::Data;
#![allow(dead_code, unused_imports)]
use actix_web::web::{Data, Query};
use actix_web::{web::scope, App, HttpServer};
use log::info;
// use uuid::Uuid;
mod routes;
mod state;
mod types;
use log::info;
use routes::{get_posts, new_post, test};
use types::AppState;
use routes::{get_posts, new_post, register, test};
use sqlx::ConnectOptions;
use state::AppState;
use sqlx::{migrate::MigrateDatabase, query, sqlite};
struct User {
name: String,
pass: String,
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("debug")).init();
info!("Starting server...");
let data = AppState::new();
let data = AppState::new().await;
info!("Spinning up server on http://localhost:8080");
HttpServer::new(move || {
App::new().service(
scope("api")
@ -23,6 +33,7 @@ async fn main() -> std::io::Result<()> {
.service(new_post)
.service(routes::vote)
.service(test)
.service(register)
.app_data(Data::new(data.clone())),
)
})

View file

@ -1,6 +1,9 @@
use crate::types::{AppState, NewPost, Post};
use crate::types::{NewPost, Post};
use crate::AppState;
use actix_web::web::{Data, Path};
use actix_web::{get, post, web::Json, HttpResponse, Responder};
use actix_web::{get, post, web::Json, HttpResponse, Responder, Result};
use argon2::password_hash::SaltString;
use log::*;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
@ -24,6 +27,13 @@ pub async fn new_post(new_post: Json<NewPost>, data: Data<AppState>) -> impl Res
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);
@ -86,3 +96,44 @@ pub async fn vote(params: Path<(Uuid, VoteDirection)>, data: Data<AppState>) ->
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct RegisterData {
username: String,
password: String,
captcha: String,
}
use argon2::password_hash::rand_core::OsRng;
use argon2::password_hash::*;
use argon2::Algorithm;
use argon2::Argon2;
use argon2::PasswordHasher;
use argon2::PasswordVerifier;
use argon2::Version;
#[post("/register")]
pub async fn register(data: Json<RegisterData>, state: Data<AppState>) -> Result<impl Responder> {
let q = "SELECT password FROM users WHERE username = ?";
let query = sqlx::query(q).bind(&data.username);
let result = query.fetch_one(&state.pool).await.ok();
if result.is_some() {
info!("User \"{}\" already exists", data.username);
return Ok(HttpResponse::BadRequest().json("Error"));
}
let password = data.password.clone();
let salt = SaltString::generate(&mut OsRng);
let phc_hash = Argon2::default().hash_password(password.as_bytes(), &salt);
if let Ok(phc_hash) = phc_hash {
info!("User: {} registered", data.username);
let phc_hash = phc_hash.to_string();
let q = "INSERT INTO users (username, password) VALUES (?, ?)";
let query = sqlx::query(q).bind(&data.username).bind(&phc_hash);
query.execute(&state.pool).await.unwrap();
} else {
return Ok(HttpResponse::BadRequest().json("Error"));
}
Ok(HttpResponse::Ok().json("User registered"))
}

31
server/src/state.rs Normal file
View file

@ -0,0 +1,31 @@
use crate::types::Post;
use sqlx::Sqlite;
use sqlx::{self, sqlite};
use sqlx::{AnyPool, Pool};
use std::collections::BTreeMap;
use std::sync::Arc;
use std::sync::Mutex;
use uuid::Uuid;
#[derive(Clone)]
pub struct AppState {
pub posts: Arc<Mutex<BTreeMap<Uuid, Post>>>,
pub pool: Pool<Sqlite>,
}
impl AppState {
pub async fn new() -> Self {
let pool = sqlite::SqlitePoolOptions::new()
.max_connections(5)
.connect(":memory:")
.await
.unwrap();
sqlx::migrate!("./migrations").run(&pool).await.unwrap();
Self {
posts: Arc::new(Mutex::new(BTreeMap::new())),
pool: pool,
}
}
}

View file

@ -4,16 +4,17 @@ use std::sync::Arc;
use std::sync::Mutex;
use uuid::Uuid;
// The post as it is received from the client
#[derive(Debug, Serialize, Deserialize)]
pub struct NewPost {
title: String,
content: String,
token: String,
}
// The post as it is stored in the database
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Post {
pub uuid: Uuid,
pub title: String,
pub content: String,
pub votes: VoteCount,
}
@ -22,26 +23,12 @@ impl From<NewPost> for Post {
fn from(post: NewPost) -> Self {
Self {
uuid: Uuid::new_v4(),
title: post.title,
content: post.content,
votes: VoteCount::new(),
}
}
}
#[derive(Clone)]
pub struct AppState {
pub posts: Arc<Mutex<BTreeMap<Uuid, Post>>>,
}
impl AppState {
pub fn new() -> Self {
Self {
posts: Arc::new(Mutex::new(BTreeMap::new())),
}
}
}
// Part of the post struct
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct VoteCount {