chore: bump deps; fix clippy lints & cleanup
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I4c4815ad145650a07f108614034d2e996a6a6964
This commit is contained in:
parent
c535650f45
commit
cd1161ee5d
41 changed files with 1528 additions and 953 deletions
|
|
@ -136,7 +136,7 @@ pub fn create_router_with_tls(
|
|||
)
|
||||
// Webhooks (read)
|
||||
.route("/webhooks", get(routes::webhooks::list_webhooks))
|
||||
// Auth endpoints (self-service) — login handled separately with stricter rate limit
|
||||
// Auth endpoints (self-service); login is handled separately with a stricter rate limit
|
||||
.route("/auth/logout", post(routes::auth::logout))
|
||||
.route("/auth/me", get(routes::auth::me))
|
||||
.route("/auth/revoke-all", post(routes::auth::revoke_all_sessions))
|
||||
|
|
|
|||
|
|
@ -721,8 +721,6 @@ impl From<pinakes_core::users::UserLibraryAccess> for UserLibraryResponse {
|
|||
}
|
||||
}
|
||||
|
||||
// ===== Social (Ratings, Comments, Favorites, Shares) =====
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct RatingResponse {
|
||||
pub id: String,
|
||||
|
|
@ -816,8 +814,6 @@ impl From<pinakes_core::social::ShareLink> for ShareLinkResponse {
|
|||
}
|
||||
}
|
||||
|
||||
// ===== Playlists =====
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct PlaylistResponse {
|
||||
pub id: String,
|
||||
|
|
@ -875,8 +871,6 @@ pub struct ReorderPlaylistRequest {
|
|||
pub new_position: i32,
|
||||
}
|
||||
|
||||
// ===== Analytics =====
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct UsageEventResponse {
|
||||
pub id: String,
|
||||
|
|
@ -924,8 +918,6 @@ pub struct WatchProgressResponse {
|
|||
pub progress_secs: f64,
|
||||
}
|
||||
|
||||
// ===== Subtitles =====
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct SubtitleResponse {
|
||||
pub id: String,
|
||||
|
|
@ -968,8 +960,6 @@ pub struct UpdateSubtitleOffsetRequest {
|
|||
pub offset_ms: i64,
|
||||
}
|
||||
|
||||
// ===== Enrichment =====
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct ExternalMetadataResponse {
|
||||
pub id: String,
|
||||
|
|
@ -1005,8 +995,6 @@ impl From<pinakes_core::enrichment::ExternalMetadata>
|
|||
}
|
||||
}
|
||||
|
||||
// ===== Transcode =====
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct TranscodeSessionResponse {
|
||||
pub id: String,
|
||||
|
|
@ -1039,8 +1027,6 @@ pub struct CreateTranscodeRequest {
|
|||
pub profile: String,
|
||||
}
|
||||
|
||||
// ===== Managed Storage / Upload =====
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct UploadResponse {
|
||||
pub media_id: String,
|
||||
|
|
@ -1081,8 +1067,6 @@ impl From<pinakes_core::model::ManagedStorageStats>
|
|||
}
|
||||
}
|
||||
|
||||
// ===== Sync =====
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct RegisterDeviceRequest {
|
||||
pub name: String,
|
||||
|
|
@ -1269,8 +1253,6 @@ pub struct AcknowledgeChangesRequest {
|
|||
pub cursor: i64,
|
||||
}
|
||||
|
||||
// ===== Enhanced Sharing =====
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CreateShareRequest {
|
||||
pub target_type: String,
|
||||
|
|
|
|||
|
|
@ -438,13 +438,123 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
},
|
||||
JobKind::Enrich { media_ids } => {
|
||||
// Enrichment job placeholder
|
||||
use pinakes_core::{
|
||||
enrichment::{
|
||||
MetadataEnricher,
|
||||
books::BookEnricher,
|
||||
lastfm::LastFmEnricher,
|
||||
musicbrainz::MusicBrainzEnricher,
|
||||
tmdb::TmdbEnricher,
|
||||
},
|
||||
media_type::MediaCategory,
|
||||
};
|
||||
|
||||
let enrich_cfg = &config.enrichment;
|
||||
let mut enrichers: Vec<Box<dyn MetadataEnricher>> = Vec::new();
|
||||
|
||||
if enrich_cfg.enabled {
|
||||
if enrich_cfg.sources.musicbrainz.enabled {
|
||||
enrichers.push(Box::new(MusicBrainzEnricher::new()));
|
||||
}
|
||||
if let (true, Some(key)) = (
|
||||
enrich_cfg.sources.tmdb.enabled,
|
||||
enrich_cfg.sources.tmdb.api_key.clone(),
|
||||
) {
|
||||
enrichers.push(Box::new(TmdbEnricher::new(key)));
|
||||
}
|
||||
if let (true, Some(key)) = (
|
||||
enrich_cfg.sources.lastfm.enabled,
|
||||
enrich_cfg.sources.lastfm.api_key.clone(),
|
||||
) {
|
||||
enrichers.push(Box::new(LastFmEnricher::new(key)));
|
||||
}
|
||||
// BookEnricher handles documents/epub. No dedicated config
|
||||
// key is required; the Google Books key is optional.
|
||||
enrichers.push(Box::new(BookEnricher::new(None)));
|
||||
}
|
||||
|
||||
let total = media_ids.len();
|
||||
let mut enriched: usize = 0;
|
||||
let mut errors: usize = 0;
|
||||
|
||||
'items: for media_id in media_ids {
|
||||
if cancel.is_cancelled() {
|
||||
break 'items;
|
||||
}
|
||||
let item = match storage.get_media(media_id).await {
|
||||
Ok(i) => i,
|
||||
Err(e) => {
|
||||
tracing::warn!(
|
||||
%media_id,
|
||||
error = %e,
|
||||
"enrich: failed to fetch media item"
|
||||
);
|
||||
errors += 1;
|
||||
continue;
|
||||
},
|
||||
};
|
||||
|
||||
// Select enrichers appropriate for this media category.
|
||||
let category = item.media_type.category();
|
||||
for enricher in &enrichers {
|
||||
let source = enricher.source();
|
||||
use pinakes_core::enrichment::EnrichmentSourceType;
|
||||
let applicable = match source {
|
||||
EnrichmentSourceType::MusicBrainz
|
||||
| EnrichmentSourceType::LastFm => {
|
||||
category == MediaCategory::Audio
|
||||
},
|
||||
EnrichmentSourceType::Tmdb => {
|
||||
category == MediaCategory::Video
|
||||
},
|
||||
EnrichmentSourceType::OpenLibrary
|
||||
| EnrichmentSourceType::GoogleBooks => {
|
||||
category == MediaCategory::Document
|
||||
},
|
||||
};
|
||||
if !applicable {
|
||||
continue;
|
||||
}
|
||||
|
||||
match enricher.enrich(&item).await {
|
||||
Ok(Some(meta)) => {
|
||||
if let Err(e) = storage.store_external_metadata(&meta).await
|
||||
{
|
||||
tracing::warn!(
|
||||
%media_id,
|
||||
%source,
|
||||
error = %e,
|
||||
"enrich: failed to store external metadata"
|
||||
);
|
||||
errors += 1;
|
||||
} else {
|
||||
enriched += 1;
|
||||
}
|
||||
},
|
||||
Ok(None) => {},
|
||||
Err(e) => {
|
||||
tracing::warn!(
|
||||
%media_id,
|
||||
%source,
|
||||
error = %e,
|
||||
"enrich: enricher returned error"
|
||||
);
|
||||
errors += 1;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JobQueue::complete(
|
||||
&jobs,
|
||||
job_id,
|
||||
serde_json::json!({"media_ids": media_ids.len(), "status": "not_implemented"}),
|
||||
)
|
||||
.await;
|
||||
&jobs,
|
||||
job_id,
|
||||
serde_json::json!({
|
||||
"total": total,
|
||||
"enriched": enriched,
|
||||
"errors": errors,
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
},
|
||||
JobKind::CleanupAnalytics => {
|
||||
let before = chrono::Utc::now() - chrono::Duration::days(90);
|
||||
|
|
@ -460,6 +570,27 @@ async fn main() -> Result<()> {
|
|||
Err(e) => JobQueue::fail(&jobs, job_id, e.to_string()).await,
|
||||
}
|
||||
},
|
||||
JobKind::TrashPurge => {
|
||||
let retention_days = config.trash.retention_days;
|
||||
let before = chrono::Utc::now()
|
||||
- chrono::Duration::days(retention_days as i64);
|
||||
|
||||
match storage.purge_old_trash(before).await {
|
||||
Ok(count) => {
|
||||
tracing::info!(count, "purged {} items from trash", count);
|
||||
JobQueue::complete(
|
||||
&jobs,
|
||||
job_id,
|
||||
serde_json::json!({"purged": count, "retention_days": retention_days}),
|
||||
)
|
||||
.await;
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::error!(error = %e, "failed to purge trash");
|
||||
JobQueue::fail(&jobs, job_id, e.to_string()).await;
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
drop(cancel);
|
||||
})
|
||||
|
|
|
|||
|
|
@ -836,8 +836,6 @@ pub async fn get_media_count(
|
|||
Ok(Json(MediaCountResponse { count }))
|
||||
}
|
||||
|
||||
// ===== File Management Endpoints =====
|
||||
|
||||
pub async fn rename_media(
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<Uuid>,
|
||||
|
|
@ -978,8 +976,6 @@ pub async fn batch_move_media(
|
|||
}
|
||||
}
|
||||
|
||||
// ===== Trash Endpoints =====
|
||||
|
||||
pub async fn soft_delete_media(
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<Uuid>,
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@ use uuid::Uuid;
|
|||
|
||||
use crate::{error::ApiError, state::AppState};
|
||||
|
||||
// ===== Response DTOs =====
|
||||
|
||||
/// Response for backlinks query
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct BacklinksResponse {
|
||||
|
|
@ -200,8 +198,6 @@ pub struct UnresolvedLinksResponse {
|
|||
pub count: u64,
|
||||
}
|
||||
|
||||
// ===== Handlers =====
|
||||
|
||||
/// Get backlinks (incoming links) to a media item.
|
||||
///
|
||||
/// GET /api/v1/media/{id}/backlinks
|
||||
|
|
|
|||
|
|
@ -93,7 +93,12 @@ pub async fn create_share(
|
|||
let recipient = match req.recipient_type.as_str() {
|
||||
"public_link" => {
|
||||
let token = generate_share_token();
|
||||
let password_hash = req.password.as_ref().map(|p| hash_share_password(p));
|
||||
let password_hash = req
|
||||
.password
|
||||
.as_ref()
|
||||
.map(|p| hash_share_password(p))
|
||||
.transpose()
|
||||
.map_err(ApiError)?;
|
||||
ShareRecipient::PublicLink {
|
||||
token,
|
||||
password_hash,
|
||||
|
|
@ -409,35 +414,37 @@ pub async fn access_shared(
|
|||
.map_err(|e| ApiError::not_found(format!("Share not found: {}", e)))?;
|
||||
|
||||
// Check expiration
|
||||
if let Some(expires_at) = share.expires_at {
|
||||
if Utc::now() > expires_at {
|
||||
return Err(ApiError::not_found("Share has expired"));
|
||||
}
|
||||
if let Some(expires_at) = share.expires_at
|
||||
&& Utc::now() > expires_at
|
||||
{
|
||||
return Err(ApiError::not_found("Share has expired"));
|
||||
}
|
||||
|
||||
// Check password if required
|
||||
if let ShareRecipient::PublicLink { password_hash, .. } = &share.recipient {
|
||||
if let Some(hash) = password_hash {
|
||||
let provided_password = params
|
||||
.password
|
||||
.as_ref()
|
||||
.ok_or_else(|| ApiError::unauthorized("Password required"))?;
|
||||
if let ShareRecipient::PublicLink {
|
||||
password_hash: Some(hash),
|
||||
..
|
||||
} = &share.recipient
|
||||
{
|
||||
let provided_password = params
|
||||
.password
|
||||
.as_ref()
|
||||
.ok_or_else(|| ApiError::unauthorized("Password required"))?;
|
||||
|
||||
if !verify_share_password(provided_password, hash) {
|
||||
// Log failed attempt
|
||||
let activity = ShareActivity {
|
||||
id: Uuid::now_v7(),
|
||||
share_id: share.id,
|
||||
actor_id: None,
|
||||
actor_ip: Some(addr.ip().to_string()),
|
||||
action: ShareActivityAction::PasswordFailed,
|
||||
details: None,
|
||||
timestamp: Utc::now(),
|
||||
};
|
||||
let _ = state.storage.record_share_activity(&activity).await;
|
||||
if !verify_share_password(provided_password, hash) {
|
||||
// Log failed attempt
|
||||
let activity = ShareActivity {
|
||||
id: Uuid::now_v7(),
|
||||
share_id: share.id,
|
||||
actor_id: None,
|
||||
actor_ip: Some(addr.ip().to_string()),
|
||||
action: ShareActivityAction::PasswordFailed,
|
||||
details: None,
|
||||
timestamp: Utc::now(),
|
||||
};
|
||||
let _ = state.storage.record_share_activity(&activity).await;
|
||||
|
||||
return Err(ApiError::unauthorized("Invalid password"));
|
||||
}
|
||||
return Err(ApiError::unauthorized("Invalid password"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -473,8 +480,6 @@ pub async fn access_shared(
|
|||
Ok(Json(item.into()))
|
||||
},
|
||||
_ => {
|
||||
// For collections/tags, return a placeholder
|
||||
// Full implementation would return the collection contents
|
||||
Err(ApiError::bad_request(
|
||||
"Collection/tag sharing not yet fully implemented",
|
||||
))
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@ pub struct ShareLinkQuery {
|
|||
pub password: Option<String>,
|
||||
}
|
||||
|
||||
// ===== Ratings =====
|
||||
|
||||
pub async fn rate_media(
|
||||
State(state): State<AppState>,
|
||||
Extension(username): Extension<String>,
|
||||
|
|
@ -46,8 +44,6 @@ pub async fn get_media_ratings(
|
|||
))
|
||||
}
|
||||
|
||||
// ===== Comments =====
|
||||
|
||||
pub async fn add_comment(
|
||||
State(state): State<AppState>,
|
||||
Extension(username): Extension<String>,
|
||||
|
|
@ -80,8 +76,6 @@ pub async fn get_media_comments(
|
|||
))
|
||||
}
|
||||
|
||||
// ===== Favorites =====
|
||||
|
||||
pub async fn add_favorite(
|
||||
State(state): State<AppState>,
|
||||
Extension(username): Extension<String>,
|
||||
|
|
@ -120,8 +114,6 @@ pub async fn list_favorites(
|
|||
Ok(Json(items.into_iter().map(MediaResponse::from).collect()))
|
||||
}
|
||||
|
||||
// ===== Share Links =====
|
||||
|
||||
pub async fn create_share_link(
|
||||
State(state): State<AppState>,
|
||||
Extension(username): Extension<String>,
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ pub async fn report_changes(
|
|||
if !config.sync.enabled {
|
||||
return Err(ApiError::bad_request("Sync is not enabled"));
|
||||
}
|
||||
let conflict_resolution = config.sync.default_conflict_resolution.clone();
|
||||
let conflict_resolution = config.sync.default_conflict_resolution;
|
||||
drop(config);
|
||||
|
||||
let mut accepted = Vec::new();
|
||||
|
|
@ -514,7 +514,7 @@ pub async fn create_upload(
|
|||
.ok_or_else(|| ApiError::unauthorized("Invalid device token"))?;
|
||||
|
||||
let chunk_size = req.chunk_size.unwrap_or(DEFAULT_CHUNK_SIZE);
|
||||
let chunk_count = (req.expected_size + chunk_size - 1) / chunk_size;
|
||||
let chunk_count = req.expected_size.div_ceil(chunk_size);
|
||||
let now = Utc::now();
|
||||
|
||||
let session = UploadSession {
|
||||
|
|
@ -784,10 +784,10 @@ pub async fn cancel_upload(
|
|||
})?;
|
||||
|
||||
// Clean up temp file if manager is available
|
||||
if let Some(ref manager) = state.chunked_upload_manager {
|
||||
if let Err(e) = manager.cancel(id).await {
|
||||
tracing::warn!(session_id = %id, error = %e, "failed to clean up temp file");
|
||||
}
|
||||
if let Some(ref manager) = state.chunked_upload_manager
|
||||
&& let Err(e) = manager.cancel(id).await
|
||||
{
|
||||
tracing::warn!(session_id = %id, error = %e, "failed to clean up temp file");
|
||||
}
|
||||
|
||||
session.status = UploadStatus::Cancelled;
|
||||
|
|
@ -827,38 +827,37 @@ pub async fn download_file(
|
|||
let file_size = metadata.len();
|
||||
|
||||
// Check for Range header
|
||||
if let Some(range_header) = headers.get(header::RANGE) {
|
||||
if let Ok(range_str) = range_header.to_str() {
|
||||
if let Some(range) = parse_range_header(range_str, file_size) {
|
||||
// Partial content response
|
||||
let (start, end) = range;
|
||||
let length = end - start + 1;
|
||||
if let Some(range_header) = headers.get(header::RANGE)
|
||||
&& let Ok(range_str) = range_header.to_str()
|
||||
&& let Some(range) = parse_range_header(range_str, file_size)
|
||||
{
|
||||
// Partial content response
|
||||
let (start, end) = range;
|
||||
let length = end - start + 1;
|
||||
|
||||
let file = tokio::fs::File::open(&item.path).await.map_err(|e| {
|
||||
ApiError::internal(format!("Failed to reopen file: {}", e))
|
||||
})?;
|
||||
let file = tokio::fs::File::open(&item.path).await.map_err(|e| {
|
||||
ApiError::internal(format!("Failed to reopen file: {}", e))
|
||||
})?;
|
||||
|
||||
let stream = ReaderStream::new(file);
|
||||
let body = Body::from_stream(stream);
|
||||
let stream = ReaderStream::new(file);
|
||||
let body = Body::from_stream(stream);
|
||||
|
||||
return Ok(
|
||||
return Ok(
|
||||
(
|
||||
StatusCode::PARTIAL_CONTENT,
|
||||
[
|
||||
(header::CONTENT_TYPE, item.media_type.mime_type()),
|
||||
(header::CONTENT_LENGTH, length.to_string()),
|
||||
(
|
||||
StatusCode::PARTIAL_CONTENT,
|
||||
[
|
||||
(header::CONTENT_TYPE, item.media_type.mime_type()),
|
||||
(header::CONTENT_LENGTH, length.to_string()),
|
||||
(
|
||||
header::CONTENT_RANGE,
|
||||
format!("bytes {}-{}/{}", start, end, file_size),
|
||||
),
|
||||
(header::ACCEPT_RANGES, "bytes".to_string()),
|
||||
],
|
||||
body,
|
||||
)
|
||||
.into_response(),
|
||||
);
|
||||
}
|
||||
}
|
||||
header::CONTENT_RANGE,
|
||||
format!("bytes {}-{}/{}", start, end, file_size),
|
||||
),
|
||||
(header::ACCEPT_RANGES, "bytes".to_string()),
|
||||
],
|
||||
body,
|
||||
)
|
||||
.into_response(),
|
||||
);
|
||||
}
|
||||
|
||||
// Full content response
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ use pinakes_core::{
|
|||
ThumbnailConfig,
|
||||
TlsConfig,
|
||||
TranscodingConfig,
|
||||
TrashConfig,
|
||||
UiConfig,
|
||||
UserAccount,
|
||||
UserRole,
|
||||
|
|
@ -151,6 +152,7 @@ fn default_config() -> Config {
|
|||
managed_storage: ManagedStorageConfig::default(),
|
||||
sync: SyncConfig::default(),
|
||||
sharing: SharingConfig::default(),
|
||||
trash: TrashConfig::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -298,10 +300,6 @@ async fn response_body(
|
|||
serde_json::from_slice(&body).unwrap_or(serde_json::Value::Null)
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Existing tests (no auth)
|
||||
// ===================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_list_media_empty() {
|
||||
let app = setup_app().await;
|
||||
|
|
@ -515,10 +513,6 @@ async fn test_user_duplicate_username() {
|
|||
assert_eq!(response.status(), StatusCode::CONFLICT);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Authentication tests
|
||||
// ===================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_unauthenticated_request_rejected() {
|
||||
let (app, ..) = setup_app_with_auth().await;
|
||||
|
|
@ -623,10 +617,6 @@ async fn test_logout() {
|
|||
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Authorization / RBAC tests
|
||||
// ===================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_viewer_cannot_access_editor_routes() {
|
||||
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
||||
|
|
@ -713,10 +703,6 @@ async fn test_admin_can_access_all() {
|
|||
assert_eq!(response.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Phase 2 feature tests: Social
|
||||
// ===================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_rating_invalid_stars_zero() {
|
||||
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
||||
|
|
@ -775,10 +761,6 @@ async fn test_favorites_list_empty() {
|
|||
assert!(body.as_array().unwrap().is_empty());
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Phase 2 feature tests: Playlists
|
||||
// ===================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_playlist_crud() {
|
||||
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
||||
|
|
@ -860,10 +842,6 @@ async fn test_playlist_empty_name() {
|
|||
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Phase 2 feature tests: Analytics
|
||||
// ===================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_most_viewed_empty() {
|
||||
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
||||
|
|
@ -896,10 +874,6 @@ async fn test_record_event_and_query() {
|
|||
assert_eq!(body["recorded"], true);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Phase 2 feature tests: Streaming/Transcode
|
||||
// ===================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_transcode_session_not_found() {
|
||||
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
||||
|
|
@ -951,10 +925,6 @@ async fn test_hls_segment_no_session() {
|
|||
);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Phase 2 feature tests: Subtitles
|
||||
// ===================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_subtitles_list() {
|
||||
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
||||
|
|
@ -974,10 +944,6 @@ async fn test_subtitles_list() {
|
|||
);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Health: public access test
|
||||
// ===================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_health_public() {
|
||||
let (app, ..) = setup_app_with_auth().await;
|
||||
|
|
@ -988,10 +954,6 @@ async fn test_health_public() {
|
|||
assert_eq!(response.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Input validation & edge case tests
|
||||
// ===================================================================
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_invalid_uuid_in_path() {
|
||||
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
||||
|
|
@ -1026,7 +988,7 @@ async fn test_share_link_expired() {
|
|||
// (need real media items). Verify the expire check logic works.
|
||||
let app = setup_app().await;
|
||||
|
||||
// First import a dummy file to get a media_id — but we can't without a real
|
||||
// First import a dummy file to get a media_id, but we can't without a real
|
||||
// file. So let's test the public share access endpoint with a nonexistent
|
||||
// token.
|
||||
let response = app
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ use pinakes_core::{
|
|||
ThumbnailConfig,
|
||||
TlsConfig,
|
||||
TranscodingConfig,
|
||||
TrashConfig,
|
||||
UiConfig,
|
||||
WebhookConfig,
|
||||
},
|
||||
|
|
@ -118,6 +119,7 @@ async fn setup_app_with_plugins()
|
|||
managed_storage: ManagedStorageConfig::default(),
|
||||
sync: SyncConfig::default(),
|
||||
sharing: SharingConfig::default(),
|
||||
trash: TrashConfig::default(),
|
||||
};
|
||||
|
||||
let job_queue =
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue