0
0
mirror of https://github.com/neon-mmd/websurfx.git synced 2024-11-21 21:48:21 -05:00

Merge pull request #383 from neon-mmd/REFACTOR/302_rewrite-the-search-engine-frontend-with-maud-html-framework

♻️ Rewrite the search engine frontend with `maud` html framework
This commit is contained in:
alamin655 2023-11-20 21:00:11 +05:30 committed by GitHub
commit 0139fc568b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 853 additions and 603 deletions

303
Cargo.lock generated
View File

@ -14,7 +14,7 @@ dependencies = [
"futures-sink",
"memchr",
"pin-project-lite",
"tokio 1.33.0",
"tokio 1.34.0",
"tokio-util",
"tracing",
]
@ -31,7 +31,7 @@ dependencies = [
"futures-util",
"log",
"once_cell",
"smallvec 1.11.1",
"smallvec 1.11.2",
]
[[package]]
@ -87,7 +87,7 @@ dependencies = [
"derive_more",
"encoding_rs",
"futures-core",
"http 0.2.9",
"http 0.2.11",
"httparse",
"httpdate",
"itoa 1.0.9",
@ -98,8 +98,8 @@ dependencies = [
"pin-project-lite",
"rand 0.8.5",
"sha1",
"smallvec 1.11.1",
"tokio 1.33.0",
"smallvec 1.11.2",
"tokio 1.34.0",
"tokio-util",
"tracing",
]
@ -121,7 +121,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66ff4d247d2b160861fa2866457e85706833527840e4133f8f49aa423a38799"
dependencies = [
"bytestring",
"http 0.2.9",
"http 0.2.11",
"regex",
"serde",
"tracing",
@ -134,7 +134,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d"
dependencies = [
"futures-core",
"tokio 1.33.0",
"tokio 1.34.0",
]
[[package]]
@ -150,7 +150,7 @@ dependencies = [
"futures-util",
"mio 0.8.9",
"socket2 0.5.5",
"tokio 1.33.0",
"tokio 1.34.0",
"tracing",
]
@ -209,7 +209,7 @@ dependencies = [
"serde",
"serde_json",
"serde_urlencoded 0.7.1",
"smallvec 1.11.1",
"smallvec 1.11.2",
"socket2 0.5.5",
"time 0.3.30",
"url 2.4.1",
@ -414,9 +414,9 @@ dependencies = [
[[package]]
name = "bstr"
version = "1.7.0"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019"
checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c"
dependencies = [
"memchr",
"serde",
@ -562,18 +562,18 @@ dependencies = [
[[package]]
name = "clap"
version = "4.4.7"
version = "4.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b"
checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.4.7"
version = "4.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663"
checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc"
dependencies = [
"anstyle",
"clap_lex",
@ -604,7 +604,7 @@ dependencies = [
"futures-core",
"memchr",
"pin-project-lite",
"tokio 1.33.0",
"tokio 1.34.0",
"tokio-util",
]
@ -828,7 +828,7 @@ dependencies = [
"dtoa-short",
"itoa 1.0.9",
"phf 0.11.2",
"smallvec 1.11.1",
"smallvec 1.11.2",
]
[[package]]
@ -841,7 +841,7 @@ dependencies = [
"dtoa-short",
"itoa 1.0.9",
"phf 0.11.2",
"smallvec 1.11.1",
"smallvec 1.11.2",
]
[[package]]
@ -974,9 +974,9 @@ dependencies = [
[[package]]
name = "env_logger"
version = "0.10.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece"
dependencies = [
"log",
]
@ -988,14 +988,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2d328fc287c61314c4a61af7cfdcbd7e678e39778488c7cb13ec133ce0f4059"
dependencies = [
"fsio",
"indexmap",
"indexmap 1.9.3",
]
[[package]]
name = "errno"
version = "0.3.6"
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8"
dependencies = [
"libc",
"windows-sys",
@ -1314,7 +1320,7 @@ dependencies = [
"parking_lot 0.12.1",
"quanta",
"rand 0.8.5",
"smallvec 1.11.1",
"smallvec 1.11.2",
]
[[package]]
@ -1328,7 +1334,7 @@ dependencies = [
"fnv",
"futures 0.1.31",
"http 0.1.21",
"indexmap",
"indexmap 1.9.3",
"log",
"slab",
"string",
@ -1337,19 +1343,19 @@ dependencies = [
[[package]]
name = "h2"
version = "0.3.21"
version = "0.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833"
checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178"
dependencies = [
"bytes 1.5.0",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http 0.2.9",
"indexmap",
"http 0.2.11",
"indexmap 2.1.0",
"slab",
"tokio 1.33.0",
"tokio 1.34.0",
"tokio-util",
"tracing",
]
@ -1360,21 +1366,6 @@ version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "handlebars"
version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c39b3bc2a8f715298032cf5087e58573809374b08160aa7d750582bdb82d2683"
dependencies = [
"log",
"pest",
"pest_derive",
"serde",
"serde_json",
"thiserror",
"walkdir",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@ -1453,9 +1444,9 @@ dependencies = [
[[package]]
name = "http"
version = "0.2.9"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb"
dependencies = [
"bytes 1.5.0",
"fnv",
@ -1481,7 +1472,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
dependencies = [
"bytes 1.5.0",
"http 0.2.9",
"http 0.2.11",
"pin-project-lite",
]
@ -1543,15 +1534,15 @@ dependencies = [
"futures-channel",
"futures-core",
"futures-util",
"h2 0.3.21",
"http 0.2.9",
"h2 0.3.22",
"http 0.2.11",
"http-body 0.4.5",
"httparse",
"httpdate",
"itoa 1.0.9",
"pin-project-lite",
"socket2 0.4.10",
"tokio 1.33.0",
"tokio 1.34.0",
"tower-service",
"tracing",
"want 0.3.1",
@ -1564,10 +1555,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
dependencies = [
"futures-util",
"http 0.2.9",
"http 0.2.11",
"hyper 0.14.27",
"rustls",
"tokio 1.33.0",
"tokio 1.34.0",
"tokio-rustls",
]
@ -1626,6 +1617,16 @@ dependencies = [
"hashbrown 0.12.3",
]
[[package]]
name = "indexmap"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
"hashbrown 0.14.2",
]
[[package]]
name = "iovec"
version = "0.1.4"
@ -1737,7 +1738,7 @@ dependencies = [
"parcel_selectors",
"paste",
"pathdiff",
"smallvec 1.11.1",
"smallvec 1.11.2",
]
[[package]]
@ -1859,6 +1860,30 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
[[package]]
name = "maud"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0bab19cef8a7fe1c18a43e881793bfc9d4ea984befec3ae5bd0415abf3ecf00"
dependencies = [
"actix-web",
"futures-util",
"itoa 1.0.9",
"maud_macros",
]
[[package]]
name = "maud_macros"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0be95d66c3024ffce639216058e5bae17a83ecaf266ffc6e4d060ad447c9eed2"
dependencies = [
"proc-macro-error",
"proc-macro2 1.0.69",
"quote 1.0.33",
"syn 1.0.109",
]
[[package]]
name = "maybe-uninit"
version = "2.0.0"
@ -1921,7 +1946,7 @@ dependencies = [
"crossbeam-utils 0.8.16",
"dashmap",
"skeptic",
"smallvec 1.11.1",
"smallvec 1.11.2",
"tagptr",
"triomphe",
]
@ -2174,7 +2199,7 @@ dependencies = [
"phf 0.10.1",
"phf_codegen 0.10.0",
"precomputed-hash",
"smallvec 1.11.1",
"smallvec 1.11.2",
]
[[package]]
@ -2222,7 +2247,7 @@ dependencies = [
"cfg-if 1.0.0",
"libc",
"redox_syscall 0.4.1",
"smallvec 1.11.1",
"smallvec 1.11.2",
"windows-targets",
]
@ -2263,51 +2288,6 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
[[package]]
name = "pest"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5"
dependencies = [
"memchr",
"thiserror",
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227"
dependencies = [
"pest",
"pest_meta",
"proc-macro2 1.0.69",
"quote 1.0.33",
"syn 2.0.39",
]
[[package]]
name = "pest_meta"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6"
dependencies = [
"once_cell",
"pest",
"sha2",
]
[[package]]
name = "phf"
version = "0.7.24"
@ -2482,6 +2462,30 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2 1.0.69",
"quote 1.0.33",
"syn 1.0.109",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2 1.0.69",
"quote 1.0.33",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "0.4.30"
@ -2725,7 +2729,7 @@ dependencies = [
"percent-encoding 2.3.0",
"pin-project-lite",
"ryu",
"tokio 1.33.0",
"tokio 1.34.0",
"tokio-retry",
"tokio-util",
"url 2.4.1",
@ -2820,8 +2824,8 @@ dependencies = [
"encoding_rs",
"futures-core",
"futures-util",
"h2 0.3.21",
"http 0.2.9",
"h2 0.3.22",
"http 0.2.11",
"http-body 0.4.5",
"hyper 0.14.27",
"hyper-rustls",
@ -2838,7 +2842,7 @@ dependencies = [
"serde_json",
"serde_urlencoded 0.7.1",
"system-configuration",
"tokio 1.33.0",
"tokio 1.34.0",
"tokio-rustls",
"tower-service",
"url 2.4.1",
@ -2895,9 +2899,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.21"
version = "0.38.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
checksum = "9ad981d6c340a49cdc40a1028d9c6084ec7e9fa33fcb839cab656a267071e234"
dependencies = [
"bitflags 2.4.1",
"errno",
@ -2908,9 +2912,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.21.8"
version = "0.21.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c"
checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9"
dependencies = [
"log",
"ring",
@ -2920,9 +2924,9 @@ dependencies = [
[[package]]
name = "rustls-pemfile"
version = "1.0.3"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2"
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
dependencies = [
"base64 0.21.5",
]
@ -3053,7 +3057,7 @@ dependencies = [
"phf_codegen 0.10.0",
"precomputed-hash",
"servo_arc",
"smallvec 1.11.1",
"smallvec 1.11.2",
]
[[package]]
@ -3155,17 +3159,6 @@ dependencies = [
"digest",
]
[[package]]
name = "sha2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if 1.0.0",
"cpufeatures",
"digest",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
@ -3222,9 +3215,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.11.1"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
dependencies = [
"serde",
]
@ -3436,26 +3429,6 @@ dependencies = [
"utf-8",
]
[[package]]
name = "thiserror"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2 1.0.69",
"quote 1.0.33",
"syn 2.0.39",
]
[[package]]
name = "thousands"
version = "0.2.0"
@ -3548,9 +3521,9 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.33.0"
version = "1.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653"
checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9"
dependencies = [
"backtrace",
"bytes 1.5.0",
@ -3609,9 +3582,9 @@ dependencies = [
[[package]]
name = "tokio-macros"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2 1.0.69",
"quote 1.0.33",
@ -3645,7 +3618,7 @@ checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f"
dependencies = [
"pin-project",
"rand 0.8.5",
"tokio 1.33.0",
"tokio 1.34.0",
]
[[package]]
@ -3655,7 +3628,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [
"rustls",
"tokio 1.33.0",
"tokio 1.34.0",
]
[[package]]
@ -3721,7 +3694,7 @@ dependencies = [
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio 1.33.0",
"tokio 1.34.0",
"tracing",
]
@ -3787,12 +3760,6 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "ucd-trie"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
[[package]]
name = "unicase"
version = "2.7.0"
@ -4022,7 +3989,7 @@ checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc"
[[package]]
name = "websurfx"
version = "1.2.25"
version = "1.2.26"
dependencies = [
"actix-cors",
"actix-files",
@ -4036,9 +4003,9 @@ dependencies = [
"error-stack",
"fake-useragent",
"futures 0.3.29",
"handlebars",
"lightningcss",
"log",
"maud",
"md5",
"mimalloc",
"mini-moka",
@ -4051,9 +4018,9 @@ dependencies = [
"scraper",
"serde",
"serde_json",
"smallvec 1.11.1",
"smallvec 1.11.2",
"tempfile",
"tokio 1.33.0",
"tokio 1.34.0",
]
[[package]]
@ -4208,18 +4175,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.7.25"
version = "0.7.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cd369a67c0edfef15010f980c3cbe45d7f651deac2cd67ce097cd801de16557"
checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.25"
version = "0.7.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2f140bda219a26ccc0cdb03dba58af72590c53b22642577d88a927bc5c87d6b"
checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f"
dependencies = [
"proc-macro2 1.0.69",
"quote 1.0.33",

View File

@ -1,6 +1,6 @@
[package]
name = "websurfx"
version = "1.2.25"
version = "1.2.26"
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"
@ -17,7 +17,7 @@ reqwest = {version="0.11.22", default-features=false, features=["rustls-tls"]}
tokio = {version="1.32.0",features=["rt-multi-thread","macros"], default-features = false}
serde = {version="1.0.190", default-features=false, features=["derive"]}
serde_json = {version="1.0.108", default-features=false}
handlebars = { version = "4.4.0", features = ["dir_source"], default-features = false }
maud = {version="0.25.0", default-features=false, features=["actix-web"]}
scraper = {version="0.18.1", default-features = false}
actix-web = {version="4.4.0", features = ["cookies", "macros"], default-features=false}
actix-files = {version="0.6.2", default-features=false}

View File

@ -1,10 +0,0 @@
{{>header this}}
<main class="error_container">
<img src="images/robot-404.svg" alt="Image of broken robot." />
<div class="error_content">
<h1>Aw! snap</h1>
<h2>404 Page Not Found!</h2>
<p>Go to <a href="/">search page</a></p>
</div>
</main>
{{>footer}}

View File

@ -1,29 +0,0 @@
{{>header this}}
<main class="about-container">
<article >
<div>
<h1 >Websurfx</h1>
<hr size="4" width="100%" color="#a6e3a1">
</div>
<p>A modern-looking, lightning-fast, privacy-respecting, secure meta search engine written in Rust. It provides a fast and secure search experience while respecting user privacy.<br> It aggregates results from multiple search engines and presents them in an unbiased manner, filtering out trackers and ads.
</p>
<h2>Some of the Top Features:</h2>
<ul><strong>Lightning fast </strong>- Results load within milliseconds for an instant search experience.</ul>
<ul><strong>Secure search</strong> - All searches are performed over an encrypted connection to prevent snooping.</ul>
<ul><strong>Ad free results</strong> - All search results are ad free and clutter free for a clean search experience.</ul>
<ul><strong>Privacy focused</strong> - Websurface does not track, store or sell your search data. Your privacy is our priority.</ul>
<ul><strong>Free and Open source</strong> - The entire project's code is open source and available for free on <a href="https://github.com/neon-mmd/websurfx">GitHub</a> under an GNU Affero General Public License.</ul>
<ul><strong>Highly customizable</strong> - Websurface comes with 9 built-in color themes and supports creating custom themes effortlessly.</ul>
</article>
<h3>Devoloped by: <a href="https://github.com/neon-mmd/websurfx">Websurfx team</a></h3>
</main>
{{>footer}}

View File

@ -1,3 +0,0 @@
<div class="search_bar">
<input type="search" name="search-box" value="{{this.pageQuery}}" placeholder="Type to search" />
<button type="submit" onclick="searchWeb()">search</button>

View File

@ -1,12 +0,0 @@
<div class="cookies tab">
<h1>Cookies</h1>
<p class="description">
This is the cookies are saved on your system and it contains the preferences
you chose in the settings page
</p>
<input type="text" name="cookie_field" value="" readonly />
<p class="description">
The cookies stored are not used by us for any malicious intend or for
tracking you in any way.
</p>
</div>

View File

@ -1,32 +0,0 @@
<div class="engines tab">
<h1>Engines</h1>
<h3>select search engines</h3>
<p class="description">
Select the search engines from the list of engines that you want results
from
</p>
<div class="engine_selection">
<div class="toggle_btn">
<label class="switch">
<input type="checkbox" class="select_all" onchange="toggleAllSelection()" />
<span class="slider round"></span>
</label>
Select All
</div>
<hr />
<div class="toggle_btn">
<label class="switch">
<input type="checkbox" class="engine" />
<span class="slider round"></span>
</label>
DuckDuckGo
</div>
<div class="toggle_btn">
<label class="switch">
<input type="checkbox" class="engine" />
<span class="slider round"></span>
</label>
Searx
</div>
</div>
</div>

View File

@ -1,16 +0,0 @@
<footer>
<div>
<span>Powered By <b>Websurfx</b></span><span>-</span><span>a lightening fast, privacy respecting, secure meta
search engine</span>
</div>
<div>
<ul>
<li><a href="https://github.com/neon-mmd/websurfx">Source Code</a></li>
<li><a href="https://github.com/neon-mmd/websurfx/issues">Issues/Bugs</a></li>
</ul>
</div>
</footer>
<script src="static/settings.js"></script>
</body>
</html>

View File

@ -1,13 +0,0 @@
<div class="general tab active">
<h1>General</h1>
<h3>Select a safe search level</h3>
<p class="description">
Select a safe search level from the menu below to filter content based on
the level.
</p>
<select name="safe_search_levels">
<option value=0>None</option>
<option value=1>Low</option>
<option value=2>Moderate</option>
</select>
</div>

View File

@ -1,16 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<title>Websurfx</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="static/colorschemes/{{colorscheme}}.css" rel="stylesheet" type="text/css" />
<link href="static/themes/{{theme}}.css" rel="stylesheet" type="text/css" />
</head>
<body onload="getClientSettings()">
<header>
<h1><a href="/">Websurfx</a></h1>
{{>navbar}}
</header>

View File

@ -1,8 +0,0 @@
{{>header this}}
<main class="search-container">
<img src="../images/websurfx_logo.png" alt="Websurfx meta-search engine logo" />
{{>bar}}
</div>
</main>
<script src="static/index.js"></script>
{{>footer}}

View File

@ -1,6 +0,0 @@
<nav>
<ul>
<li><a href="about">about</a></li>
<li><a href="settings">settings</a></li>
</ul>
</nav>

View File

@ -1,86 +0,0 @@
{{>header this.style}}
<main class="results">
{{>search_bar this}}
<div class="results_aggregated">
{{#if results}} {{#each results}}
<div class="result">
<h1><a href="{{{this.url}}}">{{{this.title}}}</a></h1>
<small>{{{this.url}}}</small>
<p>{{{this.description}}}</p>
<div class="upstream_engines">
{{#each engine}}
<span>{{{this}}}</span>
{{/each}}
</div>
</div>
{{/each}} {{else}} {{#if disallowed}}
<div class="result_disallowed">
<div class="description">
<p>
Your search - <span class="user_query">{{{this.pageQuery}}}</span> -
has been disallowed.
</p>
<p class="description_paragraph">Dear user,</p>
<p class="description_paragraph">
The query - <span class="user_query">{{{this.pageQuery}}}</span> - has
been blacklisted via server configuration and hence disallowed by the
server. Henceforth no results could be displayed for your query.
</p>
</div>
<img src="./images/barricade.png" alt="Image of a Barricade" />
</div>
{{else}} {{#if filtered}}
<div class="result_filtered">
<div class="description">
<p>
Your search - <span class="user_query">{{{this.pageQuery}}}</span> -
has been filtered.
</p>
<p class="description_paragraph">Dear user,</p>
<p class="description_paragraph">
All the search results contain results that has been configured to be
filtered out via server configuration and henceforth has been
completely filtered out.
</p>
</div>
<img src="./images/filter.png" alt="Image of a paper inside a funnel" />
</div>
{{else}} {{#if noEnginesSelected}}
<div class="result_engine_not_selected">
<div class="description">
<p>
No results could be fetched for your search "<span class="user_query">{{{this.pageQuery}}}</span>" .
</p>
<p class="description_paragraph">Dear user,</p>
<p class="description_paragraph">
No results could be retrieved from the upstream search engines as no
upstream search engines were selected from the settings page.
</p>
</div>
<img src="./images/no_selection.png" alt="Image of a white cross inside a red circle" />
</div>
{{else}}
<div class="result_not_found">
<p>Your search - {{{this.pageQuery}}} - did not match any documents.</p>
<p class="suggestions">Suggestions:</p>
<ul>
<li>Make sure that all words are spelled correctly.</li>
<li>Try different keywords.</li>
<li>Try more general keywords.</li>
</ul>
<img src="./images/no_results.gif" alt="Man fishing gif" />
</div>
{{/if}} {{/if}} {{/if}} {{/if}}
</div>
<div class="page_navigation">
<button type="button" onclick="navigate_backward()">
&#8592; previous
</button>
<button type="button" onclick="navigate_forward()">next &#8594;</button>
</div>
</main>
<script src="static/index.js"></script>
<script src="static/search_area_options.js"></script>
<script src="static/pagination.js"></script>
<script src="static/error_box.js"></script>
{{>footer}}

View File

@ -1,36 +0,0 @@
<div class="search_area">
{{>bar this}}
<div class="error_box">
{{#if engineErrorsInfo}}
<button onclick="toggleErrorBox()" class="error_box_toggle_button">
<img src="./images/warning.svg" alt="Info icon for error box" />
</button>
<div class="dropdown_error_box">
{{#each engineErrorsInfo}}
<div class="error_item">
<span class="engine_name">{{{this.engine}}}</span>
<span class="engine_name">{{{this.error}}}</span>
<span class="severity_color" style="background: {{{this.severity_color}}};"></span>
</div>
{{/each}}
</div>
{{else}}
<button onclick="toggleErrorBox()" class="error_box_toggle_button">
<img src="./images/info.svg" alt="Warning icon for error box" />
</button>
<div class="dropdown_error_box">
<div class="no_errors">
Everything looks good 🙂!!
</div>
</div>
{{/if}}
</div>
</div>
<div class="search_options">
<select name="safe_search_levels" {{#if (gte safeSearchLevel 3)}} disabled {{/if}}>
<option value=0 {{#if (eq safeSearchLevel 0)}} selected {{/if}}>SafeSearch: None</option>
<option value=1 {{#if (eq safeSearchLevel 1)}} selected {{/if}}>SafeSearch: Low</option>
<option value=2 {{#if (eq safeSearchLevel 2)}} selected {{/if}}>SafeSearch: Moderate</option>
</select>
</div>
</div>

View File

@ -1,22 +0,0 @@
{{>header this}}
<main class="settings" >
<h1>Settings</h1>
<hr />
<div class="settings_container">
<div class="sidebar">
<div class="btn active" onclick="setActiveTab(this)">general</div>
<div class="btn" onclick="setActiveTab(this)">user interface</div>
<div class="btn" onclick="setActiveTab(this)">engines</div>
<div class="btn" onclick="setActiveTab(this)">cookies</div>
</div>
<div class="main_container">
{{> general_tab}} {{> user_interface_tab}} {{> engines_tab}} {{>
cookies_tab}}
<p class="message"></p>
<button type="submit" onclick="setClientSettings()">Save</button>
</div>
</div>
</main>
<script src="static/settings.js"></script>
<script src="static/cookies.js"></script>
{{>footer}}

View File

@ -1,28 +0,0 @@
<div class="user_interface tab">
<h1>User Interface</h1>
<h3>select theme</h3>
<p class="description">
Select the theme from the available themes to be used in user interface
</p>
<select name="themes">
<option value="simple">simple</option>
</select>
<h3>select color scheme</h3>
<p class="description">
Select the color scheme for your theme to be used in user interface
</p>
<select name="colorschemes">
<option value="catppuccin-mocha">catppuccin mocha</option>
<option value="dark-chocolate">dark chocolate</option>
<option value="dracula">dracula</option>
<option value="gruvbox-dark">gruvbox dark</option>
<option value="monokai">monokai</option>
<option value="nord">nord</option>
<option value="oceanic-next">oceanic next</option>
<option value="one-dark">one dark</option>
<option value="solarized-dark">solarized dark</option>
<option value="solarized-light">solarized light</option>
<option value="tokyo-night">tokyo night</option>
<option value="tomorrow-night">tomorrow night</option>
</select>
</div>

View File

@ -3,7 +3,6 @@
use crate::handler::paths::{file_path, FileType};
use crate::models::engine_models::{EngineError, EngineHandler};
use crate::models::parser_models::{AggregatorConfig, RateLimiter, Style};
use log::LevelFilter;
use mlua::Lua;
@ -29,7 +28,7 @@ pub struct Config {
/// It stores the option to whether enable or disable debug mode.
pub debug: bool,
/// It stores all the engine names that were enabled by the user.
pub upstream_search_engines: Vec<EngineHandler>,
pub upstream_search_engines: HashMap<String, bool>,
/// It stores the time (secs) which controls the server request timeout.
pub request_timeout: u8,
/// It stores the number of threads which controls the app will use to run.
@ -109,11 +108,7 @@ impl Config {
logging,
debug,
upstream_search_engines: globals
.get::<_, HashMap<String, bool>>("upstream_search_engines")?
.into_iter()
.filter_map(|(key, value)| value.then_some(key))
.map(|engine| EngineHandler::new(&engine))
.collect::<Result<Vec<EngineHandler>, error_stack::Report<EngineError>>>()?,
.get::<_, HashMap<String, bool>>("upstream_search_engines")?,
request_timeout: globals.get::<_, u8>("request_timeout")?,
threads,
rate_limiter: RateLimiter {

View File

@ -12,6 +12,7 @@ pub mod handler;
pub mod models;
pub mod results;
pub mod server;
pub mod templates;
use std::net::TcpListener;
@ -23,7 +24,6 @@ use actix_governor::{Governor, GovernorConfigBuilder};
use actix_web::{dev::Server, http::header, middleware::Logger, web, App, HttpServer};
use cache::cacher::{Cache, SharedCache};
use config::parser::Config;
use handlebars::Handlebars;
use handler::paths::{file_path, FileType};
/// Runs the web server on the provided TCP listener and returns a `Server` instance.
@ -48,16 +48,8 @@ use handler::paths::{file_path, FileType};
/// let server = run(listener,config,cache).expect("Failed to start server");
/// ```
pub fn run(listener: TcpListener, config: Config, cache: Cache) -> std::io::Result<Server> {
let mut handlebars: Handlebars<'_> = Handlebars::new();
let public_folder_path: &str = file_path(FileType::Theme)?;
handlebars
.register_templates_directory(".html", format!("{}/templates", public_folder_path))
.unwrap();
let handlebars_ref: web::Data<Handlebars<'_>> = web::Data::new(handlebars);
let cloned_config_threads_opt: u8 = config.threads;
let cache = web::Data::new(SharedCache::new(cache));
@ -75,7 +67,6 @@ pub fn run(listener: TcpListener, config: Config, cache: Cache) -> std::io::Resu
App::new()
.wrap(Logger::default()) // added logging middleware for logging.
.app_data(handlebars_ref.clone())
.app_data(web::Data::new(config.clone()))
.app_data(cache.clone())
.wrap(cors)

View File

@ -1,11 +1,10 @@
//! This module provides public models for handling, storing and serializing of search results
//! data scraped from the upstream search engines.
use super::engine_models::EngineError;
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use super::{engine_models::EngineError, parser_models::Style};
/// A named struct to store the raw scraped search results scraped search results from the
/// upstream search engines before aggregating it.It derives the Clone trait which is needed
/// to write idiomatic rust using `Iterators`.
@ -109,10 +108,6 @@ impl EngineErrorInfo {
pub struct SearchResults {
/// Stores the individual serializable `SearchResult` struct into a vector of
pub results: Vec<SearchResult>,
/// Stores the current pages search query `q` provided in the search url.
pub page_query: String,
/// Stores the theming options for the website.
pub style: Style,
/// Stores the information on which engines failed with their engine name
/// and the type of error that caused it.
pub engine_errors_info: Vec<EngineErrorInfo>,
@ -142,15 +137,9 @@ impl SearchResults {
/// the search url.
/// * `engine_errors_info` - Takes an array of structs which contains information regarding
/// which engines failed with their names, reason and their severity color name.
pub fn new(
results: Vec<SearchResult>,
page_query: &str,
engine_errors_info: &[EngineErrorInfo],
) -> Self {
pub fn new(results: Vec<SearchResult>, engine_errors_info: &[EngineErrorInfo]) -> Self {
Self {
results,
page_query: page_query.to_owned(),
style: Style::default(),
engine_errors_info: engine_errors_info.to_owned(),
disallowed: Default::default(),
filtered: Default::default(),
@ -159,21 +148,11 @@ impl SearchResults {
}
}
/// A setter function to add website style to the return search results.
pub fn add_style(&mut self, style: &Style) {
self.style = style.clone();
}
/// A setter function that sets disallowed to true.
pub fn set_disallowed(&mut self) {
self.disallowed = true;
}
/// A setter function to set the current page search query.
pub fn set_page_query(&mut self, page: &str) {
self.page_query = page.to_owned();
}
/// A setter function that sets the filtered to true.
pub fn set_filtered(&mut self) {
self.filtered = true;

View File

@ -1,8 +1,6 @@
//! This module provides public models for handling, storing and serializing parsed config file
//! options from config.lua by grouping them together.
use serde::{Deserialize, Serialize};
/// A named struct which stores,deserializes, serializes and groups the parsed config file options
/// of theme and colorscheme names into the Style struct which derives the `Clone`, `Serialize`
/// and Deserialize traits where the `Clone` trait is derived for allowing the struct to be
@ -12,7 +10,7 @@ use serde::{Deserialize, Serialize};
/// 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(Serialize, Deserialize, Clone, Default)]
#[derive(Clone, Default)]
pub struct Style {
/// It stores the parsed theme option used to set a theme for the website.
pub theme: String,

View File

@ -166,7 +166,7 @@ pub async fn aggregate(
let results: Vec<SearchResult> = result_map.into_values().collect();
Ok(SearchResults::new(results, query, &engine_errors_info))
Ok(SearchResults::new(results, &engine_errors_info))
}
/// Filters a map of search results using a list of regex patterns.

View File

@ -7,30 +7,30 @@ use crate::{
handler::paths::{file_path, FileType},
};
use actix_web::{get, web, HttpRequest, HttpResponse};
use handlebars::Handlebars;
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(
hbs: web::Data<Handlebars<'_>>,
config: web::Data<Config>,
) -> Result<HttpResponse, Box<dyn std::error::Error>> {
let page_content: String = hbs.render("index", &config.style).unwrap();
Ok(HttpResponse::Ok().body(page_content))
pub async fn index(config: web::Data<Config>) -> Result<HttpResponse, Box<dyn std::error::Error>> {
Ok(HttpResponse::Ok().body(
crate::templates::views::index::index(&config.style.colorscheme, &config.style.theme).0,
))
}
/// Handles the route of any other accessed route/page which is not provided by the
/// website essentially the 404 error page.
pub async fn not_found(
hbs: web::Data<Handlebars<'_>>,
config: web::Data<Config>,
) -> Result<HttpResponse, Box<dyn std::error::Error>> {
let page_content: String = hbs.render("404", &config.style)?;
Ok(HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(page_content))
.body(
crate::templates::views::not_found::not_found(
&config.style.colorscheme,
&config.style.theme,
)
.0,
))
}
/// Handles the route of robots.txt page of the `websurfx` meta search engine website.
@ -45,20 +45,26 @@ pub async fn robots_data(_req: HttpRequest) -> Result<HttpResponse, Box<dyn std:
/// Handles the route of about page of the `websurfx` meta search engine website.
#[get("/about")]
pub async fn about(
hbs: web::Data<Handlebars<'_>>,
config: web::Data<Config>,
) -> Result<HttpResponse, Box<dyn std::error::Error>> {
let page_content: String = hbs.render("about", &config.style)?;
Ok(HttpResponse::Ok().body(page_content))
pub async fn about(config: web::Data<Config>) -> Result<HttpResponse, Box<dyn std::error::Error>> {
Ok(HttpResponse::Ok().body(
crate::templates::views::about::about(&config.style.colorscheme, &config.style.theme).0,
))
}
/// Handles the route of settings page of the `websurfx` meta search engine website.
#[get("/settings")]
pub async fn settings(
hbs: web::Data<Handlebars<'_>>,
config: web::Data<Config>,
) -> Result<HttpResponse, Box<dyn std::error::Error>> {
let page_content: String = hbs.render("settings", &config.style)?;
Ok(HttpResponse::Ok().body(page_content))
Ok(HttpResponse::Ok().body(
crate::templates::views::settings::settings(
&config.style.colorscheme,
&config.style.theme,
&config
.upstream_search_engines
.keys()
.collect::<Vec<&String>>(),
)?
.0,
))
}

View File

@ -6,13 +6,12 @@ use crate::{
handler::paths::{file_path, FileType},
models::{
aggregation_models::SearchResults,
engine_models::EngineHandler,
engine_models::{EngineError, EngineHandler},
server_models::{Cookie, SearchParams},
},
results::aggregator::aggregate,
};
use actix_web::{get, web, HttpRequest, HttpResponse};
use handlebars::Handlebars;
use regex::Regex;
use std::{
fs::File,
@ -20,19 +19,6 @@ use std::{
};
use tokio::join;
/// Handles the route of any other accessed route/page which is not provided by the
/// website essentially the 404 error page.
pub async fn not_found(
hbs: web::Data<Handlebars<'_>>,
config: web::Data<Config>,
) -> Result<HttpResponse, Box<dyn std::error::Error>> {
let page_content: String = hbs.render("404", &config.style)?;
Ok(HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(page_content))
}
/// Handles the route of search page of the `websurfx` meta search engine website and it takes
/// two search url parameters `q` and `page` where `page` parameter is optional.
///
@ -49,7 +35,6 @@ pub async fn not_found(
/// ```
#[get("/search")]
pub async fn search(
hbs: web::Data<Handlebars<'_>>,
req: HttpRequest,
config: web::Data<Config>,
cache: web::Data<SharedCache>,
@ -58,7 +43,7 @@ pub async fn search(
match &params.q {
Some(query) => {
if query.trim().is_empty() {
return Ok(HttpResponse::Found()
return Ok(HttpResponse::TemporaryRedirect()
.insert_header(("location", "/"))
.finish());
}
@ -112,10 +97,17 @@ pub async fn search(
)
);
let page_content: String = hbs.render("search", &results?)?;
Ok(HttpResponse::Ok().body(page_content))
Ok(HttpResponse::Ok().body(
crate::templates::views::search::search(
&config.style.colorscheme,
&config.style.theme,
query,
&results?,
)
.0,
))
}
None => Ok(HttpResponse::Found()
None => Ok(HttpResponse::TemporaryRedirect()
.insert_header(("location", "/"))
.finish()),
}
@ -171,8 +163,6 @@ async fn results(
if _flag {
results.set_disallowed();
results.add_style(&config.style);
results.set_page_query(query);
cache.cache_results(&results, &url).await?;
results.set_safe_search_level(safe_search_level);
return Ok(results);
@ -221,23 +211,27 @@ async fn results(
true => {
let mut search_results = SearchResults::default();
search_results.set_no_engines_selected();
search_results.set_page_query(query);
search_results
}
}
}
None => {
aggregate(
None => aggregate(
query,
page,
config.aggregator.random_delay,
config.debug,
&config.upstream_search_engines,
&config
.upstream_search_engines
.clone()
.into_iter()
.filter_map(|(key, value)| value.then_some(key))
.map(|engine| EngineHandler::new(&engine))
.collect::<Result<Vec<EngineHandler>, error_stack::Report<EngineError>>>(
)?,
config.request_timeout,
safe_search_level,
)
.await?
}
.await?,
};
if results.engine_errors_info().is_empty()
&& results.results().is_empty()
@ -245,7 +239,6 @@ async fn results(
{
results.set_filtered();
}
results.add_style(&config.style);
cache
.cache_results(&results, &(format!("{url}{safe_search_level}")))
.await?;

5
src/templates/mod.rs Normal file
View File

@ -0,0 +1,5 @@
//! This module provides other modules to handle both the view and its partials for the `websurfx`
//! search engine frontend.
mod partials;
pub mod views;

View File

@ -0,0 +1,21 @@
//! A module that handles `bar` partial for the `search_bar` partial and the home/index/main page in the `websurfx` frontend.
use maud::{html, Markup, PreEscaped};
/// A functions that handles the html code for the bar for the `search_bar` partial and the
/// home/index/main page in the search engine frontend.
///
/// # Arguments
///
/// * `query` - It takes the current search query provided by user as an argument.
///
/// # Returns
///
/// It returns the compiled html code for the search bar as a result.
pub fn bar(query: &str) -> Markup {
html!(
(PreEscaped("<div class=\"search_bar\">"))
input type="search" name="search-box" value=(query) placeholder="Type to search";
button type="submit" onclick="searchWeb()"{"search"}
)
}

View File

@ -0,0 +1,29 @@
//! A module that handles the footer for all the pages in the `websurfx` frontend.
use maud::{html, Markup, PreEscaped};
/// A functions that handles the html code for the footer for all the pages in the search engine
/// frontend.
///
/// # Returns
///
/// It returns the compiled html code for the footer as a result.
pub fn footer() -> Markup {
html!(
footer{
div{
span{"Powered By "b{"Websurfx"}}span{"-"}span{"a lightening fast, privacy respecting, secure meta
search engine"}
}
div{
ul{
li{a href="https://github.com/neon-mmd/websurfx"{"Source Code"}}
li{a href="https://github.com/neon-mmd/websurfx/issues"{"Issues/Bugs"}}
}
}
}
script src="static/settings.js"{}
(PreEscaped("</body>"))
(PreEscaped("</html>"))
)
}

View File

@ -0,0 +1,35 @@
//! A module that handles the header for all the pages in the `websurfx` frontend.
use crate::templates::partials::navbar::navbar;
use maud::{html, Markup, PreEscaped, DOCTYPE};
/// A function that handles the html code for the header for all the pages in the search engine frontend.
///
/// # Arguments
///
/// * `colorscheme` - It takes the colorscheme name as an argument.
/// * `theme` - It takes the theme name as an argument.
///
/// # Returns
///
/// It returns the compiled html markup code for the header as a result.
pub fn header(colorscheme: &str, theme: &str) -> Markup {
html!(
(DOCTYPE)
html lang="en"
head{
title{"Websurfx"}
meta charset="UTF-8";
meta name="viewport" content="width=device-width, initial-scale=1";
link href=(format!("static/colorschemes/{colorscheme}.css")) rel="stylesheet" type="text/css";
link href=(format!("static/themes/{theme}.css")) rel="stylesheet" type="text/css";
}
(PreEscaped("<body onload=\"getClientSettings()\">"))
header{
h1{a href="/"{"Websurfx"}}
(navbar())
}
)
}

View File

@ -0,0 +1,8 @@
//! This module provides other modules to handle the partials for the views in the `websurfx` frontend.
pub mod bar;
pub mod footer;
pub mod header;
pub mod navbar;
pub mod search_bar;
pub mod settings_tabs;

View File

@ -0,0 +1,19 @@
//! A module that handles `navbar` partial for the header partial in the `websurfx` frontend.
use maud::{html, Markup};
/// A functions that handles the html code for the header partial.
///
/// # Returns
///
/// It returns the compiled html code for the navbar as a result.
pub fn navbar() -> Markup {
html!(
nav{
ul{
li{a href="about"{"about"}}
li{a href="settings"{"settings"}}
}
}
)
}

View File

@ -0,0 +1,76 @@
//! A module that handles `search bar` partial for the search page in the `websurfx` frontend.
use maud::{html, Markup, PreEscaped};
use crate::{models::aggregation_models::EngineErrorInfo, templates::partials::bar::bar};
/// A constant holding the named safe search level options for the corresponding values 0, 1 and 2.
const SAFE_SEARCH_LEVELS_NAME: [&str; 3] = ["None", "Low", "Moderate"];
/// A functions that handles the html code for the search bar for the search page.
///
/// # Arguments
///
/// * `engine_errors_info` - It takes the engine errors list containing errors for each upstream
/// search engine which failed to provide results as an argument.
/// * `safe_search_level` - It takes the safe search level with values from 0-2 as an argument.
/// * `query` - It takes the current search query provided by user as an argument.
///
/// # Returns
///
/// It returns the compiled html code for the search bar as a result.
pub fn search_bar(
engine_errors_info: &[EngineErrorInfo],
safe_search_level: u8,
query: &str,
) -> Markup {
html!(
.search_area{
(bar(query))
.error_box {
@if !engine_errors_info.is_empty(){
button onclick="toggleErrorBox()" class="error_box_toggle_button"{
img src="./images/warning.svg" alt="Info icon for error box";
}
.dropdown_error_box{
@for errors in engine_errors_info{
.error_item{
span class="engine_name"{(errors.engine)}
span class="engine_name"{(errors.error)}
span class="severity_color" style="background: {{{this.severity_color}}};"{}
}
}
}
}
@else {
button onclick="toggleErrorBox()" class="error_box_toggle_button"{
img src="./images/info.svg" alt="Warning icon for error box";
}
.dropdown_error_box {
.no_errors{
"Everything looks good 🙂!!"
}
}
}
}
(PreEscaped("</div>"))
.search_options {
@if safe_search_level >= 3 {
(PreEscaped("<select name=\"safe_search_levels\" disabled>"))
}
@else{
(PreEscaped("<select name=\"safe_search_levels\">"))
}
@for (idx, name) in SAFE_SEARCH_LEVELS_NAME.iter().enumerate() {
@if (safe_search_level as usize) == idx {
option value=(idx) selected {(format!("SafeSearch: {name}"))}
}
@else{
option value=(idx) {(format!("SafeSearch: {name}"))}
}
}
(PreEscaped("</select>"))
}
}
)
}

View File

@ -0,0 +1,25 @@
//! A module that handles the engines tab for setting page view in the `websurfx` frontend.
use maud::{html, Markup};
/// A functions that handles the html code for the cookies tab for the settings page for the search page.
///
/// # Returns
///
/// It returns the compiled html markup code for the cookies tab.
pub fn cookies() -> Markup {
html!(
div class="cookies tab"{
h1{"Cookies"}
p class="description"{
"This is the cookies are saved on your system and it contains the preferences
you chose in the settings page"
}
input type="text" name="cookie_field" value="" readonly;
p class="description"{
"The cookies stored are not used by us for any malicious intend or for
tracking you in any way."
}
}
)
}

View File

@ -0,0 +1,43 @@
//! A module that handles the engines tab for setting page view in the `websurfx` frontend.
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.
///
/// # Returns
///
/// It returns the compiled html markup code for the engines tab.
pub fn engines(engine_names: &[&String]) -> Markup {
html!(
div class="engines tab"{
h1{"Engines"}
h3{"select search engines"}
p class="description"{
"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"
}
hr;
@for engine_name in engine_names{
.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()))
}
}
}
}
)
}

View File

@ -0,0 +1,28 @@
//! A module that handles the general tab for setting page view in the `websurfx` frontend.
use maud::{html, Markup};
/// A constant holding the named safe search level options for the corresponding values 0, 1 and 2.
const SAFE_SEARCH_LEVELS: [(u8, &str); 3] = [(0, "None"), (1, "Low"), (2, "Moderate")];
/// A functions that handles the html code for the general tab for the settings page for the search page.
///
/// # Returns
///
/// It returns the compiled html markup code for the general tab.
pub fn general() -> Markup {
html!(
div class="general tab active"{
h1{"General"}
h3{"Select a safe search level"}
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)}
}
}
}
)
}

View File

@ -0,0 +1,7 @@
//! This module provides other modules to handle the partials for the tabs for the settings page
//! view in the `websurfx` frontend.
pub mod cookies;
pub mod engines;
pub mod general;
pub mod user_interface;

View File

@ -0,0 +1,65 @@
//! A module that handles the user interface tab for setting page view in the `websurfx` frontend.
use crate::handler::paths::{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.
///
/// # 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.
fn style_option_list(
style_type: &str,
) -> Result<Vec<(String, String)>, Box<dyn std::error::Error + '_>> {
let mut style_option_names: Vec<(String, String)> = Vec::new();
for file in read_dir(format!(
"{}static/{}/",
file_path(FileType::Theme)?,
style_type,
))? {
let style_name = file?.file_name().to_str().unwrap().replace(".css", "");
style_option_names.push((style_name.clone(), style_name.replace('-', " ")));
}
Ok(style_option_names)
}
/// A functions that handles the html code for the user interface tab for the settings page for the search page.
///
/// # Error
///
/// 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<Markup, Box<dyn std::error::Error>> {
Ok(html!(
div class="user_interface tab"{
h1{"User Interface"}
h3{"select theme"}
p class="description"{
"Select the theme from the available themes to be used in user interface"
}
select name="themes"{
@for (k,v) in style_option_list("themes")?{
option value=(k){(v)}
}
}
h3{"select color scheme"}
p class="description"{
"Select the color scheme for your theme to be used in user interface"
}
select name="colorschemes"{
@for (k,v) in style_option_list("colorschemes")?{
option value=(k){(v)}
}
}
}
))
}

View File

@ -0,0 +1,48 @@
//! A module that handles the view for the about page in the `websurfx` frontend.
use maud::{html, Markup};
use crate::templates::partials::{footer::footer, header::header};
/// A function that handles the html code for the about page view in the search engine frontend.
///
/// # Arguments
///
/// * `colorscheme` - It takes the colorscheme name as an argument.
/// * `theme` - It takes the theme name as an argument.
///
/// # Returns
///
/// It returns the compiled html markup code as a result.
pub fn about(colorscheme: &str, theme: &str) -> Markup {
html!(
(header(colorscheme, theme))
main class="about-container"{
article {
div{
h1{"Websurfx"}
hr size="4" width="100%" color="#a6e3a1"{}
}
p{"A modern-looking, lightning-fast, privacy-respecting, secure meta search engine written in Rust. It provides a fast and secure search experience while respecting user privacy."br{}" It aggregates results from multiple search engines and presents them in an unbiased manner, filtering out trackers and ads."
}
h2{"Some of the Top Features:"}
ul{strong{"Lightning fast "}"- Results load within milliseconds for an instant search experience."}
ul{strong{"Secure search"}" - All searches are performed over an encrypted connection to prevent snooping."}
ul{strong{"Ad free results"}" - All search results are ad free and clutter free for a clean search experience."}
ul{strong{"Privacy focused"}" - Websurfx does not track, store or sell your search data. Your privacy is our priority."}
ul{strong{"Free and Open source"}" - The entire project's code is open source and available for free on "{a href="https://github.com/neon-mmd/websurfx"{"GitHub"}}" under an GNU Affero General Public License."}
ul{strong{"Highly customizable"}" - Websurfx comes with 9 built-in color themes and supports creating custom themes effortlessly."}
}
h3{"Devoloped by: "{a href="https://github.com/neon-mmd/websurfx"{"Websurfx team"}}}
}
(footer())
)
}

View File

@ -0,0 +1,28 @@
//! A module that handles the view for the index/home/main page in the `websurfx` frontend.
use maud::{html, Markup, PreEscaped};
use crate::templates::partials::{bar::bar, footer::footer, header::header};
/// A function that handles the html code for the index/html/main page view in the search engine frontend.
///
/// # Arguments
///
/// * `colorscheme` - It takes the colorscheme name as an argument.
/// * `theme` - It takes the theme name as an argument.
///
/// # Returns
///
/// It returns the compiled html markup code as a result.
pub fn index(colorscheme: &str, theme: &str) -> Markup {
html!(
(header(colorscheme, theme))
main class="search-container"{
img src="../images/websurfx_logo.png" alt="Websurfx meta-search engine logo";
(bar(&String::default()))
(PreEscaped("</div>"))
}
script src="static/index.js"{}
(footer())
)
}

View File

@ -0,0 +1,8 @@
//! This module provides other modules to handle view for each individual page in the
//! `websurfx` frontend.
pub mod about;
pub mod index;
pub mod not_found;
pub mod search;
pub mod settings;

View File

@ -0,0 +1,29 @@
//! A module that handles the view for the 404 page in the `websurfx` frontend.
use crate::templates::partials::{footer::footer, header::header};
use maud::{html, Markup};
/// A function that handles the html code for the 404 page view in the search engine frontend.
///
/// # Arguments
///
/// * `colorscheme` - It takes the colorscheme name as an argument.
/// * `theme` - It takes the theme name as an argument.
///
/// # Returns
///
/// It returns the compiled html markup code as a result.
pub fn not_found(colorscheme: &str, theme: &str) -> Markup {
html!(
(header(colorscheme, theme))
main class="error_container"{
img src="images/robot-404.svg" alt="Image of broken robot.";
.error_content{
h1{"Aw! snap"}
h2{"404 Page Not Found!"}
p{"Go to "{a href="/"{"search page"}}}
}
}
(footer())
)
}

View File

@ -0,0 +1,122 @@
//! A module that handles the view for the search page in the `websurfx` frontend.
use maud::{html, Markup, PreEscaped};
use crate::{
models::aggregation_models::SearchResults,
templates::partials::{footer::footer, header::header, search_bar::search_bar},
};
/// A function that handles the html code for the search page view in the search engine frontend.
///
/// # Arguments
///
/// * `colorscheme` - It takes the colorscheme name as an argument.
/// * `theme` - It takes the theme name as an argument.
/// * `query` - It takes the current search query provided by the user as an argument.
/// * `search_results` - It takes the aggregated search results as an argument.
///
/// # Returns
///
/// It returns the compiled html markup code as a result.
pub fn search(
colorscheme: &str,
theme: &str,
query: &str,
search_results: &SearchResults,
) -> Markup {
html!(
(header(colorscheme, theme))
main class="results"{
(search_bar(&search_results.engine_errors_info, search_results.safe_search_level, query))
.results_aggregated{
@if !search_results.results.is_empty() {
@for result in search_results.results.iter(){
.result {
h1{a href=(result.url){(PreEscaped(&result.title))}}
small{(result.url)}
p{(PreEscaped(&result.description))}
.upstream_engines{
@for name in result.clone().engine{
span{(name)}
}
}
}
}
}
@else if search_results.disallowed{
.result_disallowed{
.description{
p{
"Your search - "{span class="user_query"{(query)}}" -
has been disallowed."
}
p class="description_paragraph"{"Dear user,"}
p class="description_paragraph"{
"The query - "{span class="user_query"{(query)}}" - has
been blacklisted via server configuration and hence disallowed by the
server. Henceforth no results could be displayed for your query."
}
}
img src="./images/barricade.png" alt="Image of a Barricade";
}
}
@else if search_results.filtered {
.result_filtered{
.description{
p{
"Your search - "{span class="user_query"{(query)}}" -
has been filtered."
}
p class="description_paragraph"{"Dear user,"}
p class="description_paragraph"{
"All the search results contain results that has been configured to be
filtered out via server configuration and henceforth has been
completely filtered out."
}
}
img src="./images/filter.png" alt="Image of a paper inside a funnel";
}
}
@else if search_results.no_engines_selected {
.result_engine_not_selected{
.description{
p{
"No results could be fetched for your search '{span class="user_query"{(query)}}'."
}
p class="description_paragraph"{"Dear user,"}
p class="description_paragraph"{
"No results could be retrieved from the upstream search engines as no
upstream search engines were selected from the settings page."
}
}
img src="./images/no_selection.png" alt="Image of a white cross inside a red circle";
}
}
@else{
.result_not_found {
p{"Your search - "{(query)}" - did not match any documents."}
p class="suggestions"{"Suggestions:"}
ul{
li{"Make sure that all words are spelled correctly."}
li{"Try different keywords."}
li{"Try more general keywords."}
}
img src="./images/no_results.gif" alt="Man fishing gif";
}
}
}
.page_navigation {
button type="button" onclick="navigate_backward()"{
(PreEscaped("&#8592;")) "previous"
}
button type="button" onclick="navigate_forward()"{"next" (PreEscaped("&#8594;"))}
}
}
script src="static/index.js"{}
script src="static/search_area_options.js"{}
script src="static/pagination.js"{}
script src="static/error_box.js"{}
(footer())
)
}

View File

@ -0,0 +1,56 @@
//! A module that handles the view for the settings page in the `websurfx` frontend.
use maud::{html, Markup};
use crate::templates::partials::{
footer::footer,
header::header,
settings_tabs::{
cookies::cookies, engines::engines, general::general, user_interface::user_interface,
},
};
/// A function that handles the html code for the settings page view in the search engine frontend.
///
/// # Arguments
///
/// * `colorscheme` - It takes the colorscheme name as an argument.
/// * `theme` - It takes the theme name as an argument.
/// * `engine_names` - It takes a list of engine names as an argument.
///
/// # Error
///
/// This function returns a compiled html markup code on success otherwise returns a standard error
/// message.
pub fn settings(
colorscheme: &str,
theme: &str,
engine_names: &[&String],
) -> Result<Markup, Box<dyn std::error::Error>> {
Ok(html!(
(header(colorscheme, theme))
main class="settings"{
h1{"Settings"}
hr;
.settings_container{
.sidebar{
div class="btn active" onclick="setActiveTab(this)"{"general"}
.btn onclick="setActiveTab(this)"{"user interface"}
.btn onclick="setActiveTab(this)"{"engines"}
.btn onclick="setActiveTab(this)"{"cookies"}
}
.main_container{
(general())
(user_interface()?)
(engines(engine_names))
(cookies())
p class="message"{}
button type="submit" onclick="setClientSettings()"{"Save"}
}
}
}
script src="static/settings.js"{}
script src="static/cookies.js"{}
(footer())
))
}

View File

@ -1,7 +1,6 @@
use std::net::TcpListener;
use handlebars::Handlebars;
use websurfx::{config::parser::Config, run};
use websurfx::{config::parser::Config, run, templates::views};
// Starts a new instance of the HTTP server, bound to a random available port
fn spawn_app() -> String {
@ -21,18 +20,6 @@ fn spawn_app() -> String {
format!("http://127.0.0.1:{}/", port)
}
// Creates a new instance of Handlebars and registers the templates directory.
// This is used to compare the rendered template with the response body.
fn handlebars() -> Handlebars<'static> {
let mut handlebars = Handlebars::new();
handlebars
.register_templates_directory(".html", "./public/templates")
.unwrap();
handlebars
}
#[tokio::test]
async fn test_index() {
let address = spawn_app();
@ -41,9 +28,8 @@ async fn test_index() {
let res = client.get(address).send().await.unwrap();
assert_eq!(res.status(), 200);
let handlebars = handlebars();
let config = Config::parse(true).unwrap();
let template = handlebars.render("index", &config.style).unwrap();
let template = views::index::index(&config.style.colorscheme, &config.style.theme).0;
assert_eq!(res.text().await.unwrap(), template);
}