mirror of
https://gitlab.com/comunic/comunicapiv3
synced 2024-11-22 13:29:21 +00:00
Create server
This commit is contained in:
parent
22d47efa9c
commit
1170a94309
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]
|
[dependencies]
|
||||||
yaml-rust = "0.4.3"
|
yaml-rust = "0.4.3"
|
||||||
mysql = "18.2.0"
|
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(())
|
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
|
/// 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 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::data::config::{conf, Config};
|
||||||
use comunic_server::helpers::database;
|
use comunic_server::helpers::database;
|
||||||
use mysql::prelude::Queryable;
|
use comunic_server::controllers::server;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[actix_rt::main]
|
||||||
struct User {
|
async fn main() -> std::io::Result<()> {
|
||||||
id : i32,
|
|
||||||
name : String,
|
|
||||||
email: String,
|
|
||||||
age: i32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
|
|
||||||
// Load configuration
|
// Load configuration
|
||||||
Config::load("config.yaml").expect("Could not load configuration!");
|
Config::load("config.yaml").expect("Could not load configuration!");
|
||||||
@ -18,13 +11,6 @@ fn main() {
|
|||||||
// Connect to the database
|
// Connect to the database
|
||||||
database::connect(&conf().database).expect("Could not connect to database!");
|
database::connect(&conf().database).expect("Could not connect to database!");
|
||||||
|
|
||||||
let mut conn = database::get_connection().unwrap();
|
// Start the server
|
||||||
let res = conn.query_map("SELECT id, name, email, age FROM user", |(id, name, email, age)| {
|
server::start_server(conf()).await
|
||||||
User {id, name, email, age}
|
|
||||||
}).unwrap();
|
|
||||||
println!("{:#?}", res);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
println!("Hello, world!");
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user