feat: integrate files and portal surfaces

This commit is contained in:
2026-08-07 14:53:46 +02:00
parent 60f50f7906
commit 11f175cbb3
13 changed files with 512 additions and 13 deletions
+116 -1
View File
@@ -24,6 +24,10 @@ from govoplan_core.core.postbox import (
PostboxTargetRef,
PostboxWrappedKeyRef,
)
from govoplan_core.core.files import (
PostboxFileReferenceRequest,
postbox_file_reference_provider,
)
from govoplan_core.db.session import get_session
from govoplan_postbox.backend.manifest import (
ACKNOWLEDGE_SCOPE,
@@ -34,9 +38,11 @@ from govoplan_postbox.backend.manifest import (
SEND_SCOPE,
TEMPLATE_ADMIN_SCOPE,
)
from govoplan_postbox.backend.runtime import get_service
from govoplan_postbox.backend.runtime import get_registry, get_service
from govoplan_postbox.backend.schemas import (
PostboxAccessDecisionResponse,
PostboxAttachmentResolutionItem,
PostboxAttachmentResolutionResponse,
PostboxDeliveryCreateRequest,
PostboxDeliveryResponse,
PostboxDirectoryItem,
@@ -237,6 +243,7 @@ def _template_item(template) -> PostboxTemplateItem:
"address_pattern": revision.address_pattern,
"classification": revision.classification,
"allow_vacant_delivery": revision.allow_vacant_delivery,
"portal_visible": revision.portal_visible,
"encryption_profile": revision.encryption_profile,
"encryption_vault_id": revision.encryption_vault_id,
"history_policy": dict(revision.history_policy or {}),
@@ -486,6 +493,114 @@ def api_get_postbox_message(
return _message_item(message)
@router.get(
"/messages/{message_id}/attachment-resolutions",
response_model=PostboxAttachmentResolutionResponse,
)
def api_resolve_postbox_message_attachments(
message_id: str,
assignment_context_id: str | None = None,
session: Session = Depends(get_session),
principal: ApiPrincipal = Depends(get_api_principal),
) -> PostboxAttachmentResolutionResponse:
_require(principal, READ_SCOPE)
try:
message = get_service().get_message(
session,
tenant_id=principal.tenant_id,
message_id=message_id,
actor=_actor(
principal,
assignment_context_id=assignment_context_id,
),
)
except PostboxError as exc:
raise _http_error(exc) from exc
if message is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Postbox message not found.",
)
provider = postbox_file_reference_provider(get_registry())
file_types = {"file", "file_asset", "files:file", "file_version", "files:file_version"}
requests = tuple(
PostboxFileReferenceRequest(
reference_type=attachment.reference_type,
reference_id=attachment.reference_id,
postbox_id=message.postbox_id,
message_id=message.id,
)
for attachment in message.attachments
if attachment.reference_type.strip().casefold() in file_types
)
resolved = (
provider.resolve_postbox_references(
session,
principal,
tenant_id=principal.tenant_id,
requests=requests,
)
if provider is not None and requests
else ()
)
by_reference = {
(item.reference_type, item.reference_id): item for item in resolved
}
items: list[PostboxAttachmentResolutionItem] = []
for attachment in message.attachments:
resolution = by_reference.get(
(attachment.reference_type, attachment.reference_id)
)
is_file = attachment.reference_type.strip().casefold() in file_types
attachment_payload = asdict(attachment)
attachment_payload.update(
{
"name": (
resolution.filename
if resolution and resolution.filename
else attachment.name
),
"media_type": (
resolution.content_type
if resolution and resolution.content_type
else attachment.media_type
),
"size_bytes": (
resolution.size_bytes
if resolution and resolution.size_bytes is not None
else attachment.size_bytes
),
"digest": (
resolution.sha256
if resolution and resolution.sha256
else attachment.digest
),
}
)
items.append(
PostboxAttachmentResolutionItem(
**attachment_payload,
available=bool(resolution and resolution.available),
reason_code=(
resolution.reason_code
if resolution is not None
else (
"files_provider_unavailable"
if is_file
else "reference_provider_unavailable"
)
),
file_asset_id=resolution.file_asset_id if resolution else None,
file_version_id=resolution.file_version_id if resolution else None,
download_path=resolution.download_path if resolution else None,
provenance=dict(resolution.provenance) if resolution else {},
)
)
session.commit()
return PostboxAttachmentResolutionResponse(attachments=items)
@router.patch(
"/messages/{message_id}/state",
response_model=PostboxMessageItem,