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    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
126/// Create a CORS configuration for the server.
127fn 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
144/// Start all scheduled jobs that get run in the backend.
145fn start_cronjobs(pool: Arc<Pool>) {
146    tokio::spawn(cleanup_maps(Arc::clone(&pool)));
147    tokio::spawn(cleanup_layers(pool));
148}