headless_lms_server/programs/
start_server.rs

1use crate::{
2    config::{self, ServerConfigBuilder},
3    setup_tracing,
4};
5use actix_session::{
6    SessionMiddleware,
7    config::{CookieContentSecurity, PersistentSession, SessionLifecycle, TtlExtensionPolicy},
8    storage::CookieSessionStore,
9};
10use actix_web::{
11    App, HttpServer,
12    cookie::{Key, SameSite},
13    middleware::Logger,
14};
15use dotenv::dotenv;
16use listenfd::ListenFd;
17use rustls::crypto::ring;
18use std::env;
19
20/// The entrypoint to the server.
21pub async fn main() -> anyhow::Result<()> {
22    dotenv().ok();
23    setup_tracing()?;
24
25    // Required by rustls 0.23 so kube-client can build TLS configs.
26    ring::default_provider()
27        .install_default()
28        .expect("failed to install rustls ring crypto provider");
29
30    // read environment variables
31    let private_cookie_key =
32        env::var("PRIVATE_COOKIE_KEY").expect("PRIVATE_COOKIE_KEY must be defined");
33    let test_mode = env::var("TEST_MODE").is_ok();
34    let allow_no_https_for_development = env::var("ALLOW_NO_HTTPS_FOR_DEVELOPMENT").is_ok();
35
36    if test_mode {
37        info!("***********************************");
38        info!("*  Starting backend in test mode  *");
39        info!("***********************************");
40    }
41    let server_config = ServerConfigBuilder::try_from_env()
42        .expect("Failed to create server config builder")
43        .build()
44        .await
45        .expect("Failed to create server config");
46    let mut server = HttpServer::new(move || {
47        let server_config = server_config.clone();
48        App::new()
49            .configure(move |config| config::configure(config, server_config))
50            .wrap(
51                SessionMiddleware::builder(
52                    CookieSessionStore::default(),
53                    Key::from(private_cookie_key.as_bytes()),
54                )
55                .cookie_name("session".to_string())
56                .cookie_secure(!allow_no_https_for_development)
57                .cookie_same_site(SameSite::Strict) // Default api can only be accessed from the main website. Public api will be less strict on this.
58                .cookie_http_only(true) // Cookie is inaccessible from javascript for security
59                .cookie_path("/api".to_string()) // browser won't send the cookie unless this path exists in the request url
60                .cookie_content_security(CookieContentSecurity::Private)
61                .session_lifecycle(SessionLifecycle::PersistentSession(
62                    PersistentSession::default()
63                        .session_ttl(actix_web::cookie::time::Duration::days(100))
64                        .session_ttl_extension_policy(TtlExtensionPolicy::OnEveryRequest),
65                ))
66                .build(),
67            )
68            .wrap(Logger::new(
69                "Completed %r %s %b bytes - %D ms, request_id=%{request-id}o",
70            ))
71    });
72
73    // this will enable us to keep application running during recompile: systemfd --no-pid -s http::5000 -- cargo watch -x run
74    let mut listenfd = ListenFd::from_env();
75    server = match listenfd.take_tcp_listener(0)? {
76        Some(listener) => server.listen(listener)?,
77        None => {
78            let host = env::var("HOST").unwrap_or_else(|_| "0.0.0.0".to_string());
79            let port = env::var("PORT").unwrap_or_else(|_| "3001".to_string());
80            let bind_address = format!("{}:{}", host, port);
81            info!("Binding to address: {}", bind_address);
82            server.bind(bind_address)?
83        }
84    };
85
86    info!("Starting server.");
87    server.run().await?;
88
89    Ok(())
90}