@cosmicdrift/kumiko-samples 0.361.0 → 0.363.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/package.json +1 -1
- package/packages/bundled-features/package.json +17 -6
- package/packages/bundled-features/src/admin-shell/changes.json +6 -0
- package/packages/bundled-features/src/admin-shell/feature.ts +8 -1
- package/packages/bundled-features/src/agent-tools/agent-manifest.ts +3 -4
- package/packages/bundled-features/src/agent-tools/filter-ops.ts +1 -3
- package/packages/bundled-features/src/agent-tools/tool-catalog.ts +1 -3
- package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +2 -1
- package/packages/bundled-features/src/auth-email-password/changes.json +14 -0
- package/packages/bundled-features/src/auth-email-password/email-templates.ts +12 -138
- package/packages/bundled-features/src/auth-email-password/feature.ts +30 -9
- package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +4 -5
- package/packages/bundled-features/src/auth-email-password/handlers/request-account-unlock.write.ts +2 -5
- package/packages/bundled-features/src/auth-email-password/handlers/request-email-verification.write.ts +2 -5
- package/packages/bundled-features/src/auth-email-password/handlers/request-password-reset.write.ts +2 -5
- package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +3 -4
- package/packages/bundled-features/src/auth-email-password/handlers/token-request-handler.ts +3 -12
- package/packages/bundled-features/src/auth-email-password/index.ts +2 -17
- package/packages/bundled-features/src/auth-email-password/magic-link-mail.ts +30 -22
- package/packages/bundled-features/src/auth-email-password/mail-notifications.ts +158 -0
- package/packages/bundled-features/src/auth-email-password/signup-activation.ts +2 -3
- package/packages/bundled-features/src/auth-foundation/types.ts +8 -18
- package/packages/bundled-features/src/auth-mfa/feature.ts +8 -10
- package/packages/bundled-features/src/auth-mfa/hmac-token-codec.ts +4 -4
- package/packages/bundled-features/src/auth-mfa/mfa-challenge-token.ts +3 -3
- package/packages/bundled-features/src/auth-mfa/mfa-preauth-setup-token.ts +3 -3
- package/packages/bundled-features/src/auth-mfa/mfa-setup-token.ts +8 -8
- package/packages/bundled-features/src/billing-foundation/changes.json +7 -0
- package/packages/bundled-features/src/billing-foundation/constants.ts +5 -4
- package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts +38 -44
- package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts +3 -3
- package/packages/bundled-features/src/billing-foundation/consumer-protection/mail-notifications.ts +115 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-consent.ts +2 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-mail.ts +30 -30
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-notify.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-pages.ts +1 -4
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/handlers/create-portal-session.write.ts +4 -9
- package/packages/bundled-features/src/billing-foundation/handlers/list-subscriptions.query.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/handlers/process-payment-event.write.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +3 -6
- package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +2 -1
- package/packages/bundled-features/src/cap-overview/web/cap-usage-cell.tsx +6 -4
- package/packages/bundled-features/src/channel-email/changes.json +19 -0
- package/packages/bundled-features/src/channel-email/email-channel.ts +122 -29
- package/packages/bundled-features/src/channel-email/feature.ts +6 -3
- package/packages/bundled-features/src/channel-email/index.ts +7 -1
- package/packages/bundled-features/src/channel-email/pii-guard.ts +13 -0
- package/packages/bundled-features/src/channel-email/smtp-transport.ts +15 -1
- package/packages/bundled-features/src/channel-email/types.ts +23 -0
- package/packages/bundled-features/src/channel-in-app/handlers/mark-read.write.ts +2 -2
- package/packages/bundled-features/src/config/handlers/set.write.ts +1 -5
- package/packages/bundled-features/src/custom-fields/db/queries/user-data-rights.ts +2 -2
- package/packages/bundled-features/src/custom-fields/lib/value-schema.ts +10 -11
- package/packages/bundled-features/src/custom-fields/wire-user-data-rights.ts +7 -6
- package/packages/bundled-features/src/data-retention/changes.json +6 -0
- package/packages/bundled-features/src/data-retention/index.ts +1 -0
- package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +8 -6
- package/packages/bundled-features/src/delivery/attachment-ref.ts +84 -0
- package/packages/bundled-features/src/delivery/changes.json +48 -0
- package/packages/bundled-features/src/delivery/channel-context.ts +129 -4
- package/packages/bundled-features/src/delivery/constants.ts +2 -0
- package/packages/bundled-features/src/delivery/delivery-service.ts +104 -26
- package/packages/bundled-features/src/delivery/feature.ts +1 -1
- package/packages/bundled-features/src/delivery/index.ts +17 -2
- package/packages/bundled-features/src/delivery/jobs.ts +48 -5
- package/packages/bundled-features/src/delivery/notify-factory.ts +14 -9
- package/packages/bundled-features/src/delivery/testing.ts +9 -1
- package/packages/bundled-features/src/delivery/types.ts +28 -18
- package/packages/bundled-features/src/delivery/unsubscribe.ts +5 -2
- package/packages/bundled-features/src/derivatives-sharp/render.ts +1 -22
- package/packages/bundled-features/src/document-ingest-foundation/aggregate-id.ts +10 -0
- package/packages/bundled-features/src/document-ingest-foundation/changes.json +6 -0
- package/packages/bundled-features/src/document-ingest-foundation/events.ts +3 -1
- package/packages/bundled-features/src/document-ingest-foundation/feature.ts +5 -3
- package/packages/bundled-features/src/document-ingest-foundation/index.ts +1 -0
- package/packages/bundled-features/src/document-ingest-foundation/pages.ts +6 -3
- package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +4 -4
- package/packages/bundled-features/src/file-foundation/index.ts +7 -0
- package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +86 -0
- package/packages/bundled-features/src/folders/web/folder-manager.tsx +5 -2
- package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +11 -11
- package/packages/bundled-features/src/form-draft/db/queries/draft-count.ts +2 -2
- package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +2 -2
- package/packages/bundled-features/src/form-draft/release-file-refs.ts +3 -2
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +6 -6
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-accounts.query.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-messages.query.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/scope-visibility.ts +2 -2
- package/packages/bundled-features/src/inbound-mail-foundation/oauth-state.ts +7 -7
- package/packages/bundled-features/src/inbound-mail-foundation/retention-sweep.ts +2 -6
- package/packages/bundled-features/src/inbound-mail-foundation/tenant-destroy-hook.ts +5 -13
- package/packages/bundled-features/src/inbound-mail-foundation/types.ts +4 -6
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +40 -25
- package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +4 -3
- package/packages/bundled-features/src/jobs/handlers/catalog.query.ts +1 -1
- package/packages/bundled-features/src/ledger/recurring.ts +1 -3
- package/packages/bundled-features/src/managed-pages/branding.ts +33 -9
- package/packages/bundled-features/src/managed-pages/changes.json +6 -0
- package/packages/bundled-features/src/managed-pages/feature.ts +1 -0
- package/packages/bundled-features/src/managed-pages/i18n.ts +1 -0
- package/packages/bundled-features/src/managed-pages/screens/branding-screen.ts +8 -0
- package/packages/bundled-features/src/page-render/branding.ts +3 -0
- package/packages/bundled-features/src/page-render/markdown.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/feature.ts +2 -4
- package/packages/bundled-features/src/personal-access-tokens/handlers/list.query.ts +2 -1
- package/packages/bundled-features/src/personal-access-tokens/index.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/revoke-for-user.ts +2 -1
- package/packages/bundled-features/src/renderer-foundation/README.md +11 -10
- package/packages/bundled-features/src/renderer-foundation/api.ts +14 -14
- package/packages/bundled-features/src/renderer-foundation/changes.json +39 -0
- package/packages/bundled-features/src/renderer-foundation/constants.ts +6 -8
- package/packages/bundled-features/src/renderer-foundation/feature.ts +7 -7
- package/packages/bundled-features/src/renderer-foundation/index.ts +13 -2
- package/packages/bundled-features/src/renderer-foundation/types.ts +132 -35
- package/packages/bundled-features/src/renderer-pdf/feature.ts +7 -1
- package/packages/bundled-features/src/sessions/db/queries/cleanup.ts +2 -2
- package/packages/bundled-features/src/sessions/feature.ts +2 -4
- package/packages/bundled-features/src/sessions/handlers/list.query.ts +2 -1
- package/packages/bundled-features/src/shared/attachment-file-name.ts +39 -0
- package/packages/bundled-features/src/shared/index.ts +6 -0
- package/packages/bundled-features/src/shared/is-tenant-db.ts +1 -1
- package/packages/bundled-features/src/shared/mail-template-body.ts +25 -0
- package/packages/bundled-features/src/shared/sharp-input-limits.ts +26 -0
- package/packages/bundled-features/src/shared/signup-handover.ts +4 -4
- package/packages/bundled-features/src/tags/handlers/assign-tag.write.ts +5 -1
- package/packages/bundled-features/src/tags/web/tag-chip.tsx +2 -1
- package/packages/bundled-features/src/template-resolver/README.md +63 -7
- package/packages/bundled-features/src/template-resolver/api.ts +83 -8
- package/packages/bundled-features/src/template-resolver/changes.json +118 -0
- package/packages/bundled-features/src/template-resolver/compile-document-body.ts +214 -0
- package/packages/bundled-features/src/template-resolver/constants.ts +39 -11
- package/packages/bundled-features/src/template-resolver/document-sanitizer.ts +335 -0
- package/packages/bundled-features/src/template-resolver/document-schema.ts +323 -0
- package/packages/bundled-features/src/template-resolver/feature.ts +56 -6
- package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +188 -0
- package/packages/bundled-features/src/template-resolver/handlers/collection-set.write.ts +30 -2
- package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +142 -0
- package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +72 -0
- package/packages/bundled-features/src/template-resolver/handlers/export-bundle.query.ts +83 -0
- package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +3 -3
- package/packages/bundled-features/src/template-resolver/handlers/import-bundle-system.write.ts +42 -0
- package/packages/bundled-features/src/template-resolver/handlers/import-bundle.write.ts +22 -0
- package/packages/bundled-features/src/template-resolver/handlers/layout-source.query.ts +108 -0
- package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +5 -7
- package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +250 -0
- package/packages/bundled-features/src/template-resolver/handlers/preview.query.ts +25 -0
- package/packages/bundled-features/src/template-resolver/handlers/reset-to-system.write.ts +58 -0
- package/packages/bundled-features/src/template-resolver/handlers/resource-link-pdf.write.ts +34 -0
- package/packages/bundled-features/src/template-resolver/handlers/resource-remove.write.ts +50 -0
- package/packages/bundled-features/src/template-resolver/handlers/resource-shared.ts +24 -0
- package/packages/bundled-features/src/template-resolver/handlers/resource-upload.write.ts +135 -0
- package/packages/bundled-features/src/template-resolver/handlers/send-test.write.ts +106 -0
- package/packages/bundled-features/src/template-resolver/handlers/set.write.ts +32 -3
- package/packages/bundled-features/src/template-resolver/handlers/shared.ts +7 -2
- package/packages/bundled-features/src/template-resolver/handlers/template-gates.ts +184 -0
- package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +183 -0
- package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +55 -0
- package/packages/bundled-features/src/template-resolver/handlers/toggle-status.write.ts +57 -10
- package/packages/bundled-features/src/template-resolver/handlers/upsert-system.write.ts +19 -71
- package/packages/bundled-features/src/template-resolver/handlers/upsert-tenant.write.ts +15 -76
- package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +17 -0
- package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +116 -0
- package/packages/bundled-features/src/template-resolver/index.ts +100 -0
- package/packages/bundled-features/src/template-resolver/layout-regions.ts +206 -0
- package/packages/bundled-features/src/template-resolver/linked-resources.ts +102 -0
- package/packages/bundled-features/src/template-resolver/preview-display-html.ts +36 -0
- package/packages/bundled-features/src/template-resolver/print-document.ts +58 -0
- package/packages/bundled-features/src/template-resolver/qualified-names.ts +30 -0
- package/packages/bundled-features/src/template-resolver/record-mapping.ts +26 -0
- package/packages/bundled-features/src/template-resolver/resource-image.ts +47 -0
- package/packages/bundled-features/src/template-resolver/seeding.ts +147 -17
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +416 -0
- package/packages/bundled-features/src/template-resolver/table.ts +14 -0
- package/packages/bundled-features/src/template-resolver/template-bundle.ts +79 -0
- package/packages/bundled-features/src/template-resolver/template-diff.ts +78 -0
- package/packages/bundled-features/src/template-resolver/template-meta.ts +129 -0
- package/packages/bundled-features/src/template-resolver/template-preview.ts +573 -0
- package/packages/bundled-features/src/template-resolver/template-resources.ts +74 -0
- package/packages/bundled-features/src/template-resolver/template-usage.ts +48 -0
- package/packages/bundled-features/src/template-resolver/template-validation.ts +229 -0
- package/packages/bundled-features/src/template-resolver/testing.ts +5 -5
- package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +9 -6
- package/packages/bundled-features/src/template-resolver/web/document-editor-extensions.ts +162 -0
- package/packages/bundled-features/src/template-resolver/web/document-editor.tsx +28 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-editor.tsx +850 -0
- package/packages/bundled-features/src/template-resolver/web/document-toolbar-blocks.ts +61 -0
- package/packages/bundled-features/src/template-resolver/web/editor-handle.ts +26 -0
- package/packages/bundled-features/src/template-resolver/web/i18n.ts +100 -0
- package/packages/bundled-features/src/template-resolver/web/index.ts +19 -0
- package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor-lazy.tsx +36 -0
- package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor.tsx +169 -0
- package/packages/bundled-features/src/template-resolver/web/placeholder-text.ts +37 -0
- package/packages/bundled-features/src/template-resolver/web/use-stable-by-key.ts +14 -0
- package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +3 -2
- package/packages/bundled-features/src/tenant/seeding.ts +41 -7
- package/packages/bundled-features/src/tenant-lifecycle/boot-checks.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +2 -1
- package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +4 -4
- package/packages/bundled-features/src/user/handlers/detail.query.ts +3 -5
- package/packages/bundled-features/src/user/roles-input-schema.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/changes.json +14 -0
- package/packages/bundled-features/src/user-data-rights/db/queries/forget-cleanup.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/email-templates.ts +5 -149
- package/packages/bundled-features/src/user-data-rights/feature.ts +72 -35
- package/packages/bundled-features/src/user-data-rights/handlers/cancel-deletion.write.ts +2 -1
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +5 -1
- package/packages/bundled-features/src/user-data-rights/handlers/list-download-attempts.query.ts +13 -1
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +5 -11
- package/packages/bundled-features/src/user-data-rights/index.ts +1 -0
- package/packages/bundled-features/src/user-data-rights/lib/default-mailers.ts +134 -53
- package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/mail-notifications.ts +157 -0
- package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/schema/download-attempt.ts +6 -12
- package/packages/bundled-features/src/user-data-rights/web/privacy-center-screen.tsx +2 -1
- package/packages/bundled-features/src/user-data-rights-defaults/feature.ts +10 -1
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/download-attempt.userdata-hook.ts +58 -0
- package/packages/bundled-features/src/user-profile/web/profile-screen.tsx +2 -1
- package/packages/bundled-features/src/waitlist/changes.json +7 -0
- package/packages/bundled-features/src/waitlist/feature.ts +17 -0
- package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +4 -5
- package/packages/bundled-features/src/waitlist/mail.ts +69 -18
- package/packages/bundled-features/src/workflow-runner/event-trigger.ts +1 -2
- package/samples/apps/ui-walkthrough/package.json +2 -0
- package/samples/apps/ui-walkthrough/src/app/auth-constants.ts +9 -0
- package/samples/apps/ui-walkthrough/src/app/client.tsx +6 -0
- package/samples/apps/ui-walkthrough/src/app/seed-templates.ts +154 -0
- package/samples/apps/ui-walkthrough/src/app/server.ts +5 -1
- package/samples/apps/ui-walkthrough/src/app/shell.tsx +12 -1
- package/samples/apps/ui-walkthrough/src/app/template-seed-data.ts +354 -0
- package/samples/apps/ui-walkthrough/src/features/billing/feature.ts +68 -0
- package/samples/apps/ui-walkthrough/src/features/billing/i18n.ts +18 -0
- package/samples/apps/ui-walkthrough/src/features/billing/index.ts +2 -0
- package/samples/apps/ui-walkthrough/src/features/billing/web/index.ts +7 -0
- package/samples/apps/ui-walkthrough/src/features/rental/screens.ts +1 -1
- package/samples/apps/ui-walkthrough/src/run-config.ts +37 -1
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-features.ts +0 -2
- package/samples/apps/use-all-bundled/feature-manifest.json +123 -22
- package/samples/apps/use-all-bundled/kumiko/migrations/0044_download-attempt-ip-bidx.rebuild.json +4 -0
- package/samples/apps/use-all-bundled/kumiko/migrations/0045_template-resource-meta.rebuild.json +4 -0
- package/samples/apps/use-all-bundled/kumiko/migrations/0046_pdf-rendered.rebuild.json +4 -0
- package/samples/apps/use-all-bundled/package.json +2 -0
- package/samples/apps/use-all-bundled/playwright.config.ts +38 -3
- package/samples/apps/use-all-bundled/schema/generate.ts +13 -3
- package/samples/apps/use-all-bundled/schema/schema.generated.ts +3 -0
- package/samples/apps/use-all-bundled/scripts/check-coverage.ts +3 -0
- package/samples/apps/use-all-bundled/src/app/auth-constants.ts +3 -0
- package/samples/apps/use-all-bundled/src/app/client.tsx +4 -0
- package/samples/apps/use-all-bundled/src/app/seed-documents.ts +128 -0
- package/samples/apps/use-all-bundled/src/app/seed.ts +4 -0
- package/samples/apps/use-all-bundled/src/run-config.ts +11 -11
- package/samples/recipes/delivery-notifications/README.md +26 -25
- package/samples/recipes/delivery-notifications/package.json +2 -1
- package/samples/recipes/delivery-notifications/src/feature.ts +64 -58
- package/samples/recipes/seed-migration/README.md +1 -1
- package/packages/bundled-features/src/renderer-simple/changes.json +0 -27
- package/packages/bundled-features/src/renderer-simple/feature.ts +0 -66
- package/packages/bundled-features/src/renderer-simple/index.ts +0 -8
- package/packages/bundled-features/src/renderer-simple/mail-blocks.ts +0 -138
- package/packages/bundled-features/src/renderer-simple/resolve-variables.ts +0 -57
- package/packages/bundled-features/src/renderer-simple/simple-renderer.ts +0 -304
package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts
CHANGED
|
@@ -95,7 +95,7 @@ export const connectAccountHandler: WriteHandlerDef = {
|
|
|
95
95
|
authMethod: payload.authMethod,
|
|
96
96
|
ownerUserId,
|
|
97
97
|
displayName: payload.displayName,
|
|
98
|
-
address: encryptedFields
|
|
98
|
+
address: encryptedFields.address,
|
|
99
99
|
// active ab Connect — der Supervisor greift den Account beim
|
|
100
100
|
// nächsten Tick auf und degradiert ihn bei Credential-Fehlern.
|
|
101
101
|
status: InboundMailAccountStatuses.active,
|
package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts
CHANGED
|
@@ -199,11 +199,11 @@ export const ingestMessageHandler: WriteHandlerDef = {
|
|
|
199
199
|
ownerUserId: payload.ownerUserId,
|
|
200
200
|
messageIdHeader: effectiveMessageIdHeader,
|
|
201
201
|
threadKey,
|
|
202
|
-
from: encryptedMessageFields
|
|
203
|
-
to: encryptedMessageFields
|
|
204
|
-
cc: encryptedMessageFields
|
|
205
|
-
subject: encryptedMessageFields
|
|
206
|
-
snippet: encryptedMessageFields
|
|
202
|
+
from: encryptedMessageFields.from,
|
|
203
|
+
to: encryptedMessageFields.to,
|
|
204
|
+
cc: encryptedMessageFields.cc,
|
|
205
|
+
subject: encryptedMessageFields.subject,
|
|
206
|
+
snippet: encryptedMessageFields.snippet,
|
|
207
207
|
receivedAtIso: payload.receivedAtIso,
|
|
208
208
|
bodyRef: payload.bodyRef,
|
|
209
209
|
scope: payload.scope,
|
|
@@ -296,7 +296,7 @@ export const ingestMessageHandler: WriteHandlerDef = {
|
|
|
296
296
|
|
|
297
297
|
const threadEventPayload: MailThreadEventPayload = {
|
|
298
298
|
threadKey,
|
|
299
|
-
subject: encryptedThreadFields
|
|
299
|
+
subject: encryptedThreadFields.subject,
|
|
300
300
|
lastMessageAtIso: newLastAt,
|
|
301
301
|
messageCount,
|
|
302
302
|
};
|
package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-accounts.query.ts
CHANGED
|
@@ -34,7 +34,7 @@ export const listAccountsQuery: QueryHandlerDef = {
|
|
|
34
34
|
if (!piiKms) return { rows };
|
|
35
35
|
const decryptedRows = await Promise.all(
|
|
36
36
|
rows.map((row) =>
|
|
37
|
-
decryptPiiFieldValues(row
|
|
37
|
+
decryptPiiFieldValues(row, MAIL_ACCOUNT_PII_FIELDS, piiKms, {
|
|
38
38
|
requestId: `inbound-mail-foundation:list-accounts:${ctx.user.tenantId}`,
|
|
39
39
|
}),
|
|
40
40
|
),
|
package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-messages.query.ts
CHANGED
|
@@ -49,7 +49,7 @@ export const listMessagesQuery: QueryHandlerDef = {
|
|
|
49
49
|
if (!piiKms) return { rows };
|
|
50
50
|
const decryptedRows = await Promise.all(
|
|
51
51
|
rows.map((row) =>
|
|
52
|
-
decryptPiiFieldValues(row
|
|
52
|
+
decryptPiiFieldValues(row, INBOUND_MESSAGE_PII_FIELDS, piiKms, {
|
|
53
53
|
requestId: `inbound-mail-foundation:list-messages:${ctx.user.tenantId}`,
|
|
54
54
|
}),
|
|
55
55
|
),
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
// für read_mail_accounts und read_inbound_messages (Scope-Vererbung).
|
|
5
5
|
|
|
6
6
|
export function isVisibleToCaller(
|
|
7
|
-
row: unknown
|
|
7
|
+
row: Readonly<Record<string, unknown>>,
|
|
8
8
|
user: { readonly id: string; readonly roles: readonly string[] },
|
|
9
9
|
): boolean {
|
|
10
|
-
const owner =
|
|
10
|
+
const owner = row["ownerUserId"];
|
|
11
11
|
if (owner == null || owner === "") return true;
|
|
12
12
|
if (owner === user.id) return true;
|
|
13
13
|
return user.roles.includes("TenantAdmin") || user.roles.includes("SystemAdmin");
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { createHmac, randomUUID, timingSafeEqual } from "node:crypto";
|
|
14
14
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
15
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
15
16
|
|
|
16
17
|
const STATE_PURPOSE = "inbound-mail-oauth-connect";
|
|
17
18
|
|
|
@@ -91,13 +92,12 @@ export function verifyOAuthState(
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
function isOAuthStatePayload(v: unknown): v is OAuthStatePayload {
|
|
94
|
-
if (
|
|
95
|
-
const o = v as Record<string, unknown>;
|
|
95
|
+
if (!isPlainObject(v)) return false;
|
|
96
96
|
return (
|
|
97
|
-
typeof
|
|
98
|
-
(
|
|
99
|
-
typeof
|
|
100
|
-
typeof
|
|
101
|
-
typeof
|
|
97
|
+
typeof v["tenantId"] === "string" &&
|
|
98
|
+
(v["ownerUserId"] === null || typeof v["ownerUserId"] === "string") &&
|
|
99
|
+
typeof v["providerKey"] === "string" &&
|
|
100
|
+
typeof v["mailbox"] === "string" &&
|
|
101
|
+
typeof v["nonce"] === "string"
|
|
102
102
|
);
|
|
103
103
|
}
|
|
@@ -157,12 +157,8 @@ async function purgeExpiredSeenAnchors(
|
|
|
157
157
|
// direkt tenant-scoped, kein Account-Join. Trifft auch Anker, deren Account
|
|
158
158
|
// schon disconnected/geloescht ist.
|
|
159
159
|
const where = { tenantId, seenAt: { lt: cutoff } };
|
|
160
|
-
const expired = await selectMany<{ accountId: string }>(
|
|
161
|
-
db,
|
|
162
|
-
seenMessageTable as EntityTableMeta,
|
|
163
|
-
where,
|
|
164
|
-
);
|
|
160
|
+
const expired = await selectMany<{ accountId: string }>(db, seenMessageTable, where);
|
|
165
161
|
if (expired.length === 0) return 0;
|
|
166
|
-
await deleteMany(db, seenMessageTable
|
|
162
|
+
await deleteMany(db, seenMessageTable, where);
|
|
167
163
|
return expired.length;
|
|
168
164
|
}
|
|
@@ -62,27 +62,19 @@ async function archiveAndDeleteRows(
|
|
|
62
62
|
export async function mailAccountTenantDestroyHook(ctx: TenantDataHookCtx): Promise<void> {
|
|
63
63
|
const accountIds = await archiveAndDeleteRows(
|
|
64
64
|
ctx,
|
|
65
|
-
mailAccountsProjectionTable
|
|
65
|
+
mailAccountsProjectionTable,
|
|
66
66
|
MAIL_ACCOUNT_AGGREGATE_TYPE,
|
|
67
67
|
);
|
|
68
68
|
for (const accountId of accountIds) {
|
|
69
|
-
await ctx.db.deleteMany(syncCursorTable
|
|
70
|
-
await ctx.db.deleteMany(seenMessageTable
|
|
69
|
+
await ctx.db.deleteMany(syncCursorTable, { accountId });
|
|
70
|
+
await ctx.db.deleteMany(seenMessageTable, { accountId });
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
export async function inboundMessageTenantDestroyHook(ctx: TenantDataHookCtx): Promise<void> {
|
|
75
|
-
await archiveAndDeleteRows(
|
|
76
|
-
ctx,
|
|
77
|
-
inboundMessagesProjectionTable as EntityTableMeta,
|
|
78
|
-
INBOUND_MESSAGE_AGGREGATE_TYPE,
|
|
79
|
-
);
|
|
75
|
+
await archiveAndDeleteRows(ctx, inboundMessagesProjectionTable, INBOUND_MESSAGE_AGGREGATE_TYPE);
|
|
80
76
|
}
|
|
81
77
|
|
|
82
78
|
export async function mailThreadTenantDestroyHook(ctx: TenantDataHookCtx): Promise<void> {
|
|
83
|
-
await archiveAndDeleteRows(
|
|
84
|
-
ctx,
|
|
85
|
-
mailThreadsProjectionTable as EntityTableMeta,
|
|
86
|
-
MAIL_THREAD_AGGREGATE_TYPE,
|
|
87
|
-
);
|
|
79
|
+
await archiveAndDeleteRows(ctx, mailThreadsProjectionTable, MAIL_THREAD_AGGREGATE_TYPE);
|
|
88
80
|
}
|
|
@@ -257,17 +257,15 @@ export class InboundTransientError extends Error {
|
|
|
257
257
|
}
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
-
type KindCarrier = { readonly kind?: unknown };
|
|
261
|
-
|
|
262
260
|
export function isInboundAuthError(e: unknown): e is InboundAuthError {
|
|
263
|
-
return e instanceof Error &&
|
|
261
|
+
return e instanceof Error && "kind" in e && e.kind === "inbound-auth-error";
|
|
264
262
|
}
|
|
265
263
|
export function isInboundRateLimitError(e: unknown): e is InboundRateLimitError {
|
|
266
|
-
return e instanceof Error &&
|
|
264
|
+
return e instanceof Error && "kind" in e && e.kind === "inbound-rate-limit-error";
|
|
267
265
|
}
|
|
268
266
|
export function isInboundCursorInvalidError(e: unknown): e is InboundCursorInvalidError {
|
|
269
|
-
return e instanceof Error &&
|
|
267
|
+
return e instanceof Error && "kind" in e && e.kind === "inbound-cursor-invalid-error";
|
|
270
268
|
}
|
|
271
269
|
export function isInboundTransientError(e: unknown): e is InboundTransientError {
|
|
272
|
-
return e instanceof Error &&
|
|
270
|
+
return e instanceof Error && "kind" in e && e.kind === "inbound-transient-error";
|
|
273
271
|
}
|
|
@@ -49,9 +49,10 @@ import {
|
|
|
49
49
|
configuredPiiSubjectKms,
|
|
50
50
|
decryptPiiFieldValues,
|
|
51
51
|
} from "@cosmicdrift/kumiko-framework/crypto";
|
|
52
|
-
import type { DbConnection
|
|
52
|
+
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
53
53
|
import type { DistributedLock } from "@cosmicdrift/kumiko-framework/pipeline";
|
|
54
54
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
55
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
55
56
|
import { InboundMailAccountStatuses, InboundMailFoundationHandlers } from "./constants.js";
|
|
56
57
|
import { MAIL_ACCOUNT_PII_FIELDS, syncCursorTable } from "./entities.js";
|
|
57
58
|
import {
|
|
@@ -158,6 +159,17 @@ export type InboundMailSupervisor = {
|
|
|
158
159
|
readonly stop: () => Promise<void>;
|
|
159
160
|
};
|
|
160
161
|
|
|
162
|
+
type ActiveMailAccountRow = {
|
|
163
|
+
readonly id: string;
|
|
164
|
+
readonly tenantId: string;
|
|
165
|
+
readonly provider: string;
|
|
166
|
+
readonly authMethod: string;
|
|
167
|
+
readonly ownerUserId?: string | null;
|
|
168
|
+
readonly address: string;
|
|
169
|
+
readonly displayName?: string;
|
|
170
|
+
readonly watchState?: string;
|
|
171
|
+
};
|
|
172
|
+
|
|
161
173
|
export function createInboundMailSupervisor(
|
|
162
174
|
deps: InboundMailSupervisorDeps,
|
|
163
175
|
): InboundMailSupervisor {
|
|
@@ -206,30 +218,32 @@ export function createInboundMailSupervisor(
|
|
|
206
218
|
// (Provider arbeiten nie mit Ciphertext).
|
|
207
219
|
// ---------------------------------------------------------------
|
|
208
220
|
async function listActiveAccounts(): Promise<readonly MailAccountRecord[]> {
|
|
209
|
-
const rows = await selectMany(deps.db, mailAccountsProjectionTable, {
|
|
221
|
+
const rows = await selectMany<ActiveMailAccountRow>(deps.db, mailAccountsProjectionTable, {
|
|
210
222
|
status: InboundMailAccountStatuses.active,
|
|
211
223
|
});
|
|
212
224
|
const piiKms = configuredPiiSubjectKms();
|
|
213
225
|
const records: MailAccountRecord[] = [];
|
|
214
|
-
for (const
|
|
215
|
-
const
|
|
216
|
-
? await decryptPiiFieldValues(
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
226
|
+
for (const row of rows) {
|
|
227
|
+
const decrypted = piiKms
|
|
228
|
+
? await decryptPiiFieldValues(row, MAIL_ACCOUNT_PII_FIELDS, piiKms, {
|
|
229
|
+
requestId: "inbound-mail-foundation:supervisor:list-accounts",
|
|
230
|
+
})
|
|
231
|
+
: row;
|
|
232
|
+
const decryptedAddress = decrypted["address"];
|
|
233
|
+
// Falling back to row.address would hand the ciphertext to a provider.
|
|
234
|
+
if (typeof decryptedAddress !== "string") {
|
|
235
|
+
throw new Error(`inbound-mail: address of mail account ${row.id} did not decrypt to text`);
|
|
236
|
+
}
|
|
223
237
|
records.push({
|
|
224
|
-
id: row
|
|
225
|
-
tenantId: row
|
|
226
|
-
provider: row
|
|
227
|
-
authMethod: row
|
|
228
|
-
ownerUserId:
|
|
229
|
-
address:
|
|
230
|
-
displayName:
|
|
238
|
+
id: row.id,
|
|
239
|
+
tenantId: row.tenantId,
|
|
240
|
+
provider: row.provider,
|
|
241
|
+
authMethod: row.authMethod,
|
|
242
|
+
ownerUserId: row.ownerUserId ?? null,
|
|
243
|
+
address: decryptedAddress,
|
|
244
|
+
displayName: row.displayName ?? "",
|
|
231
245
|
status: InboundMailAccountStatuses.active,
|
|
232
|
-
watchState:
|
|
246
|
+
watchState: row.watchState ?? "idle",
|
|
233
247
|
});
|
|
234
248
|
}
|
|
235
249
|
return records;
|
|
@@ -239,13 +253,14 @@ export function createInboundMailSupervisor(
|
|
|
239
253
|
// Cursor-Persistenz (unmanaged direct-write store).
|
|
240
254
|
// ---------------------------------------------------------------
|
|
241
255
|
async function loadCursor(accountId: string): Promise<SyncCursorPayload | null> {
|
|
242
|
-
const row = await fetchOne<{ cursor: string }>(deps.db, syncCursorTable
|
|
256
|
+
const row = await fetchOne<{ cursor: string }>(deps.db, syncCursorTable, {
|
|
243
257
|
accountId,
|
|
244
258
|
scope: CURSOR_SCOPE,
|
|
245
259
|
});
|
|
246
260
|
if (!row) return null;
|
|
247
261
|
try {
|
|
248
|
-
|
|
262
|
+
const parsed: unknown = JSON.parse(row.cursor);
|
|
263
|
+
return isPlainObject(parsed) ? parsed : null;
|
|
249
264
|
} catch {
|
|
250
265
|
return null; // korrupter Cursor = wie kein Cursor → Backfill, Dedup fängt Dubletten
|
|
251
266
|
}
|
|
@@ -258,21 +273,21 @@ export function createInboundMailSupervisor(
|
|
|
258
273
|
const accountId = account.id;
|
|
259
274
|
const now = Temporal.Now.instant().toString();
|
|
260
275
|
const serialized = JSON.stringify(cursor);
|
|
261
|
-
const existing = await fetchOne<{ id: string }>(deps.db, syncCursorTable
|
|
276
|
+
const existing = await fetchOne<{ id: string }>(deps.db, syncCursorTable, {
|
|
262
277
|
accountId,
|
|
263
278
|
scope: CURSOR_SCOPE,
|
|
264
279
|
});
|
|
265
280
|
if (existing) {
|
|
266
281
|
await updateMany(
|
|
267
282
|
deps.db,
|
|
268
|
-
syncCursorTable
|
|
283
|
+
syncCursorTable,
|
|
269
284
|
{ cursor: serialized, updatedAt: now },
|
|
270
285
|
{ accountId, scope: CURSOR_SCOPE },
|
|
271
286
|
);
|
|
272
287
|
// skip: update-Pfad fertig — nicht in den insert-Zweig durchfallen.
|
|
273
288
|
return;
|
|
274
289
|
}
|
|
275
|
-
await insertOne(deps.db, syncCursorTable
|
|
290
|
+
await insertOne(deps.db, syncCursorTable, {
|
|
276
291
|
id: crypto.randomUUID(),
|
|
277
292
|
// Direct-write store: no executor stamps the NOT NULL tenant_id for us.
|
|
278
293
|
tenantId: account.tenantId,
|
|
@@ -288,7 +303,7 @@ export function createInboundMailSupervisor(
|
|
|
288
303
|
// → nächster fetch läuft als Backfill.
|
|
289
304
|
await updateMany(
|
|
290
305
|
deps.db,
|
|
291
|
-
syncCursorTable
|
|
306
|
+
syncCursorTable,
|
|
292
307
|
{ cursor: "", updatedAt: Temporal.Now.instant().toString() },
|
|
293
308
|
{ accountId, scope: CURSOR_SCOPE },
|
|
294
309
|
);
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
19
19
|
import { legacyDateToInstant } from "@cosmicdrift/kumiko-framework/time";
|
|
20
20
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
21
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
21
22
|
import { ImapFlow } from "imapflow";
|
|
22
23
|
import { type AddressObject, type ParsedMail, simpleParser } from "mailparser";
|
|
23
24
|
import type { ImapCredentialDocument } from "./credential-document.js";
|
|
@@ -97,11 +98,11 @@ export function mapImapError(err: unknown, host: string): Error {
|
|
|
97
98
|
// imapflow-Fehler tragen die Server-Antwort in responseText, die
|
|
98
99
|
// message ist nur "Command failed" — beides in den Match einbeziehen;
|
|
99
100
|
// authenticationFailed ist das verlässliche Flag.
|
|
100
|
-
const carrier = err
|
|
101
|
-
const responseText = typeof carrier?.responseText === "string" ? carrier
|
|
101
|
+
const carrier = isPlainObject(err) ? err : undefined;
|
|
102
|
+
const responseText = typeof carrier?.["responseText"] === "string" ? carrier["responseText"] : "";
|
|
102
103
|
const msg = `${err instanceof Error ? err.message : String(err)} ${responseText}`.trim();
|
|
103
104
|
if (
|
|
104
|
-
carrier?.authenticationFailed === true ||
|
|
105
|
+
carrier?.["authenticationFailed"] === true ||
|
|
105
106
|
/auth(entication)? failed|invalid credentials|invalid login|LOGIN failed|AUTHENTICATIONFAILED/i.test(
|
|
106
107
|
msg,
|
|
107
108
|
)
|
|
@@ -12,7 +12,7 @@ export type ManualJobCatalogEntry = {
|
|
|
12
12
|
function payloadSchemaJson(job: JobDefinition): Record<string, unknown> | null {
|
|
13
13
|
if (job.schema === undefined) return null;
|
|
14
14
|
try {
|
|
15
|
-
return z.toJSONSchema(job.schema)
|
|
15
|
+
return z.toJSONSchema(job.schema);
|
|
16
16
|
} catch {
|
|
17
17
|
// Non-JSON-Schema-able Zod types (transforms/effects) — UI still lists the job.
|
|
18
18
|
return null;
|
|
@@ -63,9 +63,7 @@ export function findReversedIds(
|
|
|
63
63
|
): Set<string> {
|
|
64
64
|
const ids = new Set(rows.map((r) => r.id));
|
|
65
65
|
return new Set(
|
|
66
|
-
rows
|
|
67
|
-
.filter((r) => r.reference !== null && ids.has(r.reference))
|
|
68
|
-
.map((r) => r.reference as string),
|
|
66
|
+
rows.flatMap((r) => (r.reference !== null && ids.has(r.reference) ? [r.reference] : [])),
|
|
69
67
|
);
|
|
70
68
|
}
|
|
71
69
|
|
|
@@ -50,6 +50,11 @@ const DESCRIPTION_PATTERN = { regex: "^[\\s\\S]{0,500}$" } as const;
|
|
|
50
50
|
// there.
|
|
51
51
|
const CUSTOM_CSS_PATTERN = { regex: "^[\\s\\S]{0,8000}$" } as const;
|
|
52
52
|
|
|
53
|
+
// Empty or a FileRef id (UUID); the upload UI that fills it ships separately.
|
|
54
|
+
const FILE_REF_ID_PATTERN = {
|
|
55
|
+
regex: "^$|^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
|
|
56
|
+
} as const;
|
|
57
|
+
|
|
53
58
|
export const LAYOUT_PRESETS = ["minimal", "centered", "wide"] as const;
|
|
54
59
|
|
|
55
60
|
// Companion toggle feature name. managed-pages reads ctx.hasFeature() against it
|
|
@@ -91,6 +96,12 @@ export const BRANDING_KEYS = {
|
|
|
91
96
|
pattern: HTTPS_PATTERN,
|
|
92
97
|
write: BRANDING_WRITE,
|
|
93
98
|
}),
|
|
99
|
+
// Ownership is checked where it is read (renderer-mail-html: FileRef must belong to the rendering tenant).
|
|
100
|
+
brandingLogoFile: createTenantConfig("text", {
|
|
101
|
+
default: "",
|
|
102
|
+
pattern: FILE_REF_ID_PATTERN,
|
|
103
|
+
write: BRANDING_WRITE,
|
|
104
|
+
}),
|
|
94
105
|
brandingLayoutPreset: createTenantConfig("select", {
|
|
95
106
|
default: "centered",
|
|
96
107
|
options: LAYOUT_PRESETS,
|
|
@@ -112,6 +123,7 @@ export const BRANDING_QN = {
|
|
|
112
123
|
siteUrl: "managed-pages:config:branding-site-url",
|
|
113
124
|
accentColor: "managed-pages:config:branding-accent-color",
|
|
114
125
|
logoUrl: "managed-pages:config:branding-logo-url",
|
|
126
|
+
logoFile: "managed-pages:config:branding-logo-file",
|
|
115
127
|
layoutPreset: "managed-pages:config:branding-layout-preset",
|
|
116
128
|
customCss: "managed-pages:config:branding-custom-css",
|
|
117
129
|
} as const;
|
|
@@ -129,17 +141,28 @@ async function readText(config: ConfigAccessor, qualifiedKey: string): Promise<s
|
|
|
129
141
|
// publicstatus' branding read), not a crash.
|
|
130
142
|
export async function readBranding(config: ConfigAccessor | undefined): Promise<BrandingTokens> {
|
|
131
143
|
if (!config) return EMPTY_BRANDING;
|
|
132
|
-
const [title, description, siteUrl, accentColor, logoUrl, layoutPreset] =
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
144
|
+
const [title, description, siteUrl, accentColor, logoUrl, logoFile, layoutPreset] =
|
|
145
|
+
await Promise.all([
|
|
146
|
+
readText(config, BRANDING_QN.title),
|
|
147
|
+
readText(config, BRANDING_QN.description),
|
|
148
|
+
readText(config, BRANDING_QN.siteUrl),
|
|
149
|
+
readText(config, BRANDING_QN.accentColor),
|
|
150
|
+
readText(config, BRANDING_QN.logoUrl),
|
|
151
|
+
readText(config, BRANDING_QN.logoFile),
|
|
152
|
+
readText(config, BRANDING_QN.layoutPreset),
|
|
153
|
+
]);
|
|
140
154
|
// customCss stays "" here — it is gated (allowCustomCss + per-tenant toggle)
|
|
141
155
|
// and read separately by the branding query via readCustomCss.
|
|
142
|
-
return {
|
|
156
|
+
return {
|
|
157
|
+
title,
|
|
158
|
+
description,
|
|
159
|
+
siteUrl,
|
|
160
|
+
accentColor,
|
|
161
|
+
logoUrl,
|
|
162
|
+
logoFile,
|
|
163
|
+
layoutPreset,
|
|
164
|
+
customCss: "",
|
|
165
|
+
};
|
|
143
166
|
}
|
|
144
167
|
|
|
145
168
|
// Read the raw, untrusted custom CSS for the request's tenant. The caller (the
|
|
@@ -167,6 +190,7 @@ export function coerceBranding(value: unknown): BrandingTokens {
|
|
|
167
190
|
siteUrl: stringField(source, "siteUrl"),
|
|
168
191
|
accentColor: stringField(source, "accentColor"),
|
|
169
192
|
logoUrl: stringField(source, "logoUrl"),
|
|
193
|
+
logoFile: stringField(source, "logoFile"),
|
|
170
194
|
layoutPreset: stringField(source, "layoutPreset"),
|
|
171
195
|
customCss: stringField(source, "customCss"),
|
|
172
196
|
};
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.363.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "Branding screen edits the logo file and readBranding returns logoFile",
|
|
6
|
+
"detail": "The branding settings screen has a logo upload for managed-pages:config:branding-logo-file, and readBranding and the branding query include logoFile next to logoUrl. The template resource errors template_resource_too_large and template_resource_unsupported_image have German and Spanish texts."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.353.0",
|
|
4
10
|
"type": "fix",
|
|
@@ -42,6 +42,7 @@ export const MANAGED_PAGES_I18N: Readonly<Record<string, LocalizedString>> = {
|
|
|
42
42
|
en: "Accent color",
|
|
43
43
|
},
|
|
44
44
|
"managed-pages:entity:__config-edit__:field:logoUrl": { en: "Logo URL" },
|
|
45
|
+
"managed-pages:entity:__config-edit__:field:logoFile": { en: "Logo file" },
|
|
45
46
|
"managed-pages:entity:__config-edit__:field:layoutPreset": { en: "Layout" },
|
|
46
47
|
|
|
47
48
|
// Section-Header
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type ConfigEditScreenDefinition,
|
|
3
|
+
createImageField,
|
|
3
4
|
createSelectField,
|
|
4
5
|
createTextField,
|
|
5
6
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
@@ -35,6 +36,7 @@ export function createBrandingSettingsScreen(opts: {
|
|
|
35
36
|
siteUrl: BRANDING_QN.siteUrl,
|
|
36
37
|
accentColor: BRANDING_QN.accentColor,
|
|
37
38
|
logoUrl: BRANDING_QN.logoUrl,
|
|
39
|
+
logoFile: BRANDING_QN.logoFile,
|
|
38
40
|
layoutPreset: BRANDING_QN.layoutPreset,
|
|
39
41
|
},
|
|
40
42
|
fields: {
|
|
@@ -62,6 +64,11 @@ export function createBrandingSettingsScreen(opts: {
|
|
|
62
64
|
personal: false,
|
|
63
65
|
reason: "is_business_data",
|
|
64
66
|
}),
|
|
67
|
+
logoFile: createImageField({
|
|
68
|
+
accept: ["image/png", "image/jpeg", "image/webp"],
|
|
69
|
+
personal: false,
|
|
70
|
+
reason: "is_business_data",
|
|
71
|
+
}),
|
|
65
72
|
layoutPreset: createSelectField({ options: LAYOUT_PRESETS }),
|
|
66
73
|
},
|
|
67
74
|
layout: {
|
|
@@ -75,6 +82,7 @@ export function createBrandingSettingsScreen(opts: {
|
|
|
75
82
|
{ field: "description", span: 2 },
|
|
76
83
|
{ field: "siteUrl", span: 1 },
|
|
77
84
|
{ field: "logoUrl", span: 1 },
|
|
85
|
+
{ field: "logoFile", span: 1 },
|
|
78
86
|
{ field: "accentColor", span: 1 },
|
|
79
87
|
],
|
|
80
88
|
},
|
|
@@ -13,6 +13,8 @@ export type BrandingTokens = {
|
|
|
13
13
|
readonly siteUrl: string;
|
|
14
14
|
readonly accentColor: string;
|
|
15
15
|
readonly logoUrl: string;
|
|
16
|
+
// FileRef id of an uploaded logo ("" = none); the renderer resolves it, tenant ownership is checked there.
|
|
17
|
+
readonly logoFile: string;
|
|
16
18
|
readonly layoutPreset: string;
|
|
17
19
|
// RAW, untrusted tenant CSS — "" unless the custom-css capability is gated on
|
|
18
20
|
// (managed-pages allowCustomCss + the per-tenant toggle). It is NOT pre-
|
|
@@ -29,6 +31,7 @@ export const EMPTY_BRANDING: BrandingTokens = {
|
|
|
29
31
|
siteUrl: "",
|
|
30
32
|
accentColor: "",
|
|
31
33
|
logoUrl: "",
|
|
34
|
+
logoFile: "",
|
|
32
35
|
layoutPreset: "",
|
|
33
36
|
customCss: "",
|
|
34
37
|
};
|
|
@@ -25,7 +25,7 @@ safeRenderer.use({
|
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
export function renderSafeMarkdown(markdown: string): string {
|
|
28
|
-
// @cast-boundary marked.parse return-type ist `string | Promise<string>`;
|
|
28
|
+
// @cast-boundary third-party-type-gap marked.parse return-type ist `string | Promise<string>`;
|
|
29
29
|
// `{ async: false }` garantiert sync (string) — Cast nur API-Vertragsfix.
|
|
30
30
|
return safeRenderer.parse(markdown, { async: false }) as string;
|
|
31
31
|
}
|
|
@@ -42,11 +42,9 @@ export function bindPatAutoRevokeOnPasswordChangeFromFeature(
|
|
|
42
42
|
): BindPatAutoRevokeOnPasswordChange | undefined {
|
|
43
43
|
const exports = feature.exports;
|
|
44
44
|
if (exports && typeof exports === "object" && "bindAutoRevokeOnPasswordChange" in exports) {
|
|
45
|
-
const { bindAutoRevokeOnPasswordChange } = exports
|
|
46
|
-
bindAutoRevokeOnPasswordChange: unknown;
|
|
47
|
-
};
|
|
45
|
+
const { bindAutoRevokeOnPasswordChange } = exports;
|
|
48
46
|
if (typeof bindAutoRevokeOnPasswordChange === "function") {
|
|
49
|
-
// @cast-boundary exports
|
|
47
|
+
// @cast-boundary feature-exports — feature.exports is untyped by design
|
|
50
48
|
return bindAutoRevokeOnPasswordChange as BindPatAutoRevokeOnPasswordChange;
|
|
51
49
|
}
|
|
52
50
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { definePagedQueryHandler, MAX_LIST_LIMIT } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
3
4
|
import * as z from "zod";
|
|
4
5
|
import { decryptStoredPii } from "../../shared/index.js";
|
|
5
6
|
import { isExpiredAt } from "../expiry.js";
|
|
@@ -13,7 +14,7 @@ const SORTABLE_COLUMNS = ["id", "name", "createdAt", "expiresAt", "revokedAt"] a
|
|
|
13
14
|
type SortableColumn = (typeof SORTABLE_COLUMNS)[number];
|
|
14
15
|
|
|
15
16
|
function isSortableColumn(value: string): value is SortableColumn {
|
|
16
|
-
return (SORTABLE_COLUMNS
|
|
17
|
+
return isOneOf(SORTABLE_COLUMNS, value);
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
type PatStatus = "active" | "revoked" | "expired";
|
|
@@ -29,7 +29,7 @@ export { expandScopes } from "./scopes.js";
|
|
|
29
29
|
export function patRateLimitFromFeature(feature: FeatureDefinition): PatRateLimit {
|
|
30
30
|
const exports = feature.exports;
|
|
31
31
|
if (exports && typeof exports === "object" && "rateLimit" in exports) {
|
|
32
|
-
const { rateLimit } = exports
|
|
32
|
+
const { rateLimit } = exports;
|
|
33
33
|
if (
|
|
34
34
|
rateLimit &&
|
|
35
35
|
typeof rateLimit === "object" &&
|
|
@@ -4,6 +4,7 @@ import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
|
4
4
|
import { append } from "@cosmicdrift/kumiko-framework/event-store";
|
|
5
5
|
import { generateId } from "@cosmicdrift/kumiko-framework/utils";
|
|
6
6
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
7
|
+
import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
7
8
|
import {
|
|
8
9
|
PAT_REVOKED_AGGREGATE_TYPE,
|
|
9
10
|
PAT_REVOKED_EVENT_QN,
|
|
@@ -13,7 +14,7 @@ import { apiTokenTable } from "./schema/api-token.js";
|
|
|
13
14
|
|
|
14
15
|
// A pool has begin(); a tx/savepoint handle does not and is already atomic.
|
|
15
16
|
function canBeginTransaction(db: DbRunner): boolean {
|
|
16
|
-
return typeof (db
|
|
17
|
+
return typeof propertyOf(db, "begin") === "function";
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
// Cross-tenant revoke: password-change and MFA-enable/disable are account-
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# renderer-foundation
|
|
2
2
|
|
|
3
|
-
Plugin-Foundation für Renderer (
|
|
3
|
+
Plugin-Foundation für Renderer (HTML-Mail, PDF, Image). Plugins (`renderer-mail-html`, `renderer-puppeteer-client`) registrieren sich via `r.useExtension("renderer", "<name>", { kinds, render })`.
|
|
4
4
|
|
|
5
5
|
**Plan-Doc:** [`kumiko-platform/docs/plans/features/renderer-foundation.md`](../../../../../../kumiko-platform/docs/plans/features/renderer-foundation.md)
|
|
6
6
|
|
|
@@ -16,8 +16,7 @@ import {
|
|
|
16
16
|
const features = [
|
|
17
17
|
createTemplateResolverFeature(),
|
|
18
18
|
createRendererFoundationFeature(),
|
|
19
|
-
|
|
20
|
-
createRendererMailHtmlFeature(), // Plugin (enterprise)
|
|
19
|
+
createRendererMailHtmlFeature(), // Plugin: kind "mail"
|
|
21
20
|
// weitere Plugins...
|
|
22
21
|
];
|
|
23
22
|
|
|
@@ -38,23 +37,25 @@ import { requireRendererFoundation } from "@cosmicdrift/kumiko-bundled-features/
|
|
|
38
37
|
|
|
39
38
|
async function sendMail(ctx, tenantId) {
|
|
40
39
|
const foundation = requireRendererFoundation(ctx, "sendMail");
|
|
41
|
-
const renderer = foundation.createRendererForTenant({ tenantId, kind: "mail
|
|
40
|
+
const renderer = foundation.createRendererForTenant({ tenantId, kind: "mail" });
|
|
42
41
|
const result = await renderer.render(
|
|
43
42
|
{
|
|
44
|
-
kind: "mail
|
|
45
|
-
payload: {
|
|
43
|
+
kind: "mail",
|
|
44
|
+
payload: { slug: "billing:notify:invoice-ready", locale: "de", variables: { name: "Frau Schmidt" } },
|
|
46
45
|
},
|
|
47
|
-
{ db: ctx.db, registry: ctx.registry, tenantId },
|
|
46
|
+
{ db: ctx.db, registry: ctx.registry, tenantId, config: ctx.config },
|
|
48
47
|
);
|
|
49
|
-
// result.html, result.text
|
|
48
|
+
// result.subject, result.html, result.text, result.previewText
|
|
50
49
|
}
|
|
51
50
|
```
|
|
52
51
|
|
|
53
|
-
Plugins ohne Service-Deps
|
|
52
|
+
Plugins ohne Service-Deps ignorieren den zweiten `ctx`-Parameter.
|
|
53
|
+
|
|
54
|
+
Mails einer `r.notification` rendert die Delivery selbst: `template` + `meta` an der Notification werden beim Boot gesät (`seedNotificationTemplates`) und über kind `"mail"` gerendert. Ohne `template` + `meta` scheitert der Versand über den Email-Channel.
|
|
54
55
|
|
|
55
56
|
## Plugin-Auswahl-Reihenfolge
|
|
56
57
|
|
|
57
|
-
1. Tenant-Override (Config-Key `rendererPluginByKind`, z.B. `{ "mail
|
|
58
|
+
1. Tenant-Override (Config-Key `rendererPluginByKind`, z.B. `{ "mail": "mail-html" }`)
|
|
58
59
|
2. `DEFAULT_PLUGIN_BY_KIND` aus constants
|
|
59
60
|
3. Erstes Plugin im Pool das das kind bedient
|
|
60
61
|
4. `RendererError("no_plugin_for_kind")` wenn nichts passt
|