feat: project role-bound postboxes
This commit is contained in:
@@ -5,6 +5,7 @@ from govoplan_core.core.institutional import (
|
||||
CAPABILITY_SERVICE_DEFINITIONS,
|
||||
service_launch_capability,
|
||||
)
|
||||
from govoplan_core.core.postbox import CAPABILITY_POSTBOX_PORTAL
|
||||
from govoplan_core.core.modules import (
|
||||
CapabilityDocumentation,
|
||||
DocumentationLink,
|
||||
@@ -61,11 +62,13 @@ manifest = ModuleManifest(
|
||||
"forms",
|
||||
"forms_runtime",
|
||||
"workflow_engine",
|
||||
"postbox",
|
||||
),
|
||||
optional_capabilities=(
|
||||
CAPABILITY_SERVICE_DEFINITIONS,
|
||||
CAPABILITY_SERVICE_AVAILABILITY,
|
||||
*SERVICE_LAUNCH_CAPABILITIES,
|
||||
CAPABILITY_POSTBOX_PORTAL,
|
||||
),
|
||||
permissions=(
|
||||
PermissionDefinition(
|
||||
@@ -103,6 +106,12 @@ manifest = ModuleManifest(
|
||||
)
|
||||
for capability in SERVICE_LAUNCH_CAPABILITIES
|
||||
),
|
||||
ModuleInterfaceRequirement(
|
||||
name=CAPABILITY_POSTBOX_PORTAL,
|
||||
version_min="0.1.0",
|
||||
version_max_exclusive="0.2.0",
|
||||
optional=True,
|
||||
),
|
||||
),
|
||||
capability_factories={
|
||||
CAPABILITY_PORTAL_SERVICE_DIRECTORY: _service_directory,
|
||||
@@ -162,6 +171,34 @@ manifest = ModuleManifest(
|
||||
),
|
||||
},
|
||||
documentation=(
|
||||
DocumentationTopic(
|
||||
id="portal.function-postboxes",
|
||||
title="Portal-facing function Postboxes",
|
||||
summary="Open explicitly published function Postboxes without moving their access rules into Portal.",
|
||||
body=(
|
||||
"When Postbox is installed, Portal can display Postboxes whose exact definition or published template revision is marked portal-visible. "
|
||||
"Postbox re-evaluates the current function assignment, classification, and read authority for every projection. Portal stores no Postbox ACL, "
|
||||
"does not expose vacant or inaccessible addresses, and links back to the authoritative Postbox surface."
|
||||
),
|
||||
layer="configured",
|
||||
documentation_types=("admin", "user"),
|
||||
audience=("user", "operator", "module_admin"),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Portal-sichtbare Funktionspostfächer",
|
||||
"summary": "Ausdrücklich veröffentlichte Funktionspostfächer öffnen, ohne ihre Zugriffsregeln in das Portal zu verlagern.",
|
||||
"body": (
|
||||
"Ist Postbox installiert, kann das Portal Postfächer anzeigen, deren exakte Definition oder veröffentlichte Vorlagenrevision als portalsichtbar markiert ist. "
|
||||
"Postbox prüft für jede Projektion die aktuelle Funktionszuordnung, Klassifikation und Leseberechtigung erneut. Das Portal speichert keine Postfach-ACL, "
|
||||
"zeigt keine unbesetzten oder nicht zugänglichen Adressen und verweist auf die maßgebliche Postbox-Oberfläche."
|
||||
),
|
||||
}
|
||||
},
|
||||
links=(DocumentationLink(label="Portal", href="/portal", kind="runtime"),),
|
||||
related_modules=("postbox", "idm", "organizations"),
|
||||
metadata={"kind": "guide", "help_contexts": ["portal.postboxes"]},
|
||||
order=20,
|
||||
),
|
||||
DocumentationTopic(
|
||||
id="portal.service-directory",
|
||||
title="Service directory",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
from dataclasses import asdict
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
@@ -10,11 +11,13 @@ from govoplan_core.core.institutional import (
|
||||
InstitutionalContextError,
|
||||
InstitutionalReference,
|
||||
)
|
||||
from govoplan_core.core.postbox import postbox_portal_projection_provider
|
||||
from govoplan_core.db.session import get_session
|
||||
from govoplan_portal.backend.schemas import (
|
||||
PortalServiceLaunchRequest,
|
||||
PortalServiceLaunchResponse,
|
||||
PortalServiceListResponse,
|
||||
PortalPostboxListResponse,
|
||||
)
|
||||
from govoplan_portal.backend.service_directory import (
|
||||
PortalServiceDirectory,
|
||||
@@ -66,6 +69,32 @@ def api_list_portal_services(
|
||||
)
|
||||
|
||||
|
||||
@router.get("/postboxes", response_model=PortalPostboxListResponse)
|
||||
def api_list_portal_postboxes(
|
||||
limit: int = Query(default=100, ge=1, le=500),
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> PortalPostboxListResponse:
|
||||
if not has_scope(principal, READ_SCOPE):
|
||||
raise HTTPException(status_code=403, detail=f"Missing scope: {READ_SCOPE}")
|
||||
provider = postbox_portal_projection_provider(_registry)
|
||||
if provider is None:
|
||||
return PortalPostboxListResponse(
|
||||
provider_available=False,
|
||||
postboxes=[],
|
||||
)
|
||||
entries = provider.list_portal_entries(
|
||||
session,
|
||||
principal,
|
||||
tenant_id=principal.tenant_id,
|
||||
limit=limit,
|
||||
)
|
||||
return PortalPostboxListResponse(
|
||||
provider_available=True,
|
||||
postboxes=[asdict(entry) for entry in entries],
|
||||
)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/services/{service_id}/launch",
|
||||
response_model=PortalServiceLaunchResponse,
|
||||
|
||||
@@ -22,6 +22,22 @@ class PortalServiceListResponse(BaseModel):
|
||||
services: list[PortalServiceEntryResponse]
|
||||
|
||||
|
||||
class PortalPostboxEntryResponse(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
postbox: dict[str, Any]
|
||||
unread_count: int = Field(default=0, ge=0)
|
||||
latest_message_at: datetime | None = None
|
||||
route_path: str
|
||||
|
||||
|
||||
class PortalPostboxListResponse(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
provider_available: bool
|
||||
postboxes: list[PortalPostboxEntryResponse] = Field(default_factory=list)
|
||||
|
||||
|
||||
class PortalServiceLaunchRequest(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
@@ -49,4 +65,6 @@ __all__ = [
|
||||
"PortalServiceLaunchRequest",
|
||||
"PortalServiceLaunchResponse",
|
||||
"PortalServiceListResponse",
|
||||
"PortalPostboxEntryResponse",
|
||||
"PortalPostboxListResponse",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user