1#![recursion_limit = "1024"]
4#![warn(clippy::pedantic)]
7#![warn(clippy::nursery)]
8#![warn(clippy::cargo)]
9#![warn(
11 clippy::clone_on_ref_ptr,
12 clippy::empty_structs_with_brackets,
13 clippy::exit,
14 clippy::expect_used,
15 clippy::format_push_string,
16 clippy::get_unwrap,
17 clippy::if_then_some_else_none,
18 clippy::indexing_slicing,
19 clippy::integer_division,
20 clippy::large_include_file,
21 clippy::missing_docs_in_private_items,
22 clippy::mixed_read_write_in_expression,
23 clippy::multiple_inherent_impl,
24 clippy::mutex_atomic,
25 clippy::panic_in_result_fn,
26 clippy::partial_pub_fields,
27 clippy::print_stderr,
28 clippy::print_stdout,
29 clippy::rc_buffer,
30 clippy::rc_mutex,
31 clippy::rest_pat_in_fully_bound_structs,
32 clippy::same_name_method,
33 clippy::shadow_unrelated,
34 clippy::str_to_string,
35 clippy::string_to_string,
36 clippy::suspicious_xor_used_as_pow,
37 clippy::todo,
38 clippy::try_err,
39 clippy::unimplemented,
40 clippy::unnecessary_self_imports,
41 clippy::unneeded_field_pattern,
42 clippy::unreachable,
43 clippy::unseparated_literal_suffix,
44 clippy::unwrap_in_result,
45 clippy::unwrap_used,
46 clippy::use_debug,
47 clippy::verbose_file_reads
48)]
49#![allow(clippy::multiple_crate_versions)]
51#![allow(clippy::get_first)]
53#![allow(clippy::module_name_repetitions)]
55
56use actix_cors::Cors;
57use actix_web::{http, middleware::Logger, App, HttpServer};
58use config::{api_doc, auth::Config, routes};
59use db::{
60 connection::Pool,
61 cronjobs::{cleanup_layers, cleanup_maps},
62};
63use std::sync::Arc;
64
65pub mod config;
66pub mod controller;
67pub mod db;
68pub mod error;
69pub mod keycloak_api;
70pub mod model;
71#[allow(clippy::wildcard_imports)]
73#[allow(clippy::missing_docs_in_private_items)]
74pub mod schema;
75pub mod service;
76pub mod sse;
77#[cfg(test)]
78pub mod test;
79
80#[actix_web::main]
82async fn main() -> std::io::Result<()> {
83 let config = match config::app::Config::from_env() {
84 Ok(config) => config,
85 Err(e) => {
86 return Err(std::io::Error::new(
87 std::io::ErrorKind::Other,
88 format!("Error reading configuration: {e}"),
89 ));
90 }
91 };
92
93 env_logger::init();
94
95 log::info!("Configuration loaded: {:#?}", config);
96
97 Config::init(&config).await;
98
99 log::info!(
100 "Starting server on {}:{}",
101 config.bind_address.0,
102 config.bind_address.1
103 );
104
105 let data_init = config::data::init(&config);
106 let pool = data_init.pool.clone().into_inner();
107 start_cronjobs(pool);
108
109 HttpServer::new(move || {
110 App::new()
111 .wrap(cors_configuration())
112 .app_data(data_init.pool.clone())
113 .app_data(data_init.broadcaster.clone())
114 .app_data(data_init.http_client.clone())
115 .app_data(data_init.keycloak_api.clone())
116 .configure(routes::config)
117 .configure(api_doc::config)
118 .wrap(Logger::default())
119 })
120 .shutdown_timeout(5)
121 .bind(config.bind_address)?
122 .run()
123 .await
124}
125
126fn cors_configuration() -> Cors {
128 Cors::default()
129 .allowed_origin("http://localhost:5173")
130 .allowed_origin("https://mr.permaplant.net")
131 .allowed_origin("https://dev.permaplant.net")
132 .allowed_origin("https://master.permaplant.net")
133 .allowed_origin("https://www.permaplant.net")
134 .allowed_origin("https://cloud.permaplant.net")
135 .allowed_methods(vec!["GET", "POST", "PUT", "PATCH", "DELETE"])
136 .allowed_headers(vec![
137 http::header::AUTHORIZATION,
138 http::header::ACCEPT,
139 http::header::CONTENT_TYPE,
140 ])
141 .max_age(3600)
142}
143
144fn start_cronjobs(pool: Arc<Pool>) {
146 tokio::spawn(cleanup_maps(Arc::clone(&pool)));
147 tokio::spawn(cleanup_layers(pool));
148}