mirror of
				https://gitlab.com/comunic/comunicapiv3
				synced 2025-10-30 23:24:42 +00:00 
			
		
		
		
	Create server
This commit is contained in:
		
							
								
								
									
										1006
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1006
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -9,5 +9,6 @@ edition = "2018" | ||||
| [dependencies] | ||||
| yaml-rust = "0.4.3" | ||||
| mysql = "18.2.0" | ||||
|  | ||||
|  | ||||
| actix-web = "2.0.0" | ||||
| actix-rt = "1.1.1" | ||||
| serde = "1.0.110" | ||||
							
								
								
									
										4
									
								
								src/controllers/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/controllers/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| pub mod routes; | ||||
| pub mod server; | ||||
|  | ||||
| pub mod server_controller; | ||||
							
								
								
									
										41
									
								
								src/controllers/routes.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/controllers/routes.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| use std::error::Error; | ||||
|  | ||||
| use crate::controllers::routes::Method::GET; | ||||
| use crate::controllers::server_controller; | ||||
| use crate::data::http_request_handler::HttpRequestHandler; | ||||
|  | ||||
| /// Project routes | ||||
| /// | ||||
| /// @author Pierre Hubert | ||||
|  | ||||
| #[derive(PartialEq)] | ||||
| pub enum Method { | ||||
|     GET, | ||||
|     POST, | ||||
| } | ||||
|  | ||||
| /// Define types | ||||
| pub type RequestResult = Result<(), Box<dyn Error>>; | ||||
| pub type RequestProcess = Box<dyn Fn(&mut HttpRequestHandler) -> RequestResult>; | ||||
|  | ||||
| pub struct Route { | ||||
|     /// The Verb used for the request | ||||
|     pub method: Method, | ||||
|  | ||||
|     /// The URI of the request, with the leading "/" | ||||
|     pub uri: &'static str, | ||||
|  | ||||
|     /// If set to true, unauthenticated requests will be rejected | ||||
|     pub need_login: bool, | ||||
|  | ||||
|     /// The function called to process a request | ||||
|     pub func: RequestProcess, | ||||
| } | ||||
|  | ||||
| /// Get the list of routes available | ||||
| pub fn get_routes() -> Vec<Route> { | ||||
|     vec![ | ||||
|         // Server meta routes | ||||
|         Route { method: GET, uri: "/", need_login: false, func: Box::new(server_controller::main_index) } | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										80
									
								
								src/controllers/server.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								src/controllers/server.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | ||||
| use actix_web::{App, HttpResponse, HttpServer, web, http}; | ||||
|  | ||||
| use crate::controllers::routes::{get_routes, Route}; | ||||
| use crate::data::config::Config; | ||||
| use crate::data::http_error::HttpError; | ||||
| use crate::controllers::routes::Method::{GET, POST}; | ||||
| use crate::data::http_request_handler::HttpRequestHandler; | ||||
|  | ||||
| /// Main server functions | ||||
| /// | ||||
| /// @author Pierre Hubert | ||||
|  | ||||
| /// Process an incoming request | ||||
| async fn process_request(req: web::HttpRequest) -> HttpResponse { | ||||
|     let routes = get_routes(); | ||||
|  | ||||
|     // We search the appropriate route for the request | ||||
|     let mut route: Option<&Route> = None; | ||||
|     for el in &routes { | ||||
|  | ||||
|         // Check verb | ||||
|         if !(req.method() == http::Method::GET && el.method == GET) && | ||||
|             !(req.method() == http::Method::POST && el.method == POST) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         // Check path | ||||
|         if !el.uri.eq(req.uri()) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         route = Some(el); | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     // Check if a route was found | ||||
|     if let None = route { | ||||
|         return HttpResponse::NotFound().json(HttpError::not_found("Method not found!")); | ||||
|     } | ||||
|     let route = route.unwrap(); | ||||
|  | ||||
|     // Execute the request | ||||
|     let mut request = HttpRequestHandler::new(req); | ||||
|  | ||||
|     match (route.func)(&mut request) { | ||||
|  | ||||
|         // Set default error response if required | ||||
|         Err(e) => { | ||||
|             if !request.has_response() { | ||||
|                 request.internal_error(e).unwrap_err(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Set default success response if required | ||||
|         Ok(_) => { | ||||
|             if !request.has_response() { | ||||
|                 request.success("Success").unwrap() | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // I use this to be quiet with IntelliJ | ||||
|         #[allow(unreachable_patterns)] | ||||
|         _ => {println!("Unexpected case (server.rs)!")} | ||||
|     } | ||||
|  | ||||
|     request.response() | ||||
| } | ||||
|  | ||||
|  | ||||
| /// Given the configuration, start the server | ||||
| pub async fn start_server(conf: &Config) -> std::io::Result<()> { | ||||
|     let addr = conf.server_listen_address(); | ||||
|     println!("Start to listen on http://{}/", addr); | ||||
|  | ||||
|     HttpServer::new(|| { | ||||
|         App::new() | ||||
|             .route("**", web::get().to(process_request)) | ||||
|             .route("**", web::post().to(process_request)) | ||||
|     }).bind(&addr)?.run().await | ||||
| } | ||||
							
								
								
									
										11
									
								
								src/controllers/server_controller.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/controllers/server_controller.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| use crate::data::http_request_handler::HttpRequestHandler; | ||||
| use crate::controllers::routes::RequestResult; | ||||
|  | ||||
| /// Main server controller | ||||
| /// | ||||
| /// @author Pierre Hubert | ||||
|  | ||||
| /// Root server index | ||||
| pub fn main_index(request:  &mut HttpRequestHandler) -> RequestResult { | ||||
|     request.success("Comunic API server V3. (c) Pierre Hubert 2020") | ||||
| } | ||||
| @@ -87,6 +87,11 @@ impl Config { | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Get the address this server listen to | ||||
|     pub fn server_listen_address(&self) -> String { | ||||
|         format!("{}:{}", self.listen_address, self.port) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Get an instance of the configuration | ||||
|   | ||||
							
								
								
									
										30
									
								
								src/data/http_error.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/data/http_error.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| use serde::Serialize; | ||||
|  | ||||
| /// HTTP request error | ||||
| /// | ||||
| /// @author Pierre Hubert | ||||
|  | ||||
| #[derive(Serialize)] | ||||
| pub struct HttpError { | ||||
|     pub code: u16, | ||||
|     pub message: String, | ||||
| } | ||||
|  | ||||
| impl HttpError { | ||||
|  | ||||
|     /// Generate a 404 error | ||||
|     pub fn not_found(message: &str) -> HttpError { | ||||
|         HttpError { | ||||
|             code: 404, | ||||
|             message: message.to_string() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Generate a 500 error | ||||
|     pub fn internal_error(message: &str) -> HttpError { | ||||
|         HttpError { | ||||
|             code: 500, | ||||
|             message: message.to_string() | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										56
									
								
								src/data/http_request_handler.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/data/http_request_handler.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| use actix_web::{web, HttpRequest, HttpResponse}; | ||||
| use crate::controllers::routes::RequestResult; | ||||
| use crate::data::http_error::HttpError; | ||||
| use std::convert::TryFrom; | ||||
| use std::error::Error; | ||||
| use serde::Serialize; | ||||
|  | ||||
| /// Http request handler | ||||
| /// | ||||
| /// @author Pierre Hubert | ||||
|  | ||||
| #[derive(Serialize)] | ||||
| struct SuccessMessage { | ||||
|     success: String | ||||
| } | ||||
|  | ||||
| pub struct HttpRequestHandler { | ||||
|     request: web::HttpRequest, | ||||
|     response: Option<web::HttpResponse> | ||||
| } | ||||
|  | ||||
| impl HttpRequestHandler { | ||||
|  | ||||
|     /// Construct a new request handler | ||||
|     pub fn new(req: HttpRequest) -> HttpRequestHandler { | ||||
|         HttpRequestHandler { | ||||
|             request: req, | ||||
|             response: None | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Check if a response has been set for this request | ||||
|     pub fn has_response(&self) -> bool { | ||||
|         self.response.is_some() | ||||
|     } | ||||
|  | ||||
|     /// Take the response from this struct | ||||
|     pub fn response(self) -> HttpResponse { | ||||
|         self.response.unwrap() | ||||
|     } | ||||
|  | ||||
|     /// Success message | ||||
|     pub fn success(&mut self, message: &str) -> RequestResult { | ||||
|         self.response = Some(HttpResponse::Ok().json(SuccessMessage { | ||||
|             success: message.to_string() | ||||
|         })); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Internal error message | ||||
|     pub fn internal_error(&mut self, error: Box<dyn Error>) -> RequestResult { | ||||
|         self.response = Some(HttpResponse::InternalServerError().json( | ||||
|             HttpError::internal_error("Internal server error."))); | ||||
|         Err(Box::try_from(actix_web::error::ErrorInternalServerError(error)).unwrap()) | ||||
|     } | ||||
| } | ||||
| @@ -1 +1,3 @@ | ||||
| pub mod config; | ||||
| pub mod config; | ||||
| pub mod http_error; | ||||
| pub mod http_request_handler; | ||||
| @@ -1,2 +1,3 @@ | ||||
| pub mod data; | ||||
| pub mod helpers; | ||||
| pub mod helpers; | ||||
| pub mod controllers; | ||||
							
								
								
									
										24
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								src/main.rs
									
									
									
									
									
								
							| @@ -1,16 +1,9 @@ | ||||
| use comunic_server::data::config::{conf, Config}; | ||||
| use comunic_server::helpers::database; | ||||
| use mysql::prelude::Queryable; | ||||
| use comunic_server::controllers::server; | ||||
|  | ||||
| #[derive(Debug, PartialEq, Eq)] | ||||
| struct User { | ||||
|     id : i32, | ||||
|     name : String, | ||||
|     email: String, | ||||
|     age: i32 | ||||
| } | ||||
|  | ||||
| fn main() { | ||||
| #[actix_rt::main] | ||||
| async fn main() -> std::io::Result<()> { | ||||
|  | ||||
|     // Load configuration | ||||
|     Config::load("config.yaml").expect("Could not load configuration!"); | ||||
| @@ -18,13 +11,6 @@ fn main() { | ||||
|     // Connect to the database | ||||
|     database::connect(&conf().database).expect("Could not connect to database!"); | ||||
|  | ||||
|     let mut conn = database::get_connection().unwrap(); | ||||
|     let res = conn.query_map("SELECT id, name, email, age FROM user", |(id, name, email, age)| { | ||||
|         User {id, name, email, age} | ||||
|     }).unwrap(); | ||||
|     println!("{:#?}", res); | ||||
|  | ||||
|  | ||||
|  | ||||
|     println!("Hello, world!"); | ||||
|     // Start the server | ||||
|     server::start_server(conf()).await | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user