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:
+28
-17
@@ -1,3 +1,6 @@
|
||||
mod indexer_controller;
|
||||
mod torrent_controller;
|
||||
|
||||
use axum::{
|
||||
extract::{Path, Query, State},
|
||||
http::StatusCode,
|
||||
@@ -18,20 +21,22 @@ pub fn routes(state: AppState) -> Router {
|
||||
.route("/tracks/:id", delete(delete_track))
|
||||
.route("/tracks/search", get(search_tracks))
|
||||
.route("/stats", get(get_stats))
|
||||
.nest("/indexers", indexer_controller::routes())
|
||||
.nest("/torrents", torrent_controller::routes())
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
async fn list_tracks(State(state): State<AppState>) -> Json<Vec<Track>> {
|
||||
let agg = state.read().await;
|
||||
Json(agg.get_all().to_vec())
|
||||
let state = state.read().await;
|
||||
Json(state.aggregator.get_all().to_vec())
|
||||
}
|
||||
|
||||
async fn create_track(
|
||||
State(state): State<AppState>,
|
||||
Json(input): Json<CreateTrack>,
|
||||
) -> (StatusCode, Json<Track>) {
|
||||
let mut agg = state.write().await;
|
||||
let track = agg.add_track(input.into());
|
||||
let mut state = state.write().await;
|
||||
let track = state.aggregator.add_track(input.into());
|
||||
(StatusCode::CREATED, Json(track))
|
||||
}
|
||||
|
||||
@@ -39,19 +44,18 @@ async fn get_track(
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<Json<Track>, StatusCode> {
|
||||
let agg = state.read().await;
|
||||
agg.get_by_id(id)
|
||||
let state = state.read().await;
|
||||
state
|
||||
.aggregator
|
||||
.get_by_id(id)
|
||||
.cloned()
|
||||
.map(Json)
|
||||
.ok_or(StatusCode::NOT_FOUND)
|
||||
}
|
||||
|
||||
async fn delete_track(
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<Uuid>,
|
||||
) -> StatusCode {
|
||||
let mut agg = state.write().await;
|
||||
if agg.delete(id) {
|
||||
async fn delete_track(State(state): State<AppState>, Path(id): Path<Uuid>) -> StatusCode {
|
||||
let mut state = state.write().await;
|
||||
if state.aggregator.delete(id) {
|
||||
StatusCode::NO_CONTENT
|
||||
} else {
|
||||
StatusCode::NOT_FOUND
|
||||
@@ -67,8 +71,15 @@ async fn search_tracks(
|
||||
State(state): State<AppState>,
|
||||
Query(query): Query<SearchQuery>,
|
||||
) -> Json<Vec<Track>> {
|
||||
let agg = state.read().await;
|
||||
Json(agg.search_by_artist(&query.artist).into_iter().cloned().collect())
|
||||
let state = state.read().await;
|
||||
Json(
|
||||
state
|
||||
.aggregator
|
||||
.search_by_artist(&query.artist)
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
@@ -78,9 +89,9 @@ struct Stats {
|
||||
}
|
||||
|
||||
async fn get_stats(State(state): State<AppState>) -> Json<Stats> {
|
||||
let agg = state.read().await;
|
||||
let state = state.read().await;
|
||||
Json(Stats {
|
||||
track_count: agg.get_all().len(),
|
||||
total_duration_secs: agg.total_duration(),
|
||||
track_count: state.aggregator.get_all().len(),
|
||||
total_duration_secs: state.aggregator.total_duration(),
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user