Replace Rusqlite by Sqlx and Actix by Axum (A lot of changes)
This commit is contained in:
parent
57d7e7a3ce
commit
980c5884a4
28 changed files with 2860 additions and 2262 deletions
|
|
@ -1,9 +1,17 @@
|
|||
use std::path::Path;
|
||||
use std::{net::SocketAddr, path::Path};
|
||||
|
||||
use actix_files as fs;
|
||||
use actix_web::{middleware, web, App, HttpServer};
|
||||
use axum::{
|
||||
extract::{ConnectInfo, FromRef, Request, State},
|
||||
middleware::{self, Next},
|
||||
response::{Response, Result},
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use axum_extra::extract::cookie::CookieJar;
|
||||
use chrono::prelude::*;
|
||||
use clap::Parser;
|
||||
use config::Config;
|
||||
use tower_http::services::ServeDir;
|
||||
|
||||
use data::db;
|
||||
|
||||
|
|
@ -16,49 +24,121 @@ mod model;
|
|||
mod services;
|
||||
mod utils;
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
if process_args() {
|
||||
return Ok(());
|
||||
}
|
||||
#[derive(Clone)]
|
||||
struct AppState {
|
||||
config: Config,
|
||||
db_connection: db::Connection,
|
||||
}
|
||||
|
||||
std::env::set_var("RUST_LOG", "info,actix_web=info");
|
||||
env_logger::init();
|
||||
impl FromRef<AppState> for Config {
|
||||
fn from_ref(app_state: &AppState) -> Config {
|
||||
app_state.config.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRef<AppState> for db::Connection {
|
||||
fn from_ref(app_state: &AppState) -> db::Connection {
|
||||
app_state.db_connection.clone()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Should main returns 'Result'?
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
if process_args().await {
|
||||
return;
|
||||
}
|
||||
|
||||
println!("Starting Recipes as web server...");
|
||||
|
||||
let config = web::Data::new(config::load());
|
||||
let port = config.as_ref().port;
|
||||
let config = config::load();
|
||||
let port = config.port;
|
||||
|
||||
println!("Configuration: {:?}", config);
|
||||
|
||||
let db_connection = web::Data::new(db::Connection::new().unwrap());
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let server = HttpServer::new(move || {
|
||||
App::new()
|
||||
.wrap(middleware::Logger::default())
|
||||
.wrap(middleware::Compress::default())
|
||||
.app_data(db_connection.clone())
|
||||
.app_data(config.clone())
|
||||
.service(services::home_page)
|
||||
.service(services::sign_up_get)
|
||||
.service(services::sign_up_post)
|
||||
.service(services::sign_up_check_email)
|
||||
.service(services::sign_up_validation)
|
||||
.service(services::sign_in_get)
|
||||
.service(services::sign_in_post)
|
||||
.service(services::sign_out)
|
||||
.service(services::view_recipe)
|
||||
.service(services::edit_recipe)
|
||||
.service(services::new_recipe)
|
||||
.service(services::webapi::set_recipe_title)
|
||||
.service(services::webapi::set_recipe_description)
|
||||
.service(fs::Files::new("/static", "static"))
|
||||
.default_service(web::to(services::not_found))
|
||||
});
|
||||
//.workers(1);
|
||||
let db_connection = db::Connection::new().await.unwrap();
|
||||
|
||||
server.bind(&format!("0.0.0.0:{}", port))?.run().await
|
||||
let state = AppState {
|
||||
config,
|
||||
db_connection,
|
||||
};
|
||||
|
||||
let app = Router::new()
|
||||
.route("/", get(services::home_page))
|
||||
.route(
|
||||
"/signup",
|
||||
get(services::sign_up_get).post(services::sign_up_post),
|
||||
)
|
||||
.route("/validation", get(services::sign_up_validation))
|
||||
.route("/recipe/view/:id", get(services::view_recipe))
|
||||
.route(
|
||||
"/signin",
|
||||
get(services::sign_in_get).post(services::sign_in_post),
|
||||
)
|
||||
.route("/signout", get(services::sign_out))
|
||||
.route_layer(middleware::from_fn_with_state(
|
||||
state.clone(),
|
||||
user_authentication,
|
||||
))
|
||||
.nest_service("/static", ServeDir::new("static"))
|
||||
.fallback(services::not_found)
|
||||
.with_state(state)
|
||||
.into_make_service_with_connect_info::<SocketAddr>();
|
||||
|
||||
let addr = SocketAddr::from(([0, 0, 0, 0], port));
|
||||
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
||||
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
}
|
||||
|
||||
async fn user_authentication(
|
||||
ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
||||
State(connection): State<db::Connection>,
|
||||
mut req: Request,
|
||||
next: Next,
|
||||
) -> Result<Response> {
|
||||
let jar = CookieJar::from_headers(req.headers());
|
||||
let (client_ip, client_user_agent) = utils::get_ip_and_user_agent(req.headers(), addr);
|
||||
let user = get_current_user(connection, &jar, &client_ip, &client_user_agent).await;
|
||||
req.extensions_mut().insert(user);
|
||||
Ok(next.run(req).await)
|
||||
}
|
||||
|
||||
async fn get_current_user(
|
||||
connection: db::Connection,
|
||||
jar: &CookieJar,
|
||||
client_ip: &str,
|
||||
client_user_agent: &str,
|
||||
) -> Option<model::User> {
|
||||
match jar.get(consts::COOKIE_AUTH_TOKEN_NAME) {
|
||||
Some(token_cookie) => match connection
|
||||
.authentication(token_cookie.value(), &client_ip, &client_user_agent)
|
||||
.await
|
||||
{
|
||||
Ok(db::AuthenticationResult::NotValidToken) => {
|
||||
// TODO: remove cookie?
|
||||
None
|
||||
}
|
||||
Ok(db::AuthenticationResult::Ok(user_id)) => {
|
||||
match connection.load_user(user_id).await {
|
||||
Ok(user) => user,
|
||||
Err(error) => {
|
||||
// TODO: Return 'Result'?
|
||||
println!("Error during authentication: {}", error);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
// TODO: Return 'Result'?
|
||||
println!("Error during authentication: {}", error);
|
||||
None
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
|
|
@ -68,7 +148,7 @@ struct Args {
|
|||
dbtest: bool,
|
||||
}
|
||||
|
||||
fn process_args() -> bool {
|
||||
async fn process_args() -> bool {
|
||||
let args = Args::parse();
|
||||
|
||||
if args.dbtest {
|
||||
|
|
@ -93,16 +173,18 @@ fn process_args() -> bool {
|
|||
.expect(&format!("Unable to remove db file: {:?}", &db_path));
|
||||
}
|
||||
|
||||
match db::Connection::new() {
|
||||
match db::Connection::new().await {
|
||||
Ok(con) => {
|
||||
if let Err(error) = con.execute_file("sql/data_test.sql") {
|
||||
if let Err(error) = con.execute_file("sql/data_test.sql").await {
|
||||
eprintln!("{}", error);
|
||||
}
|
||||
// Set the creation datetime to 'now'.
|
||||
con.execute_sql(
|
||||
"UPDATE [User] SET [creation_datetime] = ?1 WHERE [email] = 'paul@test.org'",
|
||||
[Utc::now()],
|
||||
sqlx::query(
|
||||
"UPDATE [User] SET [creation_datetime] = ?1 WHERE [email] = 'paul@test.org'")
|
||||
.bind(Utc::now())
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
Err(error) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue