Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Export/Import settings to and from a json file support for the UI #629

1,546 changes: 1,062 additions & 484 deletions Cargo.lock

Large diffs are not rendered by default.

17 changes: 11 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "websurfx"
version = "1.18.0"
version = "1.21.0"
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"
Expand Down Expand Up @@ -43,17 +43,17 @@ actix-cors = { version = "0.7.0", default-features = false }
fake-useragent = { version = "0.1.3", default-features = false }
env_logger = { version = "0.11.6", default-features = false }
log = { version = "0.4.21", default-features = false }
mlua = { version = "0.9.9", features = [
mlua = { version = "0.10.2", features = [
"luajit",
"vendored",
], default-features = false }
redis = { version = "0.27.5", features = [
redis = { version = "0.28.1", features = [
"tokio-comp",
"connection-manager",
"tcp_nodelay"
], default-features = false, optional = true }
blake3 = { version = "1.5.5", default-features = false }
error-stack = { version = "0.5.0", default-features = false, features = [
blake3 = { version = "1.5.4", default-features = false }
error-stack = { version = "0.4.0", default-features = false, features = [
"std",
] }
async-trait = { version = "0.1.80", default-features = false }
Expand All @@ -62,7 +62,7 @@ futures = { version = "0.3.31", default-features = false, features = ["alloc"] }
dhat = { version = "0.3.2", optional = true, default-features = false }
mimalloc = { version = "0.1.43", default-features = false }
async-once-cell = { version = "0.5.4", default-features = false }
actix-governor = { version = "0.7.0", default-features = false }
actix-governor = { version = "0.8.0", default-features = false }
moka = { version = "0.12.8", optional = true, default-features = false, features = [
"future",
] }
Expand All @@ -87,6 +87,11 @@ stop-words = { version = "0.8.0", default-features = false, features = ["iso"] }
thesaurus = { version = "0.5.2", default-features = false, optional = true, features = [
"moby",
]}

actix-multipart = { version = "0.7.2", default-features = false, features = [
"derive",
"tempfile",
]}
itertools = {version = "0.14.0", default-features = false}

[dev-dependencies]
Expand Down
59 changes: 46 additions & 13 deletions public/static/themes/simple.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
font-weight: 200 600;
font-stretch: 0% 200%;
font-display: swap;
src: url("https://fonts.gstatic.com/s/rubik/v28/iJWKBXyIfDnIV7nErXyi0A.woff2") format('woff2');
src: url('https://fonts.gstatic.com/s/rubik/v28/iJWKBXyIfDnIV7nErXyi0A.woff2')
format('woff2');
}

* {
Expand Down Expand Up @@ -97,7 +98,7 @@ button {
outline-offset: 3px;
outline: 2px solid transparent;
border: none;
transition: .1s;
transition: 0.1s;
gap: 0;
background-color: var(--color-six);
color: var(--background-color);
Expand All @@ -107,10 +108,10 @@ button {
}

.search_bar button img {
position:absolute;
left:50%;
top:50%;
transform:translate(-50%, -50%);
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}

.search_bar button:active {
Expand Down Expand Up @@ -253,7 +254,6 @@ button {

/* styles for the footer and header */


header {
width: 100%;
background: var(--background-color);
Expand Down Expand Up @@ -341,7 +341,7 @@ footer div {
.results_aggregated {
display: flex;
flex-direction: column;
justify-content: space-between;
justify-content: space-between;
margin: 2rem 0;
content-visibility: auto;
}
Expand Down Expand Up @@ -714,7 +714,8 @@ footer div {
}

.settings_container .user_interface select,
.settings_container .general select {
.settings_container .general select,
.settings_container .general form input {
margin: 0.7rem 0;
width: 20rem;
background-color: var(--color-one);
Expand All @@ -726,6 +727,38 @@ footer div {
text-transform: capitalize;
}

.settings_container .general form input {
padding: 0;
width: 30rem;
text-align: center;
text-transform: none;
}

.settings_container .general form input::file-selector-button {
content: 'Browse';
padding: 1rem 2rem;
font-size: 1.5rem;
background: var(--color-three);
color: var(--background-color);
border-radius: 0.5rem;
border: 2px solid transparent;
font-weight: bold;
transition: all 0.1s ease-out;
cursor: pointer;
box-shadow: 5px 5px;
outline: none;
translate: -1rem 0;
}

.settings_container .general form input::file-selector-button:active {
box-shadow: none;
translate: 5px 5px;
}

.settings_container .general .export_btn {
margin-bottom: 1rem;
}

.settings_container .user_interface option:hover,
.settings_container .general option:hover {
background-color: var(--color-one);
Expand Down Expand Up @@ -798,7 +831,7 @@ footer div {
left: 0.4rem;
bottom: 0.4rem;
background-color: var(--background-color);
transition: .2s;
transition: 0.2s;
}

input:checked + .slider {
Expand All @@ -822,7 +855,7 @@ input:checked + .slider::before {
border-radius: 50%;
}

@media screen and (width <= 1136px) {
@media screen and (width <=1136px) {
.hero-text-container {
width: unset;
}
Expand All @@ -832,7 +865,7 @@ input:checked + .slider::before {
}
}

@media screen and (width <= 706px) {
@media screen and (width <=706px) {
.about-container article .logo-container svg {
width: clamp(200px, 290px, 815px);
}
Expand All @@ -856,7 +889,7 @@ input:checked + .slider::before {
.features {
grid-template-columns: 1fr;
}

.feature-list {
padding: 35px 0;
}
Expand Down
2 changes: 0 additions & 2 deletions src/cache/cacher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
/// # Arguments
///
/// * `bytes` - It takes a slice of bytes as an argument.
/// * `encrypt` - A boolean to choose whether to encrypt or decrypt the bytes

Check warning on line 84 in src/cache/cacher.rs

View workflow job for this annotation

GitHub Actions / Rust project

empty line after outer attribute

Check warning on line 84 in src/cache/cacher.rs

View workflow job for this annotation

GitHub Actions / Rust project

empty line after outer attribute

///
/// # Error
Expand Down Expand Up @@ -129,7 +129,7 @@
///
/// # Arguments
///
/// * `bytes` - It takes a slice of bytes as an argument.

Check warning on line 132 in src/cache/cacher.rs

View workflow job for this annotation

GitHub Actions / Rust project

empty line after outer attribute

Check warning on line 132 in src/cache/cacher.rs

View workflow job for this annotation

GitHub Actions / Rust project

empty line after outer attribute

///
/// # Error
Expand Down Expand Up @@ -159,7 +159,7 @@
///
/// # Arguments
///
/// * `bytes` - It takes a slice of bytes as an argument.

Check warning on line 162 in src/cache/cacher.rs

View workflow job for this annotation

GitHub Actions / Rust project

empty line after outer attribute

Check warning on line 162 in src/cache/cacher.rs

View workflow job for this annotation

GitHub Actions / Rust project

empty line after outer attribute

///
/// # Error
Expand All @@ -186,7 +186,7 @@
/// If bytes where
/// # Arguments
///
/// * `bytes` - It takes a slice of bytes as an argument.

Check warning on line 189 in src/cache/cacher.rs

View workflow job for this annotation

GitHub Actions / Rust project

empty lines after outer attribute

Check warning on line 189 in src/cache/cacher.rs

View workflow job for this annotation

GitHub Actions / Rust project

empty lines after outer attribute

///
/// # Error
Expand All @@ -213,12 +213,10 @@
}

/// A helper function that compresses or encrypts search results before they're inserted into a cache store

/// # Arguments
///
/// * `search_results` - A reference to the search_Results to process.
///

///
/// # Error
/// Returns a Vec of compressed or encrypted bytes on success otherwise it returns a CacheError
Expand Down Expand Up @@ -251,7 +249,7 @@
Ok(bytes)
}

/// A helper function that decompresses or decrypts search results after they're fetched from the cache-store

Check warning on line 252 in src/cache/cacher.rs

View workflow job for this annotation

GitHub Actions / Rust project

empty lines after outer attribute

Check warning on line 252 in src/cache/cacher.rs

View workflow job for this annotation

GitHub Actions / Rust project

empty lines after outer attribute

/// # Arguments
///
Expand Down Expand Up @@ -295,7 +293,7 @@
/// If bytes where
/// # Arguments
///
/// * `bytes` - It takes a slice of bytes as an argument.

Check warning on line 296 in src/cache/cacher.rs

View workflow job for this annotation

GitHub Actions / Rust project

empty lines after doc comment

Check warning on line 296 in src/cache/cacher.rs

View workflow job for this annotation

GitHub Actions / Rust project

empty lines after doc comment

///
/// # Error
Expand Down
45 changes: 22 additions & 23 deletions src/config/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ impl Config {
lua.load(&fs::read_to_string(file_path(FileType::Config)?)?)
.exec()?;

let parsed_threads: u8 = globals.get::<_, u8>("threads")?;
let parsed_threads: u8 = globals.get("threads")?;

let debug: bool = globals.get::<_, bool>("debug")?;
let logging: bool = globals.get::<_, bool>("logging")?;
let adaptive_window: bool = globals.get::<_, bool>("adaptive_window")?;
let debug: bool = globals.get("debug")?;
let logging: bool = globals.get("logging")?;
let adaptive_window: bool = globals.get("adaptive_window")?;

if !logging_initialized {
set_logging_level(debug, logging);
Expand All @@ -99,9 +99,9 @@ impl Config {
parsed_threads
};

let rate_limiter = globals.get::<_, HashMap<String, u8>>("rate_limiter")?;
let rate_limiter: HashMap<String, u8> = globals.get("rate_limiter")?;

let parsed_safe_search: u8 = globals.get::<_, u8>("safe_search")?;
let parsed_safe_search: u8 = globals.get::<_>("safe_search")?;
let safe_search: u8 = match parsed_safe_search {
0..=4 => parsed_safe_search,
_ => {
Expand All @@ -112,7 +112,7 @@ impl Config {
};

#[cfg(any(feature = "redis-cache", feature = "memory-cache"))]
let parsed_cet = globals.get::<_, u16>("cache_expiry_time")?;
let parsed_cet = globals.get::<_>("cache_expiry_time")?;
#[cfg(any(feature = "redis-cache", feature = "memory-cache"))]
let cache_expiry_time = match parsed_cet {
0..=59 => {
Expand All @@ -125,7 +125,7 @@ impl Config {
_ => parsed_cet,
};

let proxy_opt = globals.get::<_, Option<String>>("proxy")?;
let proxy_opt: Option<String> = globals.get::<_>("proxy")?;
let proxy = proxy_opt.and_then(|proxy_str| {
Proxy::all(proxy_str).ok().and_then(|_| {
log::error!("Invalid proxy url, defaulting to no proxy.");
Expand All @@ -135,30 +135,29 @@ impl Config {

Ok(Config {
operating_system_tls_certificates: globals
.get::<_, bool>("operating_system_tls_certificates")?,
port: globals.get::<_, u16>("port")?,
binding_ip: globals.get::<_, String>("binding_ip")?,
.get::<_>("operating_system_tls_certificates")?,
port: globals.get::<_>("port")?,
binding_ip: globals.get::<_>("binding_ip")?,
style: Style::new(
globals.get::<_, String>("theme")?,
globals.get::<_, String>("colorscheme")?,
globals.get::<_, Option<String>>("animation")?,
globals.get::<_>("theme")?,
globals.get::<_>("colorscheme")?,
globals.get::<_>("animation")?,
),
#[cfg(feature = "redis-cache")]
redis_url: globals.get::<_, String>("redis_url")?,
redis_url: globals.get::<_>("redis_url")?,
aggregator: AggregatorConfig {
random_delay: globals.get::<_, bool>("production_use")?,
random_delay: globals.get::<_>("production_use")?,
},
logging,
debug,
adaptive_window,
upstream_search_engines: globals
.get::<_, HashMap<String, bool>>("upstream_search_engines")?,
request_timeout: globals.get::<_, u8>("request_timeout")?,
tcp_connection_keep_alive: globals.get::<_, u8>("tcp_connection_keep_alive")?,
pool_idle_connection_timeout: globals.get::<_, u8>("pool_idle_connection_timeout")?,
number_of_https_connections: globals.get::<_, u8>("number_of_https_connections")?,
upstream_search_engines: globals.get::<_>("upstream_search_engines")?,
request_timeout: globals.get::<_>("request_timeout")?,
tcp_connection_keep_alive: globals.get::<_>("tcp_connection_keep_alive")?,
pool_idle_connection_timeout: globals.get::<_>("pool_idle_connection_timeout")?,
number_of_https_connections: globals.get::<_>("number_of_https_connections")?,
threads,
client_connection_keep_alive: globals.get::<_, u8>("client_connection_keep_alive")?,
client_connection_keep_alive: globals.get::<_>("client_connection_keep_alive")?,
rate_limiter: RateLimiter {
number_of_requests: rate_limiter["number_of_requests"],
time_limit: rate_limiter["time_limit"],
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ pub fn run(
.service(server::routes::search::search) // search page
.service(router::about) // about page
.service(router::settings) // settings page
.service(server::routes::export_import::download) // download page
.default_service(web::route().to(router::not_found)) // error page
})
.workers(config.threads as usize)
Expand Down
2 changes: 1 addition & 1 deletion src/models/parser_models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
/// order to allow the deserializing the json back to struct in aggregate function in
/// aggregator.rs and create a new struct out of it and then serialize it back to json and pass
/// it to the template files.
#[derive(Default)]
#[derive(Default, Clone)]
pub struct Style {
/// It stores the parsed theme option used to set a theme for the website.
pub theme: String,
Expand Down
14 changes: 12 additions & 2 deletions src/models/server_models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! engine website.
use std::borrow::Cow;

use serde::Deserialize;
use serde::{Deserialize, Serialize};

use super::parser_models::Style;

Expand All @@ -22,16 +22,22 @@ pub struct SearchParams {

/// A named struct which is used to deserialize the cookies fetched from the client side.
#[allow(dead_code)]
#[derive(Deserialize)]
#[derive(Deserialize, Serialize)]
pub struct Cookie<'a> {
#[serde(borrow)]
/// It stores the theme name used in the website.
pub theme: Cow<'a, str>,
#[serde(borrow)]
/// It stores the colorscheme name used for the website theme.
pub colorscheme: Cow<'a, str>,
#[serde(borrow)]
/// It stores the user selected upstream search engines selected from the UI.
pub engines: Cow<'a, [Cow<'a, str>]>,
/// It stores the user selected safe search level from the UI.
pub safe_search_level: u8,
#[serde(borrow)]
/// It stores the animation name used for the website theme.
pub animation: Option<Cow<'a, str>>,
}

impl<'a> Cookie<'a> {
Expand All @@ -43,6 +49,10 @@ impl<'a> Cookie<'a> {
colorscheme: Cow::Borrowed(&style.colorscheme),
engines: Cow::Owned(engines),
safe_search_level,
animation: style
.animation
.as_ref()
.map(|str| Cow::Borrowed(str.as_str())),
}
}
}
Loading
Loading