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 http; |
||||
mod models; |
mod models; |
||||
mod parsers; |
mod parsers; |
||||
|
mod controllers; |
||||
|
mod utils; |
||||
|
mod services; |
||||
mod routes; |
mod routes; |
||||
|
mod middlewares; |
||||
|
use dotenv::dotenv; |
||||
|
|
||||
#[tokio::main] |
#[tokio::main] |
||||
async fn main() { |
async fn main() { |
||||
|
dotenv().ok(); |
||||
println!("Starting application..."); |
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 HttpRequest; |
||||
pub mod http_header; |
pub mod http_header; |
||||
|
pub mod credentials; |
||||
pub mod user; |
pub mod user; |
||||
@ -1,59 +1,47 @@ |
|||||
use crate::models::{HttpRequest::HttpRequest, user::User}; |
use crate::models::HttpRequest::HttpRequest; |
||||
use crate::parsers::parse_json_body; |
use crate::controllers::auth; |
||||
use std::net::TcpStream; |
use tokio::net::TcpStream as TokioTcpStream; |
||||
use std::io::prelude::*; |
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() { |
match http_request.headers.method.as_ref().expect("").as_str() { |
||||
"POST" => { |
"POST" => { |
||||
// Handle login or registration
|
// Handle login or registration
|
||||
post(http_request, &stream); |
post(http_request, stream).await; |
||||
} |
} |
||||
"GET" => { |
"GET" => { |
||||
// Handle fetching user info or logout
|
// Handle fetching user info or logout
|
||||
println!("Handling auth GET request for path: {}", http_request.headers.path.as_ref().expect("").join("/")); |
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"; |
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
|
// Method not allowed
|
||||
let response = "HTTP/1.1 405 METHOD NOT ALLOWED\r\n\r\nMethod 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; |
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("/")); |
println!("Handling auth POST request: {}", http_request.headers.path.as_ref().expect("").join("/")); |
||||
match http_request.headers.path.clone().expect("").join("/").as_str() { |
match http_request.headers.path.clone().expect("").join("/").as_str() { |
||||
"/api/auth/login" => { |
"/api/auth/login" => { |
||||
println!("Handling login"); |
auth::login(http_request, stream).await; |
||||
let response = "HTTP/1.1 200 OK\r\n\r\nLogin Successful"; |
|
||||
stream.write_all(response.as_bytes()).unwrap(); |
|
||||
} |
} |
||||
"/api/auth/register" => { |
"/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"; |
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::models::HttpRequest::HttpRequest; |
||||
use crate::routes::statics; |
use crate::routes::statics; |
||||
use std::net::TcpStream; |
use tokio::net::TcpStream as TokioTcpStream; |
||||
use crate::routes::auth; |
use crate::routes::auth; |
||||
|
|
||||
fn is_file(file: &str) -> bool { |
fn is_file(file: &str) -> bool { |
||||
file.contains('.') |
file.contains('.') |
||||
} |
} |
||||
|
|
||||
pub fn proxy(http_request: HttpRequest, stream: &TcpStream) { |
pub async fn proxy(http_request: HttpRequest, stream: &mut TokioTcpStream) { |
||||
println!("Proxying request..."); |
println!("Proxying request..."); |
||||
|
|
||||
if is_file(http_request.clone().headers.path.expect("").join("/").as_str()) { |
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 { |
} 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()); |
println!("{:?}", http_request.headers.path.clone().expect("")[0].as_str()); |
||||
match http_request.headers.path.clone().expect("")[1].as_str() { |
match http_request.headers.path.clone().expect("")[1].as_str() { |
||||
"api" => { |
"api" => { |
||||
match http_request.headers.path.clone().expect("")[2].as_str() { |
match http_request.headers.path.clone().expect("")[2].as_str() { |
||||
"auth" => auth::handle_auth_request(http_request, &stream), |
"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; |
||||
|
use sqlx::PgPool; |
||||
|
|
||||
pub async fn establish_connection() -> Result<PgPool, sqlx::Error> { |
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?; |
let pool = PgPool::connect(database_url).await?; |
||||
Ok(pool) |
Ok(pool) |
||||
} |
} |
||||
@ -0,0 +1,2 @@ |
|||||
|
pub mod db; |
||||
|
pub mod crypt; |
||||
Loading…
Reference in new issue