#
mcaptcha/static_assets/
static_files.rs

    1// Copyright (C) 2022  Aravinth Manivannan <realaravinth@batsense.net>
    2// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
    3//
    4// SPDX-License-Identifier: AGPL-3.0-or-later
    5
    6use std::borrow::Cow;
    7
    8use actix_web::body::BoxBody;
    9use actix_web::{get, http::header, web, HttpResponse, Responder};
    10use log::debug;
    11use mime_guess::from_path;
    12use rust_embed::RustEmbed;
    13
    14use crate::CACHE_AGE;
    15
    16pub mod assets {
    17    use lazy_static::lazy_static;
    18
    19    use crate::FILES;
    20
    21    type Img = (&'static str, &'static str);
    22
    23    lazy_static! {
    24        pub static ref KEY: Img =
    25            (FILES.get("./static/cache/img/svg/key.svg").unwrap(), "key");
    26        pub static ref GITHUB: Img = (
    27            FILES.get("./static/cache/img/svg/github.svg").unwrap(),
    28            "Source code"
    29        );
    30        pub static ref HOME: Img = (
    31            FILES.get("./static/cache/img/svg/home.svg").unwrap(),
    32            "Home"
    33        );
    34        pub static ref SETTINGS_ICON: Img = (
    35            FILES.get("./static/cache/img/svg/settings.svg").unwrap(),
    36            "Settings"
    37        );
    38        pub static ref CREDIT_CARD: Img = (
    39            FILES.get("./static/cache/img/svg/credit-card.svg").unwrap(),
    40            "Payment"
    41        );
    42        pub static ref HELP_CIRCLE: Img = (
    43            FILES.get("./static/cache/img/svg/help-circle.svg").unwrap(),
    44            "Help"
    45        );
    46        pub static ref MESSAGE: Img = (
    47            FILES
    48                .get("./static/cache/img/svg/message-square.svg")
    49                .unwrap(),
    50            "Message"
    51        );
    52        pub static ref DOCS_ICON: Img = (
    53            FILES.get("./static/cache/img/svg/file-text.svg").unwrap(),
    54            "Documentation"
    55        );
    56        pub static ref MCAPTCHA_TRANS_ICON: Img = (
    57            FILES.get("./static/cache/img/icon-trans.png").unwrap(),
    58            "Logo"
    59        );
    60        pub static ref BAR_CHART: Img = (
    61            FILES.get("./static/cache/img/svg/bar-chart.svg").unwrap(),
    62            "Statistics"
    63        );
    64    }
    65}
    66
    67#[derive(RustEmbed)]
    68#[folder = "assets/"]
    69struct Asset;
    70
    71fn handle_assets(path: &str) -> HttpResponse {
    72    match Asset::get(path) {
    73        Some(content) => {
    74            let body: BoxBody = match content.data {
    75                Cow::Borrowed(bytes) => BoxBody::new(bytes),
    76                Cow::Owned(bytes) => BoxBody::new(bytes),
    77            };
    78
    79            HttpResponse::Ok()
    80                .insert_header(header::CacheControl(vec![
    81                    header::CacheDirective::Public,
    82                    header::CacheDirective::Extension("immutable".into(), None),
    83                    header::CacheDirective::MaxAge(CACHE_AGE),
    84                ]))
    85                .content_type(from_path(path).first_or_octet_stream().as_ref())
    86                .body(body)
    87        }
    88        None => HttpResponse::NotFound().body("404 Not Found"),
    89    }
    90}
    91
    92#[get("/assets/{_:.*}")]
    93pub async fn static_files(path: web::Path<String>) -> impl Responder {
    94    handle_assets(&path)
    95}
    96
    97#[derive(RustEmbed)]
    98#[folder = "static/favicons/"]
    99struct Favicons;
    100
    101fn handle_favicons(path: &str) -> HttpResponse {
    102    match Favicons::get(path) {
    103        Some(content) => {
    104            let body: BoxBody = match content.data {
    105                Cow::Borrowed(bytes) => BoxBody::new(bytes),
    106                Cow::Owned(bytes) => BoxBody::new(bytes),
    107            };
    108
    109            HttpResponse::Ok()
    110                .insert_header(header::CacheControl(vec![
    111                    header::CacheDirective::Public,
    112                    header::CacheDirective::Extension("immutable".into(), None),
    113                    header::CacheDirective::MaxAge(CACHE_AGE),
    114                ]))
    115                .content_type(from_path(path).first_or_octet_stream().as_ref())
    116                .body(body)
    117        }
    118        None => HttpResponse::NotFound().body("404 Not Found"),
    119    }
    120}
    121
    122#[get("/{file}")]
    123pub async fn favicons(path: web::Path<String>) -> impl Responder {
    124    debug!("searching favicons");
    125    handle_favicons(&path)
    126}
    127
    128#[cfg(test)]
    129mod tests {
    130    use actix_web::http::StatusCode;
    131    use actix_web::test;
    132
    133    use crate::*;
    134
    135    #[actix_rt::test]
    136    async fn static_assets_work() {
    137        let app = get_app!().await;
    138
    139        let urls = [
    140            *crate::JS,
    141            *crate::VERIFICATIN_WIDGET_JS,
    142            *crate::VERIFICATIN_WIDGET_CSS,
    143            crate::FILES
    144                .get("./static/cache/img/icon-trans.png")
    145                .unwrap(),
    146            "/favicon.ico",
    147        ];
    148
    149        for u in urls.iter() {
    150            println!("[*] Testing static asset at URL: {u}");
    151            let resp =
    152                test::call_service(&app, test::TestRequest::get().uri(u).to_request())
    153                    .await;
    154            assert_eq!(resp.status(), StatusCode::OK);
    155        }
    156    }
    157}