feat: add indexer and torrent REST controllers with service layer
- Add config module for yaml config (database, indexers, torrent) - Add indexer module with Torznab protocol support - Add IndexerService and TorrentService for business logic - Add REST controllers for indexer search and torrent management - Add Docker Compose for PostgreSQL and Jackett - Add ERD documentation for database schema
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
routing::{get, post},
|
||||
Json, Router,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::indexer::{MusicSearchCriteria, SearchResult};
|
||||
use crate::services::IndexerInfo;
|
||||
use crate::AppState;
|
||||
|
||||
pub fn routes() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/", get(list_indexers))
|
||||
.route("/search", post(search))
|
||||
.route("/:name/test", get(test_indexer))
|
||||
}
|
||||
|
||||
async fn list_indexers(State(state): State<AppState>) -> Json<Vec<IndexerInfo>> {
|
||||
let state = state.read().await;
|
||||
Json(state.indexer_service.list_indexers())
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct SearchRequest {
|
||||
pub artist: String,
|
||||
pub album: Option<String>,
|
||||
pub year: Option<u32>,
|
||||
pub limit: Option<u32>,
|
||||
pub indexer: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct SearchResponse {
|
||||
pub results: Vec<SearchResult>,
|
||||
pub total: usize,
|
||||
}
|
||||
|
||||
async fn search(
|
||||
State(state): State<AppState>,
|
||||
Json(req): Json<SearchRequest>,
|
||||
) -> Result<Json<SearchResponse>, (StatusCode, String)> {
|
||||
let mut criteria = MusicSearchCriteria::new(&req.artist);
|
||||
|
||||
if let Some(album) = &req.album {
|
||||
criteria = criteria.with_album(album);
|
||||
}
|
||||
if let Some(year) = req.year {
|
||||
criteria = criteria.with_year(year);
|
||||
}
|
||||
if let Some(limit) = req.limit {
|
||||
criteria = criteria.with_limit(limit);
|
||||
}
|
||||
|
||||
let state = state.read().await;
|
||||
let results = state
|
||||
.indexer_service
|
||||
.search(&criteria, req.indexer.as_deref())
|
||||
.await
|
||||
.map_err(|e| (StatusCode::BAD_GATEWAY, e.to_string()))?;
|
||||
|
||||
let total = results.len();
|
||||
Ok(Json(SearchResponse { results, total }))
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct TestResponse {
|
||||
pub success: bool,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
async fn test_indexer(
|
||||
State(state): State<AppState>,
|
||||
Path(name): Path<String>,
|
||||
) -> Result<Json<TestResponse>, (StatusCode, Json<TestResponse>)> {
|
||||
let state = state.read().await;
|
||||
|
||||
match state.indexer_service.test_indexer(&name).await {
|
||||
Ok(()) => Ok(Json(TestResponse {
|
||||
success: true,
|
||||
message: "Connection successful".to_string(),
|
||||
})),
|
||||
Err(e) => Err((
|
||||
StatusCode::BAD_GATEWAY,
|
||||
Json(TestResponse {
|
||||
success: false,
|
||||
message: e.to_string(),
|
||||
}),
|
||||
)),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user