#
mcaptcha/
main.rs

    1#![allow(warnings)]
    2// Copyright (C) 2022  Aravinth Manivannan <realaravinth@batsense.net>
    3// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
    4//
    5// SPDX-License-Identifier: AGPL-3.0-or-later
    6
    7use std::env;
    8use std::sync::Arc;
    9
    10use actix_identity::{CookieIdentityPolicy, IdentityService};
    11use actix_web::{
    12    error::InternalError, http::StatusCode, middleware as actix_middleware,
    13    web::JsonConfig, App, HttpServer,
    14};
    15use lazy_static::lazy_static;
    16use log::info;
    17use tokio::task::JoinHandle;
    18
    19mod api;
    20mod data;
    21mod date;
    22mod db;
    23mod demo;
    24mod docs;
    25mod easy;
    26mod email;
    27mod errors;
    28#[macro_use]
    29mod pages;
    30#[macro_use]
    31mod routes;
    32mod settings;
    33mod static_assets;
    34mod stats;
    35mod survey;
    36#[cfg(test)]
    37#[macro_use]
    38mod tests;
    39mod widget;
    40
    41pub use crate::data::Data;
    42pub use crate::static_assets::static_files::assets::*;
    43pub use api::v1::ROUTES as V1_API_ROUTES;
    44pub use docs::DOCS;
    45pub use pages::routes::ROUTES as PAGES;
    46pub use settings::Settings;
    47use static_assets::FileMap;
    48pub use widget::WIDGET_ROUTES;
    49
    50use crate::demo::DemoUser;
    51use survey::SurveyClientTrait;
    52
    53lazy_static! {
    54    pub static ref SETTINGS: Settings = Settings::new().unwrap();
    55//    pub static ref S: String = env::var("S").unwrap();
    56    pub static ref FILES: FileMap = FileMap::new();
    57    pub static ref JS: &'static str =
    58        FILES.get("./static/cache/bundle/bundle.js").unwrap();
    59    pub static ref CSS: &'static str =
    60        FILES.get("./static/cache/bundle/css/main.css").unwrap();
    61    pub static ref MOBILE_CSS: &'static str =
    62        FILES.get("./static/cache/bundle/css/mobile.css").unwrap();
    63
    64    pub static ref VERIFICATIN_WIDGET_JS: &'static str =
    65        FILES.get("./static/cache/bundle/verificationWidget.js").unwrap();
    66    pub static ref VERIFICATIN_WIDGET_CSS: &'static str =
    67        FILES.get("./static/cache/bundle/css/widget.css").unwrap();
    68
    69    /// points to source files matching build commit
    70    pub static ref SOURCE_FILES_OF_INSTANCE: String = {
    71        let mut url = SETTINGS.source_code.clone();
    72        if !url.ends_with('/') {
    73            url.push('/');
    74        }
    75        let mut  base = url::Url::parse(&url).unwrap();
    76        base =  base.join("tree/").unwrap();
    77        base =  base.join(GIT_COMMIT_HASH).unwrap();
    78        base.into()
    79    };
    80
    81}
    82
    83pub const COMPILED_DATE: &str = env!("COMPILED_DATE");
    84pub const GIT_COMMIT_HASH: &str = env!("GIT_HASH");
    85pub const VERSION: &str = env!("CARGO_PKG_VERSION");
    86pub const PKG_NAME: &str = env!("CARGO_PKG_NAME");
    87pub const PKG_DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION");
    88pub const PKG_HOMEPAGE: &str = env!("CARGO_PKG_HOMEPAGE");
    89
    90pub const CACHE_AGE: u32 = 604800;
    91
    92pub type ArcData = Arc<crate::data::Data>;
    93pub type AppData = actix_web::web::Data<ArcData>;
    94
    95#[actix_web::main]
    96async fn main() -> std::io::Result<()> {
    97    use std::time::Duration;
    98
    99    if env::var("RUST_LOG").is_err() {
    100        env::set_var("RUST_LOG", "info");
    101    }
    102
    103    pretty_env_logger::init();
    104    info!(
    105        "{}: {}.\nFor more information, see: {}\nBuild info:\nVersion: {} commit: {}",
    106        PKG_NAME, PKG_DESCRIPTION, PKG_HOMEPAGE, VERSION, GIT_COMMIT_HASH
    107    );
    108
    109    let settings = Settings::new().unwrap();
    110    let secrets = survey::SecretsStore::default();
    111    let data = Data::new(&settings, secrets.clone()).await;
    112    let data = actix_web::web::Data::new(data);
    113
    114    let mut demo_user: Option<(DemoUser, JoinHandle<()>)> = None;
    115
    116    if settings.allow_demo && settings.allow_registration {
    117        demo_user = Some(DemoUser::spawn(data.clone(), 60 * 30).await.unwrap());
    118    }
    119
    120    let mut update_easy_captcha: Option<(easy::UpdateEasyCaptcha, JoinHandle<()>)> =
    121        None;
    122    if settings
    123        .captcha
    124        .default_difficulty_strategy
    125        .avg_traffic_time
    126        .is_some()
    127    {
    128        update_easy_captcha = Some(
    129            easy::UpdateEasyCaptcha::spawn(data.clone(), 60 * 30)
    130                .await
    131                .unwrap(),
    132        );
    133    }
    134
    135    let (mut survey_upload_tx, mut survey_upload_handle) = (None, None);
    136    if settings.survey.is_some() {
    137        let survey_runner_ctx = survey::Survey::new(data.clone());
    138        let (x, y) = survey_runner_ctx.start_job().await.unwrap();
    139        (survey_upload_tx, survey_upload_handle) = (Some(x), Some(y));
    140    }
    141
    142    let ip = settings.server.get_ip();
    143    println!("Starting server on: http://{ip}");
    144
    145    HttpServer::new(move || {
    146        App::new()
    147            .wrap(actix_middleware::Logger::default())
    148            .wrap(
    149                actix_middleware::DefaultHeaders::new()
    150                    .add(("Permissions-Policy", "interest-cohort=()")),
    151            )
    152            .wrap(get_identity_service(&settings))
    153            .wrap(actix_middleware::Compress::default())
    154            .app_data(data.clone())
    155            .wrap(actix_middleware::NormalizePath::new(
    156                actix_middleware::TrailingSlash::Trim,
    157            ))
    158            .configure(routes::services)
    159            .app_data(get_json_err())
    160    })
    161    .bind(&ip)
    162    .unwrap()
    163    .run()
    164    .await?;
    165
    166    if let Some(survey_upload_tx) = survey_upload_tx {
    167        survey_upload_tx.send(()).unwrap();
    168    }
    169
    170    if let Some(demo_user) = demo_user {
    171        demo_user.0.abort();
    172        demo_user.1.await.unwrap();
    173    }
    174
    175    if let Some(update_easy_captcha) = update_easy_captcha {
    176        update_easy_captcha.0.abort();
    177        update_easy_captcha.1.await.unwrap();
    178    }
    179
    180    if let Some(survey_upload_handle) = survey_upload_handle {
    181        survey_upload_handle.await.unwrap();
    182    }
    183
    184    Ok(())
    185}
    186
    187pub fn get_json_err() -> JsonConfig {
    188    JsonConfig::default().error_handler(|err, _| {
    189        //debug!("JSON deserialization error: {:?}", &err);
    190        InternalError::new(err, StatusCode::BAD_REQUEST).into()
    191    })
    192}
    193
    194pub fn get_identity_service(
    195    settings: &Settings,
    196) -> IdentityService<CookieIdentityPolicy> {
    197    let cookie_secret = &settings.server.cookie_secret;
    198    IdentityService::new(
    199        CookieIdentityPolicy::new(cookie_secret.as_bytes())
    200            .name("Authorization")
    201            //TODO change cookie age
    202            .max_age_secs(216000)
    203            .domain(&settings.server.domain)
    204            .secure(false),
    205    )
    206}
    207
    208#[cfg(test)]
    209mod test {
    210    #[test]
    211    fn version_source_code_url_works() {
    212        assert_eq!(
    213            &*crate::SOURCE_FILES_OF_INSTANCE,
    214            &format!(
    215                "https://github.com/mCaptcha/mCaptcha/tree/{}",
    216                crate::GIT_COMMIT_HASH
    217            )
    218        );
    219    }
    220}