mirror of
				https://gitlab.com/comunic/comunicapiv3
				synced 2025-11-04 09:34:04 +00:00 
			
		
		
		
	Create server
This commit is contained in:
		
							
								
								
									
										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