-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore:experimental modifications, part of re-writting backend in rust…
… fully, wip
- Loading branch information
1 parent
75a0892
commit 77bbe46
Showing
3 changed files
with
274 additions
and
127 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,129 +1,47 @@ | ||
// #[allow(unused_imports)] | ||
// #[allow(dead_code)] | ||
// #[allow(unused_variables)] | ||
// #[macro_use] extern crate rocket; | ||
// #[cfg(test)] mod test; | ||
// use rocket::serde::json::{json, Value}; | ||
// use rocket::{State, Shutdown}; | ||
// use rocket::fs::{relative, FileServer}; | ||
// use rocket::form::Form; | ||
// use rocket::response::stream::{EventStream, Event}; | ||
// use rocket::serde::{Serialize, Deserialize}; | ||
// use rocket::tokio::sync::broadcast::{channel, Sender, error::RecvError}; | ||
// use rocket::tokio::select; | ||
// mod utils; | ||
|
||
// // type Id = usize; | ||
// // type MessageList = Mutex<Vec<String>>; | ||
// // type Messages<'r> = &'r State<MessageList>; // nested type alias | ||
|
||
// /// only primary modification has been made to Message | ||
// // #[derive(Debug, Clone, FromForm, Serialize, Deserialize)] | ||
// // #[cfg_attr(test, derive(PartialEq, UriDisplayQuery))] | ||
// // #[serde(crate = "rocket::serde")] | ||
// // struct Message { | ||
// // #[field(validate = len(1..20))] | ||
// // pub room: String, | ||
// // #[field(validate = len(1..50))] | ||
// // pub user_email: String, | ||
// // pub sender : String, | ||
// // pub reciever : String, | ||
// // // TODO : uncomment later | ||
// // // pub sequenceNumber : u32, | ||
|
||
// // #[field(validate=len(1..300))] | ||
// // pub message: String, | ||
// // } | ||
|
||
// #[derive(Debug, Clone, FromForm, Serialize, Deserialize)] | ||
// #[cfg_attr(test, derive(PartialEq, UriDisplayQuery))] | ||
// #[serde(crate = "rocket::serde")] | ||
// struct Message { | ||
// #[field(validate = len(2..30))] | ||
// pub room: String, | ||
|
||
// #[field(validate = len(2..20))] | ||
// pub user_email: String, | ||
// pub sender : String, | ||
// #[field(validate = len(..300))] | ||
// pub message: String, | ||
// } | ||
// /// recieve a message from a form submission and broadcast it to any recievers | ||
// /// /events/<reciever> originally | ||
// #[get("/chat/events")] | ||
// async fn events( | ||
// queue : &State<Sender<Message>>, mut end : Shutdown) -> EventStream![] { | ||
// let mut rx = queue.subscribe(); | ||
// EventStream! { | ||
// loop { | ||
// let msg = select! { | ||
// msg = rx.recv() => match msg { | ||
// Ok(msg) => msg, | ||
// Err(RecvError::Closed) => break, | ||
// Err(RecvError::Lagged(_)) => continue, | ||
// }, | ||
// _ = &mut end => break, | ||
// }; | ||
|
||
// // println!("Message sent successfully!"); | ||
// yield Event::json(&msg); | ||
// } | ||
// } | ||
// } | ||
|
||
// #[post("/chat/message", data = "<form>")] | ||
// fn post(form: Form<Message>, queue: &State<Sender<Message>>) { | ||
// let _res = queue.send(form.into_inner()); | ||
|
||
// // format!("The response is {:#?}", _res); | ||
// println!("The response is {:#?}", _res); | ||
// } | ||
|
||
// // handles HTTP 404 code, returns this messages | ||
// #[catch(404)] | ||
// fn not_found() -> Value { | ||
// json!({ | ||
// "status" : "error", | ||
// "reason" : "Resource was not found." | ||
// }) | ||
// } | ||
// // #[post("/test_submission")] | ||
// // the return type is automatically inferrred when set to _ | ||
// // #[launch] indicates function to execute when server starts | ||
// #[launch] | ||
// fn rocket() -> _ { | ||
// // the launch function returns an instance of | ||
// // Rocket<Build> from a function named rocket | ||
// // routes parameter takes in an array of values | ||
// // specifies the type for the channel | ||
// // to be of the same as the struct we have defined | ||
// // the .build() method creates a new Rocket application using the default configuration provider | ||
|
||
// // the routes! macro takes in array containing information | ||
// // regarding each of the functions corresponding to the routes | ||
// // "/chat" specifies the base url | ||
// // in this case, that would be http://localhost:8000/chat/message_send for example --> this post method will contain the JSON data as the payload. | ||
// rocket::build().manage(channel::<Message>(1024).0).mount("/", routes![post, | ||
// events, | ||
// shutdown_custom | ||
// ]) | ||
// } | ||
|
||
// // // define the function we can use to make our api calls | ||
// // // 2 parameters accepted | ||
// // async fn make_api_calls(client : &Client, apiEndpoint : &str) -> Result<Value, reqwest::Error> { | ||
// // // we can format the url using the format!() macro | ||
// // // the return type will either be successful (Value) or result in an error that will be "containerized" | ||
// // let endpoint_url = format!("http://localhost:8000/{apiEndpoint}"); | ||
// // let response = client.get(&endpoint_url).send().await?; | ||
|
||
// // // convert the response into json data format | ||
// // let result_value = response.json::<Value>().await?; | ||
// // // explicit return | ||
// // Ok(result_value) | ||
|
||
// // } | ||
#[macro_use] | ||
extern crate diesel; | ||
use actix::*; | ||
use actix_cors::Cors; | ||
use actix_files::Files; | ||
use actix_web::{http, web, App, HttpServer}; | ||
use diesel::{ | ||
prelude::*, | ||
r2d2::{self, ConnectionManager}, | ||
}; | ||
use diesel::pg::PgConnection; | ||
use std::env; | ||
mod db; | ||
mod models; | ||
mod routes; | ||
mod schema; | ||
mod session; | ||
|
||
pub type PostgresPool = Pool<ConnectionManager<PgConnection>>; | ||
|
||
pub async fn get_pool() -> PostgresPool { | ||
dotenv().ok(); | ||
let url = env::var("DATABASE_URL").expect("no DB URL"); | ||
|
||
let migr = ConnectionManager::<PgConnection>::new(url); | ||
r2d2::Pool::builder().build(migr).expect("could not build connection pool") | ||
} | ||
|
||
pub fn main() { | ||
println!("Complete reset"); | ||
// TODO : Test this | ||
#[actix_web::main] | ||
async fn main() -> std::io::Result<()> { | ||
let server = server::ChatServer::new().start; | ||
let server_addr = "127.0.0.1"; | ||
let server_port = "8080"; | ||
let pool = get_pool().await; | ||
let app = HttpServer::new(move || { | ||
let cors = Cors::default().allowed_origin("http://localhost:3000").allowed_origin("http://localhost:8080").allowed_methods(vec!["GET","POST"]).allowed_headers(vec![http::header::Authorization, http::header::ACCEPT]).allowed_header(http::header::CONTENT_TYPE).max_age(3600); | ||
|
||
// TODO : change from /ws to /websocket for clarity | ||
// after functionality has been established | ||
App::new().app_data(web::Data::new(server.clone())).app_data(web::Data::new(pool.clone())).wrap(cors).service("/ws", web::get().to(routes::chat_server)).service(routes::create_user).service(routes::get_user_by_id).service(routes::get_user_by_phone).service(routes::get_conversation_by_id).service(routes::get_rooms).service(Files::new("/", "/.static")) | ||
}).workers(2).bind({server_addr, server_port})?.run(); | ||
println!("Server running at http://{server_addr}:{server_port}/"); | ||
|
||
// closure | ||
app.await(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
use std::time::Instant; | ||
use actix::*; | ||
use actix_files::NamedFile; | ||
use actix_web::{get, post, web, Error, HttpRequest, HttpResponse, Responder}; | ||
use actix_web_actors::ws; // websocket | ||
use diesel::{ | ||
prelude::*, | ||
r2d2::{self, ConnectionManager}, | ||
}; | ||
use serde_json::json; | ||
use uuid::Uuid; | ||
use crate::db; | ||
use crate::server; | ||
use crate::session; | ||
|
||
// NOTE: this is being re-defined here | ||
pub type PostgresPool = Pool<ConnectionManager<PgConnection>>; | ||
|
||
pub async fn index() -> impl Responder { | ||
NamedFile::open_async("./static/index.html").await.unwrap(); | ||
} | ||
|
||
pub async fn chat_server( | ||
req : HttpRequest, | ||
stream : web::Payload, | ||
pool : web::Data<DbPool>, | ||
srv : web::Data<Addr<server::ChatServer>> | ||
) -> Result<HttpResponse, Error> { | ||
ws::start( | ||
session::WsChatSession { | ||
id : 0, | ||
hb : Instant::now(), | ||
room : "main".to_string(), | ||
name : None, | ||
addr : srv.get_ref().clone(), | ||
db_pool : pool | ||
}, | ||
&req, | ||
stream | ||
) | ||
} | ||
|
||
|
||
/// actix_web::error::ErrorUnprocessableEntity is a helper function that wraps any kind of errors to generate UNPROCESSABLE_ENTITY message instead. | ||
#[post("/users/create")] | ||
pub async fn create_user( | ||
pool : web::Data<DbPool>, | ||
form : web::Json<models::NewUser> | ||
) -> Result<HttpResponse, Error> { | ||
let user = web::block(move || { | ||
let mut conn = pool.get()?; | ||
|
||
// may not need a phone number | ||
// we want only distinct emails | ||
// replace phone with emails | ||
db::insert_new_user(&mut conn, &form.username, &form.email, &form.password) | ||
}).await? | ||
.map_err(actix_web::error::ErrorUnprocessableEntity)?; | ||
Ok(HttpResponse::Ok().json(user)) | ||
} | ||
|
||
|
||
/// function to retrieve information about an user given their id | ||
#[get("/users/{user_id}")] | ||
pub async fn get_user_by_id( | ||
pool : web::Data<DbPool>, | ||
id : web::Path<Uuid> | ||
) -> Result<HttpResponse, Error> { | ||
let user_id = id.to_owned(); | ||
let user = web::block(move || { | ||
let mut conn = pool.get()?; | ||
db::find_user_by_uid(&mut conn, user_id) | ||
}) | ||
.await? | ||
.map_err(actix_web::error::ErrorInternalServerError)?; | ||
|
||
if let Some(user) = user { | ||
Ok(HttpResponse::Ok().json(user)) | ||
} else { | ||
let res = HttpResponse::NotFound().body( | ||
json!({ | ||
"error" : 404, | ||
"message" : format!("No user found with user id : {id}") | ||
}).to_string(), | ||
); | ||
Ok(res) | ||
} | ||
} | ||
|
||
/// function to define an user given their email | ||
/// originally /users/phone/{phone_number} | ||
#[get("/users/email/{email_address}")] | ||
pub async fn get_user_by_email | ||
/// function that attempts to retrieve conversation history | ||
/// given a particular room id | ||
/// actix_web::error::ErrorInternalServerError : helper function to wrap the errors and instead generating "INTERNAL_SERVER_ERROR" response instead | ||
#[get("/conversations/{uid}")] | ||
pub async fn get_conversation_by_id( | ||
pool : web::Data<DbPool>, | ||
uid : web::Path<Uuid>, | ||
) -> Result<HttpResponse, Error> { | ||
let room_id = uid.to_owned(); | ||
|
||
// define conversations as a closure | ||
let conversations = web::block(move || { | ||
let mut conn = pool.get()?; | ||
db::get_conversation_by_id(&mut conn, room_id) | ||
}) | ||
.await? | ||
.map_err(actix_web::error::ErrorInternalServerError)?; | ||
if let Some(data) = conversations { | ||
Ok(HttpResponse::Ok().json(data)) | ||
} else { | ||
let res = HttpResponse::NotFound().body( | ||
json!({ | ||
"error" : 404, | ||
"message" : format!("No conversation with room id : {room_id}") | ||
}).to_string(), | ||
); | ||
Ok(res); | ||
} | ||
} | ||
|
||
#[get("/rooms")] | ||
pub async fn get_rooms( | ||
pool : web::Data<DbPool> | ||
) -> Result<HttpResponse, Error> { | ||
|
||
// map_err : is used to transform one type of error to another | ||
let rooms = web::block(move || { | ||
let mut conn = pool.get()?; | ||
db::get_all_rooms(&mut conn) | ||
}) | ||
.await? | ||
.map_err(actix_web::error::ErrorInternalServerError)?; | ||
|
||
if !room.is_empty() { | ||
Ok(HttpResponse::Ok().json(rooms)) | ||
} else { | ||
let res = HttpResponse::NotFound().body( | ||
json!({ | ||
"error" : 404, | ||
"message" : "No Rooms available at the moment" | ||
}).to_string(), | ||
); | ||
|
||
Ok(res) | ||
} | ||
} |
Oops, something went wrong.