@cosmicdrift/kumiko-samples 0.363.0 → 0.365.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/LICENSE +1 -1
- package/package.json +1 -1
- package/packages/bundled-features/package.json +7 -1
- package/packages/bundled-features/src/audit/changes.json +18 -0
- package/packages/bundled-features/src/audit/feature.ts +1 -1
- package/packages/bundled-features/src/audit/handlers/list.query.ts +10 -5
- package/packages/bundled-features/src/audit/run-escape-hatch-retention.ts +63 -11
- package/packages/bundled-features/src/auth-email-password/bootstrap.ts +30 -11
- package/packages/bundled-features/src/auth-email-password/changes.json +55 -0
- package/packages/bundled-features/src/auth-email-password/email-rate-limits.ts +16 -0
- package/packages/bundled-features/src/auth-email-password/feature.ts +4 -7
- package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +6 -2
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +2 -3
- package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +6 -4
- package/packages/bundled-features/src/auth-email-password/handlers/logout.write.ts +7 -2
- package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +2 -1
- package/packages/bundled-features/src/auth-email-password/handlers/switch-tenant-mfa-gate.write.ts +2 -2
- package/packages/bundled-features/src/auth-email-password/handlers/token-request-handler.ts +2 -1
- package/packages/bundled-features/src/auth-email-password/magic-link-mail.ts +7 -3
- package/packages/bundled-features/src/auth-email-password/mail-i18n.ts +10 -0
- package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +24 -5
- package/packages/bundled-features/src/billing-foundation/changes.json +48 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts +57 -5
- package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-gate.ts +22 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/mail-notifications.ts +4 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-termination.ts +11 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-mail.ts +9 -3
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-receipt-limit.ts +41 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +18 -2
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +35 -19
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-texts.ts +9 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/withdrawal-period.ts +35 -0
- package/packages/bundled-features/src/billing-foundation/handlers/create-checkout-session.write.ts +11 -3
- package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +10 -4
- package/packages/bundled-features/src/billing-foundation/i18n.ts +9 -0
- package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +27 -7
- package/packages/bundled-features/src/billing-foundation/types.ts +7 -2
- package/packages/bundled-features/src/cap-counter/book-cap-usage.ts +20 -8
- package/packages/bundled-features/src/cap-counter/calendar-cap-usage-reservation.ts +2 -0
- package/packages/bundled-features/src/cap-counter/cap-reservation.ts +7 -2
- package/packages/bundled-features/src/cap-counter/changes.json +19 -0
- package/packages/bundled-features/src/cap-counter/enforce-cap.ts +75 -5
- package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +2 -0
- package/packages/bundled-features/src/cap-counter/index.ts +1 -0
- package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +3 -0
- package/packages/bundled-features/src/cap-overview/changes.json +6 -0
- package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +2 -1
- package/packages/bundled-features/src/cap-overview/handlers/tenant-options.query.ts +2 -1
- package/packages/bundled-features/src/channel-discord/discord-channel.ts +4 -2
- package/packages/bundled-features/src/channel-email/changes.json +18 -0
- package/packages/bundled-features/src/channel-email/email-channel.ts +47 -24
- package/packages/bundled-features/src/channel-email/smtp-transport.ts +7 -1
- package/packages/bundled-features/src/channel-slack/slack-channel.ts +4 -2
- package/packages/bundled-features/src/channel-teams/teams-channel.ts +4 -2
- package/packages/bundled-features/src/config/changes.json +6 -0
- package/packages/bundled-features/src/config/handlers/set.write.ts +2 -2
- package/packages/bundled-features/src/config/resolver.ts +15 -12
- package/packages/bundled-features/src/config/write-helpers.ts +23 -0
- package/packages/bundled-features/src/crypto-shredding/changes.json +6 -0
- package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +54 -27
- package/packages/bundled-features/src/custom-fields/changes.json +6 -0
- package/packages/bundled-features/src/custom-fields/db/queries/retention.ts +2 -2
- package/packages/bundled-features/src/custom-fields/handlers/update-tenant-field.write.ts +0 -1
- package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +4 -5
- package/packages/bundled-features/src/delivery/attachment-ref.ts +39 -1
- package/packages/bundled-features/src/delivery/changes.json +30 -0
- package/packages/bundled-features/src/delivery/channel-context.ts +31 -8
- package/packages/bundled-features/src/delivery/delivery-service.ts +93 -92
- package/packages/bundled-features/src/delivery/index.ts +2 -0
- package/packages/bundled-features/src/delivery/jobs.ts +51 -14
- package/packages/bundled-features/src/delivery/types.ts +7 -1
- package/packages/bundled-features/src/document-ingest-foundation/changes.json +6 -0
- package/packages/bundled-features/src/feature-toggles/changes.json +7 -0
- package/packages/bundled-features/src/feature-toggles/toggle-runtime.ts +26 -12
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +1 -1
- package/packages/bundled-features/src/file-foundation/changes.json +6 -0
- package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +6 -3
- package/packages/bundled-features/src/files-tenant-data/changes.json +6 -0
- package/packages/bundled-features/src/files-tenant-data/hooks.ts +4 -0
- package/packages/bundled-features/src/files-tenant-data/index.ts +3 -1
- package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +12 -3
- package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/changes.json +6 -0
- package/packages/bundled-features/src/inbound-mail-foundation/oauth-access-token.ts +13 -8
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +3 -1
- package/packages/bundled-features/src/jobs/changes.json +31 -0
- package/packages/bundled-features/src/jobs/db/queries/stale-run-sweep.ts +12 -4
- package/packages/bundled-features/src/jobs/feature.ts +7 -4
- package/packages/bundled-features/src/jobs/handlers/list.query.ts +3 -1
- package/packages/bundled-features/src/jobs/handlers/stale-run-sweep.job.ts +5 -2
- package/packages/bundled-features/src/jobs/job-run-logger.ts +56 -18
- package/packages/bundled-features/src/jobs/tenant-job-run-table.ts +7 -2
- package/packages/bundled-features/src/ledger/changes.json +7 -0
- package/packages/bundled-features/src/ledger/entity.ts +3 -1
- package/packages/bundled-features/src/metrics/changes.json +6 -0
- package/packages/bundled-features/src/metrics/filters.ts +28 -12
- package/packages/bundled-features/src/personal-access-tokens/changes.json +6 -0
- package/packages/bundled-features/src/personal-access-tokens/revoke-for-user.ts +7 -8
- package/packages/bundled-features/src/renderer-foundation/types.ts +2 -0
- package/packages/bundled-features/src/renderer-pdf/changes.json +12 -0
- package/packages/bundled-features/src/renderer-pdf/render-table-pdf.ts +162 -82
- package/packages/bundled-features/src/secrets/changes.json +12 -0
- package/packages/bundled-features/src/secrets/feature.ts +4 -3
- package/packages/bundled-features/src/secrets/handlers/delete.write.ts +3 -1
- package/packages/bundled-features/src/secrets/secrets-context.ts +23 -3
- package/packages/bundled-features/src/secrets/write-gate.ts +8 -2
- package/packages/bundled-features/src/shared/changes.json +6 -0
- package/packages/bundled-features/src/shared/chunked-entity-migration.ts +4 -1
- package/packages/bundled-features/src/shared/index.ts +8 -0
- package/packages/bundled-features/src/shared/mail-template-body.ts +17 -0
- package/packages/bundled-features/src/shared/resource-image-formats.ts +31 -0
- package/packages/bundled-features/src/shared/sharp-input-limits.ts +7 -8
- package/packages/bundled-features/src/step-dispatcher/webhook-runner.ts +2 -2
- package/packages/bundled-features/src/subscription-stripe/changes.json +6 -0
- package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +19 -11
- package/packages/bundled-features/src/subscription-stripe/verify-webhook.ts +3 -1
- package/packages/bundled-features/src/template-resolver/README.md +4 -2
- package/packages/bundled-features/src/template-resolver/api.ts +3 -2
- package/packages/bundled-features/src/template-resolver/changes.json +149 -0
- package/packages/bundled-features/src/template-resolver/compile-document-body.ts +148 -18
- package/packages/bundled-features/src/template-resolver/compile-steps-block.ts +68 -0
- package/packages/bundled-features/src/template-resolver/constants.ts +23 -3
- package/packages/bundled-features/src/template-resolver/document-sanitizer.ts +3 -0
- package/packages/bundled-features/src/template-resolver/document-schema.ts +33 -5
- package/packages/bundled-features/src/template-resolver/document-steps.ts +114 -0
- package/packages/bundled-features/src/template-resolver/feature.ts +16 -1
- package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +81 -39
- package/packages/bundled-features/src/template-resolver/handlers/collection-set.write.ts +4 -3
- package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +121 -53
- package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +6 -44
- package/packages/bundled-features/src/template-resolver/handlers/discard-draft.write.ts +52 -0
- package/packages/bundled-features/src/template-resolver/handlers/export-bundle.query.ts +56 -1
- package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +6 -3
- package/packages/bundled-features/src/template-resolver/handlers/import-bundle-system.write.ts +25 -15
- package/packages/bundled-features/src/template-resolver/handlers/import-bundle.write.ts +12 -1
- package/packages/bundled-features/src/template-resolver/handlers/layout-source.query.ts +73 -30
- package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +14 -11
- package/packages/bundled-features/src/template-resolver/handlers/override-diff.ts +49 -0
- package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +102 -19
- package/packages/bundled-features/src/template-resolver/handlers/resource-upload.write.ts +49 -10
- package/packages/bundled-features/src/template-resolver/handlers/send-test.write.ts +39 -10
- package/packages/bundled-features/src/template-resolver/handlers/template-gates.ts +14 -2
- package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +54 -4
- package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +96 -14
- package/packages/bundled-features/src/template-resolver/handlers/toggle-status.write.ts +51 -24
- package/packages/bundled-features/src/template-resolver/handlers/upsert-tenant.write.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +16 -2
- package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +4 -1
- package/packages/bundled-features/src/template-resolver/index.ts +18 -2
- package/packages/bundled-features/src/template-resolver/layout-appearance.ts +56 -0
- package/packages/bundled-features/src/template-resolver/mail-constants.ts +4 -0
- package/packages/bundled-features/src/template-resolver/pdf-constants.ts +12 -0
- package/packages/bundled-features/src/template-resolver/qualified-names.ts +4 -0
- package/packages/bundled-features/src/template-resolver/resource-image.ts +9 -16
- package/packages/bundled-features/src/template-resolver/seeding.ts +27 -4
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +390 -124
- package/packages/bundled-features/src/template-resolver/table.ts +13 -1
- package/packages/bundled-features/src/template-resolver/template-bundle.ts +4 -0
- package/packages/bundled-features/src/template-resolver/template-draft.ts +106 -0
- package/packages/bundled-features/src/template-resolver/template-meta.ts +95 -24
- package/packages/bundled-features/src/template-resolver/template-preview.ts +160 -57
- package/packages/bundled-features/src/template-resolver/template-resources.ts +3 -4
- package/packages/bundled-features/src/template-resolver/template-usage.ts +31 -11
- package/packages/bundled-features/src/template-resolver/template-validation.ts +16 -9
- package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +12 -8
- package/packages/bundled-features/src/template-resolver/web/document-editor-extensions.ts +97 -10
- package/packages/bundled-features/src/template-resolver/web/document-editor.tsx +15 -5
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-editor.tsx +405 -145
- package/packages/bundled-features/src/template-resolver/web/document-toolbar-blocks.ts +17 -3
- package/packages/bundled-features/src/template-resolver/web/i18n.ts +32 -4
- package/packages/bundled-features/src/template-resolver/web/index.ts +8 -0
- package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor.tsx +23 -7
- package/packages/bundled-features/src/tenant/changes.json +6 -0
- package/packages/bundled-features/src/tenant/handlers/update-member-roles.write.ts +3 -8
- package/packages/bundled-features/src/tenant/seeding.ts +33 -27
- package/packages/bundled-features/src/tenant-handover/move-entity-graph.ts +14 -3
- package/packages/bundled-features/src/tenant-lifecycle/changes.json +6 -0
- package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +9 -1
- package/packages/bundled-features/src/tenant-lifecycle/stages.ts +3 -2
- package/packages/bundled-features/src/tier-engine/changes.json +18 -0
- package/packages/bundled-features/src/tier-engine/constants.ts +9 -0
- package/packages/bundled-features/src/tier-engine/entity.ts +9 -4
- package/packages/bundled-features/src/tier-engine/feature.ts +11 -3
- package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +8 -5
- package/packages/bundled-features/src/tier-engine/index.ts +2 -0
- package/packages/bundled-features/src/user/changes.json +6 -0
- package/packages/bundled-features/src/user/constants.ts +1 -0
- package/packages/bundled-features/src/user/invited-global-roles.ts +2 -4
- package/packages/bundled-features/src/user/resolve-display-names.ts +8 -15
- package/packages/bundled-features/src/user/seeding.ts +19 -5
- package/packages/bundled-features/src/user-data-rights/changes.json +12 -0
- package/packages/bundled-features/src/user-data-rights/email-templates.ts +0 -1
- package/packages/bundled-features/src/user-data-rights/i18n.ts +5 -0
- package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +13 -2
- package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +7 -1
- package/packages/bundled-features/src/user-profile/changes.json +1 -1
- package/packages/bundled-features/src/waitlist/changes.json +18 -0
- package/packages/bundled-features/src/waitlist/feature.ts +1 -1
- package/packages/bundled-features/src/waitlist/handlers/invite.write.ts +35 -3
- package/packages/bundled-features/src/waitlist/handlers/reject.write.ts +2 -17
- package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +33 -9
- package/packages/bundled-features/src/waitlist/i18n.ts +9 -0
- package/packages/bundled-features/src/waitlist/lib.ts +20 -0
- package/packages/bundled-features/src/waitlist/options.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/changes.json +6 -0
- package/packages/bundled-features/src/workflow-runner/event-trigger.ts +2 -1
- package/packages/bundled-features/src/workflow-runner/feature.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +7 -1
- package/samples/apps/ui-walkthrough/package.json +9 -0
- package/samples/apps/ui-walkthrough/playwright.config.ts +40 -7
- package/samples/apps/ui-walkthrough/src/app/seed-templates.ts +87 -2
- package/samples/apps/ui-walkthrough/src/app/template-seed-data.ts +246 -30
- package/samples/apps/ui-walkthrough/src/features/billing/feature.ts +4 -0
- package/samples/apps/ui-walkthrough/src/run-config.ts +18 -1
- package/samples/apps/use-all-bundled/README.md +3 -13
- package/samples/apps/use-all-bundled/feature-manifest.json +7 -5
- package/samples/apps/use-all-bundled/scripts/check-coverage.ts +34 -118
- package/samples/apps/use-all-bundled/src/app/notes-feature.ts +1 -1
- package/samples/apps/use-all-bundled/src/run-config.ts +0 -3
- package/samples/recipes/auth-mfa/README.md +2 -1
- package/samples/recipes/auth-mfa/src/feature.ts +1 -0
- package/samples/recipes/custom-handlers/README.md +4 -3
- package/samples/recipes/custom-handlers/src/feature.ts +21 -12
- package/samples/recipes/dashboard-stats/README.md +38 -0
- package/samples/recipes/dashboard-stats/package.json +8 -0
- package/samples/recipes/dashboard-stats/src/feature.ts +104 -0
- package/samples/recipes/legal-pages/README.md +2 -2
- package/samples/recipes/managed-pages/README.md +2 -2
- package/samples/recipes/pdf-audit-export/README.md +2 -1
- package/samples/recipes/pdf-audit-export/package.json +1 -1
- package/samples/recipes/pdf-audit-export/src/feature.ts +68 -48
- package/samples/recipes/workflow-engine/src/db/queries/suspended-runs.ts +2 -2
- package/samples/apps/use-all-bundled/schema/generate.ts +0 -617
- package/samples/apps/use-all-bundled/schema/migrations/meta/0000_snapshot.json +0 -3055
- package/samples/apps/use-all-bundled/schema/migrations/meta/_journal.json +0 -13
- package/samples/apps/use-all-bundled/schema/schema.generated.ts +0 -94
- package/samples/apps/use-all-bundled/schema/schema.ts +0 -9
|
@@ -2,7 +2,10 @@ import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
|
2
2
|
import {
|
|
3
3
|
defineWriteHandler,
|
|
4
4
|
type HandlerContext,
|
|
5
|
+
type SessionUser,
|
|
5
6
|
SYSTEM_TENANT_ID,
|
|
7
|
+
type TenantId,
|
|
8
|
+
type WriteResult,
|
|
6
9
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
7
10
|
import {
|
|
8
11
|
AccessDeniedError,
|
|
@@ -13,10 +16,15 @@ import {
|
|
|
13
16
|
import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
14
17
|
import * as z from "zod";
|
|
15
18
|
import { requireForTenant } from "../../shared/index.js";
|
|
16
|
-
import { CONTENT_FORMATS } from "../constants.js";
|
|
17
|
-
import { parseLinkedResourceEntries } from "../linked-resources.js";
|
|
19
|
+
import { CONTENT_FORMATS, type ContentFormat } from "../constants.js";
|
|
20
|
+
import { type LinkedResourceEntry, parseLinkedResourceEntries } from "../linked-resources.js";
|
|
18
21
|
import { TemplateResolverErrors } from "../qualified-names.js";
|
|
22
|
+
import {
|
|
23
|
+
createLinkedResourceCopies,
|
|
24
|
+
prepareLinkedResourceCopiesBeforeTransaction,
|
|
25
|
+
} from "../store-template-resource.js";
|
|
19
26
|
import { type TemplateResourceRow, templateResourcesTable } from "../table.js";
|
|
27
|
+
import { toWorkingRow, type WorkingRow } from "../template-draft.js";
|
|
20
28
|
import { attachTemplateResources } from "../template-resources.js";
|
|
21
29
|
import { resolveTemplateVariables } from "../template-validation.js";
|
|
22
30
|
import { targetExistsIssue } from "./bundle-import.js";
|
|
@@ -50,7 +58,8 @@ async function findCopySource(
|
|
|
50
58
|
kind,
|
|
51
59
|
locale,
|
|
52
60
|
});
|
|
53
|
-
|
|
61
|
+
// An archived tenant row is not an active source: the system default applies instead.
|
|
62
|
+
if (scope === "system" || (own && own.status !== "archived")) return own;
|
|
54
63
|
return fetchOne<TemplateResourceRow>(readDb, templateResourcesTable, {
|
|
55
64
|
tenantId: SYSTEM_TENANT_ID,
|
|
56
65
|
slug,
|
|
@@ -60,51 +69,109 @@ async function findCopySource(
|
|
|
60
69
|
});
|
|
61
70
|
}
|
|
62
71
|
|
|
72
|
+
const copyLocaleSchema = z.object({
|
|
73
|
+
slug: templateSlugSchema,
|
|
74
|
+
kind: kindSchema,
|
|
75
|
+
fromLocale: localeSchema,
|
|
76
|
+
toLocale: localeSchema,
|
|
77
|
+
overwrite: z.boolean().default(false),
|
|
78
|
+
/** "system" copies between system-default rows; SystemAdmin only. */
|
|
79
|
+
scope: z.enum(["tenant", "system"]).default("tenant"),
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
type CopyRequest = {
|
|
83
|
+
readonly scope: CopyScope;
|
|
84
|
+
readonly tenantId: TenantId;
|
|
85
|
+
readonly source: WorkingRow;
|
|
86
|
+
readonly contentFormat: ContentFormat;
|
|
87
|
+
readonly sourceResources: Readonly<Record<string, LinkedResourceEntry>>;
|
|
88
|
+
// A system source's files belong to the system tenant: the tenant gets its own copies so
|
|
89
|
+
// previews, exports and the tenant's override keep working independent of the system row.
|
|
90
|
+
readonly sourceIsForeign: boolean;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// Runs before the upload (reject early, store nothing) and again in the transaction on the fresh rows.
|
|
94
|
+
async function checkCopyRequest(
|
|
95
|
+
ctx: WriteCtx,
|
|
96
|
+
event: {
|
|
97
|
+
readonly user: SessionUser;
|
|
98
|
+
readonly payload: z.infer<typeof copyLocaleSchema>;
|
|
99
|
+
},
|
|
100
|
+
): Promise<WriteResult<CopyRequest>> {
|
|
101
|
+
const { slug, kind, fromLocale, toLocale, overwrite, scope } = event.payload;
|
|
102
|
+
if (scope === "system" && !event.user.roles.includes("SystemAdmin")) {
|
|
103
|
+
return writeFailure(
|
|
104
|
+
new AccessDeniedError({
|
|
105
|
+
i18nKey: "errors.access.denied",
|
|
106
|
+
details: { reason: TemplateResolverErrors.systemAdminRequired },
|
|
107
|
+
}),
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
const tenantId = scope === "system" ? SYSTEM_TENANT_ID : event.user.tenantId;
|
|
111
|
+
const refused = refuseTenantScopeUnderSystemTenant(scope, tenantId);
|
|
112
|
+
if (refused !== null) return refused;
|
|
113
|
+
|
|
114
|
+
const readDb = readDbFor(ctx, scope);
|
|
115
|
+
const storedSource = await findCopySource(ctx, {
|
|
116
|
+
scope,
|
|
117
|
+
tenantId,
|
|
118
|
+
slug,
|
|
119
|
+
kind,
|
|
120
|
+
locale: fromLocale,
|
|
121
|
+
});
|
|
122
|
+
const source = storedSource && toWorkingRow(storedSource, tenantId);
|
|
123
|
+
const contentFormat = source?.contentFormat;
|
|
124
|
+
if (!source || !isOneOf(CONTENT_FORMATS, contentFormat)) {
|
|
125
|
+
return writeFailure(new NotFoundError("template-resource", `${slug}/${fromLocale}`));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!overwrite) {
|
|
129
|
+
const target = await fetchOne<TemplateResourceRow>(readDb, templateResourcesTable, {
|
|
130
|
+
tenantId,
|
|
131
|
+
slug,
|
|
132
|
+
kind,
|
|
133
|
+
locale: toLocale,
|
|
134
|
+
});
|
|
135
|
+
if (target) {
|
|
136
|
+
return writeFailure(new ValidationError({ fields: [targetExistsIssue(toLocale)] }));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
isSuccess: true,
|
|
141
|
+
data: {
|
|
142
|
+
scope,
|
|
143
|
+
tenantId,
|
|
144
|
+
source,
|
|
145
|
+
contentFormat,
|
|
146
|
+
sourceResources: parseLinkedResourceEntries(source.linkedResources),
|
|
147
|
+
sourceIsForeign: source.tenantId !== tenantId,
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
63
152
|
export const copyLocaleWrite = defineWriteHandler({
|
|
64
153
|
name: "copy-locale",
|
|
65
|
-
schema:
|
|
66
|
-
slug: templateSlugSchema,
|
|
67
|
-
kind: kindSchema,
|
|
68
|
-
fromLocale: localeSchema,
|
|
69
|
-
toLocale: localeSchema,
|
|
70
|
-
overwrite: z.boolean().default(false),
|
|
71
|
-
/** "system" copies between system-default rows; SystemAdmin only. */
|
|
72
|
-
scope: z.enum(["tenant", "system"]).default("tenant"),
|
|
73
|
-
}),
|
|
154
|
+
schema: copyLocaleSchema,
|
|
74
155
|
access: { roles: ["TenantAdmin", "SystemAdmin"] },
|
|
75
156
|
description:
|
|
76
157
|
"Copies the text of one locale of a template into another locale as a draft override of the caller's tenant, taking the tenant's own row or else the system default as the source; use it as the starting point for a translation. It refuses an existing target unless overwrite is set. With scope system (SystemAdmin only) both source and target are system-default rows and the target lands as a system draft; any other caller gets a 403.",
|
|
158
|
+
beforeTransaction: async (event, ctx) => {
|
|
159
|
+
const request = await checkCopyRequest(ctx, event);
|
|
160
|
+
if (!request.isSuccess) return request;
|
|
161
|
+
if (!request.data.sourceIsForeign) return { isSuccess: true, data: [] };
|
|
162
|
+
return prepareLinkedResourceCopiesBeforeTransaction({
|
|
163
|
+
ctx,
|
|
164
|
+
user: event.user,
|
|
165
|
+
tenantId: request.data.tenantId,
|
|
166
|
+
resources: request.data.sourceResources,
|
|
167
|
+
});
|
|
168
|
+
},
|
|
77
169
|
handler: async (event, ctx) => {
|
|
78
|
-
const { slug, kind,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
details: { reason: TemplateResolverErrors.systemAdminRequired },
|
|
84
|
-
}),
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
const tenantId = scope === "system" ? SYSTEM_TENANT_ID : event.user.tenantId;
|
|
88
|
-
const refused = refuseTenantScopeUnderSystemTenant(scope, tenantId);
|
|
89
|
-
if (refused !== null) return refused;
|
|
90
|
-
|
|
91
|
-
const readDb = readDbFor(ctx, scope);
|
|
92
|
-
const source = await findCopySource(ctx, { scope, tenantId, slug, kind, locale: fromLocale });
|
|
93
|
-
if (!source || !isOneOf(CONTENT_FORMATS, source.contentFormat)) {
|
|
94
|
-
return writeFailure(new NotFoundError("template-resource", `${slug}/${fromLocale}`));
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (!overwrite) {
|
|
98
|
-
const target = await fetchOne<TemplateResourceRow>(readDb, templateResourcesTable, {
|
|
99
|
-
tenantId,
|
|
100
|
-
slug,
|
|
101
|
-
kind,
|
|
102
|
-
locale: toLocale,
|
|
103
|
-
});
|
|
104
|
-
if (target) {
|
|
105
|
-
return writeFailure(new ValidationError({ fields: [targetExistsIssue(toLocale)] }));
|
|
106
|
-
}
|
|
107
|
-
}
|
|
170
|
+
const { slug, kind, toLocale } = event.payload;
|
|
171
|
+
const request = await checkCopyRequest(ctx, event);
|
|
172
|
+
if (!request.isSuccess) return request;
|
|
173
|
+
const { scope, tenantId, source, contentFormat, sourceResources, sourceIsForeign } =
|
|
174
|
+
request.data;
|
|
108
175
|
|
|
109
176
|
const variableSchema = storedRecord(source.variableSchema);
|
|
110
177
|
const written = await writeTemplateRow({
|
|
@@ -119,7 +186,7 @@ export const copyLocaleWrite = defineWriteHandler({
|
|
|
119
186
|
kind,
|
|
120
187
|
locale: toLocale,
|
|
121
188
|
content: source.content,
|
|
122
|
-
contentFormat
|
|
189
|
+
contentFormat,
|
|
123
190
|
variableSchema,
|
|
124
191
|
meta: parseStoredJson(source.meta),
|
|
125
192
|
title: source.title,
|
|
@@ -127,16 +194,17 @@ export const copyLocaleWrite = defineWriteHandler({
|
|
|
127
194
|
},
|
|
128
195
|
});
|
|
129
196
|
if (!written.isSuccess) return written;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
197
|
+
const resources = sourceIsForeign
|
|
198
|
+
? await createLinkedResourceCopies({ ctx, user: event.user, tenantId }, event.prepared)
|
|
199
|
+
: { isSuccess: true as const, data: sourceResources };
|
|
200
|
+
if (!resources.isSuccess) return resources;
|
|
201
|
+
const attached = await attachTemplateResources(ctx, {
|
|
202
|
+
user: event.user,
|
|
203
|
+
tenantId,
|
|
204
|
+
templateId: written.data.id,
|
|
205
|
+
resources: resources.data,
|
|
206
|
+
});
|
|
207
|
+
if (!attached.isSuccess) return attached;
|
|
140
208
|
return written;
|
|
141
209
|
},
|
|
142
210
|
});
|
|
@@ -3,29 +3,8 @@ import { defineQueryHandler, SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framew
|
|
|
3
3
|
import { NotFoundError } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
4
|
import * as z from "zod";
|
|
5
5
|
import { type TemplateResourceRow, templateResourcesTable } from "../table.js";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
function parseStoredMeta(raw: string | null): Readonly<Record<string, unknown>> {
|
|
9
|
-
if (raw === null) return {};
|
|
10
|
-
try {
|
|
11
|
-
const parsed: unknown = JSON.parse(raw);
|
|
12
|
-
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)
|
|
13
|
-
? Object.fromEntries(Object.entries(parsed))
|
|
14
|
-
: {};
|
|
15
|
-
} catch {
|
|
16
|
-
return {};
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function diffMeta(
|
|
21
|
-
system: Readonly<Record<string, unknown>>,
|
|
22
|
-
tenant: Readonly<Record<string, unknown>>,
|
|
23
|
-
) {
|
|
24
|
-
const keys = [...new Set([...Object.keys(system), ...Object.keys(tenant)])].sort();
|
|
25
|
-
return keys
|
|
26
|
-
.filter((key) => JSON.stringify(system[key]) !== JSON.stringify(tenant[key]))
|
|
27
|
-
.map((key) => ({ key, tenant: tenant[key], system: system[key] }));
|
|
28
|
-
}
|
|
6
|
+
import { isHiddenSystemDraft } from "../template-draft.js";
|
|
7
|
+
import { buildOverrideDiff } from "./override-diff.js";
|
|
29
8
|
|
|
30
9
|
export const diffToSystemQuery = defineQueryHandler({
|
|
31
10
|
name: "diff-to-system",
|
|
@@ -45,28 +24,11 @@ export const diffToSystemQuery = defineQueryHandler({
|
|
|
45
24
|
slug: tenantRow.slug,
|
|
46
25
|
kind: tenantRow.kind,
|
|
47
26
|
locale: tenantRow.locale,
|
|
27
|
+
status: "active",
|
|
48
28
|
});
|
|
49
|
-
if (!systemRow
|
|
29
|
+
if (!systemRow || isHiddenSystemDraft(query.user, systemRow))
|
|
30
|
+
throw new NotFoundError("template-resource", tenantRow.slug);
|
|
50
31
|
|
|
51
|
-
|
|
52
|
-
splitTemplateBlocks(systemRow.content ?? "", systemRow.contentFormat),
|
|
53
|
-
splitTemplateBlocks(tenantRow.content ?? "", tenantRow.contentFormat),
|
|
54
|
-
);
|
|
55
|
-
const meta = diffMeta(parseStoredMeta(systemRow.meta), parseStoredMeta(tenantRow.meta));
|
|
56
|
-
return {
|
|
57
|
-
tenant: {
|
|
58
|
-
templateId: String(tenantRow.id),
|
|
59
|
-
templateVersion: tenantRow.version,
|
|
60
|
-
contentFormat: tenantRow.contentFormat,
|
|
61
|
-
},
|
|
62
|
-
system: {
|
|
63
|
-
templateId: String(systemRow.id),
|
|
64
|
-
templateVersion: systemRow.version,
|
|
65
|
-
contentFormat: systemRow.contentFormat,
|
|
66
|
-
},
|
|
67
|
-
identical: meta.length === 0 && blocks.every((block) => block.change === "same"),
|
|
68
|
-
blocks,
|
|
69
|
-
meta,
|
|
70
|
-
};
|
|
32
|
+
return buildOverrideDiff(tenantRow, systemRow);
|
|
71
33
|
},
|
|
72
34
|
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
import { NotFoundError, ValidationError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
3
|
+
import * as z from "zod";
|
|
4
|
+
import { TemplateResolverErrors } from "../qualified-names.js";
|
|
5
|
+
import { executor } from "./shared.js";
|
|
6
|
+
import { loadTargetRow, type StatusTarget, writeAccessFor } from "./toggle-status.write.js";
|
|
7
|
+
|
|
8
|
+
function createDiscardDraftHandler(name: string, target: StatusTarget, description: string) {
|
|
9
|
+
return defineWriteHandler({
|
|
10
|
+
name,
|
|
11
|
+
schema: z.object({ id: z.string().min(1) }),
|
|
12
|
+
access: writeAccessFor(target),
|
|
13
|
+
...(target === "system" ? { agent: { risk: "high" as const } } : {}),
|
|
14
|
+
description,
|
|
15
|
+
handler: async (event, ctx) => {
|
|
16
|
+
const { db, scoped, row } = await loadTargetRow(ctx, target, event.user, event.payload.id);
|
|
17
|
+
if (!row) return writeFailure(new NotFoundError("template-resource", event.payload.id));
|
|
18
|
+
if (row.draft === null) {
|
|
19
|
+
return writeFailure(
|
|
20
|
+
new ValidationError({
|
|
21
|
+
fields: [
|
|
22
|
+
{
|
|
23
|
+
path: "id",
|
|
24
|
+
code: TemplateResolverErrors.noDraft,
|
|
25
|
+
i18nKey: "templateResolver.errors.noDraft",
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
}),
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
const result = await executor.update(
|
|
32
|
+
{ id: row.id, version: row.version, changes: { draft: null } },
|
|
33
|
+
event.user,
|
|
34
|
+
db,
|
|
35
|
+
scoped ? { streamTenantId: scoped.streamTenantId } : undefined,
|
|
36
|
+
);
|
|
37
|
+
if (!result.isSuccess) return result;
|
|
38
|
+
return { isSuccess: true as const, data: { id: String(row.id), discarded: true } };
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const discardDraftWrite = createDiscardDraftHandler(
|
|
44
|
+
"discard-draft",
|
|
45
|
+
"own",
|
|
46
|
+
"Throws away the open draft of one template resource of the caller's tenant so the editor shows the active version again; the served version is untouched. Refused with no_draft when there is no draft.",
|
|
47
|
+
);
|
|
48
|
+
export const discardDraftSystemWrite = createDiscardDraftHandler(
|
|
49
|
+
"discard-draft-system",
|
|
50
|
+
"system",
|
|
51
|
+
"Throws away the open draft of one system-default template; the served version stays. SystemAdmin only, refused with no_draft when there is no draft.",
|
|
52
|
+
);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
1
2
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
3
|
import { defineQueryHandler, SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
-
import { NotFoundError } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
|
+
import { NotFoundError, ValidationError } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
5
|
import * as z from "zod";
|
|
5
6
|
import {
|
|
6
7
|
isFileAttachmentEntry,
|
|
@@ -8,12 +9,63 @@ import {
|
|
|
8
9
|
type LinkedResourceEntry,
|
|
9
10
|
parseLinkedResourceEntries,
|
|
10
11
|
} from "../linked-resources.js";
|
|
12
|
+
import { TemplateResolverErrors } from "../qualified-names.js";
|
|
11
13
|
import { type TemplateResourceRow, templateResourcesTable } from "../table.js";
|
|
12
14
|
import type { TemplateBundle } from "../template-bundle.js";
|
|
13
15
|
import { readTemplateResource } from "../template-resources.js";
|
|
14
16
|
import { kindSchema, templateSlugSchema } from "./shared.js";
|
|
15
17
|
import { parseStoredJson, storedRecord } from "./template-gates.js";
|
|
16
18
|
|
|
19
|
+
// The files live in the system tenant's key space; readTemplateResource only
|
|
20
|
+
// serves the caller's own tenant, so anyone else would end in a misleading NotFound.
|
|
21
|
+
function assertSystemFilesReadable(
|
|
22
|
+
scope: "tenant" | "system",
|
|
23
|
+
entries: Iterable<LinkedResourceEntry>,
|
|
24
|
+
callerTenantId: string,
|
|
25
|
+
): void {
|
|
26
|
+
// skip: tenant exports read the caller's own files.
|
|
27
|
+
if (scope === "tenant") return;
|
|
28
|
+
// skip: the system tenant reads its own key space.
|
|
29
|
+
if (callerTenantId === SYSTEM_TENANT_ID) return;
|
|
30
|
+
const hasFileResources = [...entries].some((entry) => !isPdfAttachmentLink(entry));
|
|
31
|
+
// skip: PDF attachment links carry no file to read.
|
|
32
|
+
if (!hasFileResources) return;
|
|
33
|
+
throw new ValidationError({
|
|
34
|
+
fields: [
|
|
35
|
+
{
|
|
36
|
+
path: "scope",
|
|
37
|
+
code: "system_resources_need_system_tenant",
|
|
38
|
+
i18nKey: "templateResolver.errors.template.systemResourcesNeedSystemTenant",
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// A bundle carries one contentFormat and one variableSchema for all locales; importing it would
|
|
45
|
+
// relabel the rows that differ from the first.
|
|
46
|
+
function assertLocalesShareFormat(rows: readonly TemplateResourceRow[]): void {
|
|
47
|
+
const [first, ...rest] = rows;
|
|
48
|
+
// skip: a bundle without rows has nothing to compare
|
|
49
|
+
if (first === undefined) return;
|
|
50
|
+
const firstSchema = storedRecord(first.variableSchema);
|
|
51
|
+
const differing = rest.find(
|
|
52
|
+
(row) =>
|
|
53
|
+
row.contentFormat !== first.contentFormat ||
|
|
54
|
+
!isDeepStrictEqual(storedRecord(row.variableSchema), firstSchema),
|
|
55
|
+
);
|
|
56
|
+
// skip: all locales agree
|
|
57
|
+
if (differing === undefined) return;
|
|
58
|
+
throw new ValidationError({
|
|
59
|
+
fields: [
|
|
60
|
+
{
|
|
61
|
+
path: "slug",
|
|
62
|
+
code: TemplateResolverErrors.bundleLocalesDiffer,
|
|
63
|
+
i18nKey: "templateResolver.errors.template.bundleLocalesDiffer",
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
17
69
|
export const exportBundleQuery = defineQueryHandler({
|
|
18
70
|
name: "export-bundle",
|
|
19
71
|
schema: z.object({
|
|
@@ -38,6 +90,7 @@ export const exportBundleQuery = defineQueryHandler({
|
|
|
38
90
|
.sort((a, b) => a.locale.localeCompare(b.locale));
|
|
39
91
|
const first = rows[0];
|
|
40
92
|
if (!first) throw new NotFoundError("template-resource", slug);
|
|
93
|
+
assertLocalesShareFormat(rows);
|
|
41
94
|
|
|
42
95
|
const locales: Record<string, { body: string; meta?: unknown }> = {};
|
|
43
96
|
const entryByKey = new Map<string, LinkedResourceEntry>();
|
|
@@ -49,6 +102,8 @@ export const exportBundleQuery = defineQueryHandler({
|
|
|
49
102
|
}
|
|
50
103
|
}
|
|
51
104
|
|
|
105
|
+
assertSystemFilesReadable(scope, entryByKey.values(), query.user.tenantId);
|
|
106
|
+
|
|
52
107
|
const resources: TemplateBundle["resources"] = {};
|
|
53
108
|
const pdfAttachments: TemplateBundle["pdfAttachments"] = {};
|
|
54
109
|
for (const [key, entry] of entryByKey) {
|
|
@@ -2,6 +2,7 @@ import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
|
2
2
|
import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import * as z from "zod";
|
|
4
4
|
import { type TemplateResourceRow, templateResourcesTable } from "../table.js";
|
|
5
|
+
import { draftViewerTenantId, isHiddenSystemDraft, toWorkingRow } from "../template-draft.js";
|
|
5
6
|
|
|
6
7
|
// Admin-Lookup für UI-Edit-Flow. Returnt das raw Template inkl. draft/
|
|
7
8
|
// archived Status. Tenant-Isolation: User sieht nur Templates des
|
|
@@ -13,12 +14,13 @@ export const findByIdQuery = defineQueryHandler({
|
|
|
13
14
|
schema: z.object({ id: z.string().min(1) }),
|
|
14
15
|
access: { roles: ["TenantAdmin", "SystemAdmin", "User"] },
|
|
15
16
|
description:
|
|
16
|
-
"Reads one template resource by row id with
|
|
17
|
+
"Reads the version of one template resource an editor works on, by row id: the open draft if there is one (status draft), else the stored version, with content, variable schema and scope, but only when it belongs to the caller's tenant or is a served system default (a never-served system draft is visible to a SystemAdmin only); `active` is the version number and since-time the resolver serves, null when none is served. Use it to load a template into the admin editor.",
|
|
17
18
|
handler: async (query, ctx) => {
|
|
18
|
-
const
|
|
19
|
+
const stored = await fetchOne<TemplateResourceRow>(ctx.db, templateResourcesTable, {
|
|
19
20
|
id: query.payload.id,
|
|
20
21
|
});
|
|
21
|
-
if (!
|
|
22
|
+
if (!stored || isHiddenSystemDraft(query.user, stored)) return null;
|
|
23
|
+
const row = toWorkingRow(stored, draftViewerTenantId(query.user, stored));
|
|
22
24
|
const isOwnTenant = row.tenantId === query.user.tenantId;
|
|
23
25
|
const isSystemTemplate = row.scope === "system";
|
|
24
26
|
if (!isOwnTenant && !isSystemTemplate) return null;
|
|
@@ -39,6 +41,7 @@ export const findByIdQuery = defineQueryHandler({
|
|
|
39
41
|
parentTemplateId: row.parentTemplateId,
|
|
40
42
|
status: row.status,
|
|
41
43
|
modifiedAt: row.modifiedAt,
|
|
44
|
+
active: row.active,
|
|
42
45
|
};
|
|
43
46
|
},
|
|
44
47
|
});
|
package/packages/bundled-features/src/template-resolver/handlers/import-bundle-system.write.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { defineWriteHandler, SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framew
|
|
|
2
2
|
import { ValidationError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
3
3
|
import * as z from "zod";
|
|
4
4
|
import { templateBundleSchema } from "../template-bundle.js";
|
|
5
|
-
import { importTemplateBundle } from "./bundle-import.js";
|
|
5
|
+
import { importTemplateBundle, prepareTemplateBundleBeforeTransaction } from "./bundle-import.js";
|
|
6
6
|
|
|
7
7
|
export const importBundleSystemWrite = defineWriteHandler({
|
|
8
8
|
name: "import-bundle-system",
|
|
@@ -12,24 +12,23 @@ export const importBundleSystemWrite = defineWriteHandler({
|
|
|
12
12
|
agent: { risk: "high" as const },
|
|
13
13
|
description:
|
|
14
14
|
"Imports a template bundle as system-default templates, active straight away and overwriting existing system rows; SystemAdmin only. Bundles with resource files need a caller in the system tenant, because the files are stored in the caller's file space.",
|
|
15
|
-
|
|
15
|
+
beforeTransaction: async (event, ctx) => {
|
|
16
16
|
const { bundle } = event.payload;
|
|
17
|
+
// Must run before any upload.
|
|
17
18
|
if (Object.keys(bundle.resources).length > 0 && event.user.tenantId !== SYSTEM_TENANT_ID) {
|
|
18
|
-
return
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}),
|
|
29
|
-
),
|
|
19
|
+
return writeFailure(
|
|
20
|
+
new ValidationError({
|
|
21
|
+
fields: [
|
|
22
|
+
{
|
|
23
|
+
path: "bundle.resources",
|
|
24
|
+
code: "system_resources_need_system_tenant",
|
|
25
|
+
i18nKey: "templateResolver.errors.template.systemResourcesNeedSystemTenant",
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
}),
|
|
30
29
|
);
|
|
31
30
|
}
|
|
32
|
-
return
|
|
31
|
+
return prepareTemplateBundleBeforeTransaction({
|
|
33
32
|
ctx,
|
|
34
33
|
user: event.user,
|
|
35
34
|
tenantId: SYSTEM_TENANT_ID,
|
|
@@ -39,4 +38,15 @@ export const importBundleSystemWrite = defineWriteHandler({
|
|
|
39
38
|
overwrite: true,
|
|
40
39
|
});
|
|
41
40
|
},
|
|
41
|
+
handler: (event, ctx) =>
|
|
42
|
+
importTemplateBundle({
|
|
43
|
+
ctx,
|
|
44
|
+
user: event.user,
|
|
45
|
+
tenantId: SYSTEM_TENANT_ID,
|
|
46
|
+
scope: "system",
|
|
47
|
+
status: "active",
|
|
48
|
+
bundle: event.payload.bundle,
|
|
49
|
+
overwrite: true,
|
|
50
|
+
prepared: event.prepared,
|
|
51
|
+
}),
|
|
42
52
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import * as z from "zod";
|
|
3
3
|
import { templateBundleSchema } from "../template-bundle.js";
|
|
4
|
-
import { importTemplateBundle } from "./bundle-import.js";
|
|
4
|
+
import { importTemplateBundle, prepareTemplateBundleBeforeTransaction } from "./bundle-import.js";
|
|
5
5
|
|
|
6
6
|
export const importBundleWrite = defineWriteHandler({
|
|
7
7
|
name: "import-bundle",
|
|
@@ -9,6 +9,16 @@ export const importBundleWrite = defineWriteHandler({
|
|
|
9
9
|
access: { roles: ["TenantAdmin", "SystemAdmin"] },
|
|
10
10
|
description:
|
|
11
11
|
"Imports a template bundle (all locales plus resource files, as produced by export-bundle) into the caller's tenant as drafts; it refuses a locale that already has an override unless overwrite is set, and publish makes the drafts live.",
|
|
12
|
+
beforeTransaction: (event, ctx) =>
|
|
13
|
+
prepareTemplateBundleBeforeTransaction({
|
|
14
|
+
ctx,
|
|
15
|
+
user: event.user,
|
|
16
|
+
tenantId: event.user.tenantId,
|
|
17
|
+
scope: "tenant",
|
|
18
|
+
status: "draft",
|
|
19
|
+
bundle: event.payload.bundle,
|
|
20
|
+
overwrite: event.payload.overwrite,
|
|
21
|
+
}),
|
|
12
22
|
handler: (event, ctx) =>
|
|
13
23
|
importTemplateBundle({
|
|
14
24
|
ctx,
|
|
@@ -18,5 +28,6 @@ export const importBundleWrite = defineWriteHandler({
|
|
|
18
28
|
status: "draft",
|
|
19
29
|
bundle: event.payload.bundle,
|
|
20
30
|
overwrite: event.payload.overwrite,
|
|
31
|
+
prepared: event.prepared,
|
|
21
32
|
}),
|
|
22
33
|
});
|