diff --git a/Cargo.lock b/Cargo.lock index 8c3b1cd..f475e48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,15 +65,15 @@ checksum = "a2e7b88f3804e01bd4191fdb08650430bbfcb43d3d9b2890064df3551ec7d25b" dependencies = [ "actix-http", "actix-web", - "futures 0.3.29", + "futures 0.3.30", "governor", ] [[package]] name = "actix-http" -version = "3.4.0" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92ef85799cba03f76e4f7c10f533e66d87c9a7e7055f3391f09000ad8351bc9" +checksum = "129d4c88e98860e1758c5de288d1632b07970a16d59bdf7b8d66053d582bb71f" dependencies = [ "actix-codec", "actix-rt", @@ -111,14 +111,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote 1.0.33", - "syn 2.0.42", + "syn 2.0.43", ] [[package]] name = "actix-router" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66ff4d247d2b160861fa2866457e85706833527840e4133f8f49aa423a38799" +checksum = "d22475596539443685426b6bdadb926ad0ecaefdfc5fb05e5e3441f15463c511" dependencies = [ "bytestring", "http 0.2.11", @@ -177,9 +177,9 @@ dependencies = [ [[package]] name = "actix-web" -version = "4.4.0" +version = "4.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4a5b5e29603ca8c94a77c65cf874718ceb60292c5a5c3e5f4ace041af462b9" +checksum = "e43428f3bf11dee6d166b00ec2df4e3aa8cc1606aaa0b7433c146852e2f4e03b" dependencies = [ "actix-codec", "actix-http", @@ -224,7 +224,7 @@ dependencies = [ "actix-router", "proc-macro2 1.0.71", "quote 1.0.33", - "syn 2.0.42", + "syn 2.0.43", ] [[package]] @@ -313,9 +313,9 @@ checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anyhow" -version = "1.0.76" +version = "1.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355" +checksum = "c9d19de80eff169429ac1e9f48fffb163916b448a44e8e046186232046d9e1f9" [[package]] name = "arc-swap" @@ -369,7 +369,7 @@ checksum = "fdf6721fb0140e4f897002dd086c06f6c27775df19cfe1fccb21181a48fd2c98" dependencies = [ "proc-macro2 1.0.71", "quote 1.0.33", - "syn 2.0.42", + "syn 2.0.43", ] [[package]] @@ -552,9 +552,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34637b3140142bdf929fb439e8aa4ebad7651ebf7b1080b3930aa16ac1459ff" +checksum = "ceed8ef69d8518a5dda55c07425450b58a4e1946f4951eab6d7191ee86c2443d" dependencies = [ "serde", ] @@ -824,12 +824,12 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c3242926edf34aec4ac3a77108ad4854bffaa2e4ddc1824124ce59231302d5" +checksum = "82a9b73a36529d9c47029b9fb3a6f0ea3cc916a261195352ba19e770fc1748b2" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.17", + "crossbeam-utils 0.8.18", ] [[package]] @@ -882,9 +882,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.17" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" +checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" dependencies = [ "cfg-if 1.0.0", ] @@ -941,7 +941,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote 1.0.33", - "syn 2.0.42", + "syn 2.0.43", ] [[package]] @@ -1232,9 +1232,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -1247,9 +1247,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -1257,9 +1257,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-cpupool" @@ -1273,9 +1273,9 @@ dependencies = [ [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -1284,32 +1284,32 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2 1.0.71", "quote 1.0.33", - "syn 2.0.42", + "syn 2.0.43", ] [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" @@ -1319,9 +1319,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -1394,7 +1394,7 @@ checksum = "821239e5672ff23e2a7060901fa622950bbd80b649cdaadd78d1c1767ed14eb4" dependencies = [ "cfg-if 1.0.0", "dashmap", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "no-std-compat", "nonzero_ext", @@ -1725,13 +1725,13 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" dependencies = [ "hermit-abi", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2018,7 +2018,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23e0b72e7c9042467008b10279fc732326bd605459ae03bda88825909dd19b56" dependencies = [ "crossbeam-channel", - "crossbeam-utils 0.8.17", + "crossbeam-utils 0.8.18", "dashmap", "skeptic", "smallvec 1.11.2", @@ -2198,9 +2198,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -2240,7 +2240,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2 1.0.71", "quote 1.0.33", - "syn 2.0.42", + "syn 2.0.43", ] [[package]] @@ -2451,7 +2451,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2 1.0.71", "quote 1.0.33", - "syn 2.0.42", + "syn 2.0.43", ] [[package]] @@ -2498,7 +2498,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2 1.0.71", "quote 1.0.33", - "syn 2.0.42", + "syn 2.0.43", ] [[package]] @@ -2606,7 +2606,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a17e662a7a8291a865152364c20c7abc5e60486ab2001e8ec10b24862de0b9ab" dependencies = [ - "crossbeam-utils 0.8.17", + "crossbeam-utils 0.8.18", "libc", "mach2", "once_cell", @@ -2798,7 +2798,7 @@ dependencies = [ "async-trait", "bytes 1.5.0", "combine", - "futures 0.3.29", + "futures 0.3.30", "futures-util", "itoa 1.0.10", "percent-encoding 2.3.1", @@ -3047,11 +3047,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3178,7 +3178,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2 1.0.71", "quote 1.0.33", - "syn 2.0.42", + "syn 2.0.43", ] [[package]] @@ -3414,9 +3414,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.42" +version = "2.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8" +checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" dependencies = [ "proc-macro2 1.0.71", "quote 1.0.33", @@ -3474,15 +3474,15 @@ checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" [[package]] name = "tempfile" -version = "3.8.1" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" dependencies = [ "cfg-if 1.0.0", "fastrand", "redox_syscall 0.4.1", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3655,7 +3655,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2 1.0.71", "quote 1.0.33", - "syn 2.0.42", + "syn 2.0.43", ] [[package]] @@ -3993,7 +3993,7 @@ dependencies = [ "once_cell", "proc-macro2 1.0.71", "quote 1.0.33", - "syn 2.0.42", + "syn 2.0.43", "wasm-bindgen-shared", ] @@ -4027,7 +4027,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2 1.0.71", "quote 1.0.33", - "syn 2.0.42", + "syn 2.0.43", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4056,7 +4056,7 @@ checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" [[package]] name = "websurfx" -version = "1.6.8" +version = "1.6.11" dependencies = [ "actix-cors", "actix-files", @@ -4070,7 +4070,7 @@ dependencies = [ "env_logger", "error-stack", "fake-useragent", - "futures 0.3.29", + "futures 0.3.30", "lightningcss", "log", "maud", @@ -4324,5 +4324,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2 1.0.71", "quote 1.0.33", - "syn 2.0.42", + "syn 2.0.43", ] diff --git a/Cargo.toml b/Cargo.toml index 00be915..b3161ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "websurfx" -version = "1.6.8" +version = "1.6.11" edition = "2021" description = "An open-source alternative to Searx that provides clean, ad-free, and organic results with incredible speed while keeping privacy and security in mind." repository = "https://github.com/neon-mmd/websurfx" diff --git a/flake.nix b/flake.nix index ba804f1..6c41514 100644 --- a/flake.nix +++ b/flake.nix @@ -60,4 +60,4 @@ # calls the build function packages.websurfx = packages.default; }); - +} diff --git a/public/static/cookies.js b/public/static/cookies.js index 6b55e02..3525c5b 100644 --- a/public/static/cookies.js +++ b/public/static/cookies.js @@ -1,3 +1,62 @@ +/** + * This functions gets the saved cookies if it is present on the user's machine If it + * is available then it is parsed and converted to an object which is then used to + * retrieve the preferences that the user had selected previously and is then loaded + * and used for displaying the user provided settings by setting them as the selected + * options in the settings page. + * + * @function + * @param {string} cookie - It takes the client settings cookie as a string. + * @returns {void} + */ +function setClientSettingsOnPage(cookie) { + let cookie_value = cookie + .split(';') + .map((item) => item.split('=')) + .reduce((acc, [_, v]) => (acc = JSON.parse(v)) && acc, {}) + + // Loop through all select tags and add their values to the cookie dictionary + document.querySelectorAll('select').forEach((select_tag) => { + switch (select_tag.name) { + case 'themes': + select_tag.value = cookie_value['theme'] + break + case 'colorschemes': + select_tag.value = cookie_value['colorscheme'] + break + case 'animations': + select_tag.value = cookie_value['animation'] + break + case 'safe_search_levels': + select_tag.value = cookie_value['safe_search_level'] + break + } + }) + let engines = document.querySelectorAll('.engine') + let engines_cookie = cookie_value['engines'] + + if (engines_cookie.length === engines.length) { + document.querySelector('.select_all').checked = true + engines.forEach((engine_checkbox) => { + engine_checkbox.checked = true + }) + } else { + engines.forEach((engines_checkbox) => { + engines_checkbox.checked = false + }) + engines_cookie.forEach((engine_name) => { + engines.forEach((engine_checkbox) => { + if ( + engine_checkbox.parentNode.parentNode.innerText.trim() === + engine_name.trim() + ) { + engine_checkbox.checked = true + } + }) + }) + } +} + /** * This function is executed when any page on the website finishes loading and * this function retrieves the cookies if it is present on the user's machine. @@ -16,9 +75,14 @@ document.addEventListener( let cookie = decodeURIComponent(document.cookie) // Set the value of the input field to the decoded cookie value if it is not empty // Otherwise, display a message indicating that no cookies have been saved on the user's system - document.querySelector('.cookies input').value = cookie.length - ? cookie - : 'No cookies have been saved on your system' + if (cookie.length) { + document.querySelector('.cookies input').value = cookie + // This function displays the user provided settings on the settings page. + setClientSettingsOnPage(cookie) + } else { + document.querySelector('.cookies input').value = + 'No cookies have been saved on your system' + } } catch (error) { // If there is an error decoding the cookie, log the error to the console // and display an error message in the input field diff --git a/public/static/themes/simple.css b/public/static/themes/simple.css index aab743e..ff05d70 100644 --- a/public/static/themes/simple.css +++ b/public/static/themes/simple.css @@ -600,12 +600,20 @@ footer div { text-transform: capitalize; } -.settings_container .tab .description { +.settings_container .tab .description, +.settings_container .tab .admin_warning { font-size: 1.5rem; margin-bottom: 0.5rem; +} + +.settings_container .tab .description { color: var(--foreground-color); } +.settings_container .tab .admin_warning { + color: var(--color-two); +} + .settings_container .user_interface select, .settings_container .general select { margin: 0.7rem 0; diff --git a/src/engines/mod.rs b/src/engines/mod.rs index b6a50f5..d56ec6f 100644 --- a/src/engines/mod.rs +++ b/src/engines/mod.rs @@ -6,6 +6,7 @@ pub mod brave; pub mod duckduckgo; pub mod librex; +pub mod mojeek; pub mod search_result_parser; pub mod searx; pub mod startpage; diff --git a/src/engines/mojeek.rs b/src/engines/mojeek.rs new file mode 100644 index 0000000..3f7fbb1 --- /dev/null +++ b/src/engines/mojeek.rs @@ -0,0 +1,151 @@ +//! The `mojeek` module handles the scraping of results from the mojeek search engine +//! by querying the upstream mojeek search engine with user provided query and with a page +//! number if provided. + +use std::collections::HashMap; + +use reqwest::header::HeaderMap; +use reqwest::Client; +use scraper::Html; + +use crate::models::aggregation_models::SearchResult; + +use crate::models::engine_models::{EngineError, SearchEngine}; + +use error_stack::{Report, Result, ResultExt}; + +use super::search_result_parser::SearchResultParser; + +/// A new Mojeek engine type defined in-order to implement the `SearchEngine` trait which allows to +/// reduce code duplication as well as allows to create vector of different search engines easily. +pub struct Mojeek { + /// The parser, used to interpret the search result. + parser: SearchResultParser, +} + +impl Mojeek { + /// Creates the Mojeek parser. + pub fn new() -> Result { + Ok(Self { + parser: SearchResultParser::new( + ".result-col", + ".results-standard li", + "a span.url", + "h2 a.title", + "p.s", + )?, + }) + } +} + +#[async_trait::async_trait] +impl SearchEngine for Mojeek { + async fn results( + &self, + query: &str, + page: u32, + user_agent: &str, + client: &Client, + safe_search: u8, + ) -> Result, EngineError> { + // Mojeek uses `start results from this number` convention + // So, for 10 results per page, page 0 starts at 1, page 1 + // starts at 11, and so on. + let results_per_page = 10; + let start_result = results_per_page * page + 1; + + let results_per_page = results_per_page.to_string(); + let start_result = start_result.to_string(); + + let search_engines = vec![ + "Bing", + "Brave", + "DuckDuckGo", + "Ecosia", + "Google", + "Lilo", + "Metager", + "Qwant", + "Startpage", + "Swisscows", + "Yandex", + "Yep", + "You", + ]; + let qss = search_engines.join("%2C"); + let safe = if safe_search == 0 { "0" } else { "1" }; + + // Mojeek detects automated requests, these are preferences that are + // able to circumvent the countermeasure. Some of these are + // not documented in their Search API + let query_params: Vec<(&str, &str)> = vec![ + ("t", results_per_page.as_str()), + ("theme", "dark"), + ("arc", "none"), + ("date", "1"), + ("cdate", "1"), + ("tlen", "100"), + ("ref", "1"), + ("hp", "minimal"), + ("lb", "en"), + ("qss", &qss), + ("safe", safe), + ]; + + let mut query_params_string = String::new(); + for (k, v) in &query_params { + query_params_string.push_str(&format!("&{k}={v}")); + } + + let url: String = match page { + 0 => { + format!("https://www.mojeek.com/search?q={query}{query_params_string}") + } + _ => { + format!( + "https://www.mojeek.com/search?q={query}&s={start_result}{query_params_string}" + ) + } + }; + + let mut cookie_string = String::new(); + for (k, v) in &query_params { + cookie_string.push_str(&format!("{k}={v}; ")); + } + + let header_map = HeaderMap::try_from(&HashMap::from([ + ("USER_AGENT".to_string(), user_agent.to_string()), + ("REFERER".to_string(), "https://google.com/".to_string()), + ( + "CONTENT_TYPE".to_string(), + "application/x-www-form-urlencoded".to_string(), + ), + ("COOKIE".to_string(), cookie_string), + ])) + .change_context(EngineError::UnexpectedError)?; + + let document: Html = Html::parse_document( + &Mojeek::fetch_html_from_upstream(self, &url, header_map, client).await?, + ); + + if let Some(no_result_msg) = self.parser.parse_for_no_results(&document).nth(0) { + if no_result_msg + .inner_html() + .contains("No pages found matching:") + { + return Err(Report::new(EngineError::EmptyResultSet)); + } + } + + // scrape all the results from the html + self.parser + .parse_for_results(&document, |title, url, desc| { + Some(SearchResult::new( + title.inner_html().trim(), + url.inner_html().trim(), + desc.inner_html().trim(), + &["mojeek"], + )) + }) + } +} diff --git a/src/models/engine_models.rs b/src/models/engine_models.rs index 70496cd..2a698d5 100644 --- a/src/models/engine_models.rs +++ b/src/models/engine_models.rs @@ -162,6 +162,10 @@ impl EngineHandler { let engine = crate::engines::librex::LibreX::new()?; ("librex", Box::new(engine)) } + "mojeek" => { + let engine = crate::engines::mojeek::Mojeek::new()?; + ("mojeek", Box::new(engine)) + } _ => { return Err(Report::from(EngineError::NoSuchEngineFound( engine_name.to_string(), diff --git a/src/server/router.rs b/src/server/router.rs index b9fe1d4..74b8bfa 100644 --- a/src/server/router.rs +++ b/src/server/router.rs @@ -12,14 +12,16 @@ use std::fs::read_to_string; /// Handles the route of index page or main page of the `websurfx` meta search engine website. #[get("/")] pub async fn index(config: web::Data) -> Result> { - Ok(HttpResponse::Ok().body( - crate::templates::views::index::index( - &config.style.colorscheme, - &config.style.theme, - &config.style.animation, - ) - .0, - )) + Ok(HttpResponse::Ok() + .content_type("text/html; charset=utf-8") + .body( + crate::templates::views::index::index( + &config.style.colorscheme, + &config.style.theme, + &config.style.animation, + ) + .0, + )) } /// Handles the route of any other accessed route/page which is not provided by the @@ -52,14 +54,16 @@ pub async fn robots_data(_req: HttpRequest) -> Result) -> Result> { - Ok(HttpResponse::Ok().body( - crate::templates::views::about::about( - &config.style.colorscheme, - &config.style.theme, - &config.style.animation, - ) - .0, - )) + Ok(HttpResponse::Ok() + .content_type("text/html; charset=utf-8") + .body( + crate::templates::views::about::about( + &config.style.colorscheme, + &config.style.theme, + &config.style.animation, + ) + .0, + )) } /// Handles the route of settings page of the `websurfx` meta search engine website. @@ -67,16 +71,16 @@ pub async fn about(config: web::Data) -> Result, ) -> Result> { - Ok(HttpResponse::Ok().body( - crate::templates::views::settings::settings( - &config.style.colorscheme, - &config.style.theme, - &config.style.animation, - &config - .upstream_search_engines - .keys() - .collect::>(), - )? - .0, - )) + Ok(HttpResponse::Ok() + .content_type("text/html; charset=utf-8") + .body( + crate::templates::views::settings::settings( + config.safe_search, + &config.style.colorscheme, + &config.style.theme, + &config.style.animation, + &config.upstream_search_engines, + )? + .0, + )) } diff --git a/src/server/routes/search.rs b/src/server/routes/search.rs index 84ba3de..9261051 100644 --- a/src/server/routes/search.rs +++ b/src/server/routes/search.rs @@ -59,25 +59,27 @@ pub async fn search( ) }; - // .max(1) makes sure that the page > 0. - let page = params.page.unwrap_or(1).max(1); + // .max(1) makes sure that the page >= 0. + let page = params.page.unwrap_or(1).max(1) - 1; let (_, results, _) = join!( - get_results(page - 1), + get_results(page.saturating_sub(1)), get_results(page), get_results(page + 1) ); - Ok(HttpResponse::Ok().body( - crate::templates::views::search::search( - &config.style.colorscheme, - &config.style.theme, - &config.style.animation, - query, - &results?, - ) - .0, - )) + Ok(HttpResponse::Ok() + .content_type("text/html; charset=utf-8") + .body( + crate::templates::views::search::search( + &config.style.colorscheme, + &config.style.theme, + &config.style.animation, + query, + &results?, + ) + .0, + )) } None => Ok(HttpResponse::TemporaryRedirect() .insert_header(("location", "/")) diff --git a/src/templates/partials/settings_tabs/engines.rs b/src/templates/partials/settings_tabs/engines.rs index 5394178..748cc07 100644 --- a/src/templates/partials/settings_tabs/engines.rs +++ b/src/templates/partials/settings_tabs/engines.rs @@ -1,17 +1,20 @@ //! A module that handles the engines tab for setting page view in the `websurfx` frontend. +use std::collections::HashMap; + use maud::{html, Markup}; /// A functions that handles the html code for the engines tab for the settings page for the search page. /// /// # Arguments /// -/// * `engine_names` - It takes the list of all available engine names as an argument. +/// * `engine_names` - It takes the key value pair list of all available engine names and there corresponding +/// selected (enabled/disabled) value as an argument. /// /// # Returns /// /// It returns the compiled html markup code for the engines tab. -pub fn engines(engine_names: &[&String]) -> Markup { +pub fn engines(engine_names: &HashMap) -> Markup { html!( div class="engines tab"{ h1{"Engines"} @@ -20,21 +23,49 @@ pub fn engines(engine_names: &[&String]) -> Markup { "Select the search engines from the list of engines that you want results from" } .engine_selection{ - .toggle_btn{ - label class="switch"{ - input type="checkbox" class="select_all" onchange="toggleAllSelection()"; - span class="slider round"{} - } - "Select All" + // Checks whether all the engines are selected or not if they are then the + // checked `select_all` button is rendered otherwise the unchecked version + // is rendered. + @if engine_names.values().all(|selected| *selected){ + .toggle_btn{ + label class="switch"{ + input type="checkbox" class="select_all" onchange="toggleAllSelection()" checked; + span class="slider round"{} + } + "Select All" + } + } + @else{ + .toggle_btn { + label class="switch"{ + input type="checkbox" class="select_all" onchange="toggleAllSelection()"; + span class="slider round"{} + } + "Select All" + } } hr; - @for engine_name in engine_names{ - .toggle_btn{ - label class="switch"{ - input type="checkbox" class="engine"; - span class="slider round"{} + @for (engine_name, selected) in engine_names{ + // Checks whether the `engine_name` is selected or not if they are then the + // checked `engine` button is rendered otherwise the unchecked version is + // rendered. + @if *selected { + .toggle_btn{ + label class="switch"{ + input type="checkbox" class="engine" checked; + span class="slider round"{} + } + (format!("{}{}",engine_name[..1].to_uppercase().to_owned(), engine_name[1..].to_owned())) + } + } + @else { + .toggle_btn { + label class="switch"{ + input type="checkbox" class="engine"; + span class="slider round"{} + } + (format!("{}{}",engine_name[..1].to_uppercase().to_owned(), engine_name[1..].to_owned())) } - (format!("{}{}",engine_name[..1].to_uppercase().to_owned(), engine_name[1..].to_owned())) } } } diff --git a/src/templates/partials/settings_tabs/general.rs b/src/templates/partials/settings_tabs/general.rs index 4b0043d..7736667 100644 --- a/src/templates/partials/settings_tabs/general.rs +++ b/src/templates/partials/settings_tabs/general.rs @@ -7,10 +7,14 @@ const SAFE_SEARCH_LEVELS: [(u8, &str); 3] = [(0, "None"), (1, "Low"), (2, "Moder /// A functions that handles the html code for the general tab for the settings page for the search page. /// +/// # Arguments +/// +/// * `safe_search_level` - It takes the safe search level as an argument. +/// /// # Returns /// /// It returns the compiled html markup code for the general tab. -pub fn general() -> Markup { +pub fn general(safe_search_level: u8) -> Markup { html!( div class="general tab active"{ h1{"General"} @@ -18,9 +22,19 @@ pub fn general() -> Markup { p class="description"{ "Select a safe search level from the menu below to filter content based on the level." } - select name="safe_search_levels"{ - @for (k,v) in SAFE_SEARCH_LEVELS{ - option value=(k){(v)} + @if safe_search_level < 3 { + select name="safe_search_levels" { + // Sets the user selected safe_search_level name from the config file as the first option in the selection list. + option value=(safe_search_level){(SAFE_SEARCH_LEVELS.iter().find(|level| level.0 == safe_search_level).unwrap().1)} + @for (k,v) in SAFE_SEARCH_LEVELS.iter().filter(|level| level.0 != safe_search_level){ + option value=(k){(v)} + } + } + } + @else { + p class="admin_warning" {"⚠️ This setting is being managed by the server administrator."} + select name="safe_search_levels" disabled { + option value=(SAFE_SEARCH_LEVELS[2].0){(SAFE_SEARCH_LEVELS[2].1)} } } } diff --git a/src/templates/partials/settings_tabs/user_interface.rs b/src/templates/partials/settings_tabs/user_interface.rs index d94465b..6916b26 100644 --- a/src/templates/partials/settings_tabs/user_interface.rs +++ b/src/templates/partials/settings_tabs/user_interface.rs @@ -4,13 +4,16 @@ use crate::handler::{file_path, FileType}; use maud::{html, Markup}; use std::fs::read_dir; -/// A helper function that helps in building the list of all available colorscheme/theme names -/// present in the colorschemes and themes folder respectively. +/// 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. +/// * `selected_style` - It takes the currently selected style value provided via the config file +/// as an argument. /// /// # Error /// @@ -18,7 +21,8 @@ use std::fs::read_dir; /// returns a standard error message. fn style_option_list( style_type: &str, -) -> Result, Box> { + selected_style: &str, +) -> Result, Box> { let mut style_option_names: Vec<(String, String)> = Vec::new(); for file in read_dir(format!( "{}static/{}/", @@ -26,7 +30,13 @@ fn style_option_list( style_type, ))? { let style_name = file?.file_name().to_str().unwrap().replace(".css", ""); - style_option_names.push((style_name.clone(), style_name.replace('-', " "))); + if selected_style != style_name { + style_option_names.push((style_name.clone(), style_name.replace('-', " "))); + } + } + + if style_type == "animations" { + style_option_names.push(("".to_owned(), "none".to_owned())) } Ok(style_option_names) @@ -38,7 +48,11 @@ fn style_option_list( /// /// It returns the compiled html markup code for the user interface tab on success otherwise /// returns a standard error message. -pub fn user_interface() -> Result> { +pub fn user_interface( + theme: &str, + colorscheme: &str, + animation: &Option, +) -> Result> { Ok(html!( div class="user_interface tab"{ h1{"User Interface"} @@ -47,7 +61,9 @@ pub fn user_interface() -> Result> { "Select the theme from the available themes to be used in user interface" } select name="themes"{ - @for (k,v) in style_option_list("themes")?{ + // Sets the user selected theme name from the config file as the first option in the selection list. + option value=(theme){(theme.replace('-', " "))} + @for (k,v) in style_option_list("themes", theme)?{ option value=(k){(v)} } } @@ -56,7 +72,9 @@ pub fn user_interface() -> Result> { "Select the color scheme for your theme to be used in user interface" } select name="colorschemes"{ - @for (k,v) in style_option_list("colorschemes")?{ + // Sets the user selected colorscheme name from the config file as the first option in the selection list. + option value=(colorscheme){(colorscheme.replace('-', " "))} + @for (k,v) in style_option_list("colorschemes", colorscheme)?{ option value=(k){(v)} } } @@ -65,12 +83,12 @@ pub fn user_interface() -> Result> { "Select the animation for your theme to be used in user interface" } select name="animations"{ - option value=""{"none"} - @for (k,v) in style_option_list("animations")?{ + // Sets the user selected animation name from the config file as the first option in the selection list. + option value=(animation.as_ref().unwrap_or(&"".to_owned())){(animation.as_ref().unwrap_or(&"".to_owned()).replace('-'," "))} + @for (k,v) in style_option_list("animations", animation.as_ref().unwrap_or(&"".to_owned()))?{ option value=(k){(v)} } } - } )) } diff --git a/src/templates/views/settings.rs b/src/templates/views/settings.rs index 6da5202..4f0878a 100644 --- a/src/templates/views/settings.rs +++ b/src/templates/views/settings.rs @@ -1,5 +1,7 @@ //! A module that handles the view for the settings page in the `websurfx` frontend. +use std::collections::HashMap; + use maud::{html, Markup}; use crate::templates::partials::{ @@ -14,8 +16,10 @@ use crate::templates::partials::{ /// /// # Arguments /// +/// * `safe_search_level` - It takes the safe search level as an argument. /// * `colorscheme` - It takes the colorscheme name as an argument. /// * `theme` - It takes the theme name as an argument. +/// * `animation` - It takes the animation name as an argument. /// * `engine_names` - It takes a list of engine names as an argument. /// /// # Error @@ -23,10 +27,11 @@ use crate::templates::partials::{ /// This function returns a compiled html markup code on success otherwise returns a standard error /// message. pub fn settings( + safe_search_level: u8, colorscheme: &str, theme: &str, animation: &Option, - engine_names: &[&String], + engine_names: &HashMap, ) -> Result> { Ok(html!( (header(colorscheme, theme, animation)) @@ -41,8 +46,8 @@ pub fn settings( .btn onclick="setActiveTab(this)"{"cookies"} } .main_container{ - (general()) - (user_interface()?) + (general(safe_search_level)) + (user_interface(theme, colorscheme, animation)?) (engines(engine_names)) (cookies()) p class="message"{} diff --git a/websurfx/config.lua b/websurfx/config.lua index 2444f86..ae7a6bb 100644 --- a/websurfx/config.lua +++ b/websurfx/config.lua @@ -64,4 +64,5 @@ upstream_search_engines = { Brave = false, Startpage = false, LibreX = false, + Mojeek = false, } -- select the upstream search engines from which the results should be fetched.