rewrite readme
This commit is contained in:
parent
a02ea0d9d4
commit
2a029c4dd7
20
Cargo.lock
generated
20
Cargo.lock
generated
@ -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
79
README
@ -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!
|
||||
|
258
src/config.rs
258
src/config.rs
@ -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 }
|
||||
}
|
||||
}
|
||||
|
58
src/main.rs
58
src/main.rs
@ -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"))
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user