Spaces:
Running
Running
File size: 6,540 Bytes
dc34d51 fa57233 dc34d51 fa57233 dc34d51 fa57233 dc34d51 fa57233 dc34d51 fa57233 dc34d51 fa57233 dc34d51 fa57233 dc34d51 fa57233 dc34d51 fa57233 dc34d51 fa57233 dc34d51 fa57233 dc34d51 fa57233 dc34d51 fa57233 dc34d51 fa57233 dc34d51 fa57233 dc34d51 fa57233 dc34d51 fa57233 dc34d51 fa57233 dc34d51 fa57233 dc34d51 fa57233 dc34d51 fa57233 dc34d51 |
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 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
//! This module handles the settings and download route of the search engine website.
use crate::{
handler::{file_path, FileType},
models::{self, server_models},
Config,
};
use actix_multipart::form::{tempfile::TempFile, MultipartForm};
use actix_web::{
cookie::{
time::{Duration, OffsetDateTime},
Cookie,
},
get, post, web, HttpRequest, HttpResponse,
};
use std::borrow::Cow;
use std::io::Read;
use tokio::fs::read_dir;
/// A helper function that helps in building the list of all available colorscheme/theme/animation
/// names present in the colorschemes, animations and themes folder respectively by excluding the
/// ones that have already been selected via the config file.
///
/// # Arguments
///
/// * `style_type` - It takes the style type of the values `theme` and `colorscheme` as an
/// argument.
///
/// # Error
///
/// Returns a list of colorscheme/theme names as a vector of tuple strings on success otherwise
/// returns a standard error message.
async fn style_option_list<'a>(
style_type: &'a str,
) -> Result<Box<[Cow<'a, str>]>, Box<dyn std::error::Error>> {
let mut style_options = Vec::new();
let mut dir = read_dir(format!(
"{}static/{}/",
file_path(FileType::Theme)?,
style_type,
))
.await?;
while let Some(file) = dir.next_entry().await? {
let style_name = file.file_name().to_str().unwrap().replace(".css", "");
style_options.push(Cow::Owned(style_name));
}
if style_type == "animations" {
style_options.push(Cow::default())
}
Ok(style_options.into_boxed_slice())
}
/// A helper function which santizes user provided json data from the input file.
///
/// # Arguments
///
/// * `config` - It takes the config struct as an argument.
/// * `setting_value` - It takes the cookie struct as an argument.
///
/// # Error
///
/// returns a standard error message on failure otherwise it returns the unit type.
async fn sanitize(
config: web::Data<&'static Config>,
setting_value: &mut models::server_models::Cookie<'_>,
) -> Result<(), Box<dyn std::error::Error>> {
// Check whether the theme, colorscheme and animation option is valid by matching it against
// the available option list. If the option provided by the user via the JSON file is invalid
// then replace the user provided by the default one used by the server via the config file.
if !style_option_list("themes")
.await?
.contains(&setting_value.theme)
{
setting_value.theme = Cow::Borrowed(&config.style.theme)
} else if !style_option_list("colorschemes")
.await?
.contains(&setting_value.colorscheme)
{
setting_value.colorscheme = Cow::Borrowed(&config.style.colorscheme)
} else if !style_option_list("animations")
.await?
.contains(setting_value.animation.as_ref().unwrap())
{
setting_value.animation = config
.style
.animation
.as_ref()
.map(|str| Cow::Borrowed(str.as_str()));
}
// Filters out any engines in the list that are invalid by matching each engine against the
// available engine list.
let engines: Vec<_> = setting_value
.engines
.iter()
.cloned()
.filter_map(|engine| {
config
.upstream_search_engines
.keys()
.cloned()
.any(|other_engine| *engine == other_engine)
.then_some(engine.clone())
})
.collect();
setting_value.engines = Cow::Owned(engines);
setting_value.safe_search_level = match setting_value.safe_search_level {
0..2 => setting_value.safe_search_level,
_ => u8::default(),
};
Ok(())
}
/// A multipart struct which stores user provided input file data in memory.
#[derive(MultipartForm)]
struct File {
/// It stores the input file data in memory.
file: TempFile,
}
/// Handles the route of the post settings page.
#[post("/settings")]
pub async fn set_settings(
config: web::Data<&'static Config>,
MultipartForm(mut form): MultipartForm<File>,
) -> Result<HttpResponse, Box<dyn std::error::Error>> {
if let Some(file_name) = form.file.file_name {
let file_name_parts = file_name.split(".");
if let 2 = file_name_parts.clone().count() {
if let Some("json") = file_name_parts.last() {
if let 0 = form.file.size {
return Ok(HttpResponse::BadRequest().finish());
} else {
let mut data = String::new();
form.file.file.read_to_string(&mut data).unwrap();
let mut unsanitized_json_data: models::server_models::Cookie<'_> =
serde_json::from_str(&data)?;
sanitize(config, &mut unsanitized_json_data).await?;
let sanitized_json_data: String =
serde_json::json!(unsanitized_json_data).to_string();
return Ok(HttpResponse::Ok()
.cookie(
Cookie::build("appCookie", sanitized_json_data)
.expires(
OffsetDateTime::now_utc().saturating_add(Duration::weeks(52)),
)
.finish(),
)
.finish());
}
}
}
}
Ok(HttpResponse::Ok().finish())
}
/// Handles the route of the download page.
#[get("/download")]
pub async fn download(
config: web::Data<&'static Config>,
req: HttpRequest,
) -> Result<HttpResponse, Box<dyn std::error::Error>> {
let cookie = req.cookie("appCookie");
// Get search settings using the user's cookie or from the server's config
let preferences: server_models::Cookie<'_> = cookie
.as_ref()
.and_then(|cookie_value| serde_json::from_str(cookie_value.value()).ok())
.unwrap_or_else(|| {
server_models::Cookie::build(
&config.style,
config
.upstream_search_engines
.iter()
.filter_map(|(engine, enabled)| {
enabled.then_some(Cow::Borrowed(engine.as_str()))
})
.collect(),
u8::default(),
)
});
Ok(HttpResponse::Ok().json(preferences))
}
|