feat(drive): impl policy photo + music policies
add `include_in_photo_index` and `include_in_music_index` policies
both true for default personal drive
photo is implemented
music is not yet implemented
This commit is contained in:
@@ -416,6 +416,10 @@ pub struct UpdateDrivePoliciesDto {
|
||||
pub forbid_cross_drive_move: Option<bool>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub forbid_owner_role_change: Option<bool>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub include_in_photo_index: Option<bool>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub include_in_music_index: Option<bool>,
|
||||
}
|
||||
|
||||
/// `PATCH /api/drives/{id}/policies` — **OxiCloud-admin only** policy
|
||||
@@ -494,18 +498,22 @@ pub async fn update_drive_policies(
|
||||
serde_json::Value::Bool(v),
|
||||
);
|
||||
}
|
||||
if let Some(v) = dto.include_in_photo_index {
|
||||
partial_obj.insert("include_in_photo_index".into(), serde_json::Value::Bool(v));
|
||||
}
|
||||
if let Some(v) = dto.include_in_music_index {
|
||||
partial_obj.insert("include_in_music_index".into(), serde_json::Value::Bool(v));
|
||||
}
|
||||
// Pass the raw JSON straight through so the JSONB `||` merge in
|
||||
// the repo only touches keys the caller supplied. Round-tripping
|
||||
// via `DrivePolicies` (which has `#[serde(default)]`) would
|
||||
// silently fill every omitted field with `false` — the merge
|
||||
// would then clobber every unmentioned policy on the row.
|
||||
let partial_value = serde_json::Value::Object(partial_obj);
|
||||
let partial: crate::domain::entities::drive::DrivePolicies =
|
||||
match serde_json::from_value(partial_value) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
return AppError::bad_request(format!("invalid policy body: {e}")).into_response();
|
||||
}
|
||||
};
|
||||
|
||||
match state
|
||||
.drive_management_service
|
||||
.update_policies(auth_user.id, drive_id, partial)
|
||||
.update_policies(auth_user.id, drive_id, partial_value)
|
||||
.await
|
||||
{
|
||||
Ok(merged) => (StatusCode::OK, axum::Json(merged)).into_response(),
|
||||
|
||||
@@ -12,6 +12,8 @@ use tracing::{error, info};
|
||||
use crate::application::dtos::file_dto::FileDto;
|
||||
use crate::application::dtos::geo_dto::GeoBounds;
|
||||
use crate::common::di::AppState;
|
||||
use crate::domain::services::authorization::Subject;
|
||||
use crate::interfaces::errors::AppError;
|
||||
use crate::interfaces::middleware::auth::AuthUser;
|
||||
|
||||
/// Query parameters for the photos timeline endpoint.
|
||||
@@ -63,13 +65,29 @@ pub async fn list_photos(
|
||||
headers: HeaderMap,
|
||||
Query(params): Query<PhotosQueryParams>,
|
||||
) -> impl IntoResponse {
|
||||
let user_id = auth_user.id;
|
||||
let caller_id = auth_user.id;
|
||||
let limit = params.limit.unwrap_or(200).clamp(1, 500);
|
||||
|
||||
// Expand the caller into (subject_types, subject_ids) so group-mediated
|
||||
// drive memberships surface in the Photos timeline too. Mirrors what
|
||||
// `drive_handler::list_drives` and `trash_service::list_resources_paged`
|
||||
// already do — one call to the AuthZ engine per request.
|
||||
let (subject_types, subject_ids) = match state
|
||||
.authorization
|
||||
.expand_subject_for_listing(Subject::User(caller_id))
|
||||
.await
|
||||
{
|
||||
Ok(pair) => pair,
|
||||
Err(e) => {
|
||||
error!("list_photos: subject expansion failed: {e}");
|
||||
return AppError::from(e).into_response();
|
||||
}
|
||||
};
|
||||
|
||||
let file_read = &state.repositories.file_read_repository;
|
||||
|
||||
match file_read
|
||||
.list_media_files(user_id, params.before, limit)
|
||||
.list_media_files(&subject_types, &subject_ids, params.before, limit)
|
||||
.await
|
||||
{
|
||||
Ok((files, sort_dates, dims)) => {
|
||||
|
||||
Reference in New Issue
Block a user