main
Elnu 1 year ago
parent c9f9783421
commit befc0c0ee3

1672
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -6,3 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
actix-web = "4.3.1"
derive_more = "0.99.17"
mime = "0.3.17"
reqwest = "0.11.18"

@ -15,7 +15,7 @@ pkgs.mkShell {
ruststable ruststable
rust-analyzer rust-analyzer
bacon bacon
#pkg-config pkg-config
#openssl openssl
]; ];
} }

@ -0,0 +1,42 @@
use actix_web::{HttpResponse, ResponseError};
use derive_more::From;
use std::fmt::{self, Display};
pub type Result<T> = std::result::Result<T, Error>;
#[derive(From, Debug)]
pub enum Error {
TatoebaApi(reqwest::Error),
NotHuman { target: String },
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl ResponseError for Error {
fn status_code(&self) -> reqwest::StatusCode {
use reqwest::StatusCode;
use Error::*;
match self {
// 503 Service Unavailable
TatoebaApi(error) => error.status().unwrap_or(StatusCode::SERVICE_UNAVAILABLE),
// 403 Forbidden
NotHuman { .. } => StatusCode::FORBIDDEN,
}
}
fn error_response(&self) -> HttpResponse<actix_web::body::BoxBody> {
HttpResponse::build(self.status_code()).body(match self {
Self::NotHuman { target } => format!(
"It looks like you're accessing the Tatoeba API proxy from a script!\n\
Tatoeba CORS restrictions do not apply outside of browsers, so please access the API directly:\n\
{target}\n\
If you feel this is mistake, please open an issue:\n\
https://codeberg.org/ElnuDev/tatoeba-api-rs"),
_ => self.to_string(),
})
}
}

@ -1,3 +1,29 @@
fn main() { mod error;
println!("Hello, world!"); pub use error::{Error, Result};
pub mod utils;
use utils::is_human;
use actix_web::{get, http::header, App, HttpRequest, HttpResponse, HttpServer, Responder};
pub const TATOEBA_API: &str = "https://tatoeba.org/en/api_v0/search";
#[get("/")]
async fn root(request: HttpRequest) -> Result<impl Responder> {
let target = format!("{TATOEBA_API}?{}", request.query_string());
if !is_human(&request) {
return Err(Error::NotHuman { target });
}
let resp = reqwest::get(target).await?;
Ok(HttpResponse::Ok()
.append_header(header::ContentType(mime::APPLICATION_JSON))
.body(resp.text().await?))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().service(root))
.bind(("127.0.0.1", 3001))?
.run()
.await
} }

@ -0,0 +1,21 @@
use actix_web::{http::header, HttpRequest};
pub fn is_human(request: &HttpRequest) -> bool {
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent
const HUMANS: &[&str] = &[
"Firefox", "Chrome", // Chrome and Chromium browsers
"Opera", // Old Presto-based Opera
"Mobile", // Safari
"Trident", // Internet Explorer
];
request
.headers()
.get(header::USER_AGENT)
.and_then(|header| header.to_str().ok())
.map(|ua| {
HUMANS
.iter()
.any(|&human| ua.contains(human))
})
.unwrap_or(false)
}
Loading…
Cancel
Save