add optional api
This commit is contained in:
parent
cb0edfd4fc
commit
24b8bb749c
@ -1,8 +1,7 @@
|
||||
# See https://github.com/mat-1/metasearch2/blob/master/config-base.toml and
|
||||
# https://github.com/mat-1/metasearch2/blob/master/src/config.rs for some of
|
||||
# the possible options
|
||||
# See src/config.rs for all of the possible options
|
||||
|
||||
bind = "0.0.0.0:28019"
|
||||
api = false
|
||||
|
||||
[ui]
|
||||
# engine_list_separator = true
|
||||
|
@ -8,6 +8,8 @@ use crate::engines::Engine;
|
||||
#[derive(Debug)]
|
||||
pub struct Config {
|
||||
pub bind: SocketAddr,
|
||||
/// Whether the JSON API should be accessible.
|
||||
pub api: bool,
|
||||
pub ui: UiConfig,
|
||||
pub image_search: ImageSearchConfig,
|
||||
pub engines: EnginesConfig,
|
||||
@ -16,6 +18,7 @@ pub struct Config {
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct PartialConfig {
|
||||
pub bind: Option<SocketAddr>,
|
||||
pub api: Option<bool>,
|
||||
pub ui: Option<PartialUiConfig>,
|
||||
pub image_search: Option<PartialImageSearchConfig>,
|
||||
pub engines: Option<PartialEnginesConfig>,
|
||||
@ -24,6 +27,7 @@ pub struct PartialConfig {
|
||||
impl Config {
|
||||
pub fn overlay(&mut self, partial: PartialConfig) {
|
||||
self.bind = partial.bind.unwrap_or(self.bind);
|
||||
self.api = partial.api.unwrap_or(self.api);
|
||||
self.ui.overlay(partial.ui.unwrap_or_default());
|
||||
self.image_search
|
||||
.overlay(partial.image_search.unwrap_or_default());
|
||||
@ -79,16 +83,16 @@ impl ImageSearchConfig {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ImageProxyConfig {
|
||||
/// Whether we should proxy remote images through our server. This is mostly
|
||||
/// a privacy feature.
|
||||
pub enabled: bool,
|
||||
/// The maximum size of an image that can be proxied. This is in bytes.
|
||||
pub max_download_size: u64,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Default)]
|
||||
pub struct PartialImageProxyConfig {
|
||||
/// Whether we should proxy remote images through our server. This is mostly
|
||||
/// a privacy feature.
|
||||
pub enabled: Option<bool>,
|
||||
/// The maximum size of an image that can be proxied. This is in bytes.
|
||||
pub max_download_size: Option<u64>,
|
||||
}
|
||||
|
||||
@ -145,7 +149,9 @@ impl EnginesConfig {
|
||||
#[derive(Debug)]
|
||||
pub struct EngineConfig {
|
||||
pub enabled: bool,
|
||||
/// The priority of this engine relative to the other engines.
|
||||
pub weight: f64,
|
||||
/// Per-engine configs. These are parsed at request time.
|
||||
pub extra: toml::Table,
|
||||
}
|
||||
|
||||
@ -154,10 +160,8 @@ pub struct PartialEngineConfig {
|
||||
#[serde(default)]
|
||||
pub enabled: Option<bool>,
|
||||
|
||||
/// The priority of this engine relative to the other engines.
|
||||
#[serde(default)]
|
||||
pub weight: Option<f64>,
|
||||
/// Per-engine configs. These are parsed at request time.
|
||||
#[serde(flatten)]
|
||||
pub extra: toml::Table,
|
||||
}
|
||||
@ -194,6 +198,7 @@ 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,
|
||||
|
@ -1,7 +1,7 @@
|
||||
#[macro_export]
|
||||
macro_rules! engines {
|
||||
($($engine:ident = $id:expr),* $(,)?) => {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
|
||||
pub enum Engine {
|
||||
$($engine,)*
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ use eyre::bail;
|
||||
use futures::future::join_all;
|
||||
use maud::PreEscaped;
|
||||
use reqwest::{header::HeaderMap, RequestBuilder};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use tokio::sync::mpsc;
|
||||
use tracing::{error, info};
|
||||
|
||||
@ -204,7 +204,7 @@ impl From<HttpResponse> for reqwest::Response {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct EngineSearchResult {
|
||||
pub url: String,
|
||||
pub title: String,
|
||||
@ -261,7 +261,7 @@ impl EngineImagesResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct EngineImageResult {
|
||||
pub image_url: String,
|
||||
pub page_url: String,
|
||||
@ -602,35 +602,38 @@ pub static CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Response {
|
||||
pub search_results: Vec<SearchResult<EngineSearchResult>>,
|
||||
pub featured_snippet: Option<FeaturedSnippet>,
|
||||
pub answer: Option<Answer>,
|
||||
pub infobox: Option<Infobox>,
|
||||
#[serde(skip)]
|
||||
pub config: Arc<Config>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct ImagesResponse {
|
||||
pub image_results: Vec<SearchResult<EngineImageResult>>,
|
||||
#[serde(skip)]
|
||||
pub config: Arc<Config>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum ResponseForTab {
|
||||
All(Response),
|
||||
Images(ImagesResponse),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SearchResult<R> {
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SearchResult<R: Serialize> {
|
||||
pub result: R,
|
||||
pub engines: BTreeSet<Engine>,
|
||||
pub score: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct FeaturedSnippet {
|
||||
pub url: String,
|
||||
pub title: String,
|
||||
@ -638,14 +641,16 @@ pub struct FeaturedSnippet {
|
||||
pub engine: Engine,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Answer {
|
||||
#[serde(serialize_with = "serialize_markup")]
|
||||
pub html: PreEscaped<String>,
|
||||
pub engine: Engine,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Infobox {
|
||||
#[serde(serialize_with = "serialize_markup")]
|
||||
pub html: PreEscaped<String>,
|
||||
pub engine: Engine,
|
||||
}
|
||||
@ -654,3 +659,10 @@ pub struct AutocompleteResult {
|
||||
pub query: String,
|
||||
pub score: f64,
|
||||
}
|
||||
|
||||
fn serialize_markup<S>(markup: &PreEscaped<String>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(&markup.0)
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ use axum::{
|
||||
extract::{ConnectInfo, Query, State},
|
||||
http::{header, HeaderMap, StatusCode},
|
||||
response::IntoResponse,
|
||||
Json,
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use maud::{html, PreEscaped, DOCTYPE};
|
||||
@ -128,7 +129,7 @@ pub async fn route(
|
||||
State(config): State<Arc<Config>>,
|
||||
headers: HeaderMap,
|
||||
ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
||||
) -> impl IntoResponse {
|
||||
) -> axum::response::Response {
|
||||
let query = params
|
||||
.get("q")
|
||||
.cloned()
|
||||
@ -144,7 +145,8 @@ pub async fn route(
|
||||
(header::CONTENT_TYPE, "text/html; charset=utf-8"),
|
||||
],
|
||||
Body::from("<a href=\"/\">No query provided, click here to go back to index</a>"),
|
||||
);
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
|
||||
let search_tab = params
|
||||
@ -176,6 +178,29 @@ pub async fn route(
|
||||
config: config.clone(),
|
||||
};
|
||||
|
||||
let trying_to_use_api =
|
||||
query.request_headers.get("accept") == Some(&"application/json".to_string());
|
||||
if trying_to_use_api {
|
||||
if !config.api {
|
||||
return (StatusCode::FORBIDDEN, "API access is disabled").into_response();
|
||||
}
|
||||
|
||||
let (progress_tx, mut progress_rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let search_future = tokio::spawn(async move { engines::search(&query, progress_tx).await });
|
||||
if let Err(e) = search_future.await {
|
||||
return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response();
|
||||
}
|
||||
|
||||
let mut results = Vec::new();
|
||||
while let Some(progress_update) = progress_rx.recv().await {
|
||||
if let ProgressUpdateData::Response(r) = progress_update.data {
|
||||
results.push(r);
|
||||
}
|
||||
}
|
||||
|
||||
return Json(results).into_response();
|
||||
}
|
||||
|
||||
let s = stream! {
|
||||
type R = Result<Bytes, eyre::Error>;
|
||||
|
||||
@ -238,11 +263,11 @@ pub async fn route(
|
||||
let stream = Body::from_stream(s);
|
||||
|
||||
(
|
||||
StatusCode::OK,
|
||||
[
|
||||
(header::CONTENT_TYPE, "text/html; charset=utf-8"),
|
||||
(header::TRANSFER_ENCODING, "chunked"),
|
||||
],
|
||||
stream,
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user