feat(share): public folder browsing API + range support + zip

Five new public endpoints under /api/s/{token}/...:
  GET /contents
  GET /contents/{folder_id}
  GET /file/{file_id}
  GET /zip
  GET /zip/{folder_id}

All honour the unlock cookie from /verify, so password-protected
folder shares work end-to-end.

Folder/file IDs are validated against the share subtree via a single
ltree containment query (O(log N) on the existing GiST index).
Out-of-scope IDs return 404.

download_shared_file refactored to a Range/304/206/416-aware
serve_share_file helper, shared with the new /file/{file_id}
endpoint. content_disposition extracted from FileHandler so RFC 5987
formatting is identical across auth and share download paths.
This commit is contained in:
abnvle
2026-05-05 22:41:55 +02:00
parent 8527765bf2
commit d15d7b8f8e
8 changed files with 725 additions and 85 deletions
+12
View File
@@ -19,6 +19,7 @@ use crate::application::services::nextcloud_file_id_service::NextcloudFileIdServ
use crate::application::services::nextcloud_login_flow_service::NextcloudLoginFlowService;
use crate::application::services::recent_service::RecentService;
use crate::application::services::search_service::SearchService;
use crate::application::services::share_browse_service::ShareBrowseService;
use crate::application::services::share_service::ShareService;
use crate::application::services::trash_service::TrashService;
use crate::application::services::{
@@ -602,6 +603,15 @@ impl AppServiceFactory {
let share_service = self.create_share_service(&repos, &pool);
apps.share_service = share_service.clone();
let share_browse_service = share_service.as_ref().map(|s| {
Arc::new(ShareBrowseService::new(
s.clone(),
apps.folder_service.clone(),
apps.file_retrieval_service.clone(),
repos.folder_repository.clone(),
))
});
// 6. Database-dependent services (PgPool always available in blob model)
let favorites_service: Option<Arc<FavoritesService>>;
let recent_service: Option<Arc<RecentService>>;
@@ -739,6 +749,7 @@ impl AppServiceFactory {
migration_state: Arc::new(tokio::sync::RwLock::new(MigrationState::default())),
trash_service,
share_service,
share_browse_service,
favorites_service,
recent_service,
storage_usage_service,
@@ -1047,6 +1058,7 @@ pub struct AppState {
pub migration_state: Arc<tokio::sync::RwLock<MigrationState>>,
pub trash_service: Option<Arc<TrashService>>,
pub share_service: Option<Arc<ShareService>>,
pub share_browse_service: Option<Arc<ShareBrowseService>>,
pub favorites_service: Option<Arc<FavoritesService>>,
pub recent_service: Option<Arc<RecentService>>,
pub storage_usage_service: Option<Arc<StorageUsageService>>,