backend/
main.rs

1//! The backend of `PermaplanT`.
2
3#![recursion_limit = "1024"]
4// Enable all lints apart from clippy::restriction by default.
5// See https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints for as to why restriction is not enabled.
6#![warn(clippy::pedantic)]
7#![warn(clippy::nursery)]
8#![warn(clippy::cargo)]
9// Lints in clippy::restriction which seem useful.
10#![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// Cannot fix some errors because dependencies import them.
50#![allow(clippy::multiple_crate_versions)]
51// Clippy suggests lots of false "x.get(0)" => "x.first()"
52#![allow(clippy::get_first)]
53// We often want the same name per module (for instance every enum).
54#![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/// Auto generated by diesel.
72#[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/// Main function.
81#[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
133/// Create a CORS configuration for the server.
134fn 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
151/// Start all scheduled jobs that get run in the backend.
152fn start_cronjobs(pool: Arc<Pool>) {
153    tokio::spawn(cleanup_maps(Arc::clone(&pool)));
154    tokio::spawn(cleanup_layers(pool));
155}