19 changed files with 326 additions and 73 deletions
@ -0,0 +1,94 @@ |
|||
use tokio::net::TcpStream as TokioTcpStream; |
|||
use tokio::io::AsyncWriteExt; |
|||
use crate::models::user; |
|||
use crate::parsers::parse_json_body; |
|||
use crate::models::{HttpRequest::HttpRequest, user::User, credentials::Credentials}; |
|||
use crate::middlewares::users::do_user_already_exist; |
|||
use crate::utils::crypt; |
|||
|
|||
pub async fn register(http_request: HttpRequest, stream: &mut TokioTcpStream) { |
|||
let response = "HTTP/1.1 200 OK\r\n\r\nRegistration Successful"; |
|||
|
|||
// MIDDLEWARE: Check if user already exists
|
|||
if http_request.headers.content_type.as_ref().expect("") != "application/json" { |
|||
let response = "HTTP/1.1 400 BAD REQUEST\r\n\r\nContent-Type must be application/json"; |
|||
if let Err(e) = stream.write_all(response.as_bytes()).await { |
|||
eprintln!("Error writing response: {}", e); |
|||
} |
|||
return; |
|||
} |
|||
|
|||
let mut user = parse_json_body::<User>(&http_request.body.expect("")).unwrap(); |
|||
user.password = crate::utils::crypt::encrypt(&user.password); |
|||
|
|||
if do_user_already_exist(user.clone()).await { |
|||
let response = "HTTP/1.1 409 CONFLICT\r\n\r\nUser already exists"; |
|||
if let Err(e) = stream.write_all(response.as_bytes()).await { |
|||
eprintln!("Error writing response: {}", e); |
|||
} |
|||
return; |
|||
} |
|||
|
|||
// SERVICE: Register the user
|
|||
match crate::services::users::register(user).await { |
|||
Ok(_) => { |
|||
if let Err(e) = stream.write_all(response.as_bytes()).await { |
|||
eprintln!("Error writing response: {}", e); |
|||
} |
|||
} |
|||
Err(e) => { |
|||
let response = format!("HTTP/1.1 500 INTERNAL SERVER ERROR\r\n\r\nError: {}", e); |
|||
if let Err(e) = stream.write_all(response.as_bytes()).await { |
|||
eprintln!("Error writing response: {}", e); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
pub async fn login(http_request: HttpRequest, stream: &mut TokioTcpStream) { |
|||
|
|||
if http_request.headers.content_type.as_ref().expect("") != "application/json" { |
|||
let response = "HTTP/1.1 400 BAD REQUEST\r\n\r\nContent-Type must be application/json"; |
|||
if let Err(e) = stream.write_all(response.as_bytes()).await { |
|||
eprintln!("Error writing response: {}", e); |
|||
} |
|||
return; |
|||
} |
|||
|
|||
let body = http_request.body.as_ref().expect(""); |
|||
let creds = parse_json_body::<Credentials>(&http_request.body.expect("")).unwrap(); |
|||
|
|||
let user = match crate::services::users::get_user_by_email(&creds.email).await { |
|||
Ok(Some(user)) => user, |
|||
Ok(None) => { |
|||
let response = "HTTP/1.1 401 UNAUTHORIZED\r\n\r\nInvalid email or password"; |
|||
if let Err(e) = stream.write_all(response.as_bytes()).await { |
|||
eprintln!("Error writing response: {}", e); |
|||
} |
|||
return; |
|||
} |
|||
Err(e) => { |
|||
let response = format!("HTTP/1.1 500 INTERNAL SERVER ERROR\r\n\r\nError: {}", e); |
|||
if let Err(e) = stream.write_all(response.as_bytes()).await { |
|||
eprintln!("Error writing response: {}", e); |
|||
} |
|||
return; |
|||
} |
|||
}; |
|||
|
|||
if user.password != crate::utils::crypt::encrypt(&creds.password) { |
|||
let response = "HTTP/1.1 401 UNAUTHORIZED\r\n\r\nInvalid email or password"; |
|||
if let Err(e) = stream.write_all(response.as_bytes()).await { |
|||
eprintln!("Error writing response: {}", e); |
|||
} |
|||
return; |
|||
} |
|||
|
|||
let token = crypt::encrypt(&format!("jklhsdfhjkdfsjkhlfzerhjsdf_{}:{}", user.username, user.email)); |
|||
|
|||
let response = format!("HTTP/1.1 200 OK\r\n\r\nLogin Successful\r\nAuthorization: Bearer {}", token); |
|||
if let Err(e) = stream.write_all(response.as_bytes()).await { |
|||
eprintln!("Error writing response: {}", e); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1 @@ |
|||
pub mod auth; |
|||
@ -1,14 +1,19 @@ |
|||
mod http; |
|||
mod models; |
|||
mod parsers; |
|||
|
|||
mod controllers; |
|||
mod utils; |
|||
mod services; |
|||
mod routes; |
|||
mod middlewares; |
|||
use dotenv::dotenv; |
|||
|
|||
#[tokio::main] |
|||
async fn main() { |
|||
dotenv().ok(); |
|||
println!("Starting application..."); |
|||
|
|||
http::http_server(); |
|||
http::http_server().await; |
|||
|
|||
|
|||
} |
|||
|
|||
@ -0,0 +1 @@ |
|||
pub mod users; |
|||
@ -0,0 +1,9 @@ |
|||
use crate::models::user::User; |
|||
|
|||
|
|||
|
|||
pub async fn do_user_already_exist(user: User) -> bool { |
|||
// Placeholder for middleware logic
|
|||
let do_exist = crate::services::users::get_user_by_username(&user.username).await; |
|||
do_exist.is_ok() |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
use serde::{Serialize, Deserialize}; |
|||
|
|||
#[derive(Debug, Clone, Serialize, Deserialize)] |
|||
pub struct Credentials { |
|||
pub email: String, |
|||
pub password: String, |
|||
} |
|||
@ -1,4 +1,4 @@ |
|||
pub mod HttpRequest; |
|||
pub mod http_header; |
|||
|
|||
pub mod credentials; |
|||
pub mod user; |
|||
@ -1,59 +1,47 @@ |
|||
use crate::models::{HttpRequest::HttpRequest, user::User}; |
|||
use crate::parsers::parse_json_body; |
|||
use std::net::TcpStream; |
|||
use std::io::prelude::*; |
|||
use crate::models::HttpRequest::HttpRequest; |
|||
use crate::controllers::auth; |
|||
use tokio::net::TcpStream as TokioTcpStream; |
|||
use tokio::io::AsyncWriteExt; |
|||
|
|||
pub fn handle_auth_request(http_request: HttpRequest, mut stream: &TcpStream) { |
|||
pub async fn handle_auth_request(http_request: HttpRequest, stream: &mut TokioTcpStream) { |
|||
match http_request.headers.method.as_ref().expect("").as_str() { |
|||
"POST" => { |
|||
// Handle login or registration
|
|||
post(http_request, &stream); |
|||
post(http_request, stream).await; |
|||
} |
|||
"GET" => { |
|||
// Handle fetching user info or logout
|
|||
println!("Handling auth GET request for path: {}", http_request.headers.path.as_ref().expect("").join("/")); |
|||
let response = "HTTP/1.1 200 OK\r\n\r\nAuth GET Response"; |
|||
stream.write_all(response.as_bytes()).unwrap(); |
|||
if let Err(e) = stream.write_all(response.as_bytes()).await { |
|||
eprintln!("Error writing response: {}", e); |
|||
} |
|||
} |
|||
_ => { |
|||
// Method not allowed
|
|||
let response = "HTTP/1.1 405 METHOD NOT ALLOWED\r\n\r\nMethod Not Allowed"; |
|||
stream.write_all(response.as_bytes()).unwrap(); |
|||
if let Err(e) = stream.write_all(response.as_bytes()).await { |
|||
eprintln!("Error writing response: {}", e); |
|||
} |
|||
return; |
|||
} |
|||
} |
|||
} |
|||
|
|||
pub fn post(http_request: HttpRequest, mut stream: &TcpStream) { |
|||
pub async fn post(http_request: HttpRequest, stream: &mut TokioTcpStream) { |
|||
println!("Handling auth POST request: {}", http_request.headers.path.as_ref().expect("").join("/")); |
|||
match http_request.headers.path.clone().expect("").join("/").as_str() { |
|||
"/api/auth/login" => { |
|||
println!("Handling login"); |
|||
let response = "HTTP/1.1 200 OK\r\n\r\nLogin Successful"; |
|||
stream.write_all(response.as_bytes()).unwrap(); |
|||
auth::login(http_request, stream).await; |
|||
} |
|||
"/api/auth/register" => { |
|||
register(http_request, &stream); |
|||
auth::register(http_request, stream).await; |
|||
} |
|||
_ => { |
|||
let response = "HTTP/1.1 404 NOT FOUND\r\n\r\nAuth Endpoint Not Found"; |
|||
stream.write_all(response.as_bytes()).unwrap(); |
|||
if let Err(e) = stream.write_all(response.as_bytes()).await { |
|||
eprintln!("Error writing response: {}", e); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
pub fn register(http_request: HttpRequest, mut stream: &TcpStream) { |
|||
|
|||
let response = "HTTP/1.1 200 OK\r\n\r\nRegistration Successful"; |
|||
|
|||
if http_request.headers.content_type.as_ref().expect("") != "application/json" { |
|||
let response = "HTTP/1.1 400 BAD REQUEST\r\n\r\nContent-Type must be application/json"; |
|||
stream.write_all(response.as_bytes()).unwrap(); |
|||
return; |
|||
} |
|||
|
|||
let user = parse_json_body::<User>(&http_request.body.expect("")).unwrap(); |
|||
println!("Registered user: {}", user.username); |
|||
|
|||
stream.write_all(response.as_bytes()).unwrap(); |
|||
} |
|||
@ -1,33 +1,33 @@ |
|||
use crate::models::HttpRequest::HttpRequest; |
|||
use crate::routes::statics; |
|||
use std::net::TcpStream; |
|||
use tokio::net::TcpStream as TokioTcpStream; |
|||
use crate::routes::auth; |
|||
|
|||
fn is_file(file: &str) -> bool { |
|||
file.contains('.') |
|||
} |
|||
|
|||
pub fn proxy(http_request: HttpRequest, stream: &TcpStream) { |
|||
pub async fn proxy(http_request: HttpRequest, stream: &mut TokioTcpStream) { |
|||
println!("Proxying request..."); |
|||
|
|||
if is_file(http_request.clone().headers.path.expect("").join("/").as_str()) { |
|||
statics::serve_static_file(http_request.clone(), stream); |
|||
statics::serve_static_file(http_request.clone(), stream).await; |
|||
} else { |
|||
handle_api_request(http_request.clone(), stream); |
|||
handle_api_request(http_request.clone(), stream).await; |
|||
} |
|||
} |
|||
|
|||
pub fn handle_api_request(http_request: HttpRequest, stream: &TcpStream) { |
|||
pub async fn handle_api_request(http_request: HttpRequest, stream: &mut TokioTcpStream) { |
|||
|
|||
println!("{:?}", http_request.headers.path.clone().expect("")[0].as_str()); |
|||
match http_request.headers.path.clone().expect("")[1].as_str() { |
|||
"api" => { |
|||
match http_request.headers.path.clone().expect("")[2].as_str() { |
|||
"auth" => auth::handle_auth_request(http_request, &stream), |
|||
_ => (), |
|||
} |
|||
}, |
|||
_ => (), |
|||
}; |
|||
match http_request.headers.path.clone().expect("")[1].as_str() { |
|||
"api" => { |
|||
match http_request.headers.path.clone().expect("")[2].as_str() { |
|||
"auth" => auth::handle_auth_request(http_request, stream).await, |
|||
_ => futures::future::ready(()).await, |
|||
} |
|||
}, |
|||
_ => futures::future::ready(()).await, |
|||
}; |
|||
|
|||
} |
|||
@ -0,0 +1 @@ |
|||
pub mod users; |
|||
@ -0,0 +1,64 @@ |
|||
use crate::models::user::User; |
|||
use sqlx::Row; |
|||
|
|||
pub async fn register(user: User) -> Result<(), sqlx::Error> { |
|||
// Here you would normally insert the user into the database
|
|||
// For demonstration, we'll just print the user info
|
|||
println!("Registering user: {}", user.username); |
|||
let db = crate::utils::db::establish_connection().await?; |
|||
sqlx::query( |
|||
"INSERT INTO users (username, email, password) VALUES ($1, $2, $3)" |
|||
) |
|||
.bind(user.username) |
|||
.bind(user.email) |
|||
.bind(user.password) |
|||
.execute(&db) |
|||
.await?; |
|||
Ok(()) |
|||
} |
|||
|
|||
pub async fn get_user_by_username(username: &str) -> Result<Option<User>, sqlx::Error> { |
|||
let db = crate::utils::db::establish_connection().await?; |
|||
let user_query = sqlx::query( |
|||
"SELECT id, username, email, password FROM users WHERE username = $1" |
|||
) |
|||
.bind(username) |
|||
.fetch_optional(&db) |
|||
.await?; |
|||
|
|||
let user = if let Some(row) = user_query { |
|||
Some(User { |
|||
id: row.get("id"), |
|||
username: row.get("username"), |
|||
email: row.get("email"), |
|||
password: row.get("password"), |
|||
}) |
|||
} else { |
|||
None |
|||
}; |
|||
|
|||
Ok(user) |
|||
} |
|||
|
|||
pub async fn get_user_by_email(email: &str) -> Result<Option<User>, sqlx::Error> { |
|||
let db = crate::utils::db::establish_connection().await?; |
|||
let user_query = sqlx::query( |
|||
"SELECT id, username, email, password FROM users WHERE email = $1" |
|||
) |
|||
.bind(email) |
|||
.fetch_optional(&db) |
|||
.await?; |
|||
|
|||
let user = if let Some(row) = user_query { |
|||
Some(User { |
|||
id: row.get("id"), |
|||
username: row.get("username"), |
|||
email: row.get("email"), |
|||
password: row.get("password"), |
|||
}) |
|||
} else { |
|||
None |
|||
}; |
|||
|
|||
Ok(user) |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
|
|||
|
|||
pub fn encrypt(plaintext: &str) -> String { |
|||
let data = plaintext.as_bytes(); |
|||
let key = std::env::var("ENCRYPTION_KEY").unwrap(); |
|||
let key = key.as_bytes(); |
|||
let mut encrypted = Vec::new(); |
|||
|
|||
for (i, byte) in data.iter().enumerate() { |
|||
let key_byte = key[i % key.len()]; |
|||
let encrypted_byte = byte ^ key_byte; |
|||
print!("{:02x}", encrypted_byte); |
|||
encrypted.push(encrypted_byte); |
|||
} |
|||
println!(); |
|||
encrypted.iter().map(|b| format!("{:02x}", b)).collect() |
|||
|
|||
} |
|||
|
|||
pub fn decrypt(ciphertext: &str) -> String { |
|||
let bytes = (0..ciphertext.len()) |
|||
.step_by(2) |
|||
.map(|i| u8::from_str_radix(&ciphertext[i..i + 2], 16).unwrap()) |
|||
.collect::<Vec<u8>>(); |
|||
|
|||
let key = std::env::var("ENCRYPTION_KEY").unwrap(); |
|||
let key = key.as_bytes(); |
|||
let mut decrypted = Vec::new(); |
|||
|
|||
for (i, byte) in bytes.iter().enumerate() { |
|||
let key_byte = key[i % key.len()]; |
|||
let decrypted_byte = byte ^ key_byte; |
|||
decrypted.push(decrypted_byte); |
|||
} |
|||
|
|||
String::from_utf8(decrypted).unwrap() |
|||
} |
|||
@ -1,7 +1,8 @@ |
|||
use sqlx; |
|||
use sqlx::PgPool; |
|||
|
|||
pub async fn establish_connection() -> Result<PgPool, sqlx::Error> { |
|||
let database_url = "non"; |
|||
let database_url = "postgres://astria:Rwqfsfasxc_974@192.168.1.109/chatty"; |
|||
let pool = PgPool::connect(database_url).await?; |
|||
Ok(pool) |
|||
} |
|||
@ -0,0 +1,2 @@ |
|||
pub mod db; |
|||
pub mod crypt; |
|||
Loading…
Reference in new issue