canopycms 0.0.56 → 0.0.58
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/ai/json-to-markdown.js +74 -3
- package/dist/ai/json-to-markdown.js.map +1 -1
- package/dist/api/__test__/mock-client.d.ts +9 -5
- package/dist/api/__test__/mock-client.d.ts.map +1 -1
- package/dist/api/__test__/mock-client.js +15 -7
- package/dist/api/__test__/mock-client.js.map +1 -1
- package/dist/api/assets.d.ts +100 -36
- package/dist/api/assets.d.ts.map +1 -1
- package/dist/api/assets.js +338 -51
- package/dist/api/assets.js.map +1 -1
- package/dist/api/branch.d.ts +6 -0
- package/dist/api/branch.d.ts.map +1 -1
- package/dist/api/branch.js +63 -19
- package/dist/api/branch.js.map +1 -1
- package/dist/api/client.d.ts +16 -5
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +24 -6
- package/dist/api/client.js.map +1 -1
- package/dist/api/content.d.ts +2 -2
- package/dist/api/entries.d.ts +4 -4
- package/dist/api/entries.d.ts.map +1 -1
- package/dist/api/entries.js +10 -2
- package/dist/api/entries.js.map +1 -1
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js.map +1 -1
- package/dist/api/route-builder.d.ts +9 -0
- package/dist/api/route-builder.d.ts.map +1 -1
- package/dist/api/route-builder.js +1 -0
- package/dist/api/route-builder.js.map +1 -1
- package/dist/api/schema.d.ts +4 -4
- package/dist/api/types.d.ts +11 -1
- package/dist/api/types.d.ts.map +1 -1
- package/dist/assets/asset-prefixes.d.ts +31 -0
- package/dist/assets/asset-prefixes.d.ts.map +1 -0
- package/dist/assets/asset-prefixes.js +20 -0
- package/dist/assets/asset-prefixes.js.map +1 -0
- package/dist/assets/asset-src.d.ts +23 -0
- package/dist/assets/asset-src.d.ts.map +1 -0
- package/dist/assets/asset-src.js +28 -0
- package/dist/assets/asset-src.js.map +1 -0
- package/dist/assets/asset-url.d.ts +35 -0
- package/dist/assets/asset-url.d.ts.map +1 -0
- package/dist/assets/asset-url.js +77 -0
- package/dist/assets/asset-url.js.map +1 -0
- package/dist/assets/factory.d.ts +18 -0
- package/dist/assets/factory.d.ts.map +1 -0
- package/dist/assets/factory.js +42 -0
- package/dist/assets/factory.js.map +1 -0
- package/dist/assets/finalize.d.ts +36 -0
- package/dist/assets/finalize.d.ts.map +1 -0
- package/dist/assets/finalize.js +72 -0
- package/dist/assets/finalize.js.map +1 -0
- package/dist/assets/index.d.ts +20 -0
- package/dist/assets/index.d.ts.map +1 -0
- package/dist/assets/index.js +19 -0
- package/dist/assets/index.js.map +1 -0
- package/dist/assets/keys.d.ts +56 -0
- package/dist/assets/keys.d.ts.map +1 -0
- package/dist/assets/keys.js +88 -0
- package/dist/assets/keys.js.map +1 -0
- package/dist/assets/pipeline.d.ts +50 -0
- package/dist/assets/pipeline.d.ts.map +1 -0
- package/dist/assets/pipeline.js +202 -0
- package/dist/assets/pipeline.js.map +1 -0
- package/dist/assets/store-local.d.ts +84 -0
- package/dist/assets/store-local.d.ts.map +1 -0
- package/dist/assets/store-local.js +254 -0
- package/dist/assets/store-local.js.map +1 -0
- package/dist/assets/store-s3.d.ts +71 -0
- package/dist/assets/store-s3.d.ts.map +1 -0
- package/dist/assets/store-s3.js +183 -0
- package/dist/assets/store-s3.js.map +1 -0
- package/dist/assets/svg-sanitizer.d.ts +37 -0
- package/dist/assets/svg-sanitizer.d.ts.map +1 -0
- package/dist/assets/svg-sanitizer.js +222 -0
- package/dist/assets/svg-sanitizer.js.map +1 -0
- package/dist/assets/transform-directives.d.ts +97 -0
- package/dist/assets/transform-directives.d.ts.map +1 -0
- package/dist/assets/transform-directives.js +267 -0
- package/dist/assets/transform-directives.js.map +1 -0
- package/dist/assets/transform.d.ts +51 -0
- package/dist/assets/transform.d.ts.map +1 -0
- package/dist/assets/transform.js +190 -0
- package/dist/assets/transform.js.map +1 -0
- package/dist/assets/types.d.ts +82 -0
- package/dist/assets/types.d.ts.map +1 -0
- package/dist/assets/types.js +10 -0
- package/dist/assets/types.js.map +1 -0
- package/dist/authorization/groups/schema.d.ts +2 -2
- package/dist/authorization/permissions/schema.d.ts +2 -2
- package/dist/branch-metadata.d.ts +71 -6
- package/dist/branch-metadata.d.ts.map +1 -1
- package/dist/branch-metadata.js +132 -116
- package/dist/branch-metadata.js.map +1 -1
- package/dist/branch-registry.d.ts +78 -9
- package/dist/branch-registry.d.ts.map +1 -1
- package/dist/branch-registry.js +155 -29
- package/dist/branch-registry.js.map +1 -1
- package/dist/branch-schema-cache.d.ts +126 -11
- package/dist/branch-schema-cache.d.ts.map +1 -1
- package/dist/branch-schema-cache.js +208 -58
- package/dist/branch-schema-cache.js.map +1 -1
- package/dist/cli/generate-ai-content.js +1993 -1506
- package/dist/cli/init.js +16 -4
- package/dist/cli/migrate.d.ts.map +1 -1
- package/dist/cli/migrate.js +25 -0
- package/dist/cli/migrate.js.map +1 -1
- package/dist/cli/sync.js +5 -5
- package/dist/cli/sync.js.map +1 -1
- package/dist/comment-store.d.ts +45 -15
- package/dist/comment-store.d.ts.map +1 -1
- package/dist/comment-store.js +80 -101
- package/dist/comment-store.js.map +1 -1
- package/dist/config/helpers.d.ts.map +1 -1
- package/dist/config/helpers.js +2 -1
- package/dist/config/helpers.js.map +1 -1
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/schemas/config.d.ts +10 -0
- package/dist/config/schemas/config.d.ts.map +1 -1
- package/dist/config/schemas/field.d.ts +38 -3
- package/dist/config/schemas/field.d.ts.map +1 -1
- package/dist/config/schemas/field.js +18 -0
- package/dist/config/schemas/field.js.map +1 -1
- package/dist/config/schemas/media.d.ts +8 -0
- package/dist/config/schemas/media.d.ts.map +1 -1
- package/dist/config/schemas/media.js +9 -0
- package/dist/config/schemas/media.js.map +1 -1
- package/dist/config/types.d.ts +43 -3
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +4 -1
- package/dist/config/types.js.map +1 -1
- package/dist/content-index-generation.d.ts +40 -4
- package/dist/content-index-generation.d.ts.map +1 -1
- package/dist/content-index-generation.js +70 -73
- package/dist/content-index-generation.js.map +1 -1
- package/dist/content-store.d.ts +63 -0
- package/dist/content-store.d.ts.map +1 -1
- package/dist/content-store.js +421 -194
- package/dist/content-store.js.map +1 -1
- package/dist/editor/CanopyEditor.d.ts.map +1 -1
- package/dist/editor/CanopyEditor.js +1 -1
- package/dist/editor/CanopyEditor.js.map +1 -1
- package/dist/editor/Editor.d.ts +2 -0
- package/dist/editor/Editor.d.ts.map +1 -1
- package/dist/editor/Editor.js +82 -80
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/FormRenderer.d.ts.map +1 -1
- package/dist/editor/FormRenderer.js +9 -0
- package/dist/editor/FormRenderer.js.map +1 -1
- package/dist/editor/components/EditorSidebar.d.ts +6 -1
- package/dist/editor/components/EditorSidebar.d.ts.map +1 -1
- package/dist/editor/components/EditorSidebar.js +4 -3
- package/dist/editor/components/EditorSidebar.js.map +1 -1
- package/dist/editor/context/AssetContext.d.ts +25 -0
- package/dist/editor/context/AssetContext.d.ts.map +1 -0
- package/dist/editor/context/AssetContext.js +26 -0
- package/dist/editor/context/AssetContext.js.map +1 -0
- package/dist/editor/context/index.d.ts +1 -0
- package/dist/editor/context/index.d.ts.map +1 -1
- package/dist/editor/context/index.js +1 -0
- package/dist/editor/context/index.js.map +1 -1
- package/dist/editor/fields/ImageField.d.ts +22 -0
- package/dist/editor/fields/ImageField.d.ts.map +1 -0
- package/dist/editor/fields/ImageField.js +100 -0
- package/dist/editor/fields/ImageField.js.map +1 -0
- package/dist/editor/fields/MarkdownField.d.ts.map +1 -1
- package/dist/editor/fields/MarkdownField.js +30 -4
- package/dist/editor/fields/MarkdownField.js.map +1 -1
- package/dist/editor/fields/MdxImageDialog.d.ts +16 -0
- package/dist/editor/fields/MdxImageDialog.d.ts.map +1 -0
- package/dist/editor/fields/MdxImageDialog.js +73 -0
- package/dist/editor/fields/MdxImageDialog.js.map +1 -0
- package/dist/editor/media/AssetCard.d.ts +21 -0
- package/dist/editor/media/AssetCard.d.ts.map +1 -0
- package/dist/editor/media/AssetCard.js +41 -0
- package/dist/editor/media/AssetCard.js.map +1 -0
- package/dist/editor/media/CropStep.d.ts +26 -0
- package/dist/editor/media/CropStep.d.ts.map +1 -0
- package/dist/editor/media/CropStep.js +38 -0
- package/dist/editor/media/CropStep.js.map +1 -0
- package/dist/editor/media/MediaLibrary.d.ts +22 -0
- package/dist/editor/media/MediaLibrary.d.ts.map +1 -0
- package/dist/editor/media/MediaLibrary.js +17 -0
- package/dist/editor/media/MediaLibrary.js.map +1 -0
- package/dist/editor/media/MediaLibraryBody.d.ts +17 -0
- package/dist/editor/media/MediaLibraryBody.d.ts.map +1 -0
- package/dist/editor/media/MediaLibraryBody.js +133 -0
- package/dist/editor/media/MediaLibraryBody.js.map +1 -0
- package/dist/editor/media/crop-math.d.ts +37 -0
- package/dist/editor/media/crop-math.d.ts.map +1 -0
- package/dist/editor/media/crop-math.js +62 -0
- package/dist/editor/media/crop-math.js.map +1 -0
- package/dist/editor/media/upload-asset.d.ts +31 -0
- package/dist/editor/media/upload-asset.d.ts.map +1 -0
- package/dist/editor/media/upload-asset.js +56 -0
- package/dist/editor/media/upload-asset.js.map +1 -0
- package/dist/editor/media/upload-constants.d.ts +27 -0
- package/dist/editor/media/upload-constants.d.ts.map +1 -0
- package/dist/editor/media/upload-constants.js +40 -0
- package/dist/editor/media/upload-constants.js.map +1 -0
- package/dist/editor/media/useAssetUpload.d.ts +19 -0
- package/dist/editor/media/useAssetUpload.d.ts.map +1 -0
- package/dist/editor/media/useAssetUpload.js +53 -0
- package/dist/editor/media/useAssetUpload.js.map +1 -0
- package/dist/editor/media/xhr-upload.d.ts +20 -0
- package/dist/editor/media/xhr-upload.d.ts.map +1 -0
- package/dist/editor/media/xhr-upload.js +42 -0
- package/dist/editor/media/xhr-upload.js.map +1 -0
- package/dist/editor/theme.d.ts +2 -1
- package/dist/editor/theme.d.ts.map +1 -1
- package/dist/editor/theme.js +1 -0
- package/dist/editor/theme.js.map +1 -1
- package/dist/entry-schema.d.ts +19 -0
- package/dist/entry-schema.d.ts.map +1 -1
- package/dist/entry-schema.js.map +1 -1
- package/dist/git-manager.d.ts +15 -10
- package/dist/git-manager.d.ts.map +1 -1
- package/dist/git-manager.js +16 -11
- package/dist/git-manager.js.map +1 -1
- package/dist/http/handler.d.ts +5 -2
- package/dist/http/handler.d.ts.map +1 -1
- package/dist/http/handler.js +13 -4
- package/dist/http/handler.js.map +1 -1
- package/dist/http/index.d.ts +2 -2
- package/dist/http/index.d.ts.map +1 -1
- package/dist/http/index.js +1 -1
- package/dist/http/index.js.map +1 -1
- package/dist/http/router.d.ts +14 -1
- package/dist/http/router.d.ts.map +1 -1
- package/dist/http/router.js +4 -1
- package/dist/http/router.js.map +1 -1
- package/dist/http/types.d.ts +40 -0
- package/dist/http/types.d.ts.map +1 -1
- package/dist/http/types.js +9 -0
- package/dist/http/types.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/resource-generation.d.ts +54 -0
- package/dist/resource-generation.d.ts.map +1 -0
- package/dist/resource-generation.js +156 -0
- package/dist/resource-generation.js.map +1 -0
- package/dist/schema/schema-store.d.ts +31 -4
- package/dist/schema/schema-store.d.ts.map +1 -1
- package/dist/schema/schema-store.js +47 -8
- package/dist/schema/schema-store.js.map +1 -1
- package/dist/server.d.ts +45 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +41 -0
- package/dist/server.js.map +1 -1
- package/dist/sync-core.js +2 -2
- package/dist/sync-core.js.map +1 -1
- package/dist/utils/occ-json-write.d.ts +100 -0
- package/dist/utils/occ-json-write.d.ts.map +1 -0
- package/dist/utils/occ-json-write.js +234 -0
- package/dist/utils/occ-json-write.js.map +1 -0
- package/dist/validation/entry-validator.d.ts.map +1 -1
- package/dist/validation/entry-validator.js +56 -1
- package/dist/validation/entry-validator.js.map +1 -1
- package/dist/worker/cms-worker.d.ts.map +1 -1
- package/dist/worker/cms-worker.js +21 -3
- package/dist/worker/cms-worker.js.map +1 -1
- package/package.json +13 -1
- package/dist/asset-store.d.ts +0 -32
- package/dist/asset-store.d.ts.map +0 -1
- package/dist/asset-store.js +0 -85
- package/dist/asset-store.js.map +0 -1
package/dist/api/assets.js
CHANGED
|
@@ -1,65 +1,359 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { defineEndpoint } from './route-builder.js';
|
|
3
|
+
import { ASSET_PREFIXES } from '../assets/keys.js';
|
|
4
|
+
import { ALLOWED_UPLOAD_CONTENT_TYPES } from '../assets/pipeline.js';
|
|
5
|
+
import { finalizeStagedUpload } from '../assets/finalize.js';
|
|
6
|
+
import { assetSrc } from '../assets/asset-src.js';
|
|
7
|
+
import { formatDirectives, parseTransformPath } from '../assets/transform-directives.js';
|
|
8
|
+
import { applyTransform } from '../assets/transform.js';
|
|
9
|
+
function toAssetRecord(meta) {
|
|
10
|
+
return { ...meta, src: assetSrc(meta) };
|
|
11
|
+
}
|
|
12
|
+
const MOCK_ASSET_RECORD = {
|
|
13
|
+
hash32: 'a'.repeat(32),
|
|
14
|
+
filename: 'sample.png',
|
|
15
|
+
slug: 'sample',
|
|
16
|
+
ext: 'png',
|
|
17
|
+
mime: 'image/png',
|
|
18
|
+
size: 1024,
|
|
19
|
+
width: 100,
|
|
20
|
+
height: 100,
|
|
21
|
+
kind: 'raster',
|
|
22
|
+
uploadedAt: '2024-01-01T00:00:00.000Z',
|
|
23
|
+
src: '/assets/t/orig/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/sample.png',
|
|
24
|
+
};
|
|
3
25
|
// ============================================================================
|
|
4
26
|
// Zod Schemas for Validation
|
|
5
27
|
// ============================================================================
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
28
|
+
const filenameSchema = z.string().min(1).max(255);
|
|
29
|
+
const presignAssetBodySchema = z.object({
|
|
30
|
+
filename: filenameSchema,
|
|
31
|
+
contentType: z.string().min(1),
|
|
32
|
+
size: z.number().int().positive().optional(),
|
|
33
|
+
});
|
|
34
|
+
const finalizeAssetBodySchema = z.object({
|
|
35
|
+
stagingKey: z.string().min(1),
|
|
36
|
+
filename: filenameSchema,
|
|
10
37
|
});
|
|
11
38
|
/**
|
|
12
|
-
* List assets - any authenticated user can list assets.
|
|
13
|
-
*
|
|
14
39
|
* Declared as `params` (not just parsed ad hoc from req.query) so the client
|
|
15
40
|
* generator (scripts/generate-client.ts) sees a paramsSchema and emits a
|
|
16
|
-
* method that accepts and forwards `
|
|
17
|
-
* `assets.list()` that can never pass
|
|
41
|
+
* method that accepts and forwards `cursor`/`limit` (API-H4) instead of a
|
|
42
|
+
* no-arg `assets.list()` that can never pass them.
|
|
18
43
|
*/
|
|
19
44
|
const listAssetsParamsSchema = z.object({
|
|
20
|
-
|
|
45
|
+
cursor: z.string().optional(),
|
|
46
|
+
limit: z.coerce.number().int().positive().max(100).optional(),
|
|
21
47
|
});
|
|
22
|
-
|
|
48
|
+
/** hash32 is a sha-256 truncated to 32 hex chars (keys.ts's `hashBytes`) - never any other shape. */
|
|
49
|
+
const hash32Schema = z
|
|
50
|
+
.string()
|
|
51
|
+
.regex(/^[a-f0-9]{32}$/, 'key must be a 32-character lowercase hex string');
|
|
52
|
+
const deleteAssetParamsSchema = z.object({ key: hash32Schema });
|
|
53
|
+
// ============================================================================
|
|
54
|
+
// Handlers
|
|
55
|
+
// ============================================================================
|
|
56
|
+
/**
|
|
57
|
+
* Presign a direct (or proxied) upload target. Any authenticated user - there
|
|
58
|
+
* is no finer "editor" role, and upload needs to work for every non-admin
|
|
59
|
+
* user too (see guard semantics in .claude/future-tasks/assets-media-system.md).
|
|
60
|
+
*/
|
|
61
|
+
const presignAssetHandler = async (ctx, _req, body) => {
|
|
23
62
|
if (!ctx.assetStore)
|
|
24
63
|
return { ok: false, status: 501, error: 'Asset store not configured' };
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
64
|
+
if (!ALLOWED_UPLOAD_CONTENT_TYPES.has(body.contentType)) {
|
|
65
|
+
return { ok: false, status: 415, error: `Unsupported content type: ${body.contentType}` };
|
|
66
|
+
}
|
|
67
|
+
const target = await ctx.assetStore.beginUpload({
|
|
68
|
+
filename: body.filename,
|
|
69
|
+
contentType: body.contentType,
|
|
70
|
+
size: body.size,
|
|
71
|
+
});
|
|
72
|
+
// The client-declared size is only a fast, up-front UX check - the store's
|
|
73
|
+
// own presigned target (S3's content-length-range condition, or the local
|
|
74
|
+
// store's own cap) is what actually enforces the limit at upload time.
|
|
75
|
+
if (body.size !== undefined && body.size > target.maxBytes) {
|
|
76
|
+
return { ok: false, status: 413, error: `File exceeds the ${target.maxBytes}-byte limit` };
|
|
28
77
|
}
|
|
29
|
-
|
|
30
|
-
return { ok: true, status: 200, data: { assets } };
|
|
78
|
+
return { ok: true, status: 200, data: { upload: target } };
|
|
31
79
|
};
|
|
32
80
|
/**
|
|
33
|
-
*
|
|
81
|
+
* Finalize a staged upload: read the staged bytes, run the finalize pipeline
|
|
82
|
+
* (sniff/hash/dims/sanitize), write originals + meta, and return the result.
|
|
83
|
+
* Any authenticated user (same rationale as presign).
|
|
34
84
|
*/
|
|
35
|
-
const
|
|
85
|
+
const finalizeAssetHandler = async (ctx, req, body) => {
|
|
36
86
|
if (!ctx.assetStore)
|
|
37
87
|
return { ok: false, status: 501, error: 'Asset store not configured' };
|
|
38
|
-
const
|
|
39
|
-
|
|
88
|
+
const result = await finalizeStagedUpload(ctx.assetStore, body.stagingKey, body.filename, req.user.userId);
|
|
89
|
+
if (!result.ok) {
|
|
90
|
+
return { ok: false, status: result.status, error: result.error };
|
|
91
|
+
}
|
|
92
|
+
return { ok: true, status: 200, data: { asset: toAssetRecord(result.meta) } };
|
|
40
93
|
};
|
|
41
94
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
95
|
+
* Proxied upload for stores that don't support direct-to-storage presigning
|
|
96
|
+
* (LocalAssetStore in dev). Reads a multipart/form-data body (a `file` part,
|
|
97
|
+
* plus an optional `filename` field overriding `file.name`) rather than JSON -
|
|
98
|
+
* this route sets `bodyFormat: 'multipart'` below so the core handler skips
|
|
99
|
+
* its default `req.json()` parsing, since the body stream can only be read
|
|
100
|
+
* once (see http/handler.ts). Any authenticated user (same rationale as
|
|
101
|
+
* presign/finalize).
|
|
46
102
|
*/
|
|
47
|
-
const
|
|
48
|
-
const deleteAssetHandler = async (_gc, ctx, req) => {
|
|
103
|
+
const uploadProxiedHandler = async (ctx, req) => {
|
|
49
104
|
if (!ctx.assetStore)
|
|
50
105
|
return { ok: false, status: 501, error: 'Asset store not configured' };
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
106
|
+
if (ctx.assetStore.capabilities.directUpload) {
|
|
107
|
+
return {
|
|
108
|
+
ok: false,
|
|
109
|
+
status: 400,
|
|
110
|
+
error: 'This asset store supports direct upload - use POST /assets/presign instead',
|
|
111
|
+
};
|
|
54
112
|
}
|
|
55
|
-
|
|
113
|
+
if (!req.rawRequest?.formData) {
|
|
114
|
+
return {
|
|
115
|
+
ok: false,
|
|
116
|
+
status: 400,
|
|
117
|
+
error: 'This server adapter does not support multipart form-data uploads',
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
// Early size guard from the Content-Length header, BEFORE the multipart
|
|
121
|
+
// body is ever read - `formData()`/`filePart.arrayBuffer()` below fully
|
|
122
|
+
// buffer the upload into memory, so without this an over-cap request still
|
|
123
|
+
// pays the full read cost before the (correct, but too-late) post-read
|
|
124
|
+
// check further down rejects it. `beginUpload()` is the store-agnostic way
|
|
125
|
+
// to learn "the store's max" (mirrors presignAssetHandler's own use of it)
|
|
126
|
+
// - filename/contentType are placeholders here since only `.maxBytes` is
|
|
127
|
+
// read; the real `beginUpload()` call below (with the actual filename)
|
|
128
|
+
// still runs after the body is read to build the real staging target.
|
|
129
|
+
const contentLengthHeader = req.rawRequest.header('content-length');
|
|
130
|
+
if (contentLengthHeader !== null) {
|
|
131
|
+
const contentLength = Number(contentLengthHeader);
|
|
132
|
+
if (Number.isFinite(contentLength)) {
|
|
133
|
+
const { maxBytes } = await ctx.assetStore.beginUpload({
|
|
134
|
+
filename: 'upload',
|
|
135
|
+
contentType: 'application/octet-stream',
|
|
136
|
+
});
|
|
137
|
+
if (contentLength > maxBytes) {
|
|
138
|
+
return { ok: false, status: 413, error: `File exceeds the ${maxBytes}-byte limit` };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
let formData;
|
|
143
|
+
try {
|
|
144
|
+
formData = await req.rawRequest.formData();
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return { ok: false, status: 400, error: 'Could not parse multipart form-data body' };
|
|
148
|
+
}
|
|
149
|
+
const filePart = formData.get('file');
|
|
150
|
+
if (!(filePart instanceof Blob)) {
|
|
151
|
+
return { ok: false, status: 400, error: 'A "file" part is required' };
|
|
152
|
+
}
|
|
153
|
+
const filenameOverride = formData.get('filename');
|
|
154
|
+
const filename = typeof filenameOverride === 'string' && filenameOverride.length > 0
|
|
155
|
+
? filenameOverride
|
|
156
|
+
: filePart instanceof File
|
|
157
|
+
? filePart.name
|
|
158
|
+
: 'upload';
|
|
159
|
+
const data = new Uint8Array(await filePart.arrayBuffer());
|
|
160
|
+
const target = await ctx.assetStore.beginUpload({
|
|
161
|
+
filename,
|
|
162
|
+
contentType: filePart.type || 'application/octet-stream',
|
|
163
|
+
size: data.byteLength,
|
|
164
|
+
});
|
|
165
|
+
// Defense-in-depth: the Content-Length guard above is the primary check
|
|
166
|
+
// (it runs before the body is buffered at all) but a missing or lying
|
|
167
|
+
// Content-Length header would skip it entirely, so the actual byte count
|
|
168
|
+
// is still re-checked here against the same store-provided bound.
|
|
169
|
+
if (data.byteLength > target.maxBytes) {
|
|
170
|
+
return { ok: false, status: 413, error: `File exceeds the ${target.maxBytes}-byte limit` };
|
|
171
|
+
}
|
|
172
|
+
await ctx.assetStore.writeStaging(target.stagingKey, data, filePart.type || undefined);
|
|
173
|
+
const result = await finalizeStagedUpload(ctx.assetStore, target.stagingKey, filename, req.user.userId);
|
|
174
|
+
if (!result.ok) {
|
|
175
|
+
return { ok: false, status: result.status, error: result.error };
|
|
176
|
+
}
|
|
177
|
+
return { ok: true, status: 200, data: { asset: toAssetRecord(result.meta) } };
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* List assets - any authenticated user can list assets (key enumeration is
|
|
181
|
+
* accepted: unlisted != private, see assets-media-system.md).
|
|
182
|
+
*/
|
|
183
|
+
const listAssetsHandler = async (ctx, _req, params) => {
|
|
184
|
+
if (!ctx.assetStore)
|
|
185
|
+
return { ok: false, status: 501, error: 'Asset store not configured' };
|
|
186
|
+
const { items, nextCursor } = await ctx.assetStore.listMeta({
|
|
187
|
+
cursor: params.cursor,
|
|
188
|
+
limit: params.limit,
|
|
189
|
+
});
|
|
190
|
+
return { ok: true, status: 200, data: { assets: items.map(toAssetRecord), nextCursor } };
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* Delete asset - requires Admin access. `key` is the asset's hash32 and is
|
|
194
|
+
* validated (32 lowercase hex chars) by `deleteAssetParamsSchema` before this
|
|
195
|
+
* handler ever runs. Deletes the meta sidecar only - blobs are immortal until
|
|
196
|
+
* a future GC worker task (see assets-media-system.md).
|
|
197
|
+
*/
|
|
198
|
+
const deleteAssetHandler = async (_gc, ctx, _req, params) => {
|
|
199
|
+
if (!ctx.assetStore)
|
|
200
|
+
return { ok: false, status: 501, error: 'Asset store not configured' };
|
|
201
|
+
await ctx.assetStore.deleteMeta(params.key);
|
|
56
202
|
return { ok: true, status: 200, data: { deleted: true } };
|
|
57
203
|
};
|
|
204
|
+
/** Cache-Control applied to every transform output this route writes/serves - matches finalize.ts's PUBLIC_CACHE_CONTROL for static public objects. */
|
|
205
|
+
const TRANSFORM_CACHE_CONTROL = 'public, max-age=31536000, immutable';
|
|
206
|
+
/**
|
|
207
|
+
* Lazy dev-mode emulation of the prod transform Lambda (PR 7 reuses
|
|
208
|
+
* `parseTransformPath`/`formatDirectives`/`applyTransform` unchanged): parse
|
|
209
|
+
* the request, load the original, transform it, write the result back under
|
|
210
|
+
* its CANONICAL key (so a non-canonically-ordered directive string still
|
|
211
|
+
* dedupes with any equivalent request), then serve the bytes just computed
|
|
212
|
+
* (no re-read from the store).
|
|
213
|
+
*
|
|
214
|
+
* `key` here is the full store key already confirmed to start with the
|
|
215
|
+
* `assets/t/` prefix and to have missed the cache-hit `readPublicObject`
|
|
216
|
+
* check in `rawAssetHandler`.
|
|
217
|
+
*/
|
|
218
|
+
async function serveLazyTransform(assetStore, key) {
|
|
219
|
+
const rest = key.slice(ASSET_PREFIXES.transform.length + 1);
|
|
220
|
+
const parsed = parseTransformPath(rest.split('/'));
|
|
221
|
+
if (!parsed.ok) {
|
|
222
|
+
return { ok: false, status: 400, error: parsed.error };
|
|
223
|
+
}
|
|
224
|
+
const meta = await assetStore.getMeta(parsed.hash32);
|
|
225
|
+
if (!meta) {
|
|
226
|
+
return { ok: false, status: 404, error: 'Not found' };
|
|
227
|
+
}
|
|
228
|
+
if (meta.kind !== 'raster') {
|
|
229
|
+
return { ok: false, status: 400, error: 'Not a raster asset - svg/pdf are served statically' };
|
|
230
|
+
}
|
|
231
|
+
// When the URL omits an explicit `f=` format, the transform preserves the
|
|
232
|
+
// source format, so the URL's `{ext}` must match the source's real ext
|
|
233
|
+
// exactly - the parser alone can't check this (it doesn't know the source
|
|
234
|
+
// format until this meta lookup).
|
|
235
|
+
const requestedFormat = parsed.directives.identity ? undefined : parsed.directives.format;
|
|
236
|
+
if (requestedFormat === undefined && parsed.ext !== meta.ext) {
|
|
237
|
+
return { ok: false, status: 400, error: 'Extension does not match the source format' };
|
|
238
|
+
}
|
|
239
|
+
const original = await assetStore.readOriginal(parsed.hash32);
|
|
240
|
+
if (!original) {
|
|
241
|
+
return { ok: false, status: 404, error: 'Not found' };
|
|
242
|
+
}
|
|
243
|
+
const transformed = await applyTransform({ data: original.data, ext: original.ext }, parsed.directives);
|
|
244
|
+
if (!transformed.ok) {
|
|
245
|
+
return { ok: false, status: 502, error: transformed.error };
|
|
246
|
+
}
|
|
247
|
+
const canonicalKey = `${ASSET_PREFIXES.transform}/${formatDirectives(parsed.directives)}/${parsed.hash32}/${parsed.slug}.${parsed.ext}`;
|
|
248
|
+
await assetStore.putPublicObject({
|
|
249
|
+
key: canonicalKey,
|
|
250
|
+
data: transformed.data,
|
|
251
|
+
contentType: transformed.contentType,
|
|
252
|
+
cacheControl: TRANSFORM_CACHE_CONTROL,
|
|
253
|
+
});
|
|
254
|
+
return {
|
|
255
|
+
kind: 'binary',
|
|
256
|
+
status: 200,
|
|
257
|
+
body: transformed.data,
|
|
258
|
+
headers: { contentType: transformed.contentType, cacheControl: TRANSFORM_CACHE_CONTROL },
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Serve a public asset object (sanitized svg/pdf finalize wrote, or a
|
|
263
|
+
* previously-computed transform output) for dev-mode `/assets/*` rewrites.
|
|
264
|
+
* Hand-built (not `defineEndpoint`) rather than registered in
|
|
265
|
+
* `ASSET_ROUTES`/the client generator: this route returns raw bytes
|
|
266
|
+
* (`CanopyBinaryResponse`), not a JSON envelope, so a generated client
|
|
267
|
+
* method that calls `response.json()` would be actively wrong. Consumers hit
|
|
268
|
+
* this route directly (an `<img>`/`<a>` src, or a framework rewrite), never
|
|
269
|
+
* through `client.ts`.
|
|
270
|
+
*
|
|
271
|
+
* Transform outputs (`assets/t/...`) are cache-checked exactly like any
|
|
272
|
+
* other public object first - only a MISS under the `assets/t/` prefix falls
|
|
273
|
+
* through to `serveLazyTransform`, which computes and caches the bytes. This
|
|
274
|
+
* mirrors the prod design (CloudFront origin-group -> S3 -> Lambda on miss).
|
|
275
|
+
*/
|
|
276
|
+
const rawAssetHandler = async (ctx, _req, params) => {
|
|
277
|
+
if (!ctx.assetStore)
|
|
278
|
+
return { ok: false, status: 501, error: 'Asset store not configured' };
|
|
279
|
+
const key = params.key ?? '';
|
|
280
|
+
const publicPrefix = `${ASSET_PREFIXES.public}/`;
|
|
281
|
+
const transformPrefix = `${ASSET_PREFIXES.transform}/`;
|
|
282
|
+
// Defense-in-depth: the local store re-guards path traversal on its own key
|
|
283
|
+
// resolution, but reject obviously-wrong keys before ever touching the
|
|
284
|
+
// store, and never distinguish "malformed key" from "not found" in the
|
|
285
|
+
// response (no oracle for probing).
|
|
286
|
+
if (!key.startsWith(publicPrefix) || key.includes('..')) {
|
|
287
|
+
return { ok: false, status: 404, error: 'Not found' };
|
|
288
|
+
}
|
|
289
|
+
const object = await ctx.assetStore.readPublicObject(key);
|
|
290
|
+
if (object) {
|
|
291
|
+
return {
|
|
292
|
+
kind: 'binary',
|
|
293
|
+
status: 200,
|
|
294
|
+
body: object.data,
|
|
295
|
+
headers: {
|
|
296
|
+
contentType: object.contentType,
|
|
297
|
+
contentDisposition: object.contentDisposition,
|
|
298
|
+
cacheControl: object.cacheControl,
|
|
299
|
+
},
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
if (!key.startsWith(transformPrefix)) {
|
|
303
|
+
return { ok: false, status: 404, error: 'Not found' };
|
|
304
|
+
}
|
|
305
|
+
return serveLazyTransform(ctx.assetStore, key);
|
|
306
|
+
};
|
|
58
307
|
// ============================================================================
|
|
59
308
|
// Route Definitions with defineEndpoint
|
|
60
309
|
// ============================================================================
|
|
61
310
|
/**
|
|
62
|
-
*
|
|
311
|
+
* POST /assets/presign
|
|
312
|
+
*/
|
|
313
|
+
const presignAsset = defineEndpoint({
|
|
314
|
+
namespace: 'assets',
|
|
315
|
+
name: 'presign',
|
|
316
|
+
method: 'POST',
|
|
317
|
+
path: '/assets/presign',
|
|
318
|
+
body: presignAssetBodySchema,
|
|
319
|
+
bodyType: 'PresignAssetBody',
|
|
320
|
+
responseType: 'PresignAssetResponse',
|
|
321
|
+
response: {},
|
|
322
|
+
defaultMockData: {
|
|
323
|
+
upload: { mode: 'proxied', stagingKey: 'asset-staging/mock', maxBytes: 52428800 },
|
|
324
|
+
},
|
|
325
|
+
handler: presignAssetHandler,
|
|
326
|
+
});
|
|
327
|
+
/**
|
|
328
|
+
* POST /assets/finalize
|
|
329
|
+
*/
|
|
330
|
+
const finalizeAsset = defineEndpoint({
|
|
331
|
+
namespace: 'assets',
|
|
332
|
+
name: 'finalize',
|
|
333
|
+
method: 'POST',
|
|
334
|
+
path: '/assets/finalize',
|
|
335
|
+
body: finalizeAssetBodySchema,
|
|
336
|
+
bodyType: 'FinalizeAssetBody',
|
|
337
|
+
responseType: 'FinalizeAssetResponse',
|
|
338
|
+
response: {},
|
|
339
|
+
defaultMockData: { asset: MOCK_ASSET_RECORD },
|
|
340
|
+
handler: finalizeAssetHandler,
|
|
341
|
+
});
|
|
342
|
+
/**
|
|
343
|
+
* POST /assets/upload (proxied, multipart/form-data - dev/local-store only)
|
|
344
|
+
*/
|
|
345
|
+
const uploadProxied = defineEndpoint({
|
|
346
|
+
namespace: 'assets',
|
|
347
|
+
name: 'uploadProxied',
|
|
348
|
+
method: 'POST',
|
|
349
|
+
path: '/assets/upload',
|
|
350
|
+
bodyFormat: 'multipart',
|
|
351
|
+
responseType: 'FinalizeAssetResponse',
|
|
352
|
+
response: {},
|
|
353
|
+
defaultMockData: { asset: MOCK_ASSET_RECORD },
|
|
354
|
+
handler: uploadProxiedHandler,
|
|
355
|
+
});
|
|
356
|
+
/**
|
|
63
357
|
* GET /assets
|
|
64
358
|
*/
|
|
65
359
|
const listAssets = defineEndpoint({
|
|
@@ -74,25 +368,7 @@ const listAssets = defineEndpoint({
|
|
|
74
368
|
handler: listAssetsHandler,
|
|
75
369
|
});
|
|
76
370
|
/**
|
|
77
|
-
*
|
|
78
|
-
* POST /assets
|
|
79
|
-
*/
|
|
80
|
-
const uploadAsset = defineEndpoint({
|
|
81
|
-
namespace: 'assets',
|
|
82
|
-
name: 'upload',
|
|
83
|
-
method: 'POST',
|
|
84
|
-
path: '/assets',
|
|
85
|
-
body: uploadAssetBodySchema,
|
|
86
|
-
bodyType: 'UploadAssetBody',
|
|
87
|
-
responseType: 'AssetUploadResponse',
|
|
88
|
-
response: {},
|
|
89
|
-
defaultMockData: { asset: { key: '', url: '' } },
|
|
90
|
-
guards: ['privileged'],
|
|
91
|
-
handler: uploadAssetHandler,
|
|
92
|
-
});
|
|
93
|
-
/**
|
|
94
|
-
* Delete an asset
|
|
95
|
-
* DELETE /assets?key=...
|
|
371
|
+
* DELETE /assets?key={hash32}
|
|
96
372
|
*/
|
|
97
373
|
const deleteAsset = defineEndpoint({
|
|
98
374
|
namespace: 'assets',
|
|
@@ -107,11 +383,22 @@ const deleteAsset = defineEndpoint({
|
|
|
107
383
|
handler: deleteAssetHandler,
|
|
108
384
|
});
|
|
109
385
|
/**
|
|
110
|
-
* Exported routes for router registration
|
|
386
|
+
* Exported routes for router registration and client codegen.
|
|
111
387
|
*/
|
|
112
388
|
export const ASSET_ROUTES = {
|
|
389
|
+
presign: presignAsset,
|
|
390
|
+
finalize: finalizeAsset,
|
|
391
|
+
uploadProxied,
|
|
113
392
|
list: listAssets,
|
|
114
|
-
upload: uploadAsset,
|
|
115
393
|
delete: deleteAsset,
|
|
116
394
|
};
|
|
395
|
+
/**
|
|
396
|
+
* GET /assets/raw/{key...} - see `rawAssetHandler` above for why this is
|
|
397
|
+
* registered separately from `ASSET_ROUTES` instead of through defineEndpoint.
|
|
398
|
+
*/
|
|
399
|
+
export const assetRawRoute = {
|
|
400
|
+
method: 'GET',
|
|
401
|
+
pattern: ['assets', 'raw', '...key'],
|
|
402
|
+
handler: rawAssetHandler,
|
|
403
|
+
};
|
|
117
404
|
//# sourceMappingURL=assets.js.map
|
package/dist/api/assets.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/api/assets.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/api/assets.ts"],"names":[],"mappings":"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"}
|
package/dist/api/branch.d.ts
CHANGED
|
@@ -19,6 +19,12 @@ export type BranchListResponse = ApiResponse<{
|
|
|
19
19
|
/** Response type for branch deletion */
|
|
20
20
|
export type BranchDeleteResponse = ApiResponse<{
|
|
21
21
|
deleted: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Set when branch.json was removed (so the branch is logically gone from
|
|
24
|
+
* the registry) but the full directory removal failed -- an orphan clone
|
|
25
|
+
* persists on disk, invisible to the API, until manually cleaned up.
|
|
26
|
+
*/
|
|
27
|
+
cleanupWarning?: string;
|
|
22
28
|
}>;
|
|
23
29
|
declare const createBranchBodySchema: z.ZodObject<{
|
|
24
30
|
branch: z.ZodType<import("../paths").BranchName, z.ZodTypeDef, import("../paths").BranchName>;
|
package/dist/api/branch.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch.d.ts","sourceRoot":"","sources":["../../src/api/branch.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAuB,aAAa,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"branch.d.ts","sourceRoot":"","sources":["../../src/api/branch.ts"],"names":[],"mappings":"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"}
|
package/dist/api/branch.js
CHANGED
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { BranchWorkspaceManager } from '../branch-workspace.js';
|
|
5
5
|
import { getBranchMetadataFileManager } from '../branch-metadata.js';
|
|
6
|
+
import { withOccFileLock } from '../utils/occ-json-write.js';
|
|
6
7
|
import { defineEndpoint } from './route-builder.js';
|
|
7
8
|
import { createDebugLogger } from '../utils/debug.js';
|
|
8
9
|
import { clientOperatingStrategy } from '../operating-mode/index.js';
|
|
@@ -210,24 +211,56 @@ export const deleteBranchHandler = async (ctx, req, params) => {
|
|
|
210
211
|
error: 'Cannot delete branch with open pull request',
|
|
211
212
|
};
|
|
212
213
|
}
|
|
213
|
-
// Delete branch metadata file so it disappears from registry scans
|
|
214
|
+
// Delete branch metadata file so it disappears from registry scans.
|
|
215
|
+
// Hold the same server-enforced lockfile branch-metadata saves hold
|
|
216
|
+
// (see utils/occ-json-write.ts): an unguarded unlink racing a concurrent
|
|
217
|
+
// save() would let the save's create path resurrect a phantom branch.json
|
|
218
|
+
// inside a deleted branch, which the registry's next scan would list as a
|
|
219
|
+
// live branch with no clone. The directory removal happens inside the
|
|
220
|
+
// same hold so a racing save cannot slip between unlink and rm either.
|
|
214
221
|
const metadataFile = path.join(branchContext.branchRoot, '.canopy-meta', 'branch.json');
|
|
222
|
+
let cleanupWarning;
|
|
215
223
|
try {
|
|
216
|
-
await
|
|
224
|
+
await withOccFileLock(metadataFile, async () => {
|
|
225
|
+
try {
|
|
226
|
+
await fs.unlink(metadataFile);
|
|
227
|
+
}
|
|
228
|
+
catch (err) {
|
|
229
|
+
if (!isNotFoundError(err)) {
|
|
230
|
+
console.error(`CanopyCMS: Failed to delete branch metadata for ${branchName}:`, getErrorMessage(err));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// In multi-branch modes, also delete the entire branch directory.
|
|
234
|
+
// Retry transient EFS/NFS errors (ENOTEMPTY from a concurrent writer,
|
|
235
|
+
// EBUSY) a few times before giving up -- rm's failure must never be
|
|
236
|
+
// swallowed: metadata is gone either way (the branch is logically
|
|
237
|
+
// deleted and will no longer appear in listings), but silently
|
|
238
|
+
// succeeding here would leave a full orphan clone on disk with
|
|
239
|
+
// nothing in the API surfacing its existence.
|
|
240
|
+
if (branchContext.branchRoot !== branchContext.baseRoot) {
|
|
241
|
+
try {
|
|
242
|
+
await fs.rm(branchContext.branchRoot, {
|
|
243
|
+
recursive: true,
|
|
244
|
+
force: true,
|
|
245
|
+
maxRetries: 3,
|
|
246
|
+
retryDelay: 100,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
catch (err) {
|
|
250
|
+
cleanupWarning = `Failed to fully remove branch directory: ${getErrorMessage(err)}`;
|
|
251
|
+
console.error(`CanopyCMS: Failed to delete branch directory for ${branchName}:`, getErrorMessage(err));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
});
|
|
217
255
|
}
|
|
218
256
|
catch (err) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
await fs.rm(branchContext.branchRoot, { recursive: true, force: true });
|
|
227
|
-
}
|
|
228
|
-
catch (err) {
|
|
229
|
-
console.error(`CanopyCMS: Failed to delete branch directory for ${branchName}:`, getErrorMessage(err));
|
|
230
|
-
}
|
|
257
|
+
// Lock acquisition failed (e.g. contention past the retry budget) —
|
|
258
|
+
// surface as an error rather than silently skipping the delete.
|
|
259
|
+
return {
|
|
260
|
+
ok: false,
|
|
261
|
+
status: 409,
|
|
262
|
+
error: `Branch is busy, try again: ${getErrorMessage(err)}`,
|
|
263
|
+
};
|
|
231
264
|
}
|
|
232
265
|
// Invalidate registry cache so next list() will regenerate without this branch
|
|
233
266
|
if (!ctx.services.registry) {
|
|
@@ -238,7 +271,11 @@ export const deleteBranchHandler = async (ctx, req, params) => {
|
|
|
238
271
|
};
|
|
239
272
|
}
|
|
240
273
|
await ctx.services.registry.invalidate();
|
|
241
|
-
return {
|
|
274
|
+
return {
|
|
275
|
+
ok: true,
|
|
276
|
+
status: 200,
|
|
277
|
+
data: { deleted: true, ...(cleanupWarning && { cleanupWarning }) },
|
|
278
|
+
};
|
|
242
279
|
};
|
|
243
280
|
/**
|
|
244
281
|
* Check if a user can modify branch access.
|
|
@@ -271,10 +308,17 @@ export const updateBranchAccessHandler = async (ctx, req, params, body) => {
|
|
|
271
308
|
error: 'You do not have permission to modify this branch',
|
|
272
309
|
};
|
|
273
310
|
}
|
|
274
|
-
// Build
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
311
|
+
// Build the access DELTA from only the keys the caller actually supplied —
|
|
312
|
+
// never spread branchContext.branch.access (a snapshot resolved before
|
|
313
|
+
// this handler acquired anything) wholesale. save()'s field-level merge
|
|
314
|
+
// (branch-metadata.ts) takes the incoming access object's keys over the
|
|
315
|
+
// freshly-reloaded on-disk ones, so a full stale spread here would
|
|
316
|
+
// silently revert any key a concurrent request changed via the OTHER key
|
|
317
|
+
// in the gap between this handler's getBranchContext() and its save()
|
|
318
|
+
// call. An omitted key must be ABSENT from this object (not merely
|
|
319
|
+
// undefined-valued) so save()'s spread-merge leaves the on-disk value
|
|
320
|
+
// untouched; a supplied `[]` still comes through and clears the field.
|
|
321
|
+
const newAccess = {};
|
|
278
322
|
if (body.allowedUsers !== undefined) {
|
|
279
323
|
newAccess.allowedUsers = body.allowedUsers;
|
|
280
324
|
}
|