@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
|
@@ -4,6 +4,7 @@ import * as z from "zod";
|
|
|
4
4
|
import { DOCUMENT_HTML_KINDS } from "../constants.js";
|
|
5
5
|
import { applyDocumentOps, documentOpsSchema } from "../document-ops.js";
|
|
6
6
|
import { parseDocumentJson, serializeDocumentJson } from "../document-sanitizer.js";
|
|
7
|
+
import { refuseTemplateEdit, templateEditAccess, } from "../template-edit-policy.js";
|
|
7
8
|
import { resolveTemplateVariables } from "../template-validation.js";
|
|
8
9
|
import { loadDocumentBase } from "./document-base.js";
|
|
9
10
|
import { localeSchema, templateSlugSchema } from "./shared.js";
|
|
@@ -12,94 +13,99 @@ import { validateTemplateDraft } from "./validate.query.js";
|
|
|
12
13
|
function validationFailure(issue) {
|
|
13
14
|
return writeFailure(new ValidationError({ fields: [issue] }));
|
|
14
15
|
}
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
export function makePatchDraftWrite(policy) {
|
|
17
|
+
return defineWriteHandler({
|
|
18
|
+
name: "patch-draft",
|
|
19
|
+
schema: z.object({
|
|
20
|
+
slug: templateSlugSchema,
|
|
21
|
+
kind: z.enum(DOCUMENT_HTML_KINDS),
|
|
22
|
+
locale: localeSchema,
|
|
23
|
+
base: z.object({
|
|
24
|
+
scope: z.enum(["tenant", "system"]),
|
|
25
|
+
version: z.number().int().nonnegative(),
|
|
26
|
+
}),
|
|
27
|
+
ops: documentOpsSchema,
|
|
24
28
|
}),
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (!applied.ok)
|
|
59
|
-
return writeFailure(new ValidationError({ fields: applied.issues }));
|
|
60
|
-
const content = serializeDocumentJson(applied.doc);
|
|
61
|
-
const draftOf = (draftContent, meta, variableSchema) => validateTemplateDraft(ctx, event.user, {
|
|
62
|
-
slug,
|
|
63
|
-
kind,
|
|
64
|
-
contentFormat: "html",
|
|
65
|
-
content: draftContent,
|
|
66
|
-
meta,
|
|
67
|
-
variableSchema,
|
|
68
|
-
locale,
|
|
69
|
-
scope: "tenant",
|
|
70
|
-
});
|
|
71
|
-
const before = await draftOf(loaded.content, loaded.meta, loaded.variableSchema);
|
|
72
|
-
const after = await draftOf(content, applied.meta, applied.variableSchema);
|
|
73
|
-
const alreadyMissing = new Set(before.response.missingRequired);
|
|
74
|
-
const removed = after.response.missingRequired.filter((name) => !alreadyMissing.has(name));
|
|
75
|
-
if (removed.length > 0) {
|
|
76
|
-
return validationFailure({
|
|
77
|
-
path: "ops",
|
|
78
|
-
code: "required_placeholder_removed",
|
|
79
|
-
i18nKey: "templateResolver.errors.patch.requiredPlaceholderRemoved",
|
|
80
|
-
params: { names: removed },
|
|
29
|
+
access: templateEditAccess(policy.tenantEditable),
|
|
30
|
+
description: "Changes a mail or PDF document template with structured operations instead of a full rewrite and saves the result as the caller's tenant draft (never published, an active version keeps being served until publish): insertBlock after a block id (null = at the start; paragraph, heading, image, button, divider, pageBreak), replaceText inside one block (the text must occur exactly once), removeBlock, setResource on an image block, setMeta (flat merge, null removes a key) and setVariable (adds, replaces or with null removes a variable definition; refused when a notification declares the variables). Block ids and base come from document-blocks and refer to the state that was read; if the template changed since, the call fails with version_conflict and nothing is written. A patch that removes a required placeholder is refused. A TenantAdmin may patch only templates the app lists in tenantEditable, otherwise SystemAdmin only. Returns the written row.",
|
|
31
|
+
handler: async (event, ctx) => {
|
|
32
|
+
const { slug, kind, locale, base, ops } = event.payload;
|
|
33
|
+
const refused = refuseTemplateEdit(policy, event.user, { slug, kind });
|
|
34
|
+
if (refused !== null)
|
|
35
|
+
return refused;
|
|
36
|
+
const loaded = await loadDocumentBase(ctx, event.user, { slug, kind, locale });
|
|
37
|
+
if (loaded === null)
|
|
38
|
+
return writeFailure(new NotFoundError("template-resource", slug));
|
|
39
|
+
if (loaded.base.scope !== base.scope || loaded.base.version !== base.version) {
|
|
40
|
+
return writeFailure(new VersionConflictError({
|
|
41
|
+
entityId: loaded.rowId,
|
|
42
|
+
expectedVersion: base.version,
|
|
43
|
+
currentVersion: loaded.base.version,
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
const variableOpIndex = ops.findIndex((op) => op.op === "setVariable");
|
|
47
|
+
if (variableOpIndex !== -1 &&
|
|
48
|
+
ctx.registry.getAllNotifications().get(slug)?.variables !== undefined) {
|
|
49
|
+
return validationFailure({
|
|
50
|
+
path: `ops.${variableOpIndex}`,
|
|
51
|
+
code: "variables_declared_by_feature",
|
|
52
|
+
i18nKey: "templateResolver.errors.patch.variablesDeclaredByFeature",
|
|
53
|
+
params: { slug },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
const applied = applyDocumentOps({
|
|
57
|
+
doc: parseDocumentJson(loaded.content),
|
|
58
|
+
meta: loaded.meta,
|
|
59
|
+
variableSchema: loaded.variableSchema,
|
|
60
|
+
kind,
|
|
61
|
+
ops,
|
|
81
62
|
});
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
tenantId: event.user.tenantId,
|
|
87
|
-
scope: "tenant",
|
|
88
|
-
status: "draft",
|
|
89
|
-
variables: resolveTemplateVariables(ctx.registry, slug, applied.variableSchema),
|
|
90
|
-
payload: {
|
|
63
|
+
if (!applied.ok)
|
|
64
|
+
return writeFailure(new ValidationError({ fields: applied.issues }));
|
|
65
|
+
const content = serializeDocumentJson(applied.doc);
|
|
66
|
+
const draftOf = (draftContent, meta, variableSchema) => validateTemplateDraft(ctx, event.user, {
|
|
91
67
|
slug,
|
|
92
68
|
kind,
|
|
93
|
-
locale,
|
|
94
|
-
content,
|
|
95
69
|
contentFormat: "html",
|
|
96
|
-
|
|
97
|
-
meta
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
70
|
+
content: draftContent,
|
|
71
|
+
meta,
|
|
72
|
+
variableSchema,
|
|
73
|
+
locale,
|
|
74
|
+
scope: "tenant",
|
|
75
|
+
});
|
|
76
|
+
const before = await draftOf(loaded.content, loaded.meta, loaded.variableSchema);
|
|
77
|
+
const after = await draftOf(content, applied.meta, applied.variableSchema);
|
|
78
|
+
const alreadyMissing = new Set(before.response.missingRequired);
|
|
79
|
+
const removed = after.response.missingRequired.filter((name) => !alreadyMissing.has(name));
|
|
80
|
+
if (removed.length > 0) {
|
|
81
|
+
return validationFailure({
|
|
82
|
+
path: "ops",
|
|
83
|
+
code: "required_placeholder_removed",
|
|
84
|
+
i18nKey: "templateResolver.errors.patch.requiredPlaceholderRemoved",
|
|
85
|
+
params: { names: removed },
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return writeTemplateRow({
|
|
89
|
+
ctx,
|
|
90
|
+
user: event.user,
|
|
91
|
+
tenantId: event.user.tenantId,
|
|
92
|
+
scope: "tenant",
|
|
93
|
+
status: "draft",
|
|
94
|
+
variables: resolveTemplateVariables(ctx.registry, slug, applied.variableSchema),
|
|
95
|
+
payload: {
|
|
96
|
+
slug,
|
|
97
|
+
kind,
|
|
98
|
+
locale,
|
|
99
|
+
content,
|
|
100
|
+
contentFormat: "html",
|
|
101
|
+
variableSchema: applied.variableSchema,
|
|
102
|
+
meta: applied.meta,
|
|
103
|
+
title: loaded.title,
|
|
104
|
+
folder: loaded.folder,
|
|
105
|
+
parentTemplateId: loaded.parentTemplateId,
|
|
106
|
+
},
|
|
107
|
+
expectedVersion: loaded.base.scope === "tenant" ? loaded.base.version : null,
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
}
|
|
@@ -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 makeResetToSystemWrite(policy: TemplateEditPolicy): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"reset-to-system", z.ZodObject<{
|
|
3
4
|
id: z.ZodString;
|
|
4
5
|
}, z.core.$strip>, {
|
|
5
6
|
id: string;
|
|
@@ -3,48 +3,55 @@ import { defineWriteHandler, SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framew
|
|
|
3
3
|
import { NotFoundError, ValidationError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
4
|
import * as z from "zod";
|
|
5
5
|
import { templateResourcesTable } from "../table.js";
|
|
6
|
+
import { refuseMissingTemplate, refuseTemplateEdit, templateEditAccess, } from "../template-edit-policy.js";
|
|
6
7
|
import { executor } from "./shared.js";
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
8
|
+
export function makeResetToSystemWrite(policy) {
|
|
9
|
+
return defineWriteHandler({
|
|
10
|
+
name: "reset-to-system",
|
|
11
|
+
schema: z.object({ id: z.string().min(1) }),
|
|
12
|
+
access: templateEditAccess(policy.tenantEditable),
|
|
13
|
+
description: "Retires a tenant override by archiving it so the resolver serves the active system template again; the override and its content survive and can be published again. Refused when there is no active system template to fall back to. Outside the templates the app lists in tenantEditable only a SystemAdmin may use it.",
|
|
14
|
+
handler: async (event, ctx) => {
|
|
15
|
+
const existing = await fetchOne(ctx.db, templateResourcesTable, {
|
|
16
|
+
id: event.payload.id,
|
|
17
|
+
});
|
|
18
|
+
if (!existing || existing.tenantId !== event.user.tenantId || existing.scope !== "tenant") {
|
|
19
|
+
return (refuseMissingTemplate(policy, event.user) ??
|
|
20
|
+
writeFailure(new NotFoundError("template-resource", event.payload.id)));
|
|
21
|
+
}
|
|
22
|
+
const refused = refuseTemplateEdit(policy, event.user, existing);
|
|
23
|
+
if (refused !== null)
|
|
24
|
+
return refused;
|
|
25
|
+
const systemRow = await fetchOne(ctx.db, templateResourcesTable, {
|
|
26
|
+
tenantId: SYSTEM_TENANT_ID,
|
|
27
|
+
slug: existing.slug,
|
|
28
|
+
kind: existing.kind,
|
|
29
|
+
locale: existing.locale,
|
|
30
|
+
status: "active",
|
|
31
|
+
});
|
|
32
|
+
if (!systemRow) {
|
|
33
|
+
return writeFailure(new ValidationError({
|
|
34
|
+
fields: [
|
|
35
|
+
{
|
|
36
|
+
path: "id",
|
|
37
|
+
code: "no_system_template",
|
|
38
|
+
i18nKey: "templateResolver.errors.noSystemTemplate",
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
const response = {
|
|
44
|
+
isSuccess: true,
|
|
45
|
+
data: {
|
|
46
|
+
id: String(existing.id),
|
|
47
|
+
status: "archived",
|
|
48
|
+
systemTemplateId: String(systemRow.id),
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
if (existing.status === "archived")
|
|
52
|
+
return response;
|
|
53
|
+
const result = await executor.update({ id: existing.id, version: existing.version, changes: { status: "archived" } }, event.user, ctx.db);
|
|
54
|
+
return result.isSuccess ? response : result;
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -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 makeResourceLinkPdfWrite(policy: TemplateEditPolicy): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"resource-link-pdf", z.ZodObject<{
|
|
3
4
|
templateId: z.ZodString;
|
|
4
5
|
key: z.ZodString;
|
|
5
6
|
templateSlug: z.ZodString;
|
|
@@ -1,32 +1,38 @@
|
|
|
1
1
|
import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import * as z from "zod";
|
|
3
3
|
import { linkTemplatePdfAttachment } from "../store-template-resource.js";
|
|
4
|
+
import { refuseTemplateEditById, templateEditAccess, } from "../template-edit-policy.js";
|
|
4
5
|
import { resourceKeySchema, resourceTargetTenantId } from "./resource-shared.js";
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
ctx,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
6
|
+
export function makeResourceLinkPdfWrite(policy) {
|
|
7
|
+
return defineWriteHandler({
|
|
8
|
+
name: "resource-link-pdf",
|
|
9
|
+
schema: z.object({
|
|
10
|
+
templateId: z.string().min(1),
|
|
11
|
+
key: resourceKeySchema,
|
|
12
|
+
/** Slug of a document-pdf template of the tenant or the system. */
|
|
13
|
+
templateSlug: z.string().min(1),
|
|
14
|
+
/** Name pattern of the attached PDF; may hold {{placeholders}}, must end with .pdf. */
|
|
15
|
+
fileName: z.string().min(1),
|
|
16
|
+
/** Cross-tenant target — SystemAdmin only. */
|
|
17
|
+
tenantIdOverride: z.string().min(1).optional(),
|
|
18
|
+
}),
|
|
19
|
+
access: templateEditAccess(policy.tenantEditable),
|
|
20
|
+
description: "Links a PDF rendered from a document-pdf template into a mail template under the key. Every mail sent from the template then carries that PDF, rendered at send time with the mail's own variables and locale; nothing is uploaded or stored. fileName is a pattern that may use the mail's {{placeholders}} and must end with .pdf. The document template must exist for the tenant or the system. Counts toward the 10 attachments per mail template together with uploaded files. Outside the templates the app lists in tenantEditable only a SystemAdmin may use it.",
|
|
21
|
+
handler: async (event, ctx) => {
|
|
22
|
+
const { templateId, key, templateSlug, fileName } = event.payload;
|
|
23
|
+
const tenantId = resourceTargetTenantId(event.payload.tenantIdOverride, event.user.tenantId);
|
|
24
|
+
const refused = await refuseTemplateEditById(ctx.db, policy, event.user, tenantId, templateId);
|
|
25
|
+
if (refused !== null)
|
|
26
|
+
return refused;
|
|
27
|
+
return linkTemplatePdfAttachment({
|
|
28
|
+
ctx,
|
|
29
|
+
user: event.user,
|
|
30
|
+
tenantId,
|
|
31
|
+
templateId,
|
|
32
|
+
key,
|
|
33
|
+
templateSlug,
|
|
34
|
+
fileName,
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -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 makeResourceRemoveWrite(policy: TemplateEditPolicy): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"resource-remove", z.ZodObject<{
|
|
3
4
|
templateId: z.ZodString;
|
|
4
5
|
key: z.ZodString;
|
|
5
6
|
tenantIdOverride: z.ZodOptional<z.ZodString>;
|
|
@@ -5,40 +5,46 @@ import * as z from "zod";
|
|
|
5
5
|
import { requireForTenant } from "../../shared/index.js";
|
|
6
6
|
import { parseLinkedResourceEntries } from "../linked-resources.js";
|
|
7
7
|
import { templateResourcesTable } from "../table.js";
|
|
8
|
+
import { refuseTemplateEditById, templateEditAccess, } from "../template-edit-policy.js";
|
|
8
9
|
import { resourceKeySchema, resourceTargetTenantId } from "./resource-shared.js";
|
|
9
10
|
import { executor } from "./shared.js";
|
|
10
11
|
// The FileRef itself stays: copy-locale and tenant overrides can share ids and there is
|
|
11
12
|
// no reference count to tell when the last user is gone.
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
13
|
+
export function makeResourceRemoveWrite(policy) {
|
|
14
|
+
return defineWriteHandler({
|
|
15
|
+
name: "resource-remove",
|
|
16
|
+
schema: z.object({
|
|
17
|
+
templateId: z.string().min(1),
|
|
18
|
+
key: resourceKeySchema,
|
|
19
|
+
/** Cross-tenant target — SystemAdmin only. */
|
|
20
|
+
tenantIdOverride: z.string().min(1).optional(),
|
|
21
|
+
}),
|
|
22
|
+
access: templateEditAccess(policy.tenantEditable),
|
|
23
|
+
description: "Unlinks a resource key from a template, whether it holds an image, an attached file or a linked PDF template; removing a key the template does not have succeeds without a change. Outside the templates the app lists in tenantEditable only a SystemAdmin may use it.",
|
|
24
|
+
handler: async (event, ctx) => {
|
|
25
|
+
const { templateId, key } = event.payload;
|
|
26
|
+
const tenantId = resourceTargetTenantId(event.payload.tenantIdOverride, event.user.tenantId);
|
|
27
|
+
const refused = await refuseTemplateEditById(ctx.db, policy, event.user, tenantId, templateId);
|
|
28
|
+
if (refused !== null)
|
|
29
|
+
return refused;
|
|
30
|
+
const { db, streamTenantId } = requireForTenant(ctx, tenantId);
|
|
31
|
+
const row = await fetchOne(db, templateResourcesTable, {
|
|
32
|
+
tenantId,
|
|
33
|
+
id: templateId,
|
|
34
|
+
});
|
|
35
|
+
if (row === undefined)
|
|
36
|
+
return writeFailure(new NotFoundError("template-resource", templateId));
|
|
37
|
+
const { [key]: removed, ...remaining } = parseLinkedResourceEntries(row.linkedResources);
|
|
38
|
+
if (removed === undefined)
|
|
39
|
+
return { isSuccess: true, data: { key, removed: false } };
|
|
40
|
+
const updated = await executor.update({
|
|
41
|
+
id: row.id,
|
|
42
|
+
version: row.version,
|
|
43
|
+
changes: { linkedResources: JSON.stringify(remaining) },
|
|
44
|
+
}, event.user, db, { streamTenantId });
|
|
45
|
+
if (!updated.isSuccess)
|
|
46
|
+
return updated;
|
|
47
|
+
return { isSuccess: true, data: { key, removed: true } };
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
import { type PreparedTemplateFile, type StoredTemplateAttachment, type StoredTemplateResource } from "../store-template-resource.ts";
|
|
3
|
-
|
|
3
|
+
import { type TemplateEditPolicy } from "../template-edit-policy.ts";
|
|
4
|
+
export declare function makeResourceUploadWrite(policy: TemplateEditPolicy): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"resource-upload", z.ZodObject<{
|
|
4
5
|
templateId: z.ZodOptional<z.ZodString>;
|
|
5
6
|
key: z.ZodOptional<z.ZodString>;
|
|
6
7
|
sourceFileRefId: z.ZodUUID;
|