1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use crate::conv_req::convert_req;
use actix_web::{http::StatusCode, web, HttpRequest, HttpResponse};
use fmterr::fmt_err;
use perseus::{
    errors::err_to_status_code,
    internal::{
        i18n::TranslationsManager,
        serve::{render::get_page_for_template, ServerOptions},
    },
    stores::{ImmutableStore, MutableStore},
};
use serde::Deserialize;
use std::rc::Rc;

#[derive(Deserialize)]
pub struct PageDataReq {
    pub template_name: String,
    pub was_incremental_match: bool,
}

/// The handler for calls to `.perseus/page/*`. This will manage returning errors and the like.
pub async fn page_data<M: MutableStore, T: TranslationsManager>(
    req: HttpRequest,
    opts: web::Data<Rc<ServerOptions>>,
    immutable_store: web::Data<ImmutableStore>,
    mutable_store: web::Data<M>,
    translations_manager: web::Data<T>,
    web::Query(query_params): web::Query<PageDataReq>,
) -> HttpResponse {
    let templates = &opts.templates_map;
    let locale = req.match_info().query("locale");
    let PageDataReq {
        template_name,
        was_incremental_match,
    } = query_params;
    // Check if the locale is supported
    if opts.locales.is_supported(locale) {
        let path = req.match_info().query("filename");
        // We need to turn the Actix Web request into one acceptable for Perseus (uses `http` internally)
        let http_req = convert_req(&req);
        let http_req = match http_req {
            Ok(http_req) => http_req,
            // If this fails, the client request is malformed, so it's a 400
            Err(err) => {
                return HttpResponse::build(StatusCode::from_u16(400).unwrap()).body(fmt_err(&err))
            }
        };
        // Get the template to use
        let template = templates.get(&template_name);
        let template = match template {
            Some(template) => template,
            None => {
                // We know the template has been pre-routed and should exist, so any failure here is a 500
                return HttpResponse::InternalServerError().body("template not found".to_string());
            }
        };
        let page_data = get_page_for_template(
            path,
            locale,
            template,
            was_incremental_match,
            http_req,
            (immutable_store.get_ref(), mutable_store.get_ref()),
            translations_manager.get_ref(),
        )
        .await;
        match page_data {
            Ok(page_data) => {
                let mut http_res = HttpResponse::Ok();
                http_res.content_type("text/html");
                // Generate and add HTTP headers
                for (key, val) in template.get_headers(page_data.state.clone()) {
                    http_res.insert_header((key.unwrap(), val));
                }

                http_res.body(serde_json::to_string(&page_data).unwrap())
            }
            // We parse the error to return an appropriate status code
            Err(err) => {
                HttpResponse::build(StatusCode::from_u16(err_to_status_code(&err)).unwrap())
                    .body(fmt_err(&err))
            }
        }
    } else {
        HttpResponse::NotFound().body("locale not supported".to_string())
    }
}