Move app creation to lib.rs
All checks were successful
/ build (push) Successful in 1m55s

This makes integration testing much easier as it can reuse the app
instance.
This commit is contained in:
Simon Bruder 2024-07-27 22:15:30 +02:00
parent 79c4ab6c2b
commit b22588cd0d
Signed by: simon
GPG key ID: 347FF8699CDA0776
2 changed files with 84 additions and 64 deletions

View file

@ -11,3 +11,79 @@ pub mod middleware;
mod test_utils; mod test_utils;
pub use config::Config; pub use config::Config;
use actix_identity::IdentityMiddleware;
use actix_session::{storage::CookieSessionStore, SessionMiddleware};
use actix_web::middleware::ErrorHandlers;
use actix_web::{cookie::Key, http::StatusCode, web, App, HttpResponse};
use base64::prelude::{Engine as _, BASE64_STANDARD};
use log::warn;
use mime_guess::from_path;
use rust_embed::Embed;
use sqlx::PgPool;
use crate::database::{
ItemClassRepository, ItemEventRepository, ItemRepository, ItemStateRepository,
};
#[derive(Embed)]
#[folder = "static"]
struct Static;
pub fn app(
config: &Config,
pool: &PgPool,
) -> App<
impl actix_web::dev::ServiceFactory<
actix_web::dev::ServiceRequest,
Config = (),
Response = actix_web::dev::ServiceResponse<
actix_web::body::EitherBody<actix_web::body::BoxBody>,
>,
Error = actix_web::Error,
InitError = (),
>,
> {
let secret_key = match config.secret_key {
Some(ref encoded) => Key::from(
&BASE64_STANDARD
.decode(encoded)
.expect("failed to decode base64 in SECRET_KEY"),
),
None => {
warn!("SECRET_KEY was not specified, using randomly generated key");
Key::generate()
}
};
App::new()
.app_data(web::Data::new(config.clone()))
.app_data(web::Data::new(pool.clone()))
.app_data(web::Data::new(ItemClassRepository::new(pool.clone())))
.app_data(web::Data::new(ItemEventRepository::new(pool.clone())))
.app_data(web::Data::new(ItemStateRepository::new(pool.clone())))
.app_data(web::Data::new(ItemRepository::new(pool.clone())))
.service(web::scope("/static").route(
"/{_:.*}",
web::get().to(|path: web::Path<String>| async {
Static::get(&path)
.map(|embedded_file| match from_path(path.into_inner()).first() {
Some(mime_type) => HttpResponse::Ok()
.content_type(mime_type)
.body(embedded_file.data),
None => HttpResponse::Ok().body(embedded_file.data),
})
.unwrap_or(HttpResponse::NotFound().body(()))
}),
))
.configure(frontend::config)
.wrap(ErrorHandlers::new().handler(
StatusCode::UNAUTHORIZED,
middleware::error_handlers::redirect_to_login,
))
.wrap(IdentityMiddleware::default())
.wrap(SessionMiddleware::new(
CookieSessionStore::default(),
secret_key.clone(),
))
}

View file

@ -2,45 +2,20 @@
// //
// SPDX-License-Identifier: AGPL-3.0-or-later // SPDX-License-Identifier: AGPL-3.0-or-later
use actix_identity::IdentityMiddleware; use actix_web::HttpServer;
use actix_session::{storage::CookieSessionStore, SessionMiddleware};
use actix_web::middleware::ErrorHandlers;
use actix_web::{cookie::Key, http::StatusCode, web, App, HttpResponse, HttpServer};
use base64::prelude::{Engine as _, BASE64_STANDARD};
use clap::Parser; use clap::Parser;
use li7y::database::item_events::ItemEventRepository; use log::info;
use li7y::database::item_states::ItemStateRepository;
use li7y::database::items::ItemRepository;
use li7y::database::ItemClassRepository;
use log::{info, warn};
use mime_guess::from_path;
use rust_embed::Embed;
use li7y::Config; use li7y::Config;
#[derive(Embed)]
#[folder = "static"]
struct Static;
#[actix_web::main] #[actix_web::main]
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
let config = Config::parse(); let config = Config::parse();
let secret_key = match config.secret_key { // This cant be included in app, because app gets called in a (non-async) closure
Some(ref encoded) => Key::from( let pool = sqlx::Pool::<sqlx::postgres::Postgres>::connect(&config.database_url)
&BASE64_STANDARD
.decode(encoded)
.expect("failed to decode base64 in SECRET_KEY"),
),
None => {
warn!("SECRET_KEY was not specified, using randomly generated key");
Key::generate()
}
};
let pool: sqlx::PgPool = sqlx::Pool::<sqlx::postgres::Postgres>::connect(&config.database_url)
.await .await
.expect("failed to connect to database"); .expect("failed to connect to database");
@ -54,39 +29,8 @@ async fn main() -> std::io::Result<()> {
info!("Starting on {address}:{port}"); info!("Starting on {address}:{port}");
HttpServer::new(move || { HttpServer::new(move || li7y::app(&config, &pool))
App::new() .bind((address, port))?
.app_data(web::Data::new(config.clone())) .run()
.app_data(web::Data::new(pool.clone())) .await
.app_data(web::Data::new(ItemClassRepository::new(pool.clone())))
.app_data(web::Data::new(ItemEventRepository::new(pool.clone())))
.app_data(web::Data::new(ItemStateRepository::new(pool.clone())))
.app_data(web::Data::new(ItemRepository::new(pool.clone())))
.service(web::scope("/static").route(
"/{_:.*}",
web::get().to(|path: web::Path<String>| async {
Static::get(&path)
.map(|embedded_file| match from_path(path.into_inner()).first() {
Some(mime_type) => HttpResponse::Ok()
.content_type(mime_type)
.body(embedded_file.data),
None => HttpResponse::Ok().body(embedded_file.data),
})
.unwrap_or(HttpResponse::NotFound().body(()))
}),
))
.configure(li7y::frontend::config)
.wrap(ErrorHandlers::new().handler(
StatusCode::UNAUTHORIZED,
li7y::middleware::error_handlers::redirect_to_login,
))
.wrap(IdentityMiddleware::default())
.wrap(SessionMiddleware::new(
CookieSessionStore::default(),
secret_key.clone(),
))
})
.bind((address, port))?
.run()
.await
} }