@cosmicdrift/kumiko-bundled-features 0.377.0 → 0.378.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cap-counter/stock-cap-guard.js +8 -0
- package/dist/config/web/i18n.js +1 -0
- package/dist/inbound-mail-foundation/handlers/list-accounts.query.d.ts +1 -1
- package/dist/inbound-mail-foundation/handlers/list-accounts.query.js +11 -3
- package/dist/mail-foundation/i18n.d.ts +5 -2
- package/dist/mail-foundation/i18n.js +3 -2
- package/dist/mail-transport-smtp/i18n.d.ts +1 -1
- package/dist/mail-transport-smtp/i18n.js +1 -1
- package/dist/template-resolver/compile-document-body.d.ts +3 -0
- package/dist/template-resolver/compile-document-body.js +50 -3
- package/dist/template-resolver/constants.d.ts +3 -0
- package/dist/template-resolver/constants.js +2 -0
- package/dist/template-resolver/document-blocks.d.ts +2 -2
- package/dist/template-resolver/document-blocks.js +1 -0
- package/dist/template-resolver/document-images.d.ts +2 -0
- package/dist/template-resolver/document-images.js +28 -0
- package/dist/template-resolver/document-sanitizer.d.ts +1 -1
- package/dist/template-resolver/document-sanitizer.js +20 -0
- package/dist/template-resolver/document-schema.d.ts +2 -1
- package/dist/template-resolver/document-schema.js +4 -0
- package/dist/template-resolver/feature.d.ts +9 -0
- package/dist/template-resolver/feature.js +58 -33
- package/dist/template-resolver/handlers/collection-item.query.js +2 -1
- package/dist/template-resolver/handlers/collection-list.query.js +2 -1
- package/dist/template-resolver/handlers/collection-set.write.js +2 -2
- package/dist/template-resolver/handlers/collection-shared.d.ts +1 -2
- package/dist/template-resolver/handlers/collection-shared.js +0 -4
- package/dist/template-resolver/handlers/copy-locale.write.d.ts +2 -1
- package/dist/template-resolver/handlers/copy-locale.write.js +67 -63
- package/dist/template-resolver/handlers/discard-draft.write.d.ts +3 -2
- package/dist/template-resolver/handlers/discard-draft.write.js +18 -6
- package/dist/template-resolver/handlers/import-bundle.write.d.ts +2 -1
- package/dist/template-resolver/handlers/import-bundle.write.js +30 -25
- package/dist/template-resolver/handlers/overview.query.d.ts +2 -1
- package/dist/template-resolver/handlers/overview.query.js +46 -42
- package/dist/template-resolver/handlers/patch-draft.write.d.ts +2 -1
- package/dist/template-resolver/handlers/patch-draft.write.js +92 -86
- package/dist/template-resolver/handlers/reset-to-system.write.d.ts +2 -1
- package/dist/template-resolver/handlers/reset-to-system.write.js +51 -44
- package/dist/template-resolver/handlers/resource-link-pdf.write.d.ts +2 -1
- package/dist/template-resolver/handlers/resource-link-pdf.write.js +34 -28
- package/dist/template-resolver/handlers/resource-remove.write.d.ts +2 -1
- package/dist/template-resolver/handlers/resource-remove.write.js +39 -33
- package/dist/template-resolver/handlers/resource-upload.write.d.ts +2 -1
- package/dist/template-resolver/handlers/resource-upload.write.js +104 -96
- package/dist/template-resolver/handlers/send-test-shared.d.ts +5 -0
- package/dist/template-resolver/handlers/send-test-shared.js +19 -0
- package/dist/template-resolver/handlers/send-test-status.query.d.ts +9 -0
- package/dist/template-resolver/handlers/send-test-status.query.js +18 -0
- package/dist/template-resolver/handlers/send-test.write.d.ts +2 -1
- package/dist/template-resolver/handlers/send-test.write.js +87 -89
- package/dist/template-resolver/handlers/set.write.d.ts +2 -1
- package/dist/template-resolver/handlers/set.write.js +94 -90
- package/dist/template-resolver/handlers/toggle-status.write.d.ts +6 -7
- package/dist/template-resolver/handlers/toggle-status.write.js +28 -10
- package/dist/template-resolver/handlers/upsert-tenant.write.d.ts +2 -1
- package/dist/template-resolver/handlers/upsert-tenant.write.js +30 -24
- package/dist/template-resolver/index.d.ts +4 -2
- package/dist/template-resolver/index.js +3 -1
- package/dist/template-resolver/qualified-names.d.ts +2 -0
- package/dist/template-resolver/qualified-names.js +2 -0
- package/dist/template-resolver/template-edit-policy.d.ts +22 -0
- package/dist/template-resolver/template-edit-policy.js +45 -0
- package/dist/template-resolver/template-preview.js +7 -2
- package/dist/template-resolver/template-validation.js +33 -13
- package/dist/template-resolver/web/document-editor-extensions.d.ts +1 -0
- package/dist/template-resolver/web/document-editor-extensions.js +21 -1
- package/dist/template-resolver/web/document-tiptap-editor.d.ts +1 -1
- package/dist/template-resolver/web/document-tiptap-editor.js +22 -6
- package/dist/template-resolver/web/document-toolbar-blocks.d.ts +2 -0
- package/dist/template-resolver/web/i18n.js +4 -0
- package/package.json +8 -8
- package/src/cap-counter/changes.json +12 -0
- package/src/inbound-mail-foundation/changes.json +6 -0
- package/src/mail-foundation/changes.json +6 -0
- package/src/template-resolver/changes.json +20 -0
|
@@ -3,9 +3,9 @@ import { defineWriteHandler, } from "@cosmicdrift/kumiko-framework/engine";
|
|
|
3
3
|
import { ValidationError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
4
|
import * as z from "zod";
|
|
5
5
|
import { requireForTenant } from "../../shared/index.js";
|
|
6
|
-
import { isCollectionRefusedKind } from "../constants.js";
|
|
6
|
+
import { DEFAULT_COLLECTION_ACCESS, isCollectionRefusedKind } from "../constants.js";
|
|
7
7
|
import { documentViolationsToFieldIssues, sanitizeTemplateBody } from "../document-sanitizer.js";
|
|
8
|
-
import { collectionStore
|
|
8
|
+
import { collectionStore } from "./collection-shared.js";
|
|
9
9
|
import { contentFormatSchema, folderSchema, localeSchema, slugSchema } from "./shared.js";
|
|
10
10
|
// Upsert inside one collection. Mirrors set.write for text-blocks, but the
|
|
11
11
|
// kind comes from the declaration and the access rule is the collection's own,
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { EventStoreExecutor } from "@cosmicdrift/kumiko-framework/db";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ContentCollectionDefinition, SessionUser, TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
4
4
|
import { templateResourcesTable } from "../table.ts";
|
|
5
5
|
import { userContentEntriesTable } from "../user-content-table.ts";
|
|
6
|
-
export declare const DEFAULT_COLLECTION_ACCESS: AccessRule;
|
|
7
6
|
export type CollectionEntryRow = {
|
|
8
7
|
readonly id: string | number;
|
|
9
8
|
readonly version: number;
|
|
@@ -2,10 +2,6 @@ import { createEventStoreExecutor } from "@cosmicdrift/kumiko-framework/db";
|
|
|
2
2
|
import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { templateResourceEntity, templateResourcesTable } from "../table.js";
|
|
4
4
|
import { userContentEntriesTable, userContentEntryEntity } from "../user-content-table.js";
|
|
5
|
-
// Applies when an app mounts a collection without saying who may reach it.
|
|
6
|
-
// Deliberately narrow: a collection whose access nobody decided should be
|
|
7
|
-
// invisible to normal users rather than open by default.
|
|
8
|
-
export const DEFAULT_COLLECTION_ACCESS = { roles: ["TenantAdmin", "SystemAdmin"] };
|
|
9
5
|
const templateExecutor = createEventStoreExecutor(templateResourcesTable, templateResourceEntity, {
|
|
10
6
|
entityName: "template-resource",
|
|
11
7
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
|
-
|
|
2
|
+
import { type TemplateEditPolicy } from "../template-edit-policy.ts";
|
|
3
|
+
export declare function makeCopyLocaleWrite(policy: TemplateEditPolicy): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"copy-locale", z.ZodObject<{
|
|
3
4
|
slug: z.ZodString;
|
|
4
5
|
kind: z.ZodEnum<{
|
|
5
6
|
"ai-prompt": "ai-prompt";
|
|
@@ -10,6 +10,7 @@ import { TemplateResolverErrors } from "../qualified-names.js";
|
|
|
10
10
|
import { createLinkedResourceCopies, prepareLinkedResourceCopiesBeforeTransaction, } from "../store-template-resource.js";
|
|
11
11
|
import { templateResourcesTable } from "../table.js";
|
|
12
12
|
import { toWorkingRow } from "../template-draft.js";
|
|
13
|
+
import { refuseTemplateEdit, templateEditAccess, } from "../template-edit-policy.js";
|
|
13
14
|
import { attachTemplateResources } from "../template-resources.js";
|
|
14
15
|
import { resolveTemplateVariables } from "../template-validation.js";
|
|
15
16
|
import { targetExistsIssue } from "./bundle-import.js";
|
|
@@ -50,7 +51,7 @@ const copyLocaleSchema = z.object({
|
|
|
50
51
|
scope: z.enum(["tenant", "system"]).default("tenant"),
|
|
51
52
|
});
|
|
52
53
|
// Runs before the upload (reject early, store nothing) and again in the transaction on the fresh rows.
|
|
53
|
-
async function checkCopyRequest(ctx, event) {
|
|
54
|
+
async function checkCopyRequest(ctx, policy, event) {
|
|
54
55
|
const { slug, kind, fromLocale, toLocale, overwrite, scope } = event.payload;
|
|
55
56
|
if (scope === "system" && !event.user.roles.includes("SystemAdmin")) {
|
|
56
57
|
return writeFailure(new AccessDeniedError({
|
|
@@ -59,7 +60,8 @@ async function checkCopyRequest(ctx, event) {
|
|
|
59
60
|
}));
|
|
60
61
|
}
|
|
61
62
|
const tenantId = scope === "system" ? SYSTEM_TENANT_ID : event.user.tenantId;
|
|
62
|
-
const refused =
|
|
63
|
+
const refused = (scope === "tenant" ? refuseTemplateEdit(policy, event.user, { slug, kind }) : null) ??
|
|
64
|
+
refuseTenantScopeUnderSystemTenant(scope, tenantId) ??
|
|
63
65
|
refuseSystemOnlyTenantOverride(ctx.registry, scope, slug, kind);
|
|
64
66
|
if (refused !== null)
|
|
65
67
|
return refused;
|
|
@@ -99,65 +101,67 @@ async function checkCopyRequest(ctx, event) {
|
|
|
99
101
|
},
|
|
100
102
|
};
|
|
101
103
|
}
|
|
102
|
-
export
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
104
|
+
export function makeCopyLocaleWrite(policy) {
|
|
105
|
+
return defineWriteHandler({
|
|
106
|
+
name: "copy-locale",
|
|
107
|
+
schema: copyLocaleSchema,
|
|
108
|
+
access: templateEditAccess(policy.tenantEditable),
|
|
109
|
+
description: "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. A TenantAdmin may copy only templates the app lists in tenantEditable, otherwise SystemAdmin only.",
|
|
110
|
+
beforeTransaction: async (event, ctx) => {
|
|
111
|
+
const request = await checkCopyRequest(ctx, policy, event);
|
|
112
|
+
if (!request.isSuccess)
|
|
113
|
+
return request;
|
|
114
|
+
if (!request.data.sourceIsForeign)
|
|
115
|
+
return { isSuccess: true, data: [] };
|
|
116
|
+
return prepareLinkedResourceCopiesBeforeTransaction({
|
|
117
|
+
ctx,
|
|
118
|
+
user: event.user,
|
|
119
|
+
tenantId: request.data.tenantId,
|
|
120
|
+
resources: request.data.sourceResources,
|
|
121
|
+
});
|
|
122
|
+
},
|
|
123
|
+
handler: async (event, ctx) => {
|
|
124
|
+
const { slug, kind, toLocale } = event.payload;
|
|
125
|
+
const request = await checkCopyRequest(ctx, policy, event);
|
|
126
|
+
if (!request.isSuccess)
|
|
127
|
+
return request;
|
|
128
|
+
const { scope, tenantId, source, contentFormat, sourceResources, sourceIsForeign } = request.data;
|
|
129
|
+
const variableSchema = storedRecord(source.variableSchema);
|
|
130
|
+
const written = await writeTemplateRow({
|
|
131
|
+
ctx,
|
|
132
|
+
user: event.user,
|
|
133
|
+
tenantId,
|
|
134
|
+
scope,
|
|
135
|
+
status: "draft",
|
|
136
|
+
variables: resolveTemplateVariables(ctx.registry, slug, variableSchema),
|
|
137
|
+
payload: {
|
|
138
|
+
slug,
|
|
139
|
+
kind,
|
|
140
|
+
locale: toLocale,
|
|
141
|
+
content: source.content,
|
|
142
|
+
contentFormat,
|
|
143
|
+
variableSchema,
|
|
144
|
+
meta: parseStoredJson(source.meta),
|
|
145
|
+
title: source.title,
|
|
146
|
+
folder: source.folder,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
if (!written.isSuccess)
|
|
150
|
+
return written;
|
|
151
|
+
const resources = sourceIsForeign
|
|
152
|
+
? await createLinkedResourceCopies({ ctx, user: event.user, tenantId }, event.prepared)
|
|
153
|
+
: { isSuccess: true, data: sourceResources };
|
|
154
|
+
if (!resources.isSuccess)
|
|
155
|
+
return resources;
|
|
156
|
+
const attached = await attachTemplateResources(ctx, {
|
|
157
|
+
user: event.user,
|
|
158
|
+
tenantId,
|
|
159
|
+
templateId: written.data.id,
|
|
160
|
+
resources: resources.data,
|
|
161
|
+
});
|
|
162
|
+
if (!attached.isSuccess)
|
|
163
|
+
return attached;
|
|
147
164
|
return written;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if (!resources.isSuccess)
|
|
152
|
-
return resources;
|
|
153
|
-
const attached = await attachTemplateResources(ctx, {
|
|
154
|
-
user: event.user,
|
|
155
|
-
tenantId,
|
|
156
|
-
templateId: written.data.id,
|
|
157
|
-
resources: resources.data,
|
|
158
|
-
});
|
|
159
|
-
if (!attached.isSuccess)
|
|
160
|
-
return attached;
|
|
161
|
-
return written;
|
|
162
|
-
},
|
|
163
|
-
});
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
|
-
|
|
2
|
+
import { type TemplateEditPolicy } from "../template-edit-policy.ts";
|
|
3
|
+
export declare function makeDiscardDraftWrite(policy: TemplateEditPolicy): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<string, z.ZodObject<{
|
|
3
4
|
id: z.ZodString;
|
|
4
5
|
}, z.core.$strip>, {
|
|
5
6
|
id: string;
|
|
6
7
|
discarded: boolean;
|
|
7
8
|
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
|
|
8
|
-
export declare
|
|
9
|
+
export declare function makeDiscardDraftSystemWrite(policy: TemplateEditPolicy): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<string, z.ZodObject<{
|
|
9
10
|
id: z.ZodString;
|
|
10
11
|
}, z.core.$strip>, {
|
|
11
12
|
id: string;
|
|
@@ -2,19 +2,27 @@ import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
|
2
2
|
import { NotFoundError, ValidationError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
3
3
|
import * as z from "zod";
|
|
4
4
|
import { TemplateResolverErrors } from "../qualified-names.js";
|
|
5
|
+
import { refuseMissingTemplate, refuseTemplateEdit, } from "../template-edit-policy.js";
|
|
5
6
|
import { executor } from "./shared.js";
|
|
6
7
|
import { loadTargetRow, writeAccessFor } from "./toggle-status.write.js";
|
|
7
|
-
function createDiscardDraftHandler(name, target, description) {
|
|
8
|
+
function createDiscardDraftHandler(policy, name, target, description) {
|
|
8
9
|
return defineWriteHandler({
|
|
9
10
|
name,
|
|
10
11
|
schema: z.object({ id: z.string().min(1) }),
|
|
11
|
-
access: writeAccessFor(target),
|
|
12
|
+
access: writeAccessFor(target, policy),
|
|
12
13
|
...(target === "system" ? { agent: { risk: "high" } } : {}),
|
|
13
14
|
description,
|
|
14
15
|
handler: async (event, ctx) => {
|
|
15
16
|
const { db, scoped, row } = await loadTargetRow(ctx, target, event.user, event.payload.id);
|
|
16
|
-
if (!row)
|
|
17
|
-
|
|
17
|
+
if (!row) {
|
|
18
|
+
const refused = target === "own" ? refuseMissingTemplate(policy, event.user) : null;
|
|
19
|
+
return refused ?? writeFailure(new NotFoundError("template-resource", event.payload.id));
|
|
20
|
+
}
|
|
21
|
+
if (target === "own") {
|
|
22
|
+
const refused = refuseTemplateEdit(policy, event.user, row);
|
|
23
|
+
if (refused !== null)
|
|
24
|
+
return refused;
|
|
25
|
+
}
|
|
18
26
|
if (row.draft === null) {
|
|
19
27
|
return writeFailure(new ValidationError({
|
|
20
28
|
fields: [
|
|
@@ -33,5 +41,9 @@ function createDiscardDraftHandler(name, target, description) {
|
|
|
33
41
|
},
|
|
34
42
|
});
|
|
35
43
|
}
|
|
36
|
-
export
|
|
37
|
-
|
|
44
|
+
export function makeDiscardDraftWrite(policy) {
|
|
45
|
+
return createDiscardDraftHandler(policy, "discard-draft", "own", "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. Outside the templates the app lists in tenantEditable only a SystemAdmin may use it.");
|
|
46
|
+
}
|
|
47
|
+
export function makeDiscardDraftSystemWrite(policy) {
|
|
48
|
+
return createDiscardDraftHandler(policy, "discard-draft-system", "system", "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.");
|
|
49
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
|
-
|
|
2
|
+
import { type TemplateEditPolicy } from "../template-edit-policy.ts";
|
|
3
|
+
export declare function makeImportBundleWrite(policy: TemplateEditPolicy): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"import-bundle", z.ZodObject<{
|
|
3
4
|
bundle: z.ZodObject<{
|
|
4
5
|
slug: z.ZodString;
|
|
5
6
|
kind: z.ZodEnum<{
|
|
@@ -1,29 +1,34 @@
|
|
|
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 { refuseTemplateEdit, templateEditAccess, } from "../template-edit-policy.js";
|
|
4
5
|
import { importTemplateBundle, prepareTemplateBundleBeforeTransaction } from "./bundle-import.js";
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
ctx,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
6
|
+
export function makeImportBundleWrite(policy) {
|
|
7
|
+
return defineWriteHandler({
|
|
8
|
+
name: "import-bundle",
|
|
9
|
+
schema: z.object({ bundle: templateBundleSchema, overwrite: z.boolean().default(false) }),
|
|
10
|
+
access: templateEditAccess(policy.tenantEditable),
|
|
11
|
+
description: "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. Outside the templates the app lists in tenantEditable only a SystemAdmin may use it.",
|
|
12
|
+
beforeTransaction: async (event, ctx) => refuseTemplateEdit(policy, event.user, event.payload.bundle) ??
|
|
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
|
+
}),
|
|
22
|
+
handler: async (event, ctx) => refuseTemplateEdit(policy, event.user, event.payload.bundle) ??
|
|
23
|
+
importTemplateBundle({
|
|
24
|
+
ctx,
|
|
25
|
+
user: event.user,
|
|
26
|
+
tenantId: event.user.tenantId,
|
|
27
|
+
scope: "tenant",
|
|
28
|
+
status: "draft",
|
|
29
|
+
bundle: event.payload.bundle,
|
|
30
|
+
overwrite: event.payload.overwrite,
|
|
31
|
+
prepared: event.prepared,
|
|
32
|
+
}),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
import { type TemplateStatus } from "../constants.ts";
|
|
3
3
|
import { type TemplateResourceRow } from "../table.ts";
|
|
4
|
+
import { type TemplateEditPolicy } from "../template-edit-policy.ts";
|
|
4
5
|
import { type EffectiveMailLayoutSlugs } from "../template-usage.ts";
|
|
5
6
|
declare const OVERVIEW_KINDS: readonly ["mail", "document-pdf", "mail-layout", "pdf-layout"];
|
|
6
7
|
type OverviewKind = (typeof OVERVIEW_KINDS)[number];
|
|
@@ -38,7 +39,7 @@ export type OverviewResponse = {
|
|
|
38
39
|
readonly truncated: boolean;
|
|
39
40
|
};
|
|
40
41
|
export declare function effectiveMailLayoutSlugs(rows: readonly TemplateResourceRow[]): EffectiveMailLayoutSlugs;
|
|
41
|
-
export declare
|
|
42
|
+
export declare function makeOverviewQuery(policy: TemplateEditPolicy): import("@cosmicdrift/kumiko-types/define-handler").QueryHandlerDefinition<"overview", z.ZodObject<{
|
|
42
43
|
search: z.ZodOptional<z.ZodString>;
|
|
43
44
|
filters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
44
45
|
field: z.ZodEnum<{
|
|
@@ -4,6 +4,7 @@ import * as z from "zod";
|
|
|
4
4
|
import { DEFAULT_MAIL_LAYOUT_SLUG, FALLBACK_LOCALE } from "../constants.js";
|
|
5
5
|
import { templateResourcesTable } from "../table.js";
|
|
6
6
|
import { activeSinceOf, activeVersionOf, draftViewerTenantId, isHiddenSystemDraft, parseTemplateDraft, servedVersionOf, } from "../template-draft.js";
|
|
7
|
+
import { mayEditTemplate } from "../template-edit-policy.js";
|
|
7
8
|
import { isSystemOnlyTemplateHiddenFrom } from "../template-system-only.js";
|
|
8
9
|
import { collectTemplateUsage } from "../template-usage.js";
|
|
9
10
|
import { storedRecord } from "./template-gates.js";
|
|
@@ -171,45 +172,48 @@ const filterSchema = z.object({
|
|
|
171
172
|
op: z.literal("in"),
|
|
172
173
|
value: z.array(z.string()),
|
|
173
174
|
});
|
|
174
|
-
export
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
!
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
175
|
+
export function makeOverviewQuery(policy) {
|
|
176
|
+
return defineQueryHandler({
|
|
177
|
+
name: "overview",
|
|
178
|
+
schema: z.object({
|
|
179
|
+
search: z.string().optional(),
|
|
180
|
+
filters: z.array(filterSchema).optional(),
|
|
181
|
+
layoutSlug: z.string().optional(),
|
|
182
|
+
sort: z.string().optional(),
|
|
183
|
+
sortDirection: z.enum(["asc", "desc"]).optional(),
|
|
184
|
+
limit: z.number().int().min(1).max(200).default(50),
|
|
185
|
+
offset: z.number().int().min(0).optional(),
|
|
186
|
+
totalCount: z.boolean().optional(),
|
|
187
|
+
}),
|
|
188
|
+
access: { roles: ["TenantAdmin", "SystemAdmin"] },
|
|
189
|
+
description: "Lists mail, PDF and layout templates as one row per slug and kind for the caller's tenant (optionally only the mails and PDFs using one layout slug): where the effective version comes from (own override or system), its locales, status and version (the served one; `draftVersion` is set when an unpublished draft waits beside it, `activeSince` says since when the served version applies), the system version it overrides, the layout it uses and which notifications or templates use it; templates of notifications registered with `systemOnly` are listed for SystemAdmins only, and a TenantAdmin sees only the templates the app lists in tenantEditable; use it to browse and filter all templates at a glance.",
|
|
190
|
+
handler: async (query, ctx) => {
|
|
191
|
+
const payload = query.payload;
|
|
192
|
+
const fetchedRows = await selectMany(ctx.db, templateResourcesTable, { kind: { in: [...OVERVIEW_KINDS] } }, { orderBy: { col: "id", direction: "asc" }, limit: OVERVIEW_ROW_LIMIT + 1 });
|
|
193
|
+
const truncated = fetchedRows.length > OVERVIEW_ROW_LIMIT;
|
|
194
|
+
const rows = truncated ? fetchedRows.slice(0, OVERVIEW_ROW_LIMIT) : fetchedRows;
|
|
195
|
+
if (truncated) {
|
|
196
|
+
ctx.log?.warn("template overview: row limit reached, result is incomplete", {
|
|
197
|
+
limit: OVERVIEW_ROW_LIMIT,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
const visibleRows = rows.filter((row) => !isHiddenSystemDraft(query.user, row) &&
|
|
201
|
+
!isSystemOnlyTemplateHiddenFrom(query.user, ctx.registry, row) &&
|
|
202
|
+
mayEditTemplate(policy, query.user, row));
|
|
203
|
+
const { drafts, layoutSlugByDocument } = buildDrafts(groupBySlugAndKind(visibleRows), query.user);
|
|
204
|
+
const effectiveLayouts = effectiveMailLayoutSlugs(visibleRows);
|
|
205
|
+
const overviewRows = drafts.map((draft) => withUsage(draft, layoutSlugByDocument, ctx.registry, effectiveLayouts));
|
|
206
|
+
const filtered = filterRows(overviewRows, payload.search, payload.filters ?? [], payload.layoutSlug);
|
|
207
|
+
const sortField = payload.sort !== undefined && isSortField(payload.sort) ? payload.sort : "modifiedAt";
|
|
208
|
+
const direction = (payload.sortDirection ?? "desc") === "desc" ? -1 : 1;
|
|
209
|
+
filtered.sort((a, b) => direction * compareBy(sortField, a, b) || a.id.localeCompare(b.id));
|
|
210
|
+
const offset = payload.offset ?? 0;
|
|
211
|
+
return {
|
|
212
|
+
rows: filtered.slice(offset, offset + payload.limit),
|
|
213
|
+
nextCursor: null,
|
|
214
|
+
total: filtered.length,
|
|
215
|
+
truncated,
|
|
216
|
+
};
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
|
-
|
|
2
|
+
import { type TemplateEditPolicy } from "../template-edit-policy.ts";
|
|
3
|
+
export declare function makePatchDraftWrite(policy: TemplateEditPolicy): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"patch-draft", z.ZodObject<{
|
|
3
4
|
slug: z.ZodString;
|
|
4
5
|
kind: z.ZodEnum<{
|
|
5
6
|
"document-pdf": "document-pdf";
|