rewrite readme

This commit is contained in:
mat 2024-06-30 23:04:04 -05:00
parent a02ea0d9d4
commit 2a029c4dd7
5 changed files with 257 additions and 161 deletions

20
Cargo.lock generated
View File

@ -90,9 +90,9 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "async-compression"
version = "0.4.6"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a116f46a969224200a0a97f29cfd4c50e7534e4b4826bd23ea2c3c533039c82c"
checksum = "cd066d0b4ef8ecb03a55319dc13aa6910616d0f44008a045bb1835af830abff5"
dependencies = [
"brotli",
"flate2",
@ -275,9 +275,9 @@ dependencies = [
[[package]]
name = "brotli"
version = "3.4.0"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f"
checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
@ -286,9 +286,9 @@ dependencies = [
[[package]]
name = "brotli-decompressor"
version = "2.5.1"
version = "4.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f"
checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
@ -422,9 +422,9 @@ dependencies = [
[[package]]
name = "crc32fast"
version = "1.4.0"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
@ -564,9 +564,9 @@ checksum = "09078d60b5387e99317a3ecadd61b5a521deab55186e9dab76d7f0ff66838670"
[[package]]
name = "flate2"
version = "1.0.28"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
dependencies = [
"crc32fast",
"miniz_oxide",

79
README
View File

@ -1,25 +1,72 @@
a cute metasearch engine
==========
metasearch
==========
it sources from google, bing, brave, and a few others.
https://github.com/mat-1/metasearch2
it's written in rust, using as little client-side javascript as possible.
----
INFO
----
there's a demo instance at https://s.matdoes.dev, but don't use it as your
default or rely on it, please (so i don't get ratelimited by google).
metasearch (aka metasearch2) is a cute metasearch engine. It sources its results
from Google, Bing, Brave, and several others. It's designed to be as lightweight
as possible, both on the server and client. There is no required client-side
JavaScript.
USAGE
There's a public demo instance at https://s.matdoes.dev, but please do not use
it as your default or rely on it. This is so I don't get ratelimited by Google
or other engines. Run your own instance instead!
build it with `cargo b -r`, the resulting binary will be at
`target/release/metasearch2`.
------------
INSTALLATION
------------
the config.toml file is created in your current working directory on the first
run of metasearch2. alternatively, you can copy the config-default.toml in the
repo and rename it to config.toml.
The easiest way to install metasearch is with `cargo install metasearch`. To get
the unstable version with the latest features, you can install it with
`cargo install --git https://github.com/mat-1/metasearch2`.
the default port is 28019.
Usage: `metasearch [config_file]`
API
The config_file argument is optional; if it's not specified then it'll be
checked at the following locations:
metasearch has a JSON API that can be enabled by setting `api = true` in your config. to use it,
just set the `Accept: application/json` header. as the api works by serializing internal structs,
it's not guaranteed to be stable across metasearch versions.
- $XDG_CONFIG_HOME/metasearch/config.toml
- $HOME/.config/metasearch/config.toml
- ./config.toml
If no config file exists, it'll be created at the first valid path in the list.
By default, metasearch runs on the port 28019. You are recommended to use a
reverse proxy.
-------------
CONFIGURATION
-------------
You can see all the default config options at `src/config.rs`. Some interesting
options you may want to change are:
- bind - the host and port that the web server runs on, for example
`0.0.0.0:28019`.
- api - whether your instance is accessible through a JSON API. See below for
more details.
- ui.stylesheet_url - a link to a stylesheet that will be loaded alongside the
main one, for example `/themes/catppuccin-mocha.css`.
- image_search.enabled - add a tab for viewing image results for your query.
this is disabled by default as the image proxy could be used to make GET
requests to arbitrary URLs from your server.
- engines.google.weight - the ranking score multiplier for an engine, you can
modify this if you prefer the results from certain engines.
--------
JSON API
--------
metasearch has a JSON API that can be enabled by setting `api = true` in your
config. To use it, set the `Accept: application/json` header in your requests.
For example:
curl 'http://localhost:28019/search?q=sandcats' -H 'Accept: application/json'
The structure of the API is not guaranteed to be stable, as it relies on
serializing internal structs. It may break across versions!

View File

@ -11,6 +11,134 @@ use tracing::info;
use crate::engines::Engine;
impl Default for Config {
fn default() -> Self {
Config {
bind: "0.0.0.0:28019".parse().unwrap(),
api: false,
ui: UiConfig {
show_engine_list_separator: false,
show_version_info: false,
site_name: "metasearch".to_string(),
show_settings_link: true,
stylesheet_url: "".to_string(),
stylesheet_str: "".to_string(),
},
image_search: ImageSearchConfig {
enabled: false,
show_engines: true,
proxy: ImageProxyConfig {
enabled: true,
max_download_size: 10_000_000,
},
},
engines: Arc::new(EnginesConfig::default()),
}
}
}
impl Default for EnginesConfig {
fn default() -> Self {
use toml::value::Value;
let mut map = HashMap::new();
// engines are enabled by default, so engines that aren't listed here are
// enabled
// main search engines
map.insert(Engine::Google, EngineConfig::new().with_weight(1.05));
map.insert(Engine::Bing, EngineConfig::new().with_weight(1.0));
map.insert(Engine::Brave, EngineConfig::new().with_weight(1.25));
map.insert(
Engine::Marginalia,
EngineConfig::new().with_weight(0.15).with_extra(
vec![(
"args".to_string(),
Value::Table(
vec![
("profile".to_string(), Value::String("corpo".to_string())),
("js".to_string(), Value::String("default".to_string())),
("adtech".to_string(), Value::String("default".to_string())),
]
.into_iter()
.collect(),
),
)]
.into_iter()
.collect(),
),
);
// additional search engines
map.insert(
Engine::GoogleScholar,
EngineConfig::new().with_weight(0.50).disabled(),
);
map.insert(
Engine::RightDao,
EngineConfig::new().with_weight(0.10).disabled(),
);
map.insert(
Engine::Stract,
EngineConfig::new().with_weight(0.15).disabled(),
);
map.insert(
Engine::Yep,
EngineConfig::new().with_weight(0.10).disabled(),
);
// calculators (give them a high weight so they're always the first thing in
// autocomplete)
map.insert(Engine::Numbat, EngineConfig::new().with_weight(10.0));
map.insert(
Engine::Fend,
EngineConfig::new().with_weight(10.0).disabled(),
);
// other engines
map.insert(
Engine::Mdn,
EngineConfig::new().with_extra(
vec![("max_sections".to_string(), Value::Integer(1))]
.into_iter()
.collect(),
),
);
Self { map }
}
}
impl Default for EngineConfig {
fn default() -> Self {
Self {
enabled: true,
weight: 1.0,
extra: Default::default(),
}
}
}
static DEFAULT_ENGINE_CONFIG_REF: LazyLock<EngineConfig> = LazyLock::new(EngineConfig::default);
impl EngineConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_weight(self, weight: f64) -> Self {
Self { weight, ..self }
}
pub fn disabled(self) -> Self {
Self {
enabled: false,
..self
}
}
pub fn with_extra(self, extra: toml::Table) -> Self {
Self { extra, ..self }
}
}
//
#[derive(Debug, Clone)]
pub struct Config {
pub bind: SocketAddr,
@ -214,133 +342,3 @@ impl Config {
Ok(config)
}
}
//
// DEFAULTS
//
impl Default for Config {
fn default() -> Self {
Config {
bind: "0.0.0.0:28019".parse().unwrap(),
api: false,
ui: UiConfig {
show_engine_list_separator: false,
show_version_info: false,
site_name: "metasearch".to_string(),
show_settings_link: true,
stylesheet_url: "".to_string(),
stylesheet_str: "".to_string(),
},
image_search: ImageSearchConfig {
enabled: false,
show_engines: true,
proxy: ImageProxyConfig {
enabled: true,
max_download_size: 10_000_000,
},
},
engines: Arc::new(EnginesConfig::default()),
}
}
}
impl Default for EngineConfig {
fn default() -> Self {
Self {
enabled: true,
weight: 1.0,
extra: Default::default(),
}
}
}
static DEFAULT_ENGINE_CONFIG_REF: LazyLock<EngineConfig> = LazyLock::new(EngineConfig::default);
impl EngineConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_weight(self, weight: f64) -> Self {
Self { weight, ..self }
}
pub fn disabled(self) -> Self {
Self {
enabled: false,
..self
}
}
pub fn with_extra(self, extra: toml::Table) -> Self {
Self { extra, ..self }
}
}
impl Default for EnginesConfig {
fn default() -> Self {
use toml::value::Value;
let mut map = HashMap::new();
// engines are enabled by default, so engines that aren't listed here are
// enabled
// main search engines
map.insert(Engine::Google, EngineConfig::new().with_weight(1.05));
map.insert(Engine::Bing, EngineConfig::new().with_weight(1.0));
map.insert(Engine::Brave, EngineConfig::new().with_weight(1.25));
map.insert(
Engine::Marginalia,
EngineConfig::new().with_weight(0.15).with_extra(
vec![(
"args".to_string(),
Value::Table(
vec![
("profile".to_string(), Value::String("corpo".to_string())),
("js".to_string(), Value::String("default".to_string())),
("adtech".to_string(), Value::String("default".to_string())),
]
.into_iter()
.collect(),
),
)]
.into_iter()
.collect(),
),
);
// additional search engines
map.insert(
Engine::GoogleScholar,
EngineConfig::new().with_weight(0.50).disabled(),
);
map.insert(
Engine::RightDao,
EngineConfig::new().with_weight(0.10).disabled(),
);
map.insert(
Engine::Stract,
EngineConfig::new().with_weight(0.15).disabled(),
);
map.insert(
Engine::Yep,
EngineConfig::new().with_weight(0.10).disabled(),
);
// calculators (give them a high weight so they're always the first thing in
// autocomplete)
map.insert(Engine::Numbat, EngineConfig::new().with_weight(10.0));
map.insert(
Engine::Fend,
EngineConfig::new().with_weight(10.0).disabled(),
);
// other engines
map.insert(
Engine::Mdn,
EngineConfig::new().with_extra(
vec![("max_sections".to_string(), Value::Integer(1))]
.into_iter()
.collect(),
),
);
Self { map }
}
}

View File

@ -1,4 +1,7 @@
use std::path::Path;
use std::{
env,
path::{Path, PathBuf},
};
use config::Config;
use tracing::error;
@ -13,10 +16,13 @@ pub mod web;
async fn main() {
tracing_subscriber::fmt::init();
let config_path = std::env::args().nth(1).unwrap_or("config.toml".into());
let config_path = Path::new(&config_path);
if env::args().any(|arg| arg == "--help" || arg == "-h" || arg == "help" || arg == "h") {
println!("Usage: metasearch [config_path]");
return;
}
let config = match Config::read_or_create(config_path) {
let config_path = config_path();
let config = match Config::read_or_create(&config_path) {
Ok(config) => config,
Err(err) => {
error!("Couldn't parse config:\n{err}");
@ -25,3 +31,47 @@ async fn main() {
};
web::run(config).await;
}
fn config_path() -> PathBuf {
if let Some(config_path) = env::args().nth(1) {
return PathBuf::from(config_path);
}
let app_name = env!("CARGO_PKG_NAME");
let mut default_config_dir = None;
// $XDG_CONFIG_HOME/metasearch/config.toml
if let Ok(xdg_config_home) = env::var("XDG_CONFIG_HOME") {
let path = PathBuf::from(xdg_config_home)
.join(app_name)
.join("config.toml");
if path.is_file() {
return path;
}
if default_config_dir.is_none() {
default_config_dir = Some(path);
}
}
// $HOME/.config/metasearch/config.toml
if let Ok(home) = env::var("HOME") {
let path = PathBuf::from(home)
.join(".config")
.join(app_name)
.join("config.toml");
if path.is_file() {
return path;
}
if default_config_dir.is_none() {
default_config_dir = Some(path);
}
}
// ./config.toml
let path = Path::new("config.toml");
if path.exists() {
return path.to_path_buf();
}
default_config_dir.unwrap_or(PathBuf::from("config.toml"))
}

View File

@ -448,6 +448,7 @@ h3.answer-thesaurus-category-title {
.image-result {
min-width: 12rem;
position: relative;
flex-grow: 1;
}
.image-result-img-container {
margin: 0 auto;
@ -459,7 +460,7 @@ h3.answer-thesaurus-category-title {
}
.image-result-page-anchor {
display: block;
height: 2em;
height: 2.25em;
}
.image-result-page-url {
overflow: hidden;