182 lines
6.1 KiB
Rust
182 lines
6.1 KiB
Rust
// 负责定义稳定的 HTTP 响应模型和领域对象转换边界
|
|
|
|
use dht_search::domain::{Availability, Heat, TorrentFile, TorrentRecord};
|
|
use serde::Serialize;
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub(crate) struct StatusResponse {
|
|
pub(crate) status: &'static str,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub(crate) struct ErrorResponse {
|
|
pub(crate) error: String,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub(crate) struct StatsResponse {
|
|
pub(crate) nodes: usize,
|
|
pub(crate) udp_tx_packets: u64,
|
|
pub(crate) find_node_queries: u64,
|
|
pub(crate) peer_lookup_queries: u64,
|
|
pub(crate) peer_lookup_preferred_succeeded: u64,
|
|
pub(crate) peer_lookup_fallbacks: u64,
|
|
pub(crate) sample_candidate_queue: usize,
|
|
pub(crate) sample_candidate_queue_capacity: usize,
|
|
pub(crate) sample_candidates_routed: u64,
|
|
pub(crate) sample_candidates_fallback: u64,
|
|
pub(crate) sample_queries: u64,
|
|
pub(crate) sample_direct_queries: u64,
|
|
pub(crate) sample_snapshot_queries: u64,
|
|
pub(crate) sample_responses: u64,
|
|
pub(crate) sample_direct_responses: u64,
|
|
pub(crate) sample_snapshot_responses: u64,
|
|
pub(crate) sample_timeouts: u64,
|
|
pub(crate) sampled_hashes: u64,
|
|
pub(crate) sampled_hashes_filtered: u64,
|
|
pub(crate) sampled_hashes_duplicate: u64,
|
|
pub(crate) sampled_hashes_dropped: u64,
|
|
pub(crate) metadata_peer_attempts: u64,
|
|
pub(crate) metadata_in_flight: usize,
|
|
pub(crate) metadata_ok: u64,
|
|
pub(crate) metadata_ok_from_announce: u64,
|
|
pub(crate) metadata_ok_from_sample_direct: u64,
|
|
pub(crate) metadata_ok_from_sample_snapshot: u64,
|
|
pub(crate) metadata_ok_from_active_lookup: u64,
|
|
pub(crate) metadata_failed: u64,
|
|
pub(crate) metadata_filtered: u64,
|
|
pub(crate) metadata_filtered_too_large: u64,
|
|
pub(crate) metadata_filtered_invalid_info_hash: u64,
|
|
pub(crate) metadata_filtered_empty_name: u64,
|
|
pub(crate) metadata_filtered_name_too_long: u64,
|
|
pub(crate) metadata_filtered_invalid_name: u64,
|
|
pub(crate) metadata_filtered_empty_file_list: u64,
|
|
pub(crate) metadata_filtered_too_many_files: u64,
|
|
pub(crate) metadata_filtered_empty_path: u64,
|
|
pub(crate) metadata_filtered_path_too_long: u64,
|
|
pub(crate) metadata_filtered_path_too_deep: u64,
|
|
pub(crate) metadata_filtered_invalid_path: u64,
|
|
pub(crate) metadata_filtered_size_overflow: u64,
|
|
pub(crate) metadata_filtered_total_size_mismatch: u64,
|
|
pub(crate) persistence_accepted: u64,
|
|
pub(crate) persistence_inserted: u64,
|
|
pub(crate) persistence_updated: u64,
|
|
pub(crate) persistence_rejected_full: u64,
|
|
pub(crate) persistence_queue: usize,
|
|
pub(crate) indexed_documents: u64,
|
|
pub(crate) verification_queue: u64,
|
|
pub(crate) verification_accepted: u64,
|
|
pub(crate) verification_deduplicated: u64,
|
|
pub(crate) verification_rejected_full: u64,
|
|
pub(crate) verification_started: u64,
|
|
pub(crate) verification_succeeded: u64,
|
|
pub(crate) verification_failed: u64,
|
|
pub(crate) verification_peers_discovered: u64,
|
|
pub(crate) verification_handshakes_succeeded: u64,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub(crate) struct TorrentResponse {
|
|
pub(crate) info_hash: String,
|
|
pub(crate) magnet_link: String,
|
|
pub(crate) name: String,
|
|
pub(crate) total_size: u64,
|
|
pub(crate) file_count: usize,
|
|
pub(crate) file_offset: usize,
|
|
pub(crate) file_limit: usize,
|
|
pub(crate) files: Vec<TorrentFile>,
|
|
pub(crate) piece_length: u64,
|
|
pub(crate) content_key: String,
|
|
pub(crate) first_seen: u64,
|
|
pub(crate) last_seen: u64,
|
|
pub(crate) seen_count: u64,
|
|
pub(crate) heat: Heat,
|
|
pub(crate) availability: Availability,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub(crate) struct ContentVariantsResponse {
|
|
pub(crate) content_key: String,
|
|
pub(crate) total: u64,
|
|
pub(crate) offset: usize,
|
|
pub(crate) limit: usize,
|
|
pub(crate) variants: Vec<TorrentVariantResponse>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub(crate) struct TorrentVariantResponse {
|
|
pub(crate) info_hash: String,
|
|
pub(crate) magnet_link: String,
|
|
pub(crate) name: String,
|
|
pub(crate) total_size: u64,
|
|
pub(crate) file_count: usize,
|
|
pub(crate) first_seen: u64,
|
|
pub(crate) last_seen: u64,
|
|
pub(crate) seen_count: u64,
|
|
pub(crate) heat: Heat,
|
|
pub(crate) availability: Availability,
|
|
}
|
|
|
|
impl From<TorrentRecord> for TorrentVariantResponse {
|
|
fn from(record: TorrentRecord) -> Self {
|
|
let info_hash = record.info_hash.to_string();
|
|
let heat = record.heat(unix_timestamp());
|
|
Self {
|
|
magnet_link: format!("magnet:?xt=urn:btih:{info_hash}"),
|
|
info_hash,
|
|
name: record.name,
|
|
total_size: record.total_size,
|
|
file_count: record.files.len(),
|
|
first_seen: record.first_seen,
|
|
last_seen: record.last_seen,
|
|
seen_count: record.seen_count,
|
|
heat,
|
|
availability: record.availability,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TorrentResponse {
|
|
pub(crate) fn from_record(
|
|
record: TorrentRecord,
|
|
file_offset: usize,
|
|
file_limit: usize,
|
|
) -> Self {
|
|
let info_hash = record.info_hash.to_string();
|
|
let heat = record.heat(unix_timestamp());
|
|
let file_count = record.files.len();
|
|
let file_offset = file_offset.min(file_count);
|
|
let files = record
|
|
.files
|
|
.into_iter()
|
|
.skip(file_offset)
|
|
.take(file_limit)
|
|
.collect();
|
|
Self {
|
|
magnet_link: format!("magnet:?xt=urn:btih:{info_hash}"),
|
|
info_hash,
|
|
name: record.name,
|
|
total_size: record.total_size,
|
|
file_count,
|
|
file_offset,
|
|
file_limit,
|
|
files,
|
|
piece_length: record.piece_length,
|
|
content_key: hex::encode(record.content_key),
|
|
first_seen: record.first_seen,
|
|
last_seen: record.last_seen,
|
|
seen_count: record.seen_count,
|
|
heat,
|
|
availability: record.availability,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn unix_timestamp() -> u64 {
|
|
SystemTime::now()
|
|
.duration_since(UNIX_EPOCH)
|
|
.unwrap_or_default()
|
|
.as_secs()
|
|
}
|