amélioration de la webapp
This commit is contained in:
@@ -92,9 +92,7 @@ pub struct ErrorResponse {
|
||||
/// Liste tous les items en cache avec leurs statistiques
|
||||
///
|
||||
/// Retourne la liste complète des entrées du cache triées par nombre d'accès décroissant.
|
||||
pub async fn list_items<C: CacheConfig>(
|
||||
State(cache): State<Arc<Cache<C>>>,
|
||||
) -> impl IntoResponse {
|
||||
pub async fn list_items<C: CacheConfig>(State(cache): State<Arc<Cache<C>>>) -> impl IntoResponse {
|
||||
match cache.db.get_all() {
|
||||
Ok(entries) => (StatusCode::OK, Json(entries)).into_response(),
|
||||
Err(e) => (
|
||||
@@ -192,7 +190,10 @@ pub async fn add_item<C: CacheConfig>(
|
||||
.into_response();
|
||||
}
|
||||
|
||||
match cache.add_from_url(&req.url, req.collection.as_deref()).await {
|
||||
match cache
|
||||
.add_from_url(&req.url, req.collection.as_deref())
|
||||
.await
|
||||
{
|
||||
Ok(pk) => (
|
||||
StatusCode::CREATED,
|
||||
Json(AddItemResponse {
|
||||
@@ -268,9 +269,7 @@ pub async fn delete_item<C: CacheConfig>(
|
||||
/// Purge complètement le cache
|
||||
///
|
||||
/// Supprime tous les items et vide la base de données. Opération irréversible.
|
||||
pub async fn purge_cache<C: CacheConfig>(
|
||||
State(cache): State<Arc<Cache<C>>>,
|
||||
) -> impl IntoResponse {
|
||||
pub async fn purge_cache<C: CacheConfig>(State(cache): State<Arc<Cache<C>>>) -> impl IntoResponse {
|
||||
match cache.purge().await {
|
||||
Ok(_) => (
|
||||
StatusCode::OK,
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
//! Ce module fournit une interface générique pour gérer un cache de fichiers
|
||||
//! avec métadonnées dans une base de données SQLite.
|
||||
|
||||
use crate::cache_trait::{FileCache, pk_from_url};
|
||||
use crate::cache_trait::{pk_from_url, FileCache};
|
||||
use crate::db::DB;
|
||||
use crate::download::{Download, download_with_transformer, StreamTransformer};
|
||||
use crate::download::{download_with_transformer, Download, StreamTransformer};
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -26,10 +26,10 @@ pub trait CacheConfig: Send + Sync {
|
||||
"file"
|
||||
}
|
||||
/// Cache name (ex: "covers", "audio", "cache")
|
||||
fn cache_name() -> &'static str {
|
||||
fn cache_name() -> &'static str {
|
||||
"cache"
|
||||
}
|
||||
/// Default param extension ("orig")
|
||||
/// Default param extension ("orig")
|
||||
fn default_param() -> &'static str {
|
||||
"orig"
|
||||
}
|
||||
@@ -155,11 +155,7 @@ impl<C: CacheConfig> Cache<C> {
|
||||
|
||||
// Lancer le téléchargement avec transformer
|
||||
let transformer = self.transformer_factory.as_ref().map(|f| f());
|
||||
let download = download_with_transformer(
|
||||
&file_path,
|
||||
url,
|
||||
transformer,
|
||||
);
|
||||
let download = download_with_transformer(&file_path, url, transformer);
|
||||
|
||||
// Stocker dans la map des downloads en cours
|
||||
{
|
||||
@@ -228,7 +224,6 @@ impl<C: CacheConfig> Cache<C> {
|
||||
self.add_from_url(url, collection).await
|
||||
}
|
||||
|
||||
|
||||
/// Récupère le chemin d'un fichier dans le cache
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -289,8 +284,11 @@ impl<C: CacheConfig> Cache<C> {
|
||||
let file_path = self.file_path(&entry.pk);
|
||||
if !file_path.exists() {
|
||||
// Re-télécharger le fichier manquant
|
||||
match self.add_from_url(&entry.source_url, entry.collection.as_deref()).await {
|
||||
Ok(_) => {},
|
||||
match self
|
||||
.add_from_url(&entry.source_url, entry.collection.as_deref())
|
||||
.await
|
||||
{
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
// Si le téléchargement échoue, supprimer l'entrée DB
|
||||
self.db.delete(&entry.pk)?;
|
||||
@@ -413,7 +411,9 @@ impl<C: CacheConfig> Cache<C> {
|
||||
/// * `min_size` - Taille minimale attendue en bytes
|
||||
pub async fn wait_until_min_size(&self, pk: &str, min_size: u64) -> Result<()> {
|
||||
if let Some(download) = self.get_download(pk).await {
|
||||
download.wait_until_min_size(min_size).await
|
||||
download
|
||||
.wait_until_min_size(min_size)
|
||||
.await
|
||||
.map_err(|e| anyhow!("Download error: {}", e))
|
||||
} else {
|
||||
// Déjà terminé ou n'existe pas
|
||||
@@ -432,7 +432,9 @@ impl<C: CacheConfig> Cache<C> {
|
||||
/// * `pk` - Clé primaire du fichier
|
||||
pub async fn wait_until_finished(&self, pk: &str) -> Result<()> {
|
||||
if let Some(download) = self.get_download(pk).await {
|
||||
download.wait_until_finished().await
|
||||
download
|
||||
.wait_until_finished()
|
||||
.await
|
||||
.map_err(|e| anyhow!("Download error: {}", e))
|
||||
} else {
|
||||
// Déjà terminé ou n'existe pas
|
||||
@@ -460,7 +462,8 @@ impl<C: CacheConfig> Cache<C> {
|
||||
///
|
||||
/// Format: `{pk}.{qualifier}.{extension}`
|
||||
pub fn file_path_with_qualifier(&self, pk: &str, qualifier: &str) -> PathBuf {
|
||||
self.dir.join(format!("{}.{}.{}", pk, qualifier, C::file_extension()))
|
||||
self.dir
|
||||
.join(format!("{}.{}.{}", pk, qualifier, C::file_extension()))
|
||||
}
|
||||
|
||||
/// Valide les données avant de les stocker
|
||||
@@ -499,7 +502,9 @@ impl<C: CacheConfig> Cache<C> {
|
||||
while let Ok(Some(dir_entry)) = dir_entries.next_entry().await {
|
||||
if let Some(filename) = dir_entry.file_name().to_str() {
|
||||
// Format: {pk}.{param}.{ext}
|
||||
if filename.starts_with(&entry.pk) && filename.starts_with(&format!("{}.", entry.pk)) {
|
||||
if filename.starts_with(&entry.pk)
|
||||
&& filename.starts_with(&format!("{}.", entry.pk))
|
||||
{
|
||||
let _ = tokio::fs::remove_file(dir_entry.path()).await;
|
||||
}
|
||||
}
|
||||
@@ -515,15 +520,18 @@ impl<C: CacheConfig> Cache<C> {
|
||||
}
|
||||
|
||||
if removed > 0 {
|
||||
tracing::info!("LRU eviction: removed {} old entries (cache size: {} -> {})",
|
||||
removed, count, count - removed);
|
||||
tracing::info!(
|
||||
"LRU eviction: removed {} old entries (cache size: {} -> {})",
|
||||
removed,
|
||||
count,
|
||||
count - removed
|
||||
);
|
||||
}
|
||||
|
||||
Ok(removed)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Implémentation du trait FileCache pour Cache
|
||||
impl<C: CacheConfig> FileCache<C> for Cache<C> {
|
||||
fn get_cache_dir(&self) -> &Path {
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
//! Ce module fournit une interface générique pour gérer les métadonnées
|
||||
//! des éléments en cache, avec tracking des accès et des statistiques.
|
||||
|
||||
use rusqlite::{Connection, params};
|
||||
use serde::Serialize;
|
||||
use chrono::Utc;
|
||||
use rusqlite::{params, Connection};
|
||||
use serde::Serialize;
|
||||
use std::path::Path;
|
||||
use std::sync::Mutex;
|
||||
|
||||
@@ -32,7 +32,10 @@ pub struct CacheEntry {
|
||||
#[cfg_attr(feature = "openapi", schema(example = "2025-01-15T10:30:00Z"))]
|
||||
pub last_used: Option<String>,
|
||||
/// Métadonnées JSON optionnelles (ex: métadonnées audio, EXIF images, etc.)
|
||||
#[cfg_attr(feature = "openapi", schema(example = r#"{"title":"Track","artist":"Artist"}"#))]
|
||||
#[cfg_attr(
|
||||
feature = "openapi",
|
||||
schema(example = r#"{"title":"Track","artist":"Artist"}"#)
|
||||
)]
|
||||
pub metadata_json: Option<String>,
|
||||
}
|
||||
|
||||
@@ -161,20 +164,16 @@ impl DB {
|
||||
self.table_name
|
||||
);
|
||||
|
||||
conn.query_row(
|
||||
&sql,
|
||||
[pk],
|
||||
|row| {
|
||||
Ok(CacheEntry {
|
||||
pk: row.get(0)?,
|
||||
source_url: row.get(1)?,
|
||||
collection: row.get(2)?,
|
||||
hits: row.get(3)?,
|
||||
last_used: row.get(4)?,
|
||||
metadata_json: row.get(5)?,
|
||||
})
|
||||
},
|
||||
)
|
||||
conn.query_row(&sql, [pk], |row| {
|
||||
Ok(CacheEntry {
|
||||
pk: row.get(0)?,
|
||||
source_url: row.get(1)?,
|
||||
collection: row.get(2)?,
|
||||
hits: row.get(3)?,
|
||||
last_used: row.get(4)?,
|
||||
metadata_json: row.get(5)?,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Met à jour le compteur d'accès et la date du dernier accès
|
||||
@@ -189,10 +188,7 @@ impl DB {
|
||||
self.table_name
|
||||
);
|
||||
|
||||
conn.execute(
|
||||
&sql,
|
||||
params![Utc::now().to_rfc3339(), pk],
|
||||
)?;
|
||||
conn.execute(&sql, params![Utc::now().to_rfc3339(), pk])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -215,17 +211,18 @@ impl DB {
|
||||
|
||||
let mut stmt = conn.prepare(&sql)?;
|
||||
|
||||
let entries = stmt.query_map([], |row| {
|
||||
Ok(CacheEntry {
|
||||
pk: row.get(0)?,
|
||||
source_url: row.get(1)?,
|
||||
collection: row.get(2)?,
|
||||
hits: row.get(3)?,
|
||||
last_used: row.get(4)?,
|
||||
metadata_json: row.get(5)?,
|
||||
})
|
||||
})?
|
||||
.collect::<rusqlite::Result<Vec<_>>>()?;
|
||||
let entries = stmt
|
||||
.query_map([], |row| {
|
||||
Ok(CacheEntry {
|
||||
pk: row.get(0)?,
|
||||
source_url: row.get(1)?,
|
||||
collection: row.get(2)?,
|
||||
hits: row.get(3)?,
|
||||
last_used: row.get(4)?,
|
||||
metadata_json: row.get(5)?,
|
||||
})
|
||||
})?
|
||||
.collect::<rusqlite::Result<Vec<_>>>()?;
|
||||
|
||||
Ok(entries)
|
||||
}
|
||||
@@ -244,17 +241,18 @@ impl DB {
|
||||
|
||||
let mut stmt = conn.prepare(&sql)?;
|
||||
|
||||
let entries = stmt.query_map([collection], |row| {
|
||||
Ok(CacheEntry {
|
||||
pk: row.get(0)?,
|
||||
source_url: row.get(1)?,
|
||||
collection: row.get(2)?,
|
||||
hits: row.get(3)?,
|
||||
last_used: row.get(4)?,
|
||||
metadata_json: row.get(5)?,
|
||||
})
|
||||
})?
|
||||
.collect::<rusqlite::Result<Vec<_>>>()?;
|
||||
let entries = stmt
|
||||
.query_map([collection], |row| {
|
||||
Ok(CacheEntry {
|
||||
pk: row.get(0)?,
|
||||
source_url: row.get(1)?,
|
||||
collection: row.get(2)?,
|
||||
hits: row.get(3)?,
|
||||
last_used: row.get(4)?,
|
||||
metadata_json: row.get(5)?,
|
||||
})
|
||||
})?
|
||||
.collect::<rusqlite::Result<Vec<_>>>()?;
|
||||
|
||||
Ok(entries)
|
||||
}
|
||||
@@ -319,17 +317,18 @@ impl DB {
|
||||
|
||||
let mut stmt = conn.prepare(&sql)?;
|
||||
|
||||
let entries = stmt.query_map([limit], |row| {
|
||||
Ok(CacheEntry {
|
||||
pk: row.get(0)?,
|
||||
source_url: row.get(1)?,
|
||||
collection: row.get(2)?,
|
||||
hits: row.get(3)?,
|
||||
last_used: row.get(4)?,
|
||||
metadata_json: row.get(5)?,
|
||||
})
|
||||
})?
|
||||
.collect::<rusqlite::Result<Vec<_>>>()?;
|
||||
let entries = stmt
|
||||
.query_map([limit], |row| {
|
||||
Ok(CacheEntry {
|
||||
pk: row.get(0)?,
|
||||
source_url: row.get(1)?,
|
||||
collection: row.get(2)?,
|
||||
hits: row.get(3)?,
|
||||
last_used: row.get(4)?,
|
||||
metadata_json: row.get(5)?,
|
||||
})
|
||||
})?
|
||||
.collect::<rusqlite::Result<Vec<_>>>()?;
|
||||
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use futures_util::Future;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -5,7 +6,6 @@ use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::RwLock;
|
||||
use futures_util::Future;
|
||||
|
||||
/// Type pour une fonction de transformation de stream
|
||||
///
|
||||
@@ -248,20 +248,16 @@ async fn download_impl(
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
// Lancer la requête
|
||||
let response = client
|
||||
.get(&url)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
let error = format!("Failed to fetch URL: {}", e);
|
||||
tokio::task::block_in_place(|| {
|
||||
tokio::runtime::Handle::current().block_on(async {
|
||||
let mut s = state.write().await;
|
||||
s.error = Some(error.clone());
|
||||
});
|
||||
let response = client.get(&url).send().await.map_err(|e| {
|
||||
let error = format!("Failed to fetch URL: {}", e);
|
||||
tokio::task::block_in_place(|| {
|
||||
tokio::runtime::Handle::current().block_on(async {
|
||||
let mut s = state.write().await;
|
||||
s.error = Some(error.clone());
|
||||
});
|
||||
error
|
||||
})?;
|
||||
});
|
||||
error
|
||||
})?;
|
||||
|
||||
// Vérifier le statut
|
||||
if !response.status().is_success() {
|
||||
@@ -279,31 +275,30 @@ async fn download_impl(
|
||||
}
|
||||
|
||||
// Créer le fichier de destination
|
||||
let file = tokio::fs::File::create(&filename)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
let error = format!("Failed to create file: {}", e);
|
||||
tokio::task::block_in_place(|| {
|
||||
tokio::runtime::Handle::current().block_on(async {
|
||||
let mut s = state.write().await;
|
||||
s.error = Some(error.clone());
|
||||
s.finished = true;
|
||||
});
|
||||
let file = tokio::fs::File::create(&filename).await.map_err(|e| {
|
||||
let error = format!("Failed to create file: {}", e);
|
||||
tokio::task::block_in_place(|| {
|
||||
tokio::runtime::Handle::current().block_on(async {
|
||||
let mut s = state.write().await;
|
||||
s.error = Some(error.clone());
|
||||
s.finished = true;
|
||||
});
|
||||
error
|
||||
})?;
|
||||
});
|
||||
error
|
||||
})?;
|
||||
|
||||
// Si un transformer est fourni, l'utiliser
|
||||
if let Some(transformer) = transformer {
|
||||
// Créer un callback pour mettre à jour la progression
|
||||
let state_clone = Arc::clone(&state);
|
||||
let progress_callback: Arc<dyn Fn(u64) + Send + Sync> = Arc::new(move |transformed_bytes| {
|
||||
let state = Arc::clone(&state_clone);
|
||||
tokio::spawn(async move {
|
||||
let mut s = state.write().await;
|
||||
s.transformed_size = transformed_bytes;
|
||||
let progress_callback: Arc<dyn Fn(u64) + Send + Sync> =
|
||||
Arc::new(move |transformed_bytes| {
|
||||
let state = Arc::clone(&state_clone);
|
||||
tokio::spawn(async move {
|
||||
let mut s = state.write().await;
|
||||
s.transformed_size = transformed_bytes;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Appeler le transformer
|
||||
match transformer(response, file, progress_callback).await {
|
||||
@@ -331,8 +326,8 @@ async fn default_download(
|
||||
mut file: tokio::fs::File,
|
||||
state: Arc<RwLock<DownloadState>>,
|
||||
) -> Result<(), String> {
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use futures_util::StreamExt;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
let mut stream = response.bytes_stream();
|
||||
|
||||
|
||||
@@ -122,9 +122,9 @@
|
||||
//! - [`pmocovers`] : Cache d'images avec conversion WebP
|
||||
//! - [`pmoaudiocache`] : Cache de pistes audio
|
||||
|
||||
pub mod db;
|
||||
pub mod cache;
|
||||
pub mod cache_trait;
|
||||
pub mod db;
|
||||
pub mod download;
|
||||
|
||||
#[cfg(feature = "pmoserver")]
|
||||
@@ -136,16 +136,13 @@ pub mod api;
|
||||
#[cfg(feature = "openapi")]
|
||||
pub mod openapi;
|
||||
|
||||
pub use db::{DB, CacheEntry};
|
||||
pub use cache::{Cache, CacheConfig};
|
||||
pub use cache_trait::{FileCache, pk_from_url};
|
||||
pub use download::{Download, download, download_with_transformer, StreamTransformer};
|
||||
pub use cache_trait::{pk_from_url, FileCache};
|
||||
pub use db::{CacheEntry, DB};
|
||||
pub use download::{download, download_with_transformer, Download, StreamTransformer};
|
||||
|
||||
#[cfg(feature = "pmoserver")]
|
||||
pub use pmoserver_ext::{create_file_router, create_api_router, GenericCacheExt};
|
||||
pub use pmoserver_ext::{create_api_router, create_file_router, GenericCacheExt};
|
||||
|
||||
#[cfg(all(feature = "pmoserver", feature = "openapi"))]
|
||||
pub use api::{
|
||||
DownloadStatus, AddItemRequest, AddItemResponse,
|
||||
DeleteItemResponse, ErrorResponse,
|
||||
};
|
||||
pub use api::{AddItemRequest, AddItemResponse, DeleteItemResponse, DownloadStatus, ErrorResponse};
|
||||
|
||||
@@ -49,15 +49,15 @@ use axum::{
|
||||
Router,
|
||||
};
|
||||
#[cfg(feature = "pmoserver")]
|
||||
use std::future::Future;
|
||||
#[cfg(feature = "pmoserver")]
|
||||
use std::pin::Pin;
|
||||
#[cfg(feature = "pmoserver")]
|
||||
use std::sync::Arc;
|
||||
#[cfg(feature = "pmoserver")]
|
||||
use tokio_util::io::ReaderStream;
|
||||
#[cfg(feature = "pmoserver")]
|
||||
use tracing::warn;
|
||||
#[cfg(feature = "pmoserver")]
|
||||
use std::pin::Pin;
|
||||
#[cfg(feature = "pmoserver")]
|
||||
use std::future::Future;
|
||||
|
||||
/// Type pour le callback de génération de param
|
||||
///
|
||||
@@ -75,16 +75,20 @@ use std::future::Future;
|
||||
/// Les données générées ou None si le param n'est pas supporté
|
||||
#[cfg(feature = "pmoserver")]
|
||||
pub type ParamGenerator<C> = Arc<
|
||||
dyn Fn(Arc<Cache<C>>, String, String)
|
||||
-> Pin<Box<dyn Future<Output = Option<Vec<u8>>> + Send>>
|
||||
+ Send + Sync
|
||||
dyn Fn(Arc<Cache<C>>, String, String) -> Pin<Box<dyn Future<Output = Option<Vec<u8>>> + Send>>
|
||||
+ Send
|
||||
+ Sync,
|
||||
>;
|
||||
|
||||
/// Handler générique pour GET /{cache_name}/{cache_type}/{pk}
|
||||
/// Sert un fichier avec le param par défaut
|
||||
#[cfg(feature = "pmoserver")]
|
||||
async fn get_file<C: CacheConfig + 'static>(
|
||||
State((cache, content_type, param_generator)): State<(Arc<Cache<C>>, &'static str, Option<ParamGenerator<C>>)>,
|
||||
State((cache, content_type, param_generator)): State<(
|
||||
Arc<Cache<C>>,
|
||||
&'static str,
|
||||
Option<ParamGenerator<C>>,
|
||||
)>,
|
||||
Path(pk): Path<String>,
|
||||
) -> Response {
|
||||
// Utiliser le param par défaut
|
||||
@@ -96,7 +100,11 @@ async fn get_file<C: CacheConfig + 'static>(
|
||||
/// Sert un fichier avec un param spécifique
|
||||
#[cfg(feature = "pmoserver")]
|
||||
async fn get_file_with_param<C: CacheConfig + 'static>(
|
||||
State((cache, content_type, param_generator)): State<(Arc<Cache<C>>, &'static str, Option<ParamGenerator<C>>)>,
|
||||
State((cache, content_type, param_generator)): State<(
|
||||
Arc<Cache<C>>,
|
||||
&'static str,
|
||||
Option<ParamGenerator<C>>,
|
||||
)>,
|
||||
Path((pk, param)): Path<(String, String)>,
|
||||
) -> Response {
|
||||
serve_file_with_streaming(&cache, &pk, ¶m, content_type, param_generator).await
|
||||
@@ -122,11 +130,7 @@ async fn serve_file_with_streaming<C: CacheConfig>(
|
||||
if let Some(generator) = param_generator {
|
||||
if let Some(data) = generator(cache.clone(), pk.to_string(), param.to_string()).await {
|
||||
// Le générateur a créé les données, les servir directement
|
||||
return (
|
||||
StatusCode::OK,
|
||||
[("content-type", content_type)],
|
||||
data,
|
||||
).into_response();
|
||||
return (StatusCode::OK, [("content-type", content_type)], data).into_response();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,12 +211,7 @@ async fn serve_complete_file(
|
||||
}
|
||||
|
||||
match tokio::fs::read(&file_path).await {
|
||||
Ok(data) => (
|
||||
StatusCode::OK,
|
||||
[("content-type", content_type)],
|
||||
data,
|
||||
)
|
||||
.into_response(),
|
||||
Ok(data) => (StatusCode::OK, [("content-type", content_type)], data).into_response(),
|
||||
Err(e) => {
|
||||
warn!("Error reading file {:?}: {}", file_path, e);
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, "Error reading file").into_response()
|
||||
@@ -332,9 +331,7 @@ pub fn create_file_router_with_generator<C: CacheConfig + 'static>(
|
||||
/// - `DELETE /{pk}` - Supprimer un item
|
||||
/// - `POST /consolidate` - Consolider le cache
|
||||
#[cfg(feature = "pmoserver")]
|
||||
pub fn create_api_router<C: CacheConfig + 'static>(
|
||||
cache: Arc<Cache<C>>,
|
||||
) -> Router {
|
||||
pub fn create_api_router<C: CacheConfig + 'static>(cache: Arc<Cache<C>>) -> Router {
|
||||
use crate::api;
|
||||
|
||||
Router::new()
|
||||
@@ -346,8 +343,7 @@ pub fn create_api_router<C: CacheConfig + 'static>(
|
||||
)
|
||||
.route(
|
||||
"/{pk}",
|
||||
get(api::get_item_info::<C>)
|
||||
.delete(api::delete_item::<C>),
|
||||
get(api::get_item_info::<C>).delete(api::delete_item::<C>),
|
||||
)
|
||||
.route("/{pk}/status", get(api::get_download_status::<C>))
|
||||
.route("/consolidate", post(api::consolidate_cache::<C>))
|
||||
|
||||
Reference in New Issue
Block a user