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