#![recursion_limit = "1024"]
#![warn(clippy::pedantic)]
#![warn(clippy::nursery)]
#![warn(clippy::cargo)]
#![warn(
clippy::clone_on_ref_ptr,
clippy::empty_structs_with_brackets,
clippy::exit,
clippy::expect_used,
clippy::format_push_string,
clippy::get_unwrap,
clippy::if_then_some_else_none,
clippy::indexing_slicing,
clippy::integer_division,
clippy::large_include_file,
clippy::missing_docs_in_private_items,
clippy::mixed_read_write_in_expression,
clippy::multiple_inherent_impl,
clippy::mutex_atomic,
clippy::panic_in_result_fn,
clippy::partial_pub_fields,
clippy::print_stderr,
clippy::print_stdout,
clippy::rc_buffer,
clippy::rc_mutex,
clippy::rest_pat_in_fully_bound_structs,
clippy::same_name_method,
clippy::shadow_unrelated,
clippy::str_to_string,
clippy::string_to_string,
clippy::suspicious_xor_used_as_pow,
clippy::todo,
clippy::try_err,
clippy::unimplemented,
clippy::unnecessary_self_imports,
clippy::unneeded_field_pattern,
clippy::unreachable,
clippy::unseparated_literal_suffix,
clippy::unwrap_in_result,
clippy::unwrap_used,
clippy::use_debug,
clippy::verbose_file_reads
)]
#![allow(clippy::multiple_crate_versions)]
#![allow(clippy::get_first)]
use actix_cors::Cors;
use actix_web::{http, middleware::Logger, App, HttpServer};
use config::{api_doc, auth::Config, routes};
use db::{
connection::Pool,
cronjobs::{cleanup_layers, cleanup_maps},
};
use std::sync::Arc;
pub mod config;
pub mod controller;
pub mod db;
pub mod error;
pub mod keycloak_api;
pub mod model;
#[allow(clippy::missing_docs_in_private_items)]
pub mod schema;
pub mod service;
pub mod sse;
#[cfg(test)]
pub mod test;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let config = match config::app::Config::from_env() {
Ok(config) => config,
Err(e) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("Error reading configuration: {e}"),
));
}
};
env_logger::init();
log::info!("Configuration loaded: {:#?}", config);
Config::init(&config).await;
log::info!(
"Starting server on {}:{}",
config.bind_address.0,
config.bind_address.1
);
let data_init = config::data::init(&config);
let pool = data_init.pool.clone().into_inner();
start_cronjobs(pool);
HttpServer::new(move || {
App::new()
.wrap(cors_configuration())
.app_data(data_init.pool.clone())
.app_data(data_init.broadcaster.clone())
.app_data(data_init.http_client.clone())
.app_data(data_init.keycloak_api.clone())
.configure(routes::config)
.configure(api_doc::config)
.wrap(Logger::default())
})
.shutdown_timeout(5)
.bind(config.bind_address)?
.run()
.await
}
fn cors_configuration() -> Cors {
Cors::default()
.allowed_origin("http://localhost:5173")
.allowed_origin("https://mr.permaplant.net")
.allowed_origin("https://dev.permaplant.net")
.allowed_origin("https://master.permaplant.net")
.allowed_origin("https://www.permaplant.net")
.allowed_origin("https://cloud.permaplant.net")
.allowed_methods(vec!["GET", "POST", "PUT", "PATCH", "DELETE"])
.allowed_headers(vec![
http::header::AUTHORIZATION,
http::header::ACCEPT,
http::header::CONTENT_TYPE,
])
.max_age(3600)
}
fn start_cronjobs(pool: Arc<Pool>) {
tokio::spawn(cleanup_maps(Arc::clone(&pool)));
tokio::spawn(cleanup_layers(pool));
}