mirror of
https://github.com/YGGverse/btracker-gemini.git
synced 2026-03-31 09:05:30 +00:00
implement search feature
This commit is contained in:
parent
fdd6e10e3a
commit
96c3df1b41
3 changed files with 119 additions and 84 deletions
|
|
@ -22,6 +22,7 @@ librqbit-core = "5.0.0"
|
||||||
plurify = "0.2.0"
|
plurify = "0.2.0"
|
||||||
url = "2.5.7"
|
url = "2.5.7"
|
||||||
urlencoding = "2.1.3"
|
urlencoding = "2.1.3"
|
||||||
|
regex = "1.11.2"
|
||||||
|
|
||||||
# development
|
# development
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
|
|
|
||||||
80
src/main.rs
80
src/main.rs
|
|
@ -1,15 +1,13 @@
|
||||||
mod config;
|
mod config;
|
||||||
mod format;
|
mod format;
|
||||||
|
mod route;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use btracker_fs::public::{Order, Public, Sort, Torrent};
|
use btracker_fs::public::{Order, Public, Sort, Torrent};
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use librqbit_core::{
|
use librqbit_core::torrent_metainfo::{TorrentMetaV1Owned, torrent_from_bytes};
|
||||||
Id20,
|
|
||||||
torrent_metainfo::{TorrentMetaV1Owned, torrent_from_bytes},
|
|
||||||
};
|
|
||||||
use log::*;
|
use log::*;
|
||||||
use native_tls::{HandshakeError, Identity, TlsAcceptor, TlsStream};
|
use native_tls::{HandshakeError, Identity, TlsAcceptor, TlsStream};
|
||||||
use std::{
|
use std::{
|
||||||
|
|
@ -17,7 +15,6 @@ use std::{
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
net::{SocketAddr, TcpListener, TcpStream},
|
net::{SocketAddr, TcpListener, TcpStream},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
str::FromStr,
|
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
thread,
|
thread,
|
||||||
};
|
};
|
||||||
|
|
@ -155,23 +152,16 @@ fn response(
|
||||||
peer: &SocketAddr,
|
peer: &SocketAddr,
|
||||||
stream: &mut TlsStream<TcpStream>,
|
stream: &mut TlsStream<TcpStream>,
|
||||||
) {
|
) {
|
||||||
|
use route::Route;
|
||||||
|
|
||||||
debug!("Incoming request from `{peer}` to `{}`", request.url.path());
|
debug!("Incoming request from `{peer}` to `{}`", request.url.path());
|
||||||
let path = request.url.path().trim_matches('/');
|
|
||||||
// try index page
|
let route = Route::from_url(&request.url);
|
||||||
if path.is_empty() {
|
|
||||||
send(
|
// try index page, including optional page value
|
||||||
&match index(
|
match route {
|
||||||
config,
|
Route::List { page } => send(
|
||||||
public,
|
&match list(config, public, request.url.query(), page) {
|
||||||
request.url.query(),
|
|
||||||
request.url.query_pairs().find_map(|a| {
|
|
||||||
if a.0 == "page" {
|
|
||||||
a.1.parse::<usize>().ok()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
) {
|
|
||||||
Ok(data) => response::success::Default {
|
Ok(data) => response::success::Default {
|
||||||
data: data.as_bytes(),
|
data: data.as_bytes(),
|
||||||
meta: response::success::default::Meta {
|
meta: response::success::default::Meta {
|
||||||
|
|
@ -193,11 +183,8 @@ fn response(
|
||||||
error!("Internal server error on handle peer `{peer}` request: `{e}`")
|
error!("Internal server error on handle peer `{peer}` request: `{e}`")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
),
|
||||||
}
|
Route::Search => send(
|
||||||
// try search
|
|
||||||
else if path == "search" {
|
|
||||||
send(
|
|
||||||
&response::Input::Default(response::input::Default {
|
&response::Input::Default(response::input::Default {
|
||||||
message: Some("Keyword, file, hash...".into()),
|
message: Some("Keyword, file, hash...".into()),
|
||||||
})
|
})
|
||||||
|
|
@ -211,13 +198,9 @@ fn response(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
),
|
||||||
}
|
Route::Info(id) => match public.torrent(id) {
|
||||||
// try info page
|
Some(torrent) => send(
|
||||||
else if let Ok(id) = Id20::from_str(path)
|
|
||||||
&& let Some(torrent) = public.torrent(id)
|
|
||||||
{
|
|
||||||
send(
|
|
||||||
&match info(config, torrent) {
|
&match info(config, torrent) {
|
||||||
Ok(data) => response::success::Default {
|
Ok(data) => response::success::Default {
|
||||||
data: data.as_bytes(),
|
data: data.as_bytes(),
|
||||||
|
|
@ -240,10 +223,10 @@ fn response(
|
||||||
error!("Internal server error on handle peer `{peer}` request: `{e}`")
|
error!("Internal server error on handle peer `{peer}` request: `{e}`")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
),
|
||||||
}
|
None => todo!(),
|
||||||
// not found
|
},
|
||||||
else {
|
Route::NotFound => {
|
||||||
warn!(
|
warn!(
|
||||||
"Requested resource `{}` not found by peer `{peer}`",
|
"Requested resource `{}` not found by peer `{peer}`",
|
||||||
request.url.as_str()
|
request.url.as_str()
|
||||||
|
|
@ -264,6 +247,7 @@ fn response(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send(data: &[u8], stream: &mut TlsStream<TcpStream>, callback: impl FnOnce(Result<()>)) {
|
fn send(data: &[u8], stream: &mut TlsStream<TcpStream>, callback: impl FnOnce(Result<()>)) {
|
||||||
|
|
@ -283,7 +267,7 @@ fn send(data: &[u8], stream: &mut TlsStream<TcpStream>, callback: impl FnOnce(Re
|
||||||
|
|
||||||
// rotes
|
// rotes
|
||||||
|
|
||||||
fn index(
|
fn list(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
public: &Public,
|
public: &Public,
|
||||||
keyword: Option<&str>,
|
keyword: Option<&str>,
|
||||||
|
|
@ -291,6 +275,10 @@ fn index(
|
||||||
) -> Result<String> {
|
) -> Result<String> {
|
||||||
use plurify::Plurify;
|
use plurify::Plurify;
|
||||||
|
|
||||||
|
fn query(keyword: Option<&str>) -> String {
|
||||||
|
keyword.map(|k| format!("?{}", k)).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
let (total, torrents) = public.torrents(
|
let (total, torrents) = public.torrents(
|
||||||
keyword,
|
keyword,
|
||||||
Some((Sort::Modified, Order::Desc)),
|
Some((Sort::Modified, Order::Desc)),
|
||||||
|
|
@ -347,18 +335,22 @@ fn index(
|
||||||
|
|
||||||
if let Some(p) = page {
|
if let Some(p) = page {
|
||||||
b.push(format!(
|
b.push(format!(
|
||||||
"=> /{} Back",
|
"=> {}{} Back",
|
||||||
if p > 2 {
|
if p > 2 {
|
||||||
Some(format!("?page={}", p - 1))
|
format!("/{}", p - 1)
|
||||||
} else {
|
} else {
|
||||||
None
|
"/".into()
|
||||||
}
|
},
|
||||||
.unwrap_or_default()
|
query(keyword)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
if page.unwrap_or(1) * public.default_limit < total {
|
if page.unwrap_or(1) * public.default_limit < total {
|
||||||
b.push(format!("=> /?page={} Next", page.map_or(2, |p| p + 1)))
|
b.push(format!(
|
||||||
|
"=> /{}{} Next",
|
||||||
|
page.map_or(2, |p| p + 1),
|
||||||
|
query(keyword)
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
b.push("\n=> /search Search".into());
|
b.push("\n=> /search Search".into());
|
||||||
|
|
|
||||||
42
src/route.rs
Normal file
42
src/route.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use librqbit_core::Id20;
|
||||||
|
use regex::Regex;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
pub enum Route {
|
||||||
|
Info(Id20),
|
||||||
|
List { page: Option<usize> },
|
||||||
|
NotFound,
|
||||||
|
Search,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Route {
|
||||||
|
pub fn from_url(url: &Url) -> Self {
|
||||||
|
let p = url.path().to_lowercase();
|
||||||
|
let q = url.query();
|
||||||
|
|
||||||
|
if p.is_empty() {
|
||||||
|
return Self::List { page: None };
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(id) = Id20::from_str(p.trim_matches('/')) {
|
||||||
|
return Self::Info(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if p == "/search" && q.is_none() {
|
||||||
|
return Self::Search;
|
||||||
|
}
|
||||||
|
|
||||||
|
if Regex::new(r"^/(|search)").unwrap().is_match(&p) {
|
||||||
|
return Self::List {
|
||||||
|
page: Regex::new(r"/(\d+)$").unwrap().captures(&p).map(|c| {
|
||||||
|
c.get(1)
|
||||||
|
.map_or(1, |p| p.as_str().parse::<usize>().unwrap_or(1))
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::NotFound
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue