You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

96 lines
3.6 KiB

use std::{
env, io,
net::{SocketAddrV4},
sync::Arc,
};
use actix_identity::IdentityMiddleware;
use actix_session::{config::PersistentSession, storage::CookieSessionStore, SessionMiddleware};
use actix_web::{
cookie::{time::Duration, Key},
middleware,
web::{self},
App, HttpServer,
};
// Define our model module
mod model;
use model::user::User;
// Define our repo module
mod repo;
use repo::todo_repository::TodoRepository;
use repo::user_repository::UserRepository;
// Define our routes module
mod routes;
use routes::{todo_routes, user_routes};
/// Server address if none is given via ENV: WEBSERVER_ADDRESS
static DEFAULT_WEBSERVER_ADDRESS: &str = "127.0.0.1";
/// Server port if none is given via ENV: WEBSERVER_PORT
static DEFAULT_WEBSERVER_PORT: &str = "6969";
/// Starts the webserver and initializes required repos, etc.
#[actix_web::main]
async fn main() -> io::Result<()> {
env_logger::init_from_env(env_logger::Env::new().default_filter_or("debug"));
// TODO: Currently I do not handle the case where the cassandra server is
// rebooted during the lifetime of the webserver and loses its configuration.
// Normally this is not a real scenario because the tables should be persistent
// througout reboots but idk, it would still be cool to handle this gracefully.
// What would have to happen is a reinit of the repo classes after the connection
// is regained to reinitialize the perhaps missing keyspaces/tables
let cassandra_session = Arc::new(repo::init());
// TODO: web::Data is already an Arc
//-> Investigate if we really need the session as an Arc as well
let user = User::new("admin", "init_pw_hash", "init_salt");
let user_repo = web::Data::new(UserRepository::new(Arc::clone(&cassandra_session)));
if let Err(err) = user_repo.create(&user) {
log::debug!("Default user already exists: {err}");
}
let todo_repo = web::Data::new(TodoRepository::new(Arc::clone(&cassandra_session)));
// Secret key used to init session storage
let key = Key::generate();
// External ENV params
let socket_addr: SocketAddrV4 = format!(
"{}:{}",
env::var("WEBSERVER_ADDRESS").unwrap_or(DEFAULT_WEBSERVER_ADDRESS.to_string()),
env::var("WEBSERVER_PORT").unwrap_or(DEFAULT_WEBSERVER_PORT.to_string())
).parse().expect("A valid socket address. Check your ENV variables!");
log::info!("Starting HTTP server: http://{}:{}", socket_addr.ip(), socket_addr.port());
HttpServer::new(move || {
App::new()
.wrap(middleware::Compress::default())
.wrap(IdentityMiddleware::default())
.wrap(
SessionMiddleware::builder(CookieSessionStore::default(), key.clone())
.cookie_secure(false)
// Session lifetime
.session_lifecycle(PersistentSession::default().session_ttl(Duration::days(7)))
.build(),
)
.wrap(middleware::Logger::default())
.app_data(user_repo.clone())
.app_data(todo_repo.clone())
.service(user_routes::get_user)
.service(user_routes::put_user)
.service(user_routes::post_user)
.service(todo_routes::get_todo)
.service(todo_routes::put_todo)
.service(todo_routes::delete_todo)
.service(routes::post_login)
.service(routes::delete_logout)
.default_service(web::to(routes::index))
})
.bind(socket_addr)?
.workers(2) // number of workers per bind default ist #cpus
.run()
.await
}