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 env_logger::init();
84
85 log::debug!("Starting PermaplanT backend...");
86
87 let version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown");
88 let build_date = option_env!("BUILD_DATE").unwrap_or("unknown");
89
90 let config = match config::app::Config::from_env() {
91 Ok(config) => config,
92 Err(e) => {
93 return Err(std::io::Error::other(format!(
94 "Error reading configuration: {e}"
95 )));
96 }
97 };
98
99 log::info!("Configuration loaded: {:#?}", config);
100
101 Config::init(&config).await;
102
103 log::info!("Version : {}", version);
104 log::info!("Build date: {}", build_date);
105
106 let data_init = config::data::init(&config);
107 let pool = data_init.pool.clone().into_inner();
108 start_cronjobs(pool);
109
110 log::info!(
111 "Binding to: {}:{}",
112 config.bind_address.0,
113 config.bind_address.1
114 );
115
116 HttpServer::new(move || {
117 App::new()
118 .wrap(cors_configuration())
119 .app_data(data_init.pool.clone())
120 .app_data(data_init.broadcaster.clone())
121 .app_data(data_init.http_client.clone())
122 .app_data(data_init.keycloak_api.clone())
123 .configure(routes::config)
124 .configure(api_doc::config)
125 .wrap(Logger::default())
126 })
127 .shutdown_timeout(5)
128 .bind(config.bind_address)?
129 .run()
130 .await
131}
132
133fn cors_configuration() -> Cors {
135 Cors::default()
136 .allowed_origin("http://localhost:5173")
137 .allowed_origin("https://mr.permaplant.net")
138 .allowed_origin("https://dev.permaplant.net")
139 .allowed_origin("https://master.permaplant.net")
140 .allowed_origin("https://www.permaplant.net")
141 .allowed_origin("https://cloud.permaplant.net")
142 .allowed_methods(vec!["GET", "POST", "PUT", "PATCH", "DELETE"])
143 .allowed_headers(vec![
144 http::header::AUTHORIZATION,
145 http::header::ACCEPT,
146 http::header::CONTENT_TYPE,
147 ])
148 .max_age(3600)
149}
150
151fn start_cronjobs(pool: Arc<Pool>) {
153 tokio::spawn(cleanup_maps(Arc::clone(&pool)));
154 tokio::spawn(cleanup_layers(pool));
155}