@opengeni/api-router 0.30.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-grant-rls.d.ts +3 -0
- package/dist/admission-trace.d.ts +8 -0
- package/dist/api-websocket.d.ts +18 -0
- package/dist/app.d.ts +50 -2
- package/dist/app.js +12 -1
- package/dist/artifact-catalog-cursor.d.ts +27 -0
- package/dist/auth/managed-auth-attempt-context.d.ts +10 -0
- package/dist/auth/managed-auth-rate-limits.d.ts +167 -0
- package/dist/auth/managed-auth-session-adapter.d.ts +6 -0
- package/dist/auth/managed-auth.d.ts +102 -3
- package/dist/auth/managed-email.d.ts +29 -0
- package/dist/auth/managed-sign-in-notifications.d.ts +4 -0
- package/dist/auth/new-signups-gate.d.ts +23 -0
- package/dist/auth/organization-user-setup.d.ts +80 -0
- package/dist/auth/signup-funnel-metrics.d.ts +44 -0
- package/dist/browser-controller-authority.d.ts +1 -61
- package/dist/browser-ephemeral.d.ts +5 -0
- package/dist/browser-state-authority.d.ts +1 -27
- package/dist/chunk-6TZ5LG3O.js +123948 -0
- package/dist/chunk-6TZ5LG3O.js.map +1 -0
- package/dist/chunk-OGQDXVXY.js +18 -0
- package/dist/chunk-OGQDXVXY.js.map +1 -0
- package/dist/claude-subscription-account-conflicts.d.ts +1 -0
- package/dist/claude-subscription-account-usage.d.ts +39 -0
- package/dist/claude-subscription-oauth.d.ts +25 -0
- package/dist/claude-subscription-profile.d.ts +9 -0
- package/dist/claude-subscription-usage.d.ts +44 -0
- package/dist/claude-workspace-connection.d.ts +15 -0
- package/dist/codemode.d.ts +32 -1
- package/dist/company-brain-okf.d.ts +24 -0
- package/dist/connected-machine-computer-access.d.ts +11 -0
- package/dist/connection-ownership.d.ts +54 -0
- package/dist/controller-data-plane.d.ts +32 -0
- package/dist/editable-artifact-office-import.d.ts +7 -4
- package/dist/editable-artifact-source-session.d.ts +3 -0
- package/dist/editable-artifact-websocket.d.ts +10 -18
- package/dist/editable-artifact-workspace-files.d.ts +1 -1
- package/dist/fatal-process-boundary.d.ts +25 -0
- package/dist/github-access.d.ts +31 -2
- package/dist/http/acting-person.d.ts +24 -0
- package/dist/http/api-contract-compat.d.ts +19 -0
- package/dist/http/api-error.d.ts +27 -1
- package/dist/http/cors.d.ts +17 -0
- package/dist/http/deprecation.d.ts +42 -0
- package/dist/http/interaction-control-error.d.ts +13 -0
- package/dist/http/keyed-admission.d.ts +12 -0
- package/dist/http/local-browser-boundary.d.ts +72 -0
- package/dist/http/metrics-listener.d.ts +22 -0
- package/dist/http/path-identifier.d.ts +17 -0
- package/dist/http/registered-route-label.d.ts +16 -0
- package/dist/http/request-body.d.ts +27 -0
- package/dist/http/request-source.d.ts +52 -0
- package/dist/http/sse.d.ts +17 -6
- package/dist/http/unmatched-route.d.ts +18 -0
- package/dist/http/user-content.d.ts +36 -0
- package/dist/http/websocket-upgrade-dispatch.d.ts +18 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.js +1129 -166
- package/dist/index.js.map +1 -1
- package/dist/integration-endpoint-test.d.ts +20 -0
- package/dist/integrations/api-integrations.d.ts +2 -2
- package/dist/integrations/atlassian.d.ts +29 -3
- package/dist/integrations/connect-authority.d.ts +1 -0
- package/dist/integrations/connect-callback-return.d.ts +6 -0
- package/dist/integrations/fiken.d.ts +16 -2
- package/dist/integrations/github-app-connect.d.ts +47 -0
- package/dist/integrations/github-installation-proof.d.ts +3 -0
- package/dist/integrations/github-lens-connect.d.ts +116 -0
- package/dist/integrations/github-skill-source.d.ts +1 -5
- package/dist/integrations/google-drive.d.ts +25 -0
- package/dist/integrations/integration-commit-authority.d.ts +13 -0
- package/dist/integrations/marketplace-plugin.d.ts +2691 -0
- package/dist/integrations/oauth-client.d.ts +178 -18
- package/dist/integrations/oauth-profiles.d.ts +222 -0
- package/dist/integrations/oauth-return-path.d.ts +1 -0
- package/dist/integrations/personal-github-repositories.d.ts +84 -0
- package/dist/integrations/personal-github.d.ts +42 -0
- package/dist/integrations/pr-review-provider.d.ts +22 -0
- package/dist/integrations/provider-oauth.d.ts +19 -1
- package/dist/integrations/slack-app-home.d.ts +24 -0
- package/dist/integrations/slack-bot.d.ts +367 -12
- package/dist/integrations/slack-file-upload-flow.d.ts +62 -0
- package/dist/integrations/slack-install.d.ts +20 -0
- package/dist/integrations/slack-interactions.d.ts +113 -10
- package/dist/integrations/slack-mrkdwn.d.ts +41 -0
- package/dist/integrations/slack-routing.d.ts +120 -0
- package/dist/integrations/slack-session-defaults.d.ts +58 -0
- package/dist/integrations/slack-task-file-upload.d.ts +36 -0
- package/dist/integrations/social-oauth.d.ts +7 -0
- package/dist/interaction-agent-access.d.ts +15 -0
- package/dist/interaction-frame-proxy.d.ts +68 -0
- package/dist/interaction-metrics.d.ts +2 -0
- package/dist/mcp/action-catalog-types.d.ts +9 -0
- package/dist/mcp/action-catalog.gen.d.ts +2 -0
- package/dist/mcp/capability-account-readiness.d.ts +7 -0
- package/dist/mcp/child-read-evidence.d.ts +9 -0
- package/dist/mcp/command-read.d.ts +24 -0
- package/dist/mcp/company-profile-agent-admin.d.ts +44 -0
- package/dist/mcp/contract-input.d.ts +11 -0
- package/dist/mcp/documents.d.ts +7 -5
- package/dist/mcp/files.d.ts +2 -2
- package/dist/mcp/first-party-tool-permissions.d.ts +650 -0
- package/dist/mcp/knowledge-entries.d.ts +5 -0
- package/dist/mcp/orchestration-failure-diagnostic.d.ts +43 -0
- package/dist/mcp/receipts.d.ts +9 -0
- package/dist/mcp/request-abort.d.ts +19 -0
- package/dist/mcp/scheduled-task-input.d.ts +1189 -0
- package/dist/mcp/scheduled-task-view.d.ts +22 -4
- package/dist/mcp/server.d.ts +255 -3
- package/dist/mcp/session-event-view.d.ts +94 -0
- package/dist/mcp/session-view.d.ts +66 -2
- package/dist/mcp/session-wait.d.ts +174 -0
- package/dist/mcp-oauth.d.ts +52 -0
- package/dist/model-catalog.d.ts +11 -31
- package/dist/observability.d.ts +3 -1
- package/dist/organization-mcp.d.ts +52 -0
- package/dist/organization-recovery-notifications.d.ts +41 -0
- package/dist/routes/analytics-consent.d.ts +26 -0
- package/dist/routes/api-integrations.d.ts +25 -0
- package/dist/routes/api-keys.d.ts +17 -0
- package/dist/routes/artifact-catalog.d.ts +6 -0
- package/dist/routes/automations.d.ts +19 -0
- package/dist/routes/billing.d.ts +49 -0
- package/dist/routes/browser-sessions.d.ts +71 -3
- package/dist/routes/claude-subscription-accounts.d.ts +3 -0
- package/dist/routes/claude-subscription-oauth.d.ts +3 -0
- package/dist/routes/client-errors.d.ts +146 -0
- package/dist/routes/codex.d.ts +76 -2
- package/dist/routes/company-brain.d.ts +3 -0
- package/dist/routes/company-profile.d.ts +2 -1
- package/dist/routes/computer-sessions.d.ts +14 -0
- package/dist/routes/connect.d.ts +5 -0
- package/dist/routes/editable-artifacts.d.ts +2 -0
- package/dist/routes/external-identity-links.d.ts +6 -0
- package/dist/routes/feedback.d.ts +3 -0
- package/dist/routes/files.d.ts +4 -0
- package/dist/routes/github-browser-pages.d.ts +12 -0
- package/dist/routes/github.d.ts +2 -0
- package/dist/routes/insights-response-cache.d.ts +10 -0
- package/dist/routes/insights-usage.d.ts +12 -0
- package/dist/routes/insights.d.ts +30 -1
- package/dist/routes/knowledge.d.ts +3 -0
- package/dist/routes/managed-auth-session-sets.d.ts +26 -0
- package/dist/routes/managed-onboarding.d.ts +29 -0
- package/dist/routes/managed-sign-in-methods.d.ts +6 -0
- package/dist/routes/model-connection-access.d.ts +3 -0
- package/dist/routes/organization-integration-policy.d.ts +3 -0
- package/dist/routes/organization-integrations.d.ts +6 -0
- package/dist/routes/organization-mcp-connections.d.ts +3 -0
- package/dist/routes/organization-model-providers.d.ts +3 -0
- package/dist/routes/organization-recovery.d.ts +19 -0
- package/dist/routes/organization-service-accounts.d.ts +3 -0
- package/dist/routes/organization-sessions.d.ts +26 -0
- package/dist/routes/personal-github-git-broker.d.ts +29 -0
- package/dist/routes/personal-github.d.ts +3 -0
- package/dist/routes/pr-review-github.d.ts +3 -0
- package/dist/routes/pr-review.d.ts +3 -0
- package/dist/routes/session-artifact-associations.d.ts +4 -0
- package/dist/routes/session-history-imports.d.ts +9 -0
- package/dist/routes/sessions.d.ts +44 -6
- package/dist/routes/skill-content.d.ts +4 -0
- package/dist/routes/skill-install-authority.d.ts +6 -0
- package/dist/routes/subscription-account-pools.d.ts +34 -0
- package/dist/routes/subscription-pool-access.d.ts +16 -0
- package/dist/routes/supergrok.d.ts +6 -1
- package/dist/routes/usage-allowances.d.ts +12 -0
- package/dist/routes/user-resource-authorities.d.ts +3 -0
- package/dist/routes/workspace-artifacts.d.ts +2 -1
- package/dist/routes/workspace-capture.d.ts +10 -2
- package/dist/routes/workspace-integrations.d.ts +35 -0
- package/dist/routes/workspace-learning.d.ts +3 -0
- package/dist/routes/workspace-model-providers.d.ts +4 -0
- package/dist/routes/workspaces.d.ts +29 -0
- package/dist/sandbox/auth-callout.d.ts +8 -0
- package/dist/sandbox/channel-a.d.ts +19 -1
- package/dist/sandbox/connection-authority.d.ts +3 -0
- package/dist/sandbox/enrollment.d.ts +9 -1
- package/dist/sandbox/machines.d.ts +5 -2
- package/dist/sandbox/metrics-ingestion.d.ts +28 -20
- package/dist/sandbox/rematerialize.d.ts +2 -0
- package/dist/sandbox/session-attach-variable-sets.d.ts +30 -0
- package/dist/sandbox/viewer.d.ts +26 -1
- package/dist/sandbox-file-artifacts.d.ts +50 -0
- package/dist/scheduled-task-deletion.d.ts +13 -0
- package/dist/session-create-observability.d.ts +5 -0
- package/dist/session-event-view-MPS2OZD3.js +292 -0
- package/dist/session-event-view-MPS2OZD3.js.map +1 -0
- package/dist/site-session-origin.d.ts +13 -0
- package/dist/site-uploads.d.ts +41 -0
- package/dist/slack-reaction-files.d.ts +1 -1
- package/dist/temporal-schedule-cleanup.d.ts +1 -0
- package/dist/temporal-schedule-sync.d.ts +26 -0
- package/dist/transcription/providers/xai-subscription.d.ts +1 -0
- package/dist/transcription/service.d.ts +2 -1
- package/dist/work-discovery-observability.d.ts +33 -0
- package/dist/workspace-artifact-content.d.ts +28 -0
- package/dist/workspace-artifact-provenance.d.ts +7 -0
- package/dist/workspace-delete-observability.d.ts +10 -0
- package/dist/workspace-deletion.d.ts +6 -0
- package/dist/workspace-state-projection.d.ts +3 -5
- package/dist/workspace-tool-gateway-observability.d.ts +17 -0
- package/dist/workspace-tool-gateway.d.ts +123 -0
- package/dist/workspace-webhook-dispatch.d.ts +26 -0
- package/package.json +23 -19
- package/src/access-grant-rls.ts +75 -0
- package/src/admission-trace.ts +14 -0
- package/src/api-websocket.ts +23 -0
- package/src/app.ts +1974 -256
- package/src/artifact-catalog-cursor.ts +60 -0
- package/src/auth/managed-auth-attempt-context.ts +70 -0
- package/src/auth/managed-auth-rate-limits.ts +252 -0
- package/src/auth/managed-auth-session-adapter.ts +222 -0
- package/src/auth/managed-auth.ts +536 -60
- package/src/auth/managed-email.ts +174 -0
- package/src/auth/managed-sign-in-notifications.ts +81 -0
- package/src/auth/new-signups-gate.ts +49 -0
- package/src/auth/organization-user-setup.ts +307 -0
- package/src/auth/signup-funnel-metrics.ts +164 -0
- package/src/browser-controller-authority.ts +1 -186
- package/src/browser-ephemeral.ts +39 -0
- package/src/browser-state-authority.ts +1 -236
- package/src/claude-subscription-account-conflicts.ts +12 -0
- package/src/claude-subscription-account-usage.ts +48 -0
- package/src/claude-subscription-oauth.ts +362 -0
- package/src/claude-subscription-profile.ts +73 -0
- package/src/claude-subscription-usage.ts +97 -0
- package/src/claude-workspace-connection.ts +82 -0
- package/src/codemode.ts +194 -30
- package/src/codex-realtime.ts +8 -2
- package/src/company-brain-okf.ts +340 -0
- package/src/connected-machine-computer-access.ts +33 -0
- package/src/connection-ownership.ts +199 -0
- package/src/controller-data-plane.ts +81 -0
- package/src/editable-artifact-native-kernel.ts +26 -15
- package/src/editable-artifact-office-import.ts +77 -9
- package/src/editable-artifact-production.ts +23 -7
- package/src/editable-artifact-source-session.ts +123 -0
- package/src/editable-artifact-websocket.ts +27 -21
- package/src/editable-artifact-workspace-files.ts +31 -6
- package/src/fatal-process-boundary.ts +246 -0
- package/src/gateway-realtime.ts +1 -1
- package/src/github-access.ts +147 -5
- package/src/http/acting-person.ts +71 -0
- package/src/http/api-contract-compat.ts +49 -0
- package/src/http/api-error.ts +115 -1
- package/src/http/auth.ts +48 -0
- package/src/http/cors.ts +67 -0
- package/src/http/deprecation.ts +118 -0
- package/src/http/interaction-control-error.ts +164 -0
- package/src/http/keyed-admission.ts +29 -0
- package/src/http/local-browser-boundary.ts +343 -0
- package/src/http/metrics-listener.ts +57 -0
- package/src/http/path-identifier.ts +54 -0
- package/src/http/registered-route-label.ts +34 -0
- package/src/http/request-body.ts +127 -0
- package/src/http/request-source.ts +178 -0
- package/src/http/sse.ts +486 -132
- package/src/http/unmatched-route.ts +60 -0
- package/src/http/user-content.ts +158 -0
- package/src/http/websocket-upgrade-dispatch.ts +29 -0
- package/src/index.ts +157 -52
- package/src/integration-endpoint-test.ts +291 -0
- package/src/integrations/api-integrations.ts +98 -11
- package/src/integrations/atlassian.ts +474 -136
- package/src/integrations/connect-authority.ts +2 -0
- package/src/integrations/connect-callback-return.ts +86 -0
- package/src/integrations/fiken.ts +354 -64
- package/src/integrations/github-app-connect.ts +435 -0
- package/src/integrations/github-installation-proof.ts +60 -0
- package/src/integrations/github-lens-connect.ts +76 -0
- package/src/integrations/github-skill-source.ts +2 -142
- package/src/integrations/google-drive.ts +820 -222
- package/src/integrations/integration-commit-authority.ts +69 -0
- package/src/integrations/marketplace-plugin.ts +48 -0
- package/src/integrations/oauth-client.ts +1356 -459
- package/src/integrations/oauth-profiles.ts +537 -0
- package/src/integrations/oauth-return-path.ts +18 -0
- package/src/integrations/personal-github-repositories.ts +674 -0
- package/src/integrations/personal-github.ts +825 -0
- package/src/integrations/pr-review-provider.ts +246 -0
- package/src/integrations/provider-oauth.ts +250 -22
- package/src/integrations/slack-app-home.ts +300 -0
- package/src/integrations/slack-bot.ts +963 -239
- package/src/integrations/slack-file-upload-flow.ts +147 -0
- package/src/integrations/slack-install.ts +83 -0
- package/src/integrations/slack-interactions.ts +3170 -474
- package/src/integrations/slack-mrkdwn.ts +193 -0
- package/src/integrations/slack-routing.ts +337 -0
- package/src/integrations/slack-session-defaults.ts +224 -0
- package/src/integrations/slack-task-file-upload.ts +323 -0
- package/src/integrations/social-oauth.ts +213 -15
- package/src/interaction-agent-access.ts +48 -0
- package/src/interaction-frame-proxy.ts +409 -0
- package/src/interaction-metrics.ts +30 -0
- package/src/mcp/action-catalog-types.ts +9 -0
- package/src/mcp/action-catalog.gen.ts +6696 -0
- package/src/mcp/capability-account-readiness.ts +17 -0
- package/src/mcp/child-read-evidence.ts +19 -0
- package/src/mcp/command-read.ts +109 -0
- package/src/mcp/company-profile-agent-admin.ts +198 -0
- package/src/mcp/contract-input.ts +141 -0
- package/src/mcp/documents.ts +65 -377
- package/src/mcp/editable-artifacts.ts +12 -6
- package/src/mcp/files.ts +80 -38
- package/src/mcp/first-party-tool-permissions.ts +305 -0
- package/src/mcp/knowledge-entries.ts +322 -0
- package/src/mcp/orchestration-failure-diagnostic.ts +43 -0
- package/src/mcp/receipts.ts +34 -0
- package/src/mcp/request-abort.ts +44 -0
- package/src/mcp/scheduled-task-input.ts +76 -0
- package/src/mcp/scheduled-task-view.ts +44 -3
- package/src/mcp/server.ts +3030 -1267
- package/src/mcp/session-event-view.ts +383 -0
- package/src/mcp/session-view.ts +84 -3
- package/src/mcp/session-wait.ts +665 -0
- package/src/mcp-oauth.ts +1123 -0
- package/src/model-catalog.ts +91 -333
- package/src/observability.ts +15 -1
- package/src/organization-mcp.ts +397 -0
- package/src/organization-recovery-notifications.ts +103 -0
- package/src/routes/analytics-consent.ts +123 -0
- package/src/routes/api-integrations.ts +364 -74
- package/src/routes/api-keys.ts +336 -10
- package/src/routes/artifact-catalog.ts +279 -0
- package/src/routes/automations.ts +768 -0
- package/src/routes/billing.ts +461 -46
- package/src/routes/browser-sessions.ts +839 -131
- package/src/routes/canonical-human-identities.ts +90 -14
- package/src/routes/capabilities.ts +107 -6
- package/src/routes/channels.ts +20 -3
- package/src/routes/claude-subscription-accounts.ts +334 -0
- package/src/routes/claude-subscription-oauth.ts +142 -0
- package/src/routes/client-errors.ts +362 -0
- package/src/routes/codex.ts +833 -116
- package/src/routes/company-brain.ts +322 -0
- package/src/routes/company-profile.ts +65 -1
- package/src/routes/computer-sessions.ts +557 -106
- package/src/routes/connect.ts +1938 -0
- package/src/routes/connections.ts +1040 -349
- package/src/routes/documents.ts +488 -287
- package/src/routes/editable-artifacts.ts +79 -9
- package/src/routes/enrollments.ts +135 -39
- package/src/routes/environments.ts +154 -58
- package/src/routes/external-identity-links.ts +201 -0
- package/src/routes/feedback.ts +113 -0
- package/src/routes/files.ts +337 -39
- package/src/routes/github-browser-pages.ts +162 -0
- package/src/routes/github.ts +446 -144
- package/src/routes/insights-response-cache.ts +83 -0
- package/src/routes/insights-usage.ts +345 -0
- package/src/routes/insights.ts +153 -19
- package/src/routes/install.ts +133 -38
- package/src/routes/integration-facets.ts +107 -33
- package/src/routes/interaction-resources.ts +30 -3
- package/src/routes/knowledge.ts +488 -0
- package/src/routes/machines.ts +313 -15
- package/src/routes/managed-auth-session-sets.ts +1218 -0
- package/src/routes/managed-onboarding.ts +337 -0
- package/src/routes/managed-sign-in-methods.ts +433 -0
- package/src/routes/memory-slack-publications.ts +3 -2
- package/src/routes/model-connection-access.ts +208 -0
- package/src/routes/organization-integration-policy.ts +116 -0
- package/src/routes/organization-integrations.ts +326 -0
- package/src/routes/organization-mcp-connections.ts +178 -0
- package/src/routes/organization-memberships.ts +1019 -27
- package/src/routes/organization-model-providers.ts +293 -0
- package/src/routes/organization-recovery.ts +325 -0
- package/src/routes/organization-service-accounts.ts +128 -0
- package/src/routes/organization-sessions.ts +350 -0
- package/src/routes/personal-github-git-broker.ts +785 -0
- package/src/routes/personal-github.ts +370 -0
- package/src/routes/plugins.ts +251 -22
- package/src/routes/pr-review-github.ts +881 -0
- package/src/routes/pr-review.ts +622 -0
- package/src/routes/preference-registry.ts +12 -93
- package/src/routes/rigs.ts +167 -50
- package/src/routes/scheduled-tasks.ts +346 -41
- package/src/routes/session-artifact-associations.ts +87 -0
- package/src/routes/session-history-imports.ts +280 -0
- package/src/routes/sessions.ts +2306 -264
- package/src/routes/skill-content.ts +297 -0
- package/src/routes/skill-install-authority.ts +64 -0
- package/src/routes/skills.ts +62 -9
- package/src/routes/social.ts +46 -18
- package/src/routes/subscription-account-pools.ts +436 -0
- package/src/routes/subscription-pool-access.ts +126 -0
- package/src/routes/supergrok.ts +219 -303
- package/src/routes/transcription-recordings.ts +38 -3
- package/src/routes/transcriptions.ts +4 -0
- package/src/routes/usage-allowances.ts +378 -0
- package/src/routes/user-resource-authorities.ts +138 -0
- package/src/routes/video-generation.ts +14 -10
- package/src/routes/workspace-artifacts.ts +275 -69
- package/src/routes/workspace-capture.ts +21 -0
- package/src/routes/workspace-integrations.ts +576 -0
- package/src/routes/workspace-learning.ts +159 -0
- package/src/routes/workspace-model-providers.ts +148 -0
- package/src/routes/workspace-state.ts +38 -25
- package/src/routes/workspaces.ts +1004 -127
- package/src/sandbox/auth-callout.ts +96 -19
- package/src/sandbox/channel-a.ts +323 -24
- package/src/sandbox/connection-authority.ts +3 -0
- package/src/sandbox/enrollment.ts +81 -6
- package/src/sandbox/machines.ts +218 -76
- package/src/sandbox/metrics-ingestion.ts +411 -62
- package/src/sandbox/rematerialize.ts +121 -19
- package/src/sandbox/session-attach-variable-sets.ts +131 -0
- package/src/sandbox/viewer.ts +280 -25
- package/src/sandbox-file-artifacts.ts +413 -0
- package/src/scheduled-task-deletion.ts +138 -0
- package/src/session-create-observability.ts +46 -0
- package/src/session-realtime-context.ts +6 -3
- package/src/site-session-origin.ts +48 -0
- package/src/site-uploads.ts +224 -0
- package/src/slack-reaction-files.ts +16 -5
- package/src/temporal-schedule-cleanup.ts +13 -0
- package/src/temporal-schedule-sync.ts +74 -0
- package/src/transcription/providers/codex-subscription.ts +2 -0
- package/src/transcription/providers/openai.ts +1 -0
- package/src/transcription/providers/xai-subscription.ts +49 -3
- package/src/transcription/segmenter.ts +8 -2
- package/src/transcription/service.ts +43 -6
- package/src/work-discovery-observability.ts +121 -0
- package/src/workspace-artifact-content.ts +144 -0
- package/src/workspace-artifact-provenance.ts +104 -0
- package/src/workspace-delete-observability.ts +75 -0
- package/src/workspace-deletion.ts +73 -0
- package/src/workspace-state-projection.ts +18 -148
- package/src/workspace-tool-gateway-observability.ts +100 -0
- package/src/workspace-tool-gateway.ts +870 -0
- package/src/workspace-webhook-dispatch.ts +190 -0
- package/src/xai-subscription-auth.ts +9 -2
- package/dist/chunk-2PQMSRCB.js +0 -49759
- package/dist/chunk-2PQMSRCB.js.map +0 -1
- package/dist/routes/packs.d.ts +0 -3
- package/src/routes/packs.ts +0 -729
|
@@ -1,53 +1,1045 @@
|
|
|
1
|
-
import { ListManagedOrganizationMembershipsResponse } from "@opengeni/contracts";
|
|
2
|
-
import { getManagedSession, type ApiRouteDeps } from "@opengeni/core";
|
|
3
1
|
import {
|
|
2
|
+
lookupExternalIdentityForRequest,
|
|
3
|
+
cancelExternalWorkspaceMemberGrantForRequest,
|
|
4
|
+
updateExternalWorkspaceMemberForRequest,
|
|
5
|
+
} from "@opengeni/core";
|
|
6
|
+
import {
|
|
7
|
+
type Permission,
|
|
8
|
+
AcceptOrganizationInvitationRequest,
|
|
9
|
+
CreateAdditionalOrganizationRequest,
|
|
10
|
+
CreateAdditionalOrganizationResponse,
|
|
11
|
+
CreateOrganizationRequest,
|
|
12
|
+
CreateOrganizationResponse,
|
|
13
|
+
CreateOrganizationWorkspaceRequest,
|
|
14
|
+
CreateOrganizationInvitationRequest,
|
|
15
|
+
ListManagedOrganizationMembershipsResponse,
|
|
16
|
+
ListOrganizationInvitationsPageQuery,
|
|
17
|
+
ListOrganizationInvitationsPageResponse,
|
|
18
|
+
ListOrganizationAdministrationMembersResponse,
|
|
19
|
+
OrganizationAdministrationOverview,
|
|
20
|
+
OrganizationInvitation,
|
|
21
|
+
OrganizationUserSetupDelivery,
|
|
22
|
+
OrganizationMember,
|
|
23
|
+
OrganizationPrivateSessionSettings,
|
|
24
|
+
OrganizationRetentionPolicy,
|
|
25
|
+
OrganizationSummary,
|
|
26
|
+
OrganizationWorkspaceAccess,
|
|
27
|
+
OrganizationWorkspaceAccessMember,
|
|
28
|
+
PutOrganizationWorkspaceMemberRequest,
|
|
29
|
+
RevokeOrganizationInvitationRequest,
|
|
30
|
+
RetryOrganizationUserSetupDeliveryRequest,
|
|
31
|
+
RevokeOrganizationWorkspaceMemberRequest,
|
|
32
|
+
RevokeOrganizationWorkspaceMemberResponse,
|
|
33
|
+
UpdateOrganizationMemberRequest,
|
|
34
|
+
UpdateOrganizationNameRequest,
|
|
35
|
+
UpdateOrganizationPrivateSessionSettingsRequest,
|
|
36
|
+
UpdateOrganizationRetentionPolicyRequest,
|
|
37
|
+
UpdateOrganizationWorkspaceRequest,
|
|
38
|
+
UpdateWorkspaceSettingsRequest,
|
|
39
|
+
Workspace,
|
|
40
|
+
} from "@opengeni/contracts";
|
|
41
|
+
import {
|
|
42
|
+
getManagedSession,
|
|
43
|
+
accountScopedApiKeyWorkspaceAuthority,
|
|
44
|
+
organizationWorkspaceInScope,
|
|
45
|
+
hasPermission,
|
|
46
|
+
requireAccessContext,
|
|
47
|
+
organizationMembershipHttpStatus,
|
|
48
|
+
requireCanonicalLocalAccountAdministrator,
|
|
49
|
+
updateExternalIdentityMembershipForRequest,
|
|
50
|
+
type ApiRouteDeps,
|
|
51
|
+
} from "@opengeni/core";
|
|
52
|
+
import {
|
|
53
|
+
acceptOrganizationInvitation,
|
|
54
|
+
bindPendingOrganizationInvitationsForVerifiedEmail,
|
|
55
|
+
claimOrganizationUserSetupDelivery,
|
|
56
|
+
createAdditionalManagedOrganization,
|
|
57
|
+
createManagedOrganization,
|
|
58
|
+
createOrganizationWorkspace,
|
|
59
|
+
createOrganizationInvitation,
|
|
4
60
|
ensureManagedAccessForUserWithOrganizationMemberships,
|
|
61
|
+
getOrganizationAdministrationOverview,
|
|
62
|
+
getOrganizationInvitationForAdministration,
|
|
63
|
+
getOrganizationPrivateSessionSettings,
|
|
64
|
+
getSelfOrganizationInvitation,
|
|
65
|
+
getOrganizationRetentionPolicy,
|
|
66
|
+
requireWorkspace,
|
|
67
|
+
listOrganizationAdministrationMembers,
|
|
68
|
+
listOrganizationInvitations,
|
|
69
|
+
listSelfOrganizationInvitations,
|
|
5
70
|
nestedPostgresSqlState,
|
|
71
|
+
prepareOrganizationUserSetupDelivery,
|
|
72
|
+
revokeOrganizationInvitation,
|
|
73
|
+
settleOrganizationUserSetupDelivery,
|
|
74
|
+
putOrganizationWorkspaceMember,
|
|
75
|
+
revokeOrganizationWorkspaceMember,
|
|
76
|
+
updateOrganizationWorkspace,
|
|
77
|
+
updateOrganizationSharedWorkspaceSettings,
|
|
78
|
+
updateOrganizationMember,
|
|
79
|
+
updateOrganizationName,
|
|
80
|
+
updateOrganizationPrivateSessionSettings,
|
|
81
|
+
updateOrganizationRetentionPolicy,
|
|
6
82
|
} from "@opengeni/db";
|
|
7
|
-
import type { Hono } from "hono";
|
|
83
|
+
import type { Context, Hono } from "hono";
|
|
8
84
|
import { HTTPException } from "hono/http-exception";
|
|
85
|
+
import { z } from "zod";
|
|
86
|
+
import { deleteWorkspaceForRequest } from "../workspace-deletion";
|
|
87
|
+
import { agentActingAsPerson, organizationSettingsPermission } from "../http/acting-person";
|
|
88
|
+
|
|
89
|
+
import {
|
|
90
|
+
assertOrganizationUserSetupDeliveryConfigured,
|
|
91
|
+
resolveOrganizationUserSetupDeliveryEmail,
|
|
92
|
+
} from "../auth/organization-user-setup";
|
|
93
|
+
import { readRequestJson } from "../http/request-body";
|
|
94
|
+
|
|
95
|
+
const OrganizationId = z.string().uuid();
|
|
96
|
+
const WorkspaceId = z.string().uuid();
|
|
97
|
+
const MembershipId = z.string().uuid();
|
|
98
|
+
const InvitationId = z.string().uuid();
|
|
99
|
+
|
|
100
|
+
// Organization keys administer settings, not another person's session contents.
|
|
101
|
+
// The database independently fences current key authority, including on replay.
|
|
102
|
+
async function requirePrivateSessionAdministrator(
|
|
103
|
+
context: Context,
|
|
104
|
+
deps: ApiRouteDeps,
|
|
105
|
+
organizationId: string,
|
|
106
|
+
): Promise<{ subjectId: string }> {
|
|
107
|
+
const agent = agentActingAsPerson(
|
|
108
|
+
context,
|
|
109
|
+
organizationId,
|
|
110
|
+
organizationSettingsPermission(context),
|
|
111
|
+
);
|
|
112
|
+
if (agent) return agent;
|
|
113
|
+
if (!context.req.header("authorization")) return requireManagedHuman(context, deps);
|
|
114
|
+
const access = await requireAccessContext(context, deps);
|
|
115
|
+
const key = accountScopedApiKeyWorkspaceAuthority(access);
|
|
116
|
+
if (!key || key.accountId !== organizationId || !key.permissions.includes("workspace:admin")) {
|
|
117
|
+
throw new HTTPException(403, {
|
|
118
|
+
message: "organization administration required",
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
if (
|
|
122
|
+
key.permissionMode === "explicit" &&
|
|
123
|
+
!access.accountGrants.some(
|
|
124
|
+
(grant) => grant.accountId === organizationId && grant.permissions.includes("account:admin"),
|
|
125
|
+
)
|
|
126
|
+
) {
|
|
127
|
+
throw new HTTPException(403, { message: "organization administration requires account:admin" });
|
|
128
|
+
}
|
|
129
|
+
return { subjectId: access.subjectId };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function requireOrganizationKeyWorkspaceDeletion(
|
|
133
|
+
context: Context,
|
|
134
|
+
deps: ApiRouteDeps,
|
|
135
|
+
organizationId: string,
|
|
136
|
+
workspaceId: string,
|
|
137
|
+
): Promise<void> {
|
|
138
|
+
const access = await requireAccessContext(context, deps);
|
|
139
|
+
const key = accountScopedApiKeyWorkspaceAuthority(access);
|
|
140
|
+
if (
|
|
141
|
+
!key ||
|
|
142
|
+
key.accountId !== organizationId ||
|
|
143
|
+
!hasPermission(key.permissions, "workspace:admin")
|
|
144
|
+
) {
|
|
145
|
+
throw new HTTPException(403, {
|
|
146
|
+
message:
|
|
147
|
+
"deleting an organization workspace requires an organization owner session or an organization API key with workspace:admin",
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
const workspace = await requireWorkspace(deps.db, workspaceId).catch(() => null);
|
|
151
|
+
if (!workspace || workspace.accountId !== organizationId) {
|
|
152
|
+
throw new HTTPException(404, { message: "workspace not found" });
|
|
153
|
+
}
|
|
154
|
+
if (workspace.kind !== "shared") {
|
|
155
|
+
throw new HTTPException(403, {
|
|
156
|
+
message: "organization API keys cannot delete a Personal workspace",
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
if (!organizationWorkspaceInScope(key.workspaceScope, workspaceId))
|
|
160
|
+
throw new HTTPException(403, { message: "workspace is outside organization key scope" });
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function requireManagedHuman(context: Context, deps: ApiRouteDeps) {
|
|
164
|
+
if (
|
|
165
|
+
deps.settings.productAccessMode !== "managed" ||
|
|
166
|
+
!deps.managedAuth ||
|
|
167
|
+
!context.req.header("cookie") ||
|
|
168
|
+
context.req.header("authorization")
|
|
169
|
+
) {
|
|
170
|
+
throw new HTTPException(401, { message: "managed human session required" });
|
|
171
|
+
}
|
|
172
|
+
const session = await getManagedSession(context, deps.managedAuth, {
|
|
173
|
+
db: deps.db,
|
|
174
|
+
sessionAdapter: deps.managedAuthSessionAdapter,
|
|
175
|
+
sessionSetMode: deps.settings.managedAuthSessionSetMode,
|
|
176
|
+
});
|
|
177
|
+
if (!session?.user) {
|
|
178
|
+
throw new HTTPException(401, { message: "managed human session required" });
|
|
179
|
+
}
|
|
180
|
+
return { session, subjectId: `user:${session.user.id}` };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* A person acting in this organization: in their browser, or through an agent
|
|
185
|
+
* acting as them whose access setting includes `permission`. The database
|
|
186
|
+
* still checks the person's own role on every call.
|
|
187
|
+
*/
|
|
188
|
+
async function requireOrganizationPerson(
|
|
189
|
+
context: Context,
|
|
190
|
+
deps: ApiRouteDeps,
|
|
191
|
+
permission: Permission,
|
|
192
|
+
): Promise<{ subjectId: string }> {
|
|
193
|
+
const agent = agentActingAsPerson(context, context.req.param("organizationId") ?? "", permission);
|
|
194
|
+
if (agent) return agent;
|
|
195
|
+
return await requireManagedHuman(context, deps);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function requireOrganizationAdministrator(
|
|
199
|
+
context: Context,
|
|
200
|
+
deps: ApiRouteDeps,
|
|
201
|
+
organizationId: string,
|
|
202
|
+
): Promise<{ subjectId: string }> {
|
|
203
|
+
const agent = agentActingAsPerson(
|
|
204
|
+
context,
|
|
205
|
+
organizationId,
|
|
206
|
+
organizationSettingsPermission(context),
|
|
207
|
+
);
|
|
208
|
+
if (agent) return agent;
|
|
209
|
+
if (deps.settings.productAccessMode === "managed") {
|
|
210
|
+
return await requireManagedHuman(context, deps);
|
|
211
|
+
}
|
|
212
|
+
if (deps.settings.productAccessMode === "local") {
|
|
213
|
+
const { subjectId } = await requireCanonicalLocalAccountAdministrator(
|
|
214
|
+
context,
|
|
215
|
+
deps,
|
|
216
|
+
organizationId,
|
|
217
|
+
);
|
|
218
|
+
return { subjectId };
|
|
219
|
+
}
|
|
220
|
+
throw new HTTPException(401, {
|
|
221
|
+
message: "organization administrator session required",
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async function parseBody<S extends z.ZodType>(context: Context, schema: S): Promise<z.infer<S>> {
|
|
226
|
+
const parsed = schema.safeParse(await context.req.json().catch(() => null));
|
|
227
|
+
if (!parsed.success) {
|
|
228
|
+
throw new HTTPException(422, {
|
|
229
|
+
message: "invalid organization membership request",
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
return parsed.data;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function parseId(schema: z.ZodString, value: string, label: string): string {
|
|
236
|
+
const parsed = schema.safeParse(value);
|
|
237
|
+
if (!parsed.success) throw new HTTPException(422, { message: `invalid ${label}` });
|
|
238
|
+
return parsed.data;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function rethrowMembershipError(error: unknown, resourceLimitMessage?: string): never {
|
|
242
|
+
const sqlState = nestedPostgresSqlState(error);
|
|
243
|
+
if (sqlState === "54000" && resourceLimitMessage) {
|
|
244
|
+
throw new HTTPException(409, {
|
|
245
|
+
message: resourceLimitMessage,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
const status = organizationMembershipHttpStatus(sqlState);
|
|
249
|
+
if (status !== null) {
|
|
250
|
+
throw new HTTPException(status, {
|
|
251
|
+
message:
|
|
252
|
+
status === 403
|
|
253
|
+
? "organization administration is not authorized"
|
|
254
|
+
: status === 404
|
|
255
|
+
? "organization resource not found"
|
|
256
|
+
: status === 409
|
|
257
|
+
? "organization membership state changed; refresh and retry"
|
|
258
|
+
: "invalid organization membership operation",
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
throw error;
|
|
262
|
+
}
|
|
9
263
|
|
|
10
264
|
export function registerOrganizationMembershipRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
265
|
+
// POST carries an opaque identity reference; this lookup never provisions.
|
|
266
|
+
app.post("/v1/organizations/:organizationId/external-identities/lookup", async (context) => {
|
|
267
|
+
const organizationId = parseId(
|
|
268
|
+
OrganizationId,
|
|
269
|
+
context.req.param("organizationId"),
|
|
270
|
+
"organization id",
|
|
271
|
+
);
|
|
272
|
+
return context.json(
|
|
273
|
+
await lookupExternalIdentityForRequest(
|
|
274
|
+
context,
|
|
275
|
+
deps,
|
|
276
|
+
organizationId,
|
|
277
|
+
await context.req.json().catch(() => null),
|
|
278
|
+
),
|
|
279
|
+
);
|
|
280
|
+
});
|
|
281
|
+
app.patch("/v1/organizations/:organizationId/external-members/:membershipId", async (context) => {
|
|
282
|
+
const organizationId = parseId(
|
|
283
|
+
OrganizationId,
|
|
284
|
+
context.req.param("organizationId"),
|
|
285
|
+
"organization id",
|
|
286
|
+
);
|
|
287
|
+
const membershipId = parseId(MembershipId, context.req.param("membershipId"), "membership id");
|
|
288
|
+
try {
|
|
289
|
+
return context.json(
|
|
290
|
+
await updateExternalIdentityMembershipForRequest(
|
|
291
|
+
context,
|
|
292
|
+
deps,
|
|
293
|
+
organizationId,
|
|
294
|
+
membershipId,
|
|
295
|
+
await context.req.json().catch(() => null),
|
|
296
|
+
),
|
|
297
|
+
);
|
|
298
|
+
} catch (error) {
|
|
299
|
+
if (error instanceof HTTPException) throw error;
|
|
300
|
+
rethrowMembershipError(error);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
app.post("/v1/organizations", async (context) => {
|
|
304
|
+
const { session, subjectId } = await requireManagedHuman(context, deps);
|
|
305
|
+
const payload = await parseBody(context, CreateOrganizationRequest);
|
|
306
|
+
try {
|
|
307
|
+
return context.json(
|
|
308
|
+
CreateOrganizationResponse.parse(
|
|
309
|
+
await createManagedOrganization(deps.db, {
|
|
310
|
+
subjectId,
|
|
311
|
+
subjectLabel: session.user.email || session.user.name,
|
|
312
|
+
...payload,
|
|
313
|
+
trialCreditsEnabled:
|
|
314
|
+
deps.settings.productAccessMode === "managed" &&
|
|
315
|
+
deps.settings.verifiedSignupTrialCreditsEnabled,
|
|
316
|
+
}),
|
|
317
|
+
),
|
|
318
|
+
201,
|
|
319
|
+
);
|
|
320
|
+
} catch (error) {
|
|
321
|
+
rethrowMembershipError(error);
|
|
21
322
|
}
|
|
323
|
+
});
|
|
22
324
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
325
|
+
app.post("/v1/organizations/additional", async (context) => {
|
|
326
|
+
const { session, subjectId } = await requireManagedHuman(context, deps);
|
|
327
|
+
const payload = await parseBody(context, CreateAdditionalOrganizationRequest);
|
|
328
|
+
try {
|
|
329
|
+
return context.json(
|
|
330
|
+
CreateAdditionalOrganizationResponse.parse(
|
|
331
|
+
await createAdditionalManagedOrganization(deps.db, {
|
|
332
|
+
subjectId,
|
|
333
|
+
subjectLabel: session.user.email || session.user.name,
|
|
334
|
+
...payload,
|
|
335
|
+
}),
|
|
336
|
+
),
|
|
337
|
+
201,
|
|
338
|
+
);
|
|
339
|
+
} catch (error) {
|
|
340
|
+
rethrowMembershipError(error, "additional organization limit reached");
|
|
30
341
|
}
|
|
342
|
+
});
|
|
31
343
|
|
|
32
|
-
|
|
344
|
+
app.get("/v1/organization-memberships", async (context) => {
|
|
345
|
+
const { session } = await requireManagedHuman(context, deps);
|
|
33
346
|
try {
|
|
34
|
-
result = await ensureManagedAccessForUserWithOrganizationMemberships(deps.db, {
|
|
347
|
+
const result = await ensureManagedAccessForUserWithOrganizationMemberships(deps.db, {
|
|
35
348
|
userId: session.user.id,
|
|
36
349
|
email: session.user.email,
|
|
37
350
|
name: session.user.name,
|
|
351
|
+
emailVerified: session.user.emailVerified,
|
|
352
|
+
provisionFallbackOrganization: false,
|
|
38
353
|
});
|
|
354
|
+
return context.json(
|
|
355
|
+
ListManagedOrganizationMembershipsResponse.parse({
|
|
356
|
+
memberships: result.organizationMemberships,
|
|
357
|
+
}),
|
|
358
|
+
);
|
|
39
359
|
} catch (error) {
|
|
360
|
+
// Not dead after 0348: a terminal-only membership is now a bounded EMPTY
|
|
361
|
+
// projection rather than a 42501, but the verified-email binder still
|
|
362
|
+
// raises 42501 when the session claims `emailVerified` and the durable
|
|
363
|
+
// `auth_users` row disagrees (an email change clears it), so this stays
|
|
364
|
+
// the correct deny for a stale-verification session.
|
|
40
365
|
if (nestedPostgresSqlState(error) === "42501") {
|
|
41
366
|
throw new HTTPException(403, {
|
|
42
367
|
message: "organization membership is not active",
|
|
43
368
|
});
|
|
44
369
|
}
|
|
45
|
-
|
|
370
|
+
rethrowMembershipError(error);
|
|
46
371
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
app.get("/v1/organization-invitations", async (context) => {
|
|
375
|
+
const { session, subjectId } = await requireManagedHuman(context, deps);
|
|
376
|
+
const query = ListOrganizationInvitationsPageQuery.safeParse(context.req.query());
|
|
377
|
+
if (!query.success) {
|
|
378
|
+
throw new HTTPException(422, {
|
|
379
|
+
message: "invalid organization invitation page",
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
try {
|
|
383
|
+
if (session.user.emailVerified) {
|
|
384
|
+
await bindPendingOrganizationInvitationsForVerifiedEmail(deps.db, {
|
|
385
|
+
subjectId,
|
|
386
|
+
email: session.user.email,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
return context.json(
|
|
390
|
+
ListOrganizationInvitationsPageResponse.parse(
|
|
391
|
+
await listSelfOrganizationInvitations(deps.db, {
|
|
392
|
+
subjectId,
|
|
393
|
+
...(query.data.cursor === undefined ? {} : { cursor: query.data.cursor }),
|
|
394
|
+
limit: query.data.limit,
|
|
395
|
+
}),
|
|
396
|
+
),
|
|
397
|
+
);
|
|
398
|
+
} catch (error) {
|
|
399
|
+
rethrowMembershipError(error);
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
app.get("/v1/organizations/:organizationId/overview", async (context) => {
|
|
404
|
+
const organizationId = parseId(
|
|
405
|
+
OrganizationId,
|
|
406
|
+
context.req.param("organizationId"),
|
|
407
|
+
"organization id",
|
|
408
|
+
);
|
|
409
|
+
const { subjectId } = await requireOrganizationAdministrator(context, deps, organizationId);
|
|
410
|
+
try {
|
|
411
|
+
return context.json(
|
|
412
|
+
OrganizationAdministrationOverview.parse(
|
|
413
|
+
await getOrganizationAdministrationOverview(deps.db, {
|
|
414
|
+
organizationId,
|
|
415
|
+
actorSubjectId: subjectId,
|
|
416
|
+
}),
|
|
417
|
+
),
|
|
418
|
+
);
|
|
419
|
+
} catch (error) {
|
|
420
|
+
rethrowMembershipError(error);
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
app.patch("/v1/organizations/:organizationId", async (context) => {
|
|
425
|
+
const organizationId = parseId(
|
|
426
|
+
OrganizationId,
|
|
427
|
+
context.req.param("organizationId"),
|
|
428
|
+
"organization id",
|
|
429
|
+
);
|
|
430
|
+
const { subjectId } = await requireOrganizationAdministrator(context, deps, organizationId);
|
|
431
|
+
const payload = await parseBody(context, UpdateOrganizationNameRequest);
|
|
432
|
+
try {
|
|
433
|
+
return context.json(
|
|
434
|
+
OrganizationSummary.parse(
|
|
435
|
+
await updateOrganizationName(deps.db, {
|
|
436
|
+
organizationId,
|
|
437
|
+
actorSubjectId: subjectId,
|
|
438
|
+
...payload,
|
|
439
|
+
}),
|
|
440
|
+
),
|
|
441
|
+
);
|
|
442
|
+
} catch (error) {
|
|
443
|
+
rethrowMembershipError(error);
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
app.patch("/v1/organizations/:organizationId/workspaces/:workspaceId", async (context) => {
|
|
448
|
+
const organizationId = parseId(
|
|
449
|
+
OrganizationId,
|
|
450
|
+
context.req.param("organizationId"),
|
|
451
|
+
"organization id",
|
|
452
|
+
);
|
|
453
|
+
const { subjectId } = await requireOrganizationAdministrator(context, deps, organizationId);
|
|
454
|
+
const workspaceId = parseId(WorkspaceId, context.req.param("workspaceId"), "workspace id");
|
|
455
|
+
const payload = await parseBody(context, UpdateOrganizationWorkspaceRequest);
|
|
456
|
+
try {
|
|
457
|
+
return context.json(
|
|
458
|
+
OrganizationWorkspaceAccess.parse(
|
|
459
|
+
await updateOrganizationWorkspace(deps.db, {
|
|
460
|
+
organizationId,
|
|
461
|
+
workspaceId,
|
|
462
|
+
actorSubjectId: subjectId,
|
|
463
|
+
name: payload.name.trim(),
|
|
464
|
+
expectedUpdatedAt: payload.expectedUpdatedAt,
|
|
465
|
+
operationId: payload.operationId,
|
|
466
|
+
}),
|
|
467
|
+
),
|
|
468
|
+
);
|
|
469
|
+
} catch (error) {
|
|
470
|
+
rethrowMembershipError(error);
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
app.post("/v1/organizations/:organizationId/workspaces", async (context) => {
|
|
475
|
+
const organizationId = parseId(
|
|
476
|
+
OrganizationId,
|
|
477
|
+
context.req.param("organizationId"),
|
|
478
|
+
"organization id",
|
|
51
479
|
);
|
|
480
|
+
const { subjectId } = await requireOrganizationAdministrator(context, deps, organizationId);
|
|
481
|
+
const payload = await parseBody(context, CreateOrganizationWorkspaceRequest);
|
|
482
|
+
try {
|
|
483
|
+
return context.json(
|
|
484
|
+
OrganizationWorkspaceAccess.parse(
|
|
485
|
+
await createOrganizationWorkspace(deps.db, {
|
|
486
|
+
organizationId,
|
|
487
|
+
actorSubjectId: subjectId,
|
|
488
|
+
name: payload.name.trim(),
|
|
489
|
+
operationId: payload.operationId,
|
|
490
|
+
}),
|
|
491
|
+
),
|
|
492
|
+
201,
|
|
493
|
+
);
|
|
494
|
+
} catch (error) {
|
|
495
|
+
rethrowMembershipError(error);
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
app.delete("/v1/organizations/:organizationId/workspaces/:workspaceId", async (context) => {
|
|
500
|
+
const organizationId = parseId(
|
|
501
|
+
OrganizationId,
|
|
502
|
+
context.req.param("organizationId"),
|
|
503
|
+
"organization id",
|
|
504
|
+
);
|
|
505
|
+
if (context.req.header("authorization")) {
|
|
506
|
+
// An integrating backend deletes the organization workspaces it
|
|
507
|
+
// provisions (tenant offboarding, test cleanup) with its organization
|
|
508
|
+
// key. This is exactly the authority `DELETE /v1/workspaces/:id` already
|
|
509
|
+
// grants that key - an organization workspace (never a Personal one) in
|
|
510
|
+
// the key's own organization, with `workspace:admin` - under the same
|
|
511
|
+
// quiescence rules. It confers no organization-administrator identity.
|
|
512
|
+
const workspaceId = parseId(WorkspaceId, context.req.param("workspaceId"), "workspace id");
|
|
513
|
+
await requireOrganizationKeyWorkspaceDeletion(context, deps, organizationId, workspaceId);
|
|
514
|
+
await deleteWorkspaceForRequest(deps, { accountId: organizationId, workspaceId });
|
|
515
|
+
return context.body(null, 204);
|
|
516
|
+
}
|
|
517
|
+
const { subjectId } = await requireOrganizationAdministrator(context, deps, organizationId);
|
|
518
|
+
const workspaceId = parseId(WorkspaceId, context.req.param("workspaceId"), "workspace id");
|
|
519
|
+
await deleteWorkspaceForRequest(deps, {
|
|
520
|
+
accountId: organizationId,
|
|
521
|
+
workspaceId,
|
|
522
|
+
organizationAdministratorSubjectId: subjectId,
|
|
523
|
+
});
|
|
524
|
+
return context.body(null, 204);
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
app.patch(
|
|
528
|
+
"/v1/organizations/:organizationId/workspaces/:workspaceId/settings",
|
|
529
|
+
async (context) => {
|
|
530
|
+
const organizationId = parseId(
|
|
531
|
+
OrganizationId,
|
|
532
|
+
context.req.param("organizationId"),
|
|
533
|
+
"organization id",
|
|
534
|
+
);
|
|
535
|
+
const { subjectId } = await requireOrganizationAdministrator(context, deps, organizationId);
|
|
536
|
+
const workspaceId = parseId(WorkspaceId, context.req.param("workspaceId"), "workspace id");
|
|
537
|
+
const payload = await parseBody(context, UpdateWorkspaceSettingsRequest);
|
|
538
|
+
try {
|
|
539
|
+
return context.json(
|
|
540
|
+
Workspace.parse(
|
|
541
|
+
await updateOrganizationSharedWorkspaceSettings(deps.db, {
|
|
542
|
+
organizationId,
|
|
543
|
+
workspaceId,
|
|
544
|
+
actorSubjectId: subjectId,
|
|
545
|
+
patch: payload,
|
|
546
|
+
}),
|
|
547
|
+
),
|
|
548
|
+
);
|
|
549
|
+
} catch (error) {
|
|
550
|
+
rethrowMembershipError(error);
|
|
551
|
+
}
|
|
552
|
+
},
|
|
553
|
+
);
|
|
554
|
+
|
|
555
|
+
app.put(
|
|
556
|
+
"/v1/organizations/:organizationId/workspaces/:workspaceId/members/:membershipId",
|
|
557
|
+
async (context) => {
|
|
558
|
+
const { subjectId } = await requireOrganizationPerson(context, deps, "members:manage");
|
|
559
|
+
const organizationId = parseId(
|
|
560
|
+
OrganizationId,
|
|
561
|
+
context.req.param("organizationId"),
|
|
562
|
+
"organization id",
|
|
563
|
+
);
|
|
564
|
+
const workspaceId = parseId(WorkspaceId, context.req.param("workspaceId"), "workspace id");
|
|
565
|
+
const membershipId = parseId(
|
|
566
|
+
MembershipId,
|
|
567
|
+
context.req.param("membershipId"),
|
|
568
|
+
"membership id",
|
|
569
|
+
);
|
|
570
|
+
const payload = await parseBody(context, PutOrganizationWorkspaceMemberRequest);
|
|
571
|
+
try {
|
|
572
|
+
return context.json(
|
|
573
|
+
OrganizationWorkspaceAccessMember.parse(
|
|
574
|
+
await putOrganizationWorkspaceMember(deps.db, {
|
|
575
|
+
organizationId,
|
|
576
|
+
workspaceId,
|
|
577
|
+
actorSubjectId: subjectId,
|
|
578
|
+
targetOrganizationMembershipId: membershipId,
|
|
579
|
+
access: payload,
|
|
580
|
+
}),
|
|
581
|
+
),
|
|
582
|
+
);
|
|
583
|
+
} catch (error) {
|
|
584
|
+
rethrowMembershipError(error);
|
|
585
|
+
}
|
|
586
|
+
},
|
|
587
|
+
);
|
|
588
|
+
|
|
589
|
+
// Organization service key: replace an existing external member's
|
|
590
|
+
// permissions in one shared workspace (keyed, idempotent, non-destructive).
|
|
591
|
+
app.patch(
|
|
592
|
+
"/v1/organizations/:organizationId/workspaces/:workspaceId/external-members/:membershipId",
|
|
593
|
+
async (context) =>
|
|
594
|
+
context.json(
|
|
595
|
+
await updateExternalWorkspaceMemberForRequest(
|
|
596
|
+
context,
|
|
597
|
+
deps,
|
|
598
|
+
parseId(OrganizationId, context.req.param("organizationId"), "organization id"),
|
|
599
|
+
parseId(WorkspaceId, context.req.param("workspaceId"), "workspace id"),
|
|
600
|
+
parseId(MembershipId, context.req.param("membershipId"), "membership id"),
|
|
601
|
+
await readRequestJson(context),
|
|
602
|
+
),
|
|
603
|
+
),
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
app.post(
|
|
607
|
+
"/v1/organizations/:organizationId/workspaces/:workspaceId/members/:membershipId/revoke",
|
|
608
|
+
async (context) => {
|
|
609
|
+
const input: unknown = await context.req.json().catch(() => null);
|
|
610
|
+
if (input && typeof input === "object" && "cancelGrantOperationId" in input) {
|
|
611
|
+
return context.json(
|
|
612
|
+
await cancelExternalWorkspaceMemberGrantForRequest(
|
|
613
|
+
context,
|
|
614
|
+
deps,
|
|
615
|
+
parseId(OrganizationId, context.req.param("organizationId"), "organization id"),
|
|
616
|
+
parseId(WorkspaceId, context.req.param("workspaceId"), "workspace id"),
|
|
617
|
+
parseId(MembershipId, context.req.param("membershipId"), "membership id"),
|
|
618
|
+
input,
|
|
619
|
+
),
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
const { subjectId } = await requireOrganizationPerson(context, deps, "members:manage");
|
|
623
|
+
const organizationId = parseId(
|
|
624
|
+
OrganizationId,
|
|
625
|
+
context.req.param("organizationId"),
|
|
626
|
+
"organization id",
|
|
627
|
+
);
|
|
628
|
+
const workspaceId = parseId(WorkspaceId, context.req.param("workspaceId"), "workspace id");
|
|
629
|
+
const membershipId = parseId(
|
|
630
|
+
MembershipId,
|
|
631
|
+
context.req.param("membershipId"),
|
|
632
|
+
"membership id",
|
|
633
|
+
);
|
|
634
|
+
const payload = await parseBody(context, RevokeOrganizationWorkspaceMemberRequest);
|
|
635
|
+
try {
|
|
636
|
+
return context.json(
|
|
637
|
+
RevokeOrganizationWorkspaceMemberResponse.parse(
|
|
638
|
+
await revokeOrganizationWorkspaceMember(deps.db, {
|
|
639
|
+
organizationId,
|
|
640
|
+
workspaceId,
|
|
641
|
+
actorSubjectId: subjectId,
|
|
642
|
+
targetOrganizationMembershipId: membershipId,
|
|
643
|
+
expectedUpdatedAt: payload.expectedUpdatedAt,
|
|
644
|
+
operationId: payload.operationId,
|
|
645
|
+
}),
|
|
646
|
+
),
|
|
647
|
+
);
|
|
648
|
+
} catch (error) {
|
|
649
|
+
rethrowMembershipError(error);
|
|
650
|
+
}
|
|
651
|
+
},
|
|
652
|
+
);
|
|
653
|
+
|
|
654
|
+
app.get("/v1/organizations/:organizationId/private-session-settings", async (context) => {
|
|
655
|
+
const organizationId = parseId(
|
|
656
|
+
OrganizationId,
|
|
657
|
+
context.req.param("organizationId"),
|
|
658
|
+
"organization id",
|
|
659
|
+
);
|
|
660
|
+
const { subjectId } = await requirePrivateSessionAdministrator(context, deps, organizationId);
|
|
661
|
+
try {
|
|
662
|
+
return context.json(
|
|
663
|
+
OrganizationPrivateSessionSettings.parse(
|
|
664
|
+
await getOrganizationPrivateSessionSettings(deps.db, {
|
|
665
|
+
organizationId,
|
|
666
|
+
actorSubjectId: subjectId,
|
|
667
|
+
}),
|
|
668
|
+
),
|
|
669
|
+
);
|
|
670
|
+
} catch (error) {
|
|
671
|
+
rethrowMembershipError(error);
|
|
672
|
+
}
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
app.patch("/v1/organizations/:organizationId/private-session-settings", async (context) => {
|
|
676
|
+
const organizationId = parseId(
|
|
677
|
+
OrganizationId,
|
|
678
|
+
context.req.param("organizationId"),
|
|
679
|
+
"organization id",
|
|
680
|
+
);
|
|
681
|
+
const { subjectId } = await requirePrivateSessionAdministrator(context, deps, organizationId);
|
|
682
|
+
const payload = await parseBody(context, UpdateOrganizationPrivateSessionSettingsRequest);
|
|
683
|
+
try {
|
|
684
|
+
return context.json(
|
|
685
|
+
OrganizationPrivateSessionSettings.parse(
|
|
686
|
+
await updateOrganizationPrivateSessionSettings(deps.db, {
|
|
687
|
+
organizationId,
|
|
688
|
+
actorSubjectId: subjectId,
|
|
689
|
+
...payload,
|
|
690
|
+
}),
|
|
691
|
+
),
|
|
692
|
+
);
|
|
693
|
+
} catch (error) {
|
|
694
|
+
if (nestedPostgresSqlState(error) === "55000") {
|
|
695
|
+
throw new HTTPException(409, {
|
|
696
|
+
message: "private-session readiness is not activated for this organization",
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
rethrowMembershipError(error);
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
app.post("/v1/organizations/:organizationId/invitations", async (context) => {
|
|
704
|
+
const { subjectId } = await requireOrganizationPerson(context, deps, "members:manage");
|
|
705
|
+
const organizationId = parseId(
|
|
706
|
+
OrganizationId,
|
|
707
|
+
context.req.param("organizationId"),
|
|
708
|
+
"organization id",
|
|
709
|
+
);
|
|
710
|
+
const payload = await parseBody(context, CreateOrganizationInvitationRequest);
|
|
711
|
+
// Bearer construction configuration must fail before the invitation
|
|
712
|
+
// commits. Provider outcomes are journaled durably after this boundary.
|
|
713
|
+
try {
|
|
714
|
+
assertOrganizationUserSetupDeliveryConfigured(deps.settings, deps.managedEmailTransport);
|
|
715
|
+
} catch {
|
|
716
|
+
throw new HTTPException(503, {
|
|
717
|
+
message: "invited-user account setup delivery is not configured on this deployment",
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
try {
|
|
721
|
+
const invitation = OrganizationInvitation.parse(
|
|
722
|
+
await createOrganizationInvitation(deps.db, {
|
|
723
|
+
organizationId,
|
|
724
|
+
actorSubjectId: subjectId,
|
|
725
|
+
operationId: payload.operationId,
|
|
726
|
+
targetSubjectId: null,
|
|
727
|
+
targetEmail: payload.email.trim().toLowerCase(),
|
|
728
|
+
...(payload.name === undefined ? {} : { targetName: payload.name }),
|
|
729
|
+
initialWorkspaceIds: payload.initialWorkspaceIds,
|
|
730
|
+
role: payload.role,
|
|
731
|
+
expiresAt: payload.expiresAt,
|
|
732
|
+
}),
|
|
733
|
+
);
|
|
734
|
+
await deliverOrganizationUserSetup(deps, {
|
|
735
|
+
organizationId,
|
|
736
|
+
actorSubjectId: subjectId,
|
|
737
|
+
invitationId: invitation.id,
|
|
738
|
+
invitationOperationId: payload.operationId,
|
|
739
|
+
operationId: payload.operationId,
|
|
740
|
+
});
|
|
741
|
+
return context.json(
|
|
742
|
+
OrganizationInvitation.parse(
|
|
743
|
+
await getOrganizationInvitationForAdministration(deps.db, {
|
|
744
|
+
organizationId,
|
|
745
|
+
actorSubjectId: subjectId,
|
|
746
|
+
invitationId: invitation.id,
|
|
747
|
+
}),
|
|
748
|
+
),
|
|
749
|
+
201,
|
|
750
|
+
);
|
|
751
|
+
} catch (error) {
|
|
752
|
+
if (error instanceof HTTPException) throw error;
|
|
753
|
+
rethrowMembershipError(error);
|
|
754
|
+
}
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
app.get("/v1/organizations/:organizationId/invitations", async (context) => {
|
|
758
|
+
const { subjectId } = await requireOrganizationPerson(context, deps, "account:read");
|
|
759
|
+
const organizationId = parseId(
|
|
760
|
+
OrganizationId,
|
|
761
|
+
context.req.param("organizationId"),
|
|
762
|
+
"organization id",
|
|
763
|
+
);
|
|
764
|
+
const query = ListOrganizationInvitationsPageQuery.safeParse(context.req.query());
|
|
765
|
+
if (!query.success) {
|
|
766
|
+
throw new HTTPException(422, {
|
|
767
|
+
message: "invalid organization invitation page",
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
try {
|
|
771
|
+
return context.json(
|
|
772
|
+
ListOrganizationInvitationsPageResponse.parse(
|
|
773
|
+
await listOrganizationInvitations(deps.db, {
|
|
774
|
+
organizationId,
|
|
775
|
+
actorSubjectId: subjectId,
|
|
776
|
+
...(query.data.cursor === undefined ? {} : { cursor: query.data.cursor }),
|
|
777
|
+
limit: query.data.limit,
|
|
778
|
+
}),
|
|
779
|
+
),
|
|
780
|
+
);
|
|
781
|
+
} catch (error) {
|
|
782
|
+
rethrowMembershipError(error);
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
app.post(
|
|
787
|
+
"/v1/organizations/:organizationId/invitations/:invitationId/delivery/retry",
|
|
788
|
+
async (context) => {
|
|
789
|
+
const { subjectId } = await requireOrganizationPerson(context, deps, "members:manage");
|
|
790
|
+
const organizationId = parseId(
|
|
791
|
+
OrganizationId,
|
|
792
|
+
context.req.param("organizationId"),
|
|
793
|
+
"organization id",
|
|
794
|
+
);
|
|
795
|
+
const invitationId = parseId(
|
|
796
|
+
InvitationId,
|
|
797
|
+
context.req.param("invitationId"),
|
|
798
|
+
"invitation id",
|
|
799
|
+
);
|
|
800
|
+
const payload = await parseBody(context, RetryOrganizationUserSetupDeliveryRequest);
|
|
801
|
+
try {
|
|
802
|
+
assertOrganizationUserSetupDeliveryConfigured(deps.settings, deps.managedEmailTransport);
|
|
803
|
+
} catch {
|
|
804
|
+
throw new HTTPException(503, {
|
|
805
|
+
message: "invited-user account setup delivery is not configured on this deployment",
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
try {
|
|
809
|
+
return context.json(
|
|
810
|
+
OrganizationUserSetupDelivery.parse(
|
|
811
|
+
await deliverOrganizationUserSetup(deps, {
|
|
812
|
+
organizationId,
|
|
813
|
+
actorSubjectId: subjectId,
|
|
814
|
+
invitationId,
|
|
815
|
+
operationId: payload.operationId,
|
|
816
|
+
}),
|
|
817
|
+
),
|
|
818
|
+
);
|
|
819
|
+
} catch (error) {
|
|
820
|
+
rethrowMembershipError(error);
|
|
821
|
+
}
|
|
822
|
+
},
|
|
823
|
+
);
|
|
824
|
+
|
|
825
|
+
app.post("/v1/organization-invitations/:invitationId/accept", async (context) => {
|
|
826
|
+
const { session, subjectId } = await requireManagedHuman(context, deps);
|
|
827
|
+
const invitationId = parseId(InvitationId, context.req.param("invitationId"), "invitation id");
|
|
828
|
+
const payload = await parseBody(context, AcceptOrganizationInvitationRequest);
|
|
829
|
+
try {
|
|
830
|
+
if (session.user.emailVerified) {
|
|
831
|
+
await bindPendingOrganizationInvitationsForVerifiedEmail(deps.db, {
|
|
832
|
+
subjectId,
|
|
833
|
+
email: session.user.email,
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
const invitation = await getSelfOrganizationInvitation(deps.db, {
|
|
837
|
+
subjectId,
|
|
838
|
+
invitationId,
|
|
839
|
+
});
|
|
840
|
+
const result = await acceptOrganizationInvitation(deps.db, {
|
|
841
|
+
organizationId: invitation.organizationId,
|
|
842
|
+
actorSubjectId: subjectId,
|
|
843
|
+
operationId: payload.operationId,
|
|
844
|
+
invitationId,
|
|
845
|
+
expectedRevision: payload.expectedRevision,
|
|
846
|
+
});
|
|
847
|
+
return context.json({
|
|
848
|
+
invitation: OrganizationInvitation.parse(result.invitation),
|
|
849
|
+
membership: OrganizationMember.parse(result.membership),
|
|
850
|
+
});
|
|
851
|
+
} catch (error) {
|
|
852
|
+
if (error instanceof HTTPException) throw error;
|
|
853
|
+
rethrowMembershipError(error);
|
|
854
|
+
}
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
app.post(
|
|
858
|
+
"/v1/organizations/:organizationId/invitations/:invitationId/revoke",
|
|
859
|
+
async (context) => {
|
|
860
|
+
const { subjectId } = await requireOrganizationPerson(context, deps, "members:manage");
|
|
861
|
+
const organizationId = parseId(
|
|
862
|
+
OrganizationId,
|
|
863
|
+
context.req.param("organizationId"),
|
|
864
|
+
"organization id",
|
|
865
|
+
);
|
|
866
|
+
const invitationId = parseId(
|
|
867
|
+
InvitationId,
|
|
868
|
+
context.req.param("invitationId"),
|
|
869
|
+
"invitation id",
|
|
870
|
+
);
|
|
871
|
+
const payload = await parseBody(context, RevokeOrganizationInvitationRequest);
|
|
872
|
+
try {
|
|
873
|
+
return context.json(
|
|
874
|
+
OrganizationInvitation.parse(
|
|
875
|
+
await revokeOrganizationInvitation(deps.db, {
|
|
876
|
+
organizationId,
|
|
877
|
+
actorSubjectId: subjectId,
|
|
878
|
+
operationId: payload.operationId,
|
|
879
|
+
invitationId,
|
|
880
|
+
expectedRevision: payload.expectedRevision,
|
|
881
|
+
}),
|
|
882
|
+
),
|
|
883
|
+
);
|
|
884
|
+
} catch (error) {
|
|
885
|
+
rethrowMembershipError(error);
|
|
886
|
+
}
|
|
887
|
+
},
|
|
888
|
+
);
|
|
889
|
+
|
|
890
|
+
app.get("/v1/organizations/:organizationId/members", async (context) => {
|
|
891
|
+
const organizationId = parseId(
|
|
892
|
+
OrganizationId,
|
|
893
|
+
context.req.param("organizationId"),
|
|
894
|
+
"organization id",
|
|
895
|
+
);
|
|
896
|
+
const { subjectId } = await requireOrganizationAdministrator(context, deps, organizationId);
|
|
897
|
+
try {
|
|
898
|
+
return context.json(
|
|
899
|
+
ListOrganizationAdministrationMembersResponse.parse({
|
|
900
|
+
members: await listOrganizationAdministrationMembers(deps.db, {
|
|
901
|
+
organizationId,
|
|
902
|
+
actorSubjectId: subjectId,
|
|
903
|
+
}),
|
|
904
|
+
}),
|
|
905
|
+
);
|
|
906
|
+
} catch (error) {
|
|
907
|
+
rethrowMembershipError(error);
|
|
908
|
+
}
|
|
909
|
+
});
|
|
910
|
+
|
|
911
|
+
app.patch("/v1/organizations/:organizationId/members/:membershipId", async (context) => {
|
|
912
|
+
const { subjectId } = await requireOrganizationPerson(context, deps, "members:manage");
|
|
913
|
+
const organizationId = parseId(
|
|
914
|
+
OrganizationId,
|
|
915
|
+
context.req.param("organizationId"),
|
|
916
|
+
"organization id",
|
|
917
|
+
);
|
|
918
|
+
const membershipId = parseId(MembershipId, context.req.param("membershipId"), "membership id");
|
|
919
|
+
const payload = await parseBody(context, UpdateOrganizationMemberRequest);
|
|
920
|
+
try {
|
|
921
|
+
return context.json(
|
|
922
|
+
OrganizationMember.parse(
|
|
923
|
+
await updateOrganizationMember(deps.db, {
|
|
924
|
+
organizationId,
|
|
925
|
+
actorSubjectId: subjectId,
|
|
926
|
+
operationId: payload.operationId,
|
|
927
|
+
membershipId,
|
|
928
|
+
transition: payload,
|
|
929
|
+
}),
|
|
930
|
+
),
|
|
931
|
+
);
|
|
932
|
+
} catch (error) {
|
|
933
|
+
rethrowMembershipError(error);
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
app.get("/v1/organizations/:organizationId/retention-policy", async (context) => {
|
|
938
|
+
const organizationId = parseId(
|
|
939
|
+
OrganizationId,
|
|
940
|
+
context.req.param("organizationId"),
|
|
941
|
+
"organization id",
|
|
942
|
+
);
|
|
943
|
+
const { subjectId } = await requireOrganizationAdministrator(context, deps, organizationId);
|
|
944
|
+
try {
|
|
945
|
+
return context.json(
|
|
946
|
+
OrganizationRetentionPolicy.parse(
|
|
947
|
+
await getOrganizationRetentionPolicy(deps.db, {
|
|
948
|
+
organizationId,
|
|
949
|
+
actorSubjectId: subjectId,
|
|
950
|
+
}),
|
|
951
|
+
),
|
|
952
|
+
);
|
|
953
|
+
} catch (error) {
|
|
954
|
+
rethrowMembershipError(error);
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
app.patch("/v1/organizations/:organizationId/retention-policy", async (context) => {
|
|
959
|
+
const organizationId = parseId(
|
|
960
|
+
OrganizationId,
|
|
961
|
+
context.req.param("organizationId"),
|
|
962
|
+
"organization id",
|
|
963
|
+
);
|
|
964
|
+
const { subjectId } = await requireOrganizationAdministrator(context, deps, organizationId);
|
|
965
|
+
const payload = await parseBody(context, UpdateOrganizationRetentionPolicyRequest);
|
|
966
|
+
try {
|
|
967
|
+
return context.json(
|
|
968
|
+
OrganizationRetentionPolicy.parse(
|
|
969
|
+
await updateOrganizationRetentionPolicy(deps.db, {
|
|
970
|
+
organizationId,
|
|
971
|
+
actorSubjectId: subjectId,
|
|
972
|
+
operationId: payload.operationId,
|
|
973
|
+
mode: payload.mode,
|
|
974
|
+
retentionDays: payload.retentionDays,
|
|
975
|
+
expectedVersion: payload.expectedVersion,
|
|
976
|
+
}),
|
|
977
|
+
),
|
|
978
|
+
);
|
|
979
|
+
} catch (error) {
|
|
980
|
+
rethrowMembershipError(error);
|
|
981
|
+
}
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
async function deliverOrganizationUserSetup(
|
|
986
|
+
deps: ApiRouteDeps,
|
|
987
|
+
input: {
|
|
988
|
+
organizationId: string;
|
|
989
|
+
actorSubjectId: string;
|
|
990
|
+
invitationId: string;
|
|
991
|
+
invitationOperationId?: string;
|
|
992
|
+
operationId: string;
|
|
993
|
+
},
|
|
994
|
+
) {
|
|
995
|
+
const claim = await claimOrganizationUserSetupDelivery(deps.db, input);
|
|
996
|
+
if (!claim.claimed) return claim.delivery;
|
|
997
|
+
const prepared = await resolveOrganizationUserSetupDeliveryEmail(deps.settings, {
|
|
998
|
+
invitationId: claim.invitationId,
|
|
999
|
+
deliveryId: claim.delivery.id,
|
|
1000
|
+
senderEmail: deps.managedEmailTransport.sender,
|
|
1001
|
+
recipientEmail: claim.recipientEmail,
|
|
1002
|
+
recipientName: claim.recipientName,
|
|
1003
|
+
organizationName: claim.organizationName,
|
|
1004
|
+
organizationRole: claim.organizationRole,
|
|
1005
|
+
sharedWorkspaceAccess: claim.sharedWorkspaceAccess,
|
|
1006
|
+
providerIdempotencyScope: deps.managedEmailTransport.idempotency.scope,
|
|
1007
|
+
frozenTransport: claim.setupTokenTransport,
|
|
1008
|
+
frozenPayloadDigest: claim.payloadDigest,
|
|
1009
|
+
});
|
|
1010
|
+
await prepareOrganizationUserSetupDelivery(deps.db, {
|
|
1011
|
+
organizationId: input.organizationId,
|
|
1012
|
+
actorSubjectId: input.actorSubjectId,
|
|
1013
|
+
deliveryId: claim.delivery.id,
|
|
1014
|
+
attemptId: claim.attemptId,
|
|
1015
|
+
claimHolderId: claim.claimHolderId,
|
|
1016
|
+
tokenDigest: prepared.tokenDigest,
|
|
1017
|
+
payloadDigest: prepared.payloadDigest,
|
|
1018
|
+
setupTokenTransport: prepared.transport,
|
|
1019
|
+
providerIdempotencyScope: deps.managedEmailTransport.idempotency.scope,
|
|
1020
|
+
providerIdempotencyRetentionSeconds: deps.managedEmailTransport.idempotency.retentionSeconds,
|
|
1021
|
+
});
|
|
1022
|
+
let outcome:
|
|
1023
|
+
| { status: "sent"; providerMessageId: string | null }
|
|
1024
|
+
| { status: "failed" | "outcome_unknown"; errorClass: string };
|
|
1025
|
+
try {
|
|
1026
|
+
outcome = await deps.managedEmailTransport.send({
|
|
1027
|
+
kind: "organization_user_setup",
|
|
1028
|
+
...prepared.message,
|
|
1029
|
+
idempotencyKey: claim.providerKey,
|
|
1030
|
+
});
|
|
1031
|
+
} catch {
|
|
1032
|
+
outcome = { status: "outcome_unknown", errorClass: "transport_threw" };
|
|
1033
|
+
}
|
|
1034
|
+
return await settleOrganizationUserSetupDelivery(deps.db, {
|
|
1035
|
+
organizationId: input.organizationId,
|
|
1036
|
+
actorSubjectId: input.actorSubjectId,
|
|
1037
|
+
deliveryId: claim.delivery.id,
|
|
1038
|
+
attemptId: claim.attemptId,
|
|
1039
|
+
claimHolderId: claim.claimHolderId,
|
|
1040
|
+
outcome: outcome.status,
|
|
1041
|
+
...(outcome.status === "sent"
|
|
1042
|
+
? { providerMessageId: outcome.providerMessageId }
|
|
1043
|
+
: { errorClass: outcome.errorClass }),
|
|
52
1044
|
});
|
|
53
1045
|
}
|