From 8edc95acb3ffb9150f2e06ee43774385860a8bfd Mon Sep 17 00:00:00 2001 From: Bradley Axen Date: Fri, 3 Jul 2026 20:17:30 -0700 Subject: [PATCH] fix(media): use full community host label Replace the community alias label with the full server-resolved tenant hostname in S3 metadata and sidecars. Co-authored-by: Bradley Axen Signed-off-by: Bradley Axen --- crates/buzz-media/src/storage.rs | 33 ++++++++------- crates/buzz-media/src/upload.rs | 41 ++++++++++--------- crates/buzz-media/tests/static_creds_minio.rs | 8 ++-- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/crates/buzz-media/src/storage.rs b/crates/buzz-media/src/storage.rs index 7e0a5c0a6..999f4ae75 100644 --- a/crates/buzz-media/src/storage.rs +++ b/crates/buzz-media/src/storage.rs @@ -22,8 +22,8 @@ pub const BUZZ_UPLOADER_ID_META_KEY: &str = "buzz-uploader-id"; pub const BUZZ_UPLOADER_NAME_META_KEY: &str = "buzz-uploader-name"; /// Bare S3 user-metadata key for the server-resolved community id. pub const BUZZ_COMMUNITY_ID_META_KEY: &str = "buzz-community-id"; -/// Bare S3 user-metadata key for the human-readable community host prefix. -pub const BUZZ_COMMUNITY_ALIAS_META_KEY: &str = "buzz-community-alias"; +/// Bare S3 user-metadata key for the server-resolved community host. +pub const BUZZ_COMMUNITY_HOST_META_KEY: &str = "buzz-community-host"; /// S3-compatible object storage client. pub struct MediaStorage { @@ -407,7 +407,7 @@ mod tests { (BUZZ_UPLOADER_ID_META_KEY, "aabbcc"), (BUZZ_UPLOADER_NAME_META_KEY, "Ada"), (BUZZ_COMMUNITY_ID_META_KEY, "0000-1111"), - (BUZZ_COMMUNITY_ALIAS_META_KEY, "moderation"), + (BUZZ_COMMUNITY_HOST_META_KEY, "moderation.buzz.example"), ]) .unwrap(); assert_eq!( @@ -430,9 +430,9 @@ mod tests { ); assert_eq!( headers - .get(format!("x-amz-meta-{BUZZ_COMMUNITY_ALIAS_META_KEY}")) + .get(format!("x-amz-meta-{BUZZ_COMMUNITY_HOST_META_KEY}")) .unwrap(), - "moderation" + "moderation.buzz.example" ); // Control characters in values are rejected, not silently mangled. @@ -451,8 +451,8 @@ mod tests { "0000-1111".to_string(), ); metadata.insert( - BUZZ_COMMUNITY_ALIAS_META_KEY.to_string(), - "moderation".to_string(), + BUZZ_COMMUNITY_HOST_META_KEY.to_string(), + "moderation.buzz.example".to_string(), ); let result = s3::serde_types::HeadObjectResult { @@ -476,8 +476,8 @@ mod tests { Some(&"0000-1111".to_string()) ); assert_eq!( - head.metadata.get(BUZZ_COMMUNITY_ALIAS_META_KEY), - Some(&"moderation".to_string()) + head.metadata.get(BUZZ_COMMUNITY_HOST_META_KEY), + Some(&"moderation.buzz.example".to_string()) ); } @@ -491,28 +491,28 @@ mod tests { assert_eq!(meta.uploader_id, None); assert_eq!(meta.uploader_name, None); assert_eq!(meta.community_id, None); - assert_eq!(meta.community_alias, None); + assert_eq!(meta.community_host, None); // Absent attribution is omitted from serialized output (not null). let json = serde_json::to_value(&meta).unwrap(); assert!(json.get("uploader_id").is_none()); assert!(json.get("uploader_name").is_none()); assert!(json.get("community_id").is_none()); - assert!(json.get("community_alias").is_none()); + assert!(json.get("community_host").is_none()); // Populated attribution round-trips. let meta = BlobMeta { uploader_id: Some("aa".repeat(32)), uploader_name: Some("Ada".to_string()), community_id: Some("6b8e1c2a-0000-0000-0000-000000000000".to_string()), - community_alias: Some("moderation".to_string()), + community_host: Some("moderation.buzz.example".to_string()), ..meta }; let round: BlobMeta = serde_json::from_str(&serde_json::to_string(&meta).unwrap()).unwrap(); assert_eq!(round.uploader_id, meta.uploader_id); assert_eq!(round.uploader_name, meta.uploader_name); assert_eq!(round.community_id, meta.community_id); - assert_eq!(round.community_alias, meta.community_alias); + assert_eq!(round.community_host, meta.community_host); } } @@ -588,9 +588,8 @@ pub struct BlobMeta { /// out of its keyed location; `None` on pre-attribution sidecars. #[serde(default, skip_serializing_if = "Option::is_none")] pub community_id: Option, - /// Human-readable community alias derived from the server-resolved host's - /// first label (for example `team` from `team.example.com`). Readability - /// hint only; `community_id` remains authoritative. + /// Server-resolved community host (for example `team.example.com`). + /// Readability hint only; `community_id` remains authoritative. #[serde(default, skip_serializing_if = "Option::is_none")] - pub community_alias: Option, + pub community_host: Option, } diff --git a/crates/buzz-media/src/upload.rs b/crates/buzz-media/src/upload.rs index 24bd1f88b..704a07d72 100644 --- a/crates/buzz-media/src/upload.rs +++ b/crates/buzz-media/src/upload.rs @@ -9,7 +9,7 @@ use crate::auth::verify_blossom_upload_auth; use crate::config::MediaConfig; use crate::error::MediaError; use crate::storage::{ - BlobMeta, MediaStorage, BUZZ_COMMUNITY_ALIAS_META_KEY, BUZZ_COMMUNITY_ID_META_KEY, + BlobMeta, MediaStorage, BUZZ_COMMUNITY_HOST_META_KEY, BUZZ_COMMUNITY_ID_META_KEY, BUZZ_UPLOADER_ID_META_KEY, BUZZ_UPLOADER_NAME_META_KEY, }; use crate::thumbnail::generate_image_metadata_sync; @@ -19,13 +19,13 @@ use crate::validation::{ }; /// Readability metadata for upload attribution. The id fields remain -/// authoritative; names/aliases are best-effort labels for moderators. +/// authoritative; names/hosts are best-effort labels for moderators. #[derive(Debug, Clone, Default)] pub struct UploadAttributionLabels { /// Configured display name for the authenticated uploader, if known. pub uploader_name: Option, - /// Human-readable alias derived from the server-resolved tenant host. - pub community_alias: Option, + /// Server-resolved tenant host for the community. + pub community_host: Option, } impl UploadAttributionLabels { @@ -33,7 +33,7 @@ impl UploadAttributionLabels { pub fn from_profile_and_host(uploader_name: Option, tenant_host: &str) -> Self { Self { uploader_name: uploader_name.and_then(sanitize_label), - community_alias: community_alias_from_host(tenant_host), + community_host: community_host_from_host(tenant_host), } } } @@ -50,8 +50,8 @@ fn attribution_meta<'a>( if let Some(uploader_name) = labels.uploader_name.as_deref() { metadata.push((BUZZ_UPLOADER_NAME_META_KEY, uploader_name)); } - if let Some(community_alias) = labels.community_alias.as_deref() { - metadata.push((BUZZ_COMMUNITY_ALIAS_META_KEY, community_alias)); + if let Some(community_host) = labels.community_host.as_deref() { + metadata.push((BUZZ_COMMUNITY_HOST_META_KEY, community_host)); } metadata } @@ -82,10 +82,8 @@ fn sanitize_label(label: String) -> Option { (!out.is_empty()).then_some(out) } -fn community_alias_from_host(host: &str) -> Option { - let authority = host.split(':').next().unwrap_or(host).trim(); - let alias = authority.split('.').next().unwrap_or(authority); - sanitize_label(alias.to_string()) +fn community_host_from_host(host: &str) -> Option { + sanitize_label(host.to_string()) } /// Shared buffered-upload pipeline for the image and generic-file paths. @@ -307,7 +305,7 @@ pub async fn process_file_upload( uploader_id: Some(input.uploader_id), uploader_name: input.labels.uploader_name, community_id: Some(input.community_id), - community_alias: input.labels.community_alias, + community_host: input.labels.community_host, }; storage.put_sidecar(ctx, &input.sha256, &meta).await?; Ok(meta) @@ -514,7 +512,7 @@ pub async fn process_video_upload( uploader_id: Some(uploader_id), uploader_name: labels.uploader_name, community_id: Some(community_id), - community_alias: labels.community_alias, + community_host: labels.community_host, }; storage.put_sidecar(ctx, &sha256_hex, &meta).await?; @@ -552,7 +550,7 @@ async fn generate_and_store_metadata( meta.uploader_id = Some(input.uploader_id); meta.uploader_name = input.labels.uploader_name; meta.community_id = Some(input.community_id); - meta.community_alias = input.labels.community_alias; + meta.community_host = input.labels.community_host; if let Some(ref tb) = thumb_bytes { // The thumbnail is a derived object with its own S3 key, so it carries @@ -568,7 +566,7 @@ async fn generate_and_store_metadata( meta.community_id.as_deref().unwrap_or_default(), &UploadAttributionLabels { uploader_name: meta.uploader_name.clone(), - community_alias: meta.community_alias.clone(), + community_host: meta.community_host.clone(), }, ), ) @@ -638,7 +636,7 @@ mod tests { uploader_id: None, uploader_name: None, community_id: None, - community_alias: None, + community_host: None, }; let desc = build_descriptor( @@ -700,7 +698,7 @@ mod tests { uploader_id: None, uploader_name: None, community_id: None, - community_alias: None, + community_host: None, }; let desc = build_descriptor( @@ -759,18 +757,21 @@ mod tests { } #[test] - fn upload_attribution_labels_are_sanitized_and_host_aliased() { + fn upload_attribution_labels_are_sanitized_and_use_full_host() { let labels = UploadAttributionLabels::from_profile_and_host( Some(" Ada Lovelace\nšŸš€ ".to_string()), "moderation.buzz.example", ); assert_eq!(labels.uploader_name.as_deref(), Some("Ada Lovelace")); - assert_eq!(labels.community_alias.as_deref(), Some("moderation")); + assert_eq!( + labels.community_host.as_deref(), + Some("moderation.buzz.example") + ); let localhost = UploadAttributionLabels::from_profile_and_host(None, "localhost:3000"); assert_eq!(localhost.uploader_name, None); - assert_eq!(localhost.community_alias.as_deref(), Some("localhost")); + assert_eq!(localhost.community_host.as_deref(), Some("localhost:3000")); } #[test] diff --git a/crates/buzz-media/tests/static_creds_minio.rs b/crates/buzz-media/tests/static_creds_minio.rs index 71a9d3a12..a9161f19b 100644 --- a/crates/buzz-media/tests/static_creds_minio.rs +++ b/crates/buzz-media/tests/static_creds_minio.rs @@ -19,7 +19,7 @@ use buzz_media::config::MediaConfig; use buzz_media::storage::{ - MediaStorage, BUZZ_COMMUNITY_ALIAS_META_KEY, BUZZ_COMMUNITY_ID_META_KEY, + MediaStorage, BUZZ_COMMUNITY_HOST_META_KEY, BUZZ_COMMUNITY_ID_META_KEY, BUZZ_UPLOADER_ID_META_KEY, BUZZ_UPLOADER_NAME_META_KEY, }; @@ -60,7 +60,7 @@ async fn static_creds_round_trip_against_minio() { (BUZZ_UPLOADER_ID_META_KEY, "test-uploader"), (BUZZ_UPLOADER_NAME_META_KEY, "Test Uploader"), (BUZZ_COMMUNITY_ID_META_KEY, "test-community"), - (BUZZ_COMMUNITY_ALIAS_META_KEY, "moderation"), + (BUZZ_COMMUNITY_HOST_META_KEY, "moderation.buzz.example"), ], ) .await @@ -87,8 +87,8 @@ async fn static_creds_round_trip_against_minio() { Some(&"test-community".to_string()) ); assert_eq!( - meta.metadata.get(BUZZ_COMMUNITY_ALIAS_META_KEY), - Some(&"moderation".to_string()) + meta.metadata.get(BUZZ_COMMUNITY_HOST_META_KEY), + Some(&"moderation.buzz.example".to_string()) ); // GET round-trips the bytes