feat(group-blobs): read-only operator admin API for shared collections (M4.1+M4.2)

group_blobs_api mirrors the per-app kv_api/files_api for groups: GET
/groups/{id}/kv[/{c}/{k}], /docs[/{c}/{id}], /files[/{c}/{id}] — list +
fetch/download a group's §11.6 shared KV/docs/files. Authz GroupKvRead/
GroupDocsRead/GroupFilesRead (viewer tier, reads-open trust model). Read-only
by design (writes stay script-only). The host hoists the three group repos so
the SDK services and this router share one instance.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-01 21:22:07 +02:00
parent 2bc3fb677b
commit c21e82fb9f
3 changed files with 332 additions and 3 deletions

View File

@@ -161,9 +161,13 @@ pub async fn build_app(
));
// §11.6 shared group collections (KV): a separate store keyed by the owning
// group, resolved from cx.app_id's chain. Reuses the KV value-size cap.
// §11.6 shared-collection repos hoisted so both the SDK services AND the M4
// read-only operator admin API (`group_blobs_api`) share one instance.
let group_kv_repo = Arc::new(PostgresGroupKvRepo::new(pool.clone()));
let group_docs_repo = Arc::new(PostgresGroupDocsRepo::new(pool.clone()));
let group_kv: Arc<dyn picloud_shared::GroupKvService> = Arc::new(
GroupKvServiceImpl::with_max_value_bytes(
Arc::new(PostgresGroupKvRepo::new(pool.clone())),
group_kv_repo.clone(),
Arc::new(PostgresGroupCollectionResolver::new(pool.clone())),
authz.clone(),
picloud_manager_core::kv_service::kv_max_value_bytes_from_env(),
@@ -174,7 +178,7 @@ pub async fn build_app(
// §11.6 shared group collections (docs): group-keyed group_docs store.
let group_docs: Arc<dyn picloud_shared::GroupDocsService> = Arc::new(
GroupDocsServiceImpl::with_max_value_bytes(
Arc::new(PostgresGroupDocsRepo::new(pool.clone())),
group_docs_repo.clone(),
Arc::new(PostgresGroupCollectionResolver::new(pool.clone())),
authz.clone(),
picloud_manager_core::docs_service::docs_max_value_bytes_from_env(),
@@ -223,9 +227,10 @@ pub async fn build_app(
// `<root>/files/groups/<group_id>/...`, resolved from cx.app_id's chain.
// Shares the per-file size cap; fires `shared = true` triggers under the
// writer app (§11.6 shared-collection triggers).
let group_files_repo = Arc::new(FsGroupFilesRepo::new(pool.clone(), files_root.clone()));
let group_files: Arc<dyn picloud_shared::GroupFilesService> = Arc::new(
GroupFilesServiceImpl::new(
Arc::new(FsGroupFilesRepo::new(pool.clone(), files_root.clone())),
group_files_repo.clone(),
Arc::new(PostgresGroupCollectionResolver::new(pool.clone())),
authz.clone(),
files_max_size,
@@ -683,6 +688,16 @@ pub async fn build_app(
apps: apps_repo.clone(),
authz: authz.clone(),
}))
// §11.6 M4: read-only operator inspection of a group's shared collections.
.merge(picloud_manager_core::group_blobs_api::group_blobs_router(
picloud_manager_core::group_blobs_api::GroupBlobsState {
kv: group_kv_repo.clone(),
docs: group_docs_repo.clone(),
files: group_files_repo.clone(),
groups: groups_repo.clone(),
authz: authz.clone(),
},
))
.merge(topics_router(topics_state))
.merge(secrets_router(secrets_state))
.merge(dead_letters_router(dead_letters_state));