@lowdefy/plugin-aws 0.0.0-experimental-20260720072521 → 0.0.0-experimental-20260721080548
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/blocks/S3Download/S3Download.js +54 -12
- package/dist/blocks/S3UploadButton/S3UploadButton.js +79 -19
- package/dist/blocks/S3UploadDragger/S3UploadDragger.js +92 -19
- package/dist/blocks/S3UploadDragger/style.module.css +30 -0
- package/dist/blocks/S3UploadPhoto/S3UploadPhoto.js +93 -19
- package/dist/blocks/utils/getOnPaste.js +48 -0
- package/dist/blocks/utils/getS3Upload.js +92 -0
- package/dist/blocks/utils/useFileList.js +138 -0
- package/dist/blocks/withTheme.js +40 -0
- package/dist/connections/AwsS3Bucket/AwsS3Bucket.js +2 -6
- package/dist/connections/AwsS3Bucket/AwsS3PresignedGetObject/AwsS3PresignedGetObject.js +8 -11
- package/dist/connections/AwsS3Bucket/AwsS3PresignedGetObject/schema.js +0 -8
- package/dist/connections/AwsS3Bucket/AwsS3PresignedPostPolicy/AwsS3PresignedPostPolicy.js +12 -16
- package/dist/connections/AwsS3Bucket/schema.js +0 -21
- package/dist/types.js +1 -3
- package/package.json +15 -18
- package/dist/connections/AwsS3Bucket/AwsS3GetObject/AwsS3GetObject.js +0 -54
- package/dist/connections/AwsS3Bucket/AwsS3GetObject/schema.js +0 -42
- package/dist/connections/AwsS3Bucket/AwsS3PutObject/AwsS3PutObject.js +0 -48
- package/dist/connections/AwsS3Bucket/AwsS3PutObject/schema.js +0 -70
- package/dist/connections/AwsS3Bucket/createS3Client.js +0 -34
- package/dist/connections/AwsS3Bucket/getPublicUrl.js +0 -30
|
@@ -13,20 +13,62 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ import React, { useEffect } from 'react';
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
16
|
+
import { Upload } from 'antd';
|
|
17
|
+
import { withBlockDefaults } from '@lowdefy/block-utils';
|
|
18
|
+
import withTheme from '../withTheme.js';
|
|
19
|
+
const downloadFile = async ({ file, methods })=>{
|
|
20
|
+
const s3DownloadPolicy = await methods.triggerEvent({
|
|
21
|
+
name: '__getS3DownloadPolicy',
|
|
22
|
+
event: {
|
|
23
|
+
file
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
window.open(s3DownloadPolicy?.responses?.__getS3DownloadPolicy?.response?.[0]);
|
|
27
|
+
};
|
|
28
|
+
const S3Download = ({ blockId, classNames = {}, methods, properties, styles = {} })=>{
|
|
20
29
|
useEffect(()=>{
|
|
21
|
-
|
|
30
|
+
methods.registerEvent({
|
|
31
|
+
name: '__getS3DownloadPolicy',
|
|
32
|
+
actions: [
|
|
33
|
+
{
|
|
34
|
+
id: '__getS3DownloadPolicy',
|
|
35
|
+
type: 'Request',
|
|
36
|
+
params: [
|
|
37
|
+
properties.s3GetPolicyRequestId
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
});
|
|
22
42
|
}, []);
|
|
23
|
-
const
|
|
24
|
-
return /*#__PURE__*/ React.createElement(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
43
|
+
const showRemoveIcon = properties.showRemoveIcon ?? false;
|
|
44
|
+
return /*#__PURE__*/ React.createElement(Upload, {
|
|
45
|
+
id: blockId,
|
|
46
|
+
className: classNames.element,
|
|
47
|
+
style: styles.element,
|
|
48
|
+
fileList: properties.fileList ?? [],
|
|
49
|
+
onPreview: async (file)=>await downloadFile({
|
|
50
|
+
file,
|
|
51
|
+
methods
|
|
52
|
+
}),
|
|
53
|
+
onDownload: async (file)=>await downloadFile({
|
|
54
|
+
file,
|
|
55
|
+
methods
|
|
56
|
+
}),
|
|
57
|
+
onRemove: (file)=>{
|
|
58
|
+
methods.triggerEvent({
|
|
59
|
+
name: 'onRemove',
|
|
60
|
+
event: {
|
|
61
|
+
file
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
// Controlled fileList: the YAML handler decides whether to update state.
|
|
65
|
+
// Return false so antd doesn't fire onChange with a removed-file list.
|
|
66
|
+
return false;
|
|
67
|
+
},
|
|
68
|
+
showUploadList: {
|
|
69
|
+
showDownloadIcon: true,
|
|
70
|
+
showRemoveIcon
|
|
29
71
|
}
|
|
30
72
|
});
|
|
31
73
|
};
|
|
32
|
-
export default S3Download;
|
|
74
|
+
export default withBlockDefaults(withTheme('Upload', S3Download));
|
|
@@ -13,28 +13,88 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ import React, { useEffect } from 'react';
|
|
16
|
-
import { cn } from '@lowdefy/block-utils';
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
16
|
+
import { cn, withBlockDefaults } from '@lowdefy/block-utils';
|
|
17
|
+
import { Button } from '@lowdefy/blocks-antd/blocks';
|
|
18
|
+
import { Upload } from 'antd';
|
|
19
|
+
import useFileList from '../utils/useFileList.js';
|
|
20
|
+
import getS3Upload from '../utils/getS3Upload.js';
|
|
21
|
+
import withTheme from '../withTheme.js';
|
|
22
|
+
const S3UploadButtonBlock = ({ blockId, classNames = {}, components, events, methods, properties, styles = {}, value })=>{
|
|
23
|
+
const [state, loadFileList, setFileList, removeFile, setValue] = useFileList({
|
|
24
|
+
properties,
|
|
25
|
+
methods,
|
|
26
|
+
value
|
|
27
|
+
});
|
|
28
|
+
const s3UploadRequest = getS3Upload({
|
|
29
|
+
methods,
|
|
30
|
+
setFileList
|
|
31
|
+
});
|
|
23
32
|
useEffect(()=>{
|
|
24
|
-
|
|
33
|
+
methods.setValue({
|
|
34
|
+
file: null,
|
|
35
|
+
fileList: []
|
|
36
|
+
});
|
|
37
|
+
methods.registerEvent({
|
|
38
|
+
name: '__getS3PostPolicy',
|
|
39
|
+
actions: [
|
|
40
|
+
{
|
|
41
|
+
id: '__getS3PostPolicy',
|
|
42
|
+
type: 'Request',
|
|
43
|
+
params: [
|
|
44
|
+
properties.s3PostPolicyRequestId
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
});
|
|
25
49
|
}, []);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
50
|
+
useEffect(()=>{
|
|
51
|
+
if (JSON.stringify(value) !== JSON.stringify(state)) {
|
|
52
|
+
setValue(value);
|
|
53
|
+
}
|
|
54
|
+
}, [
|
|
55
|
+
value
|
|
56
|
+
]);
|
|
57
|
+
return /*#__PURE__*/ React.createElement("div", {
|
|
58
|
+
id: blockId,
|
|
59
|
+
className: cn('lf-s3-upload-button', classNames.element),
|
|
60
|
+
style: styles.element
|
|
61
|
+
}, /*#__PURE__*/ React.createElement(Upload, {
|
|
62
|
+
accept: properties.accept ?? '*',
|
|
63
|
+
beforeUpload: loadFileList,
|
|
30
64
|
classNames: {
|
|
31
|
-
|
|
32
|
-
|
|
65
|
+
trigger: classNames.trigger,
|
|
66
|
+
list: classNames.list,
|
|
67
|
+
item: classNames.item
|
|
33
68
|
},
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
69
|
+
styles: {
|
|
70
|
+
trigger: styles.trigger,
|
|
71
|
+
list: styles.list,
|
|
72
|
+
item: styles.item
|
|
73
|
+
},
|
|
74
|
+
customRequest: s3UploadRequest,
|
|
75
|
+
disabled: properties.disabled,
|
|
76
|
+
fileList: state.fileList,
|
|
77
|
+
maxCount: properties.maxCount,
|
|
78
|
+
multiple: !properties.singleFile,
|
|
79
|
+
onRemove: removeFile,
|
|
80
|
+
showUploadList: properties.showUploadList,
|
|
81
|
+
onChange: ()=>{
|
|
82
|
+
methods.triggerEvent({
|
|
83
|
+
name: 'onChange'
|
|
84
|
+
});
|
|
37
85
|
}
|
|
38
|
-
}
|
|
86
|
+
}, /*#__PURE__*/ React.createElement(Button, {
|
|
87
|
+
blockId: `${blockId}_button`,
|
|
88
|
+
components: components,
|
|
89
|
+
events: events,
|
|
90
|
+
properties: {
|
|
91
|
+
disabled: properties.disabled,
|
|
92
|
+
icon: 'AiOutlineUpload',
|
|
93
|
+
title: 'Upload',
|
|
94
|
+
type: 'default',
|
|
95
|
+
...properties.button
|
|
96
|
+
},
|
|
97
|
+
methods: methods
|
|
98
|
+
})));
|
|
39
99
|
};
|
|
40
|
-
export default
|
|
100
|
+
export default withBlockDefaults(withTheme('Upload', S3UploadButtonBlock));
|
|
@@ -13,28 +13,101 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ import React, { useEffect } from 'react';
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
import { Upload, theme as antdTheme } from 'antd';
|
|
17
|
+
import { cn, renderHtml, withBlockDefaults } from '@lowdefy/block-utils';
|
|
18
|
+
import { type } from '@lowdefy/helpers';
|
|
19
|
+
import useFileList from '../utils/useFileList.js';
|
|
20
|
+
import getS3Upload from '../utils/getS3Upload.js';
|
|
21
|
+
import getOnPaste from '../utils/getOnPaste.js';
|
|
22
|
+
import withTheme from '../withTheme.js';
|
|
23
|
+
import './style.module.css';
|
|
24
|
+
const { Dragger } = Upload;
|
|
25
|
+
const S3UploadDragger = ({ blockId, classNames = {}, methods, properties, styles = {}, value })=>{
|
|
26
|
+
const [state, loadFileList, setFileList, removeFile, setValue] = useFileList({
|
|
27
|
+
properties,
|
|
28
|
+
methods,
|
|
29
|
+
value
|
|
30
|
+
});
|
|
31
|
+
const s3UploadRequest = getS3Upload({
|
|
32
|
+
methods,
|
|
33
|
+
setFileList
|
|
34
|
+
});
|
|
35
|
+
const onPaste = getOnPaste({
|
|
36
|
+
s3UploadRequest,
|
|
37
|
+
properties
|
|
38
|
+
});
|
|
23
39
|
useEffect(()=>{
|
|
24
|
-
|
|
40
|
+
methods.registerEvent({
|
|
41
|
+
name: '__getS3PostPolicy',
|
|
42
|
+
actions: [
|
|
43
|
+
{
|
|
44
|
+
id: '__getS3PostPolicy',
|
|
45
|
+
type: 'Request',
|
|
46
|
+
params: [
|
|
47
|
+
properties.s3PostPolicyRequestId
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
});
|
|
25
52
|
}, []);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
53
|
+
useEffect(()=>{
|
|
54
|
+
if (JSON.stringify(value) !== JSON.stringify(state)) {
|
|
55
|
+
setValue(value);
|
|
56
|
+
}
|
|
57
|
+
}, [
|
|
58
|
+
value
|
|
59
|
+
]);
|
|
60
|
+
useEffect(()=>{
|
|
61
|
+
methods.registerMethod('uploadFromPaste', async ()=>{
|
|
62
|
+
await onPaste();
|
|
63
|
+
});
|
|
64
|
+
}, [
|
|
65
|
+
onPaste
|
|
66
|
+
]);
|
|
67
|
+
const { token } = antdTheme.useToken();
|
|
68
|
+
const height = type.isNone(properties.height) ? token.controlHeight : properties.height;
|
|
69
|
+
return /*#__PURE__*/ React.createElement("div", {
|
|
70
|
+
id: blockId,
|
|
71
|
+
className: cn('lf-s3-upload-dragger', classNames.element),
|
|
72
|
+
onPaste: onPaste,
|
|
73
|
+
style: {
|
|
74
|
+
'--lf-s3-dragger-height': type.isNumber(height) ? `${height}px` : height,
|
|
75
|
+
...styles.element
|
|
76
|
+
}
|
|
77
|
+
}, /*#__PURE__*/ React.createElement(Dragger, {
|
|
78
|
+
accept: properties.accept ?? '*',
|
|
79
|
+
beforeUpload: loadFileList,
|
|
30
80
|
classNames: {
|
|
31
|
-
|
|
32
|
-
|
|
81
|
+
trigger: classNames.trigger,
|
|
82
|
+
list: classNames.list,
|
|
83
|
+
item: classNames.item
|
|
33
84
|
},
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
85
|
+
styles: {
|
|
86
|
+
root: {
|
|
87
|
+
display: 'block'
|
|
88
|
+
},
|
|
89
|
+
trigger: styles.trigger,
|
|
90
|
+
list: styles.list,
|
|
91
|
+
item: styles.item
|
|
92
|
+
},
|
|
93
|
+
customRequest: s3UploadRequest,
|
|
94
|
+
disabled: properties.disabled,
|
|
95
|
+
fileList: state.fileList,
|
|
96
|
+
maxCount: properties.maxCount,
|
|
97
|
+
multiple: !properties.singleFile,
|
|
98
|
+
onRemove: removeFile,
|
|
99
|
+
showUploadList: properties.showUploadList,
|
|
100
|
+
onChange: ()=>{
|
|
101
|
+
methods.triggerEvent({
|
|
102
|
+
name: 'onChange'
|
|
103
|
+
});
|
|
37
104
|
}
|
|
38
|
-
}
|
|
105
|
+
}, /*#__PURE__*/ React.createElement("div", {
|
|
106
|
+
className: cn(classNames.hint),
|
|
107
|
+
style: styles.hint
|
|
108
|
+
}, renderHtml({
|
|
109
|
+
html: properties.title ?? 'Click or drag to add a file.',
|
|
110
|
+
methods
|
|
111
|
+
}))));
|
|
39
112
|
};
|
|
40
|
-
export default S3UploadDragger;
|
|
113
|
+
export default withBlockDefaults(withTheme('Upload', S3UploadDragger));
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
@layer components {
|
|
18
|
+
/* Height applies to the drop zone, not the outer wrapper, so the file list
|
|
19
|
+
(rendered as a sibling of `.ant-upload-drag` inside the Dragger root) can
|
|
20
|
+
flow inline below it instead of overflowing a fixed-height container. */
|
|
21
|
+
:global(.lf-s3-upload-dragger .ant-upload-drag) {
|
|
22
|
+
height: var(--lf-s3-dragger-height);
|
|
23
|
+
}
|
|
24
|
+
/* antd v6 applies `padding: token.padding` to `.ant-upload-drag .ant-upload`,
|
|
25
|
+
which overflows the drag area when the wrapper height is small. Scope the
|
|
26
|
+
override to this block and let users add padding via style.hint. */
|
|
27
|
+
:global(.lf-s3-upload-dragger .ant-upload-drag .ant-upload) {
|
|
28
|
+
padding: 0;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -12,29 +12,103 @@
|
|
|
12
12
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
|
-
*/ import React, { useEffect } from 'react';
|
|
16
|
-
import { cn } from '@lowdefy/block-utils';
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
15
|
+
*/ import React, { useEffect, useState } from 'react';
|
|
16
|
+
import { cn, renderHtml, withBlockDefaults } from '@lowdefy/block-utils';
|
|
17
|
+
import { Upload } from 'antd';
|
|
18
|
+
import useFileList from '../utils/useFileList.js';
|
|
19
|
+
import getS3Upload from '../utils/getS3Upload.js';
|
|
20
|
+
import withTheme from '../withTheme.js';
|
|
21
|
+
const S3UploadPhoto = ({ blockId, classNames = {}, components: { Icon }, events, methods, properties, styles = {}, value })=>{
|
|
22
|
+
const [state, loadFileList, setFileList, removeFile, setValue] = useFileList({
|
|
23
|
+
properties,
|
|
24
|
+
methods,
|
|
25
|
+
value
|
|
26
|
+
});
|
|
27
|
+
const [loading, setLoading] = useState(false);
|
|
28
|
+
const s3UploadRequest = getS3Upload({
|
|
29
|
+
methods,
|
|
30
|
+
setFileList,
|
|
31
|
+
setLoading
|
|
32
|
+
});
|
|
23
33
|
useEffect(()=>{
|
|
24
|
-
|
|
34
|
+
methods.setValue({
|
|
35
|
+
file: null,
|
|
36
|
+
fileList: []
|
|
37
|
+
});
|
|
38
|
+
methods.registerEvent({
|
|
39
|
+
name: '__getS3PostPolicy',
|
|
40
|
+
actions: [
|
|
41
|
+
{
|
|
42
|
+
id: '__getS3PostPolicy',
|
|
43
|
+
type: 'Request',
|
|
44
|
+
params: [
|
|
45
|
+
properties.s3PostPolicyRequestId
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
});
|
|
25
50
|
}, []);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
51
|
+
useEffect(()=>{
|
|
52
|
+
if (JSON.stringify(value) !== JSON.stringify(state)) {
|
|
53
|
+
setValue(value);
|
|
54
|
+
}
|
|
55
|
+
}, [
|
|
56
|
+
value
|
|
57
|
+
]);
|
|
58
|
+
return /*#__PURE__*/ React.createElement("div", {
|
|
59
|
+
id: blockId,
|
|
60
|
+
className: cn('lf-s3-upload-photo', classNames.element),
|
|
61
|
+
style: styles.element
|
|
62
|
+
}, /*#__PURE__*/ React.createElement(Upload, {
|
|
63
|
+
accept: "image/*",
|
|
64
|
+
beforeUpload: loadFileList,
|
|
30
65
|
classNames: {
|
|
31
|
-
|
|
32
|
-
|
|
66
|
+
trigger: classNames.trigger,
|
|
67
|
+
list: classNames.list,
|
|
68
|
+
item: classNames.item
|
|
69
|
+
},
|
|
70
|
+
styles: {
|
|
71
|
+
trigger: styles.trigger,
|
|
72
|
+
list: styles.list,
|
|
73
|
+
item: styles.item
|
|
33
74
|
},
|
|
75
|
+
customRequest: s3UploadRequest,
|
|
76
|
+
disabled: properties.disabled,
|
|
77
|
+
fileList: state.fileList,
|
|
78
|
+
listType: "picture-card",
|
|
79
|
+
maxCount: properties.maxCount,
|
|
80
|
+
multiple: !properties.singleFile,
|
|
81
|
+
onRemove: removeFile,
|
|
82
|
+
showUploadList: properties.showUploadList,
|
|
83
|
+
onChange: ()=>{
|
|
84
|
+
methods.triggerEvent({
|
|
85
|
+
name: 'onChange'
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}, /*#__PURE__*/ React.createElement("div", {
|
|
89
|
+
className: "lf-s3-upload-photo-content"
|
|
90
|
+
}, /*#__PURE__*/ React.createElement(Icon, {
|
|
91
|
+
blockId: `${blockId}_icon`,
|
|
92
|
+
classNames: {
|
|
93
|
+
element: cn('lf-s3-upload-photo-icon', classNames.icon)
|
|
94
|
+
},
|
|
95
|
+
events: events,
|
|
34
96
|
properties: {
|
|
35
|
-
|
|
36
|
-
|
|
97
|
+
name: loading ? 'AiOutlineLoading' : 'AiOutlineCamera',
|
|
98
|
+
size: 24
|
|
99
|
+
},
|
|
100
|
+
styles: {
|
|
101
|
+
element: styles.icon
|
|
37
102
|
}
|
|
38
|
-
})
|
|
103
|
+
}), /*#__PURE__*/ React.createElement("div", {
|
|
104
|
+
className: cn('lf-s3-upload-photo-title', classNames.title),
|
|
105
|
+
style: {
|
|
106
|
+
marginTop: 8,
|
|
107
|
+
...styles.title
|
|
108
|
+
}
|
|
109
|
+
}, renderHtml({
|
|
110
|
+
html: properties.title ?? 'Upload image',
|
|
111
|
+
methods
|
|
112
|
+
})))));
|
|
39
113
|
};
|
|
40
|
-
export default S3UploadPhoto;
|
|
114
|
+
export default withBlockDefaults(withTheme('Upload', S3UploadPhoto));
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ const getFileFromEvent = async (event)=>{
|
|
16
|
+
const items = event.clipboardData.items;
|
|
17
|
+
for (const item of items){
|
|
18
|
+
if (item.kind === 'file') {
|
|
19
|
+
return item.getAsFile();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const getFileFromNavigator = async ()=>{
|
|
24
|
+
const items = await navigator.clipboard.read();
|
|
25
|
+
for (const item of items){
|
|
26
|
+
for (const type of item.types){
|
|
27
|
+
if (type === 'image/png' || type === 'image/jpeg') {
|
|
28
|
+
const blob = await item.getType(type);
|
|
29
|
+
return new File([
|
|
30
|
+
blob
|
|
31
|
+
], 'clipboard.png', {
|
|
32
|
+
type: blob.type
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const getOnPaste = ({ s3UploadRequest, properties })=>async (event)=>{
|
|
39
|
+
event?.preventDefault?.();
|
|
40
|
+
if (properties.disabled) return;
|
|
41
|
+
const file = event ? await getFileFromEvent(event) : await getFileFromNavigator();
|
|
42
|
+
if (!file) return;
|
|
43
|
+
file.uid = `${properties.fileName ?? file.name ?? 'clipboard'}-${Date.now()}`;
|
|
44
|
+
await s3UploadRequest({
|
|
45
|
+
file
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
export default getOnPaste;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import { ServiceError } from '@lowdefy/errors';
|
|
16
|
+
const getS3Upload = ({ methods, setFileList, setLoading = ()=>null })=>async ({ file })=>{
|
|
17
|
+
if (!file) {
|
|
18
|
+
console.warn('File is undefined in useS3Upload');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
setLoading(true);
|
|
23
|
+
const { lastModified, name, size, type, uid } = file;
|
|
24
|
+
const s3PostPolicyResponse = await methods.triggerEvent({
|
|
25
|
+
name: '__getS3PostPolicy',
|
|
26
|
+
event: {
|
|
27
|
+
file: {
|
|
28
|
+
name,
|
|
29
|
+
lastModified,
|
|
30
|
+
size,
|
|
31
|
+
type,
|
|
32
|
+
uid
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
if (s3PostPolicyResponse.success !== true) {
|
|
37
|
+
throw new Error('S3 post policy request error.');
|
|
38
|
+
}
|
|
39
|
+
const { url, fields = {} } = s3PostPolicyResponse.responses.__getS3PostPolicy.response[0];
|
|
40
|
+
const { bucket, key } = fields;
|
|
41
|
+
file.bucket = bucket;
|
|
42
|
+
file.key = key;
|
|
43
|
+
file.percent = 20;
|
|
44
|
+
const formData = new FormData();
|
|
45
|
+
Object.keys(fields).forEach((field)=>{
|
|
46
|
+
formData.append(field, fields[field]);
|
|
47
|
+
});
|
|
48
|
+
formData.append('file', file);
|
|
49
|
+
await new Promise((resolve, reject)=>{
|
|
50
|
+
const xhr = new XMLHttpRequest();
|
|
51
|
+
xhr.upload.onprogress = async (event)=>{
|
|
52
|
+
if (event.lengthComputable) {
|
|
53
|
+
await setFileList({
|
|
54
|
+
event: 'onProgress',
|
|
55
|
+
file,
|
|
56
|
+
percent: event.loaded / event.total * 80 + 20
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
xhr.addEventListener('load', ()=>{
|
|
61
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
62
|
+
resolve();
|
|
63
|
+
} else {
|
|
64
|
+
reject(new Error(`S3 upload failed with status ${xhr.status}.`));
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
xhr.addEventListener('error', ()=>{
|
|
68
|
+
reject(new ServiceError(`S3 upload failed for "${name}" — CORS or network error. ` + `Check that the S3 bucket CORS configuration allows requests from this origin.`, {
|
|
69
|
+
service: 'S3'
|
|
70
|
+
}));
|
|
71
|
+
});
|
|
72
|
+
xhr.addEventListener('abort', ()=>{
|
|
73
|
+
reject(new Error(`S3 upload aborted for "${name}".`));
|
|
74
|
+
});
|
|
75
|
+
xhr.open('post', url);
|
|
76
|
+
xhr.send(formData);
|
|
77
|
+
});
|
|
78
|
+
await setFileList({
|
|
79
|
+
event: 'onSuccess',
|
|
80
|
+
file
|
|
81
|
+
});
|
|
82
|
+
setLoading(false);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
await setFileList({
|
|
85
|
+
event: 'onError',
|
|
86
|
+
file
|
|
87
|
+
});
|
|
88
|
+
setLoading(false);
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
export default getS3Upload;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import { useState } from 'react';
|
|
16
|
+
import { type } from '@lowdefy/helpers';
|
|
17
|
+
const useFileList = ({ properties, methods, value = {} })=>{
|
|
18
|
+
const checkedValue = (value)=>{
|
|
19
|
+
let file = type.isObject(value?.file) ? value.file : null;
|
|
20
|
+
if (!file && type.isObject(value?.fileList?.[0])) {
|
|
21
|
+
file = value.fileList[0];
|
|
22
|
+
}
|
|
23
|
+
const fileList = type.isArray(value?.fileList) ? value.fileList : [];
|
|
24
|
+
if (properties.singleFile === true) {
|
|
25
|
+
fileList.splice(1);
|
|
26
|
+
}
|
|
27
|
+
if (type.isInt(properties.maxCount)) {
|
|
28
|
+
fileList.splice(properties.maxCount);
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
file,
|
|
32
|
+
fileList
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
const [state, setState] = useState(checkedValue(value));
|
|
36
|
+
const setValue = (stateValue)=>{
|
|
37
|
+
setState(()=>{
|
|
38
|
+
return checkedValue(stateValue);
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
const setFileList = async ({ event, file, percent })=>{
|
|
42
|
+
if (!file) {
|
|
43
|
+
throw new Error('File is undefined in useFileList');
|
|
44
|
+
}
|
|
45
|
+
// destruct the file object to avoid the file object being mutated.
|
|
46
|
+
const { bucket, key, lastModified, name, size, status, type, uid } = file;
|
|
47
|
+
const fileObj = {
|
|
48
|
+
bucket,
|
|
49
|
+
key,
|
|
50
|
+
lastModified,
|
|
51
|
+
name,
|
|
52
|
+
percent: percent ?? file.percent ?? 0,
|
|
53
|
+
size,
|
|
54
|
+
status,
|
|
55
|
+
type,
|
|
56
|
+
uid,
|
|
57
|
+
url: file instanceof Blob || file instanceof File ? URL.createObjectURL(file) : null
|
|
58
|
+
};
|
|
59
|
+
switch(event){
|
|
60
|
+
case 'onProgress':
|
|
61
|
+
fileObj.status = 'uploading';
|
|
62
|
+
fileObj.percent = percent ?? fileObj.percent;
|
|
63
|
+
break;
|
|
64
|
+
case 'onSuccess':
|
|
65
|
+
fileObj.status = 'done';
|
|
66
|
+
fileObj.percent = 100;
|
|
67
|
+
break;
|
|
68
|
+
case 'onRemove':
|
|
69
|
+
fileObj.status = 'removed';
|
|
70
|
+
break;
|
|
71
|
+
default:
|
|
72
|
+
fileObj.status = 'error';
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
state.fileList.splice(state.fileList.findIndex((f)=>f.uid === fileObj.uid), 1, fileObj);
|
|
76
|
+
const nextState = checkedValue({
|
|
77
|
+
file: fileObj,
|
|
78
|
+
fileList: state.fileList
|
|
79
|
+
});
|
|
80
|
+
await methods.triggerEvent({
|
|
81
|
+
name: event,
|
|
82
|
+
event: nextState
|
|
83
|
+
});
|
|
84
|
+
setValue(nextState);
|
|
85
|
+
methods.setValue(nextState);
|
|
86
|
+
};
|
|
87
|
+
const loadFileList = async (file, nextFiles)=>{
|
|
88
|
+
if (properties.singleFile === true && nextFiles.filter((f)=>type.isString(f.uid)).length > 1) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
if (type.isInt(properties.maxCount) && nextFiles.filter((f)=>type.isString(f.uid)).length > properties.maxCount) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
// Extract file properties into a serialization-safe plain object.
|
|
95
|
+
// Raw File/Blob objects are destroyed by serializer.copy() in _event resolution.
|
|
96
|
+
const fileData = {
|
|
97
|
+
name: file.name,
|
|
98
|
+
type: file.type,
|
|
99
|
+
size: file.size,
|
|
100
|
+
lastModified: file.lastModified,
|
|
101
|
+
uid: file.uid
|
|
102
|
+
};
|
|
103
|
+
if (file instanceof Blob || file instanceof File) {
|
|
104
|
+
fileData.url = URL.createObjectURL(file);
|
|
105
|
+
}
|
|
106
|
+
const result = await methods.triggerEvent({
|
|
107
|
+
name: 'onBeforeUpload',
|
|
108
|
+
event: {
|
|
109
|
+
file: fileData
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
if (result.success === false) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
const nextState = {
|
|
116
|
+
file: fileData,
|
|
117
|
+
fileList: [
|
|
118
|
+
...nextFiles,
|
|
119
|
+
...state.fileList
|
|
120
|
+
]
|
|
121
|
+
};
|
|
122
|
+
setValue(nextState);
|
|
123
|
+
methods.setValue(nextState);
|
|
124
|
+
};
|
|
125
|
+
const removeFile = (file)=>{
|
|
126
|
+
state.fileList.splice(state.fileList.findIndex((f)=>f.uid === file.uid), 1);
|
|
127
|
+
setValue(state);
|
|
128
|
+
methods.setValue(state);
|
|
129
|
+
};
|
|
130
|
+
return [
|
|
131
|
+
state,
|
|
132
|
+
loadFileList,
|
|
133
|
+
setFileList,
|
|
134
|
+
removeFile,
|
|
135
|
+
setValue
|
|
136
|
+
];
|
|
137
|
+
};
|
|
138
|
+
export default useFileList;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import React from 'react';
|
|
16
|
+
import { ConfigProvider } from 'antd';
|
|
17
|
+
import { type } from '@lowdefy/helpers';
|
|
18
|
+
function withTheme(antdComponentName, BlockComponent) {
|
|
19
|
+
const Wrapped = (props)=>{
|
|
20
|
+
const { theme, ...restProperties } = props.properties;
|
|
21
|
+
// Only intercept object themes (design tokens for ConfigProvider).
|
|
22
|
+
// String themes (e.g. Menu's 'dark'/'light') pass through to the component.
|
|
23
|
+
if (!type.isObject(theme)) {
|
|
24
|
+
return /*#__PURE__*/ React.createElement(BlockComponent, props);
|
|
25
|
+
}
|
|
26
|
+
return /*#__PURE__*/ React.createElement(ConfigProvider, {
|
|
27
|
+
theme: {
|
|
28
|
+
components: {
|
|
29
|
+
[antdComponentName]: theme
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}, /*#__PURE__*/ React.createElement(BlockComponent, {
|
|
33
|
+
...props,
|
|
34
|
+
properties: restProperties
|
|
35
|
+
}));
|
|
36
|
+
};
|
|
37
|
+
Wrapped.displayName = BlockComponent.displayName || BlockComponent.name;
|
|
38
|
+
return Wrapped;
|
|
39
|
+
}
|
|
40
|
+
export default withTheme;
|
|
@@ -12,17 +12,13 @@
|
|
|
12
12
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
|
-
*/ import
|
|
16
|
-
import AwsS3PresignedGetObject from './AwsS3PresignedGetObject/AwsS3PresignedGetObject.js';
|
|
15
|
+
*/ import AwsS3PresignedGetObject from './AwsS3PresignedGetObject/AwsS3PresignedGetObject.js';
|
|
17
16
|
import AwsS3PresignedPostPolicy from './AwsS3PresignedPostPolicy/AwsS3PresignedPostPolicy.js';
|
|
18
|
-
import AwsS3PutObject from './AwsS3PutObject/AwsS3PutObject.js';
|
|
19
17
|
import schema from './schema.js';
|
|
20
18
|
export default {
|
|
21
19
|
schema,
|
|
22
20
|
requests: {
|
|
23
|
-
AwsS3GetObject,
|
|
24
21
|
AwsS3PresignedGetObject,
|
|
25
|
-
AwsS3PresignedPostPolicy
|
|
26
|
-
AwsS3PutObject
|
|
22
|
+
AwsS3PresignedPostPolicy
|
|
27
23
|
}
|
|
28
24
|
};
|
|
@@ -12,19 +12,12 @@
|
|
|
12
12
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
|
-
*/ import { GetObjectCommand } from '@aws-sdk/client-s3';
|
|
15
|
+
*/ import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';
|
|
16
16
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
17
|
-
import createS3Client from '../createS3Client.js';
|
|
18
|
-
import getPublicUrl from '../getPublicUrl.js';
|
|
19
17
|
import schema from './schema.js';
|
|
20
18
|
async function AwsS3PresignedGetObject({ request, connection }) {
|
|
19
|
+
const { accessKeyId, secretAccessKey, region } = connection;
|
|
21
20
|
const { expires, key, versionId, responseContentDisposition, responseContentType } = request;
|
|
22
|
-
if (request.public === true) {
|
|
23
|
-
return getPublicUrl({
|
|
24
|
-
connection,
|
|
25
|
-
key
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
21
|
const params = {
|
|
29
22
|
Bucket: connection.bucket,
|
|
30
23
|
Key: key,
|
|
@@ -32,8 +25,12 @@ async function AwsS3PresignedGetObject({ request, connection }) {
|
|
|
32
25
|
ResponseContentDisposition: responseContentDisposition,
|
|
33
26
|
ResponseContentType: responseContentType
|
|
34
27
|
};
|
|
35
|
-
const s3 =
|
|
36
|
-
|
|
28
|
+
const s3 = new S3Client({
|
|
29
|
+
credentials: {
|
|
30
|
+
accessKeyId,
|
|
31
|
+
secretAccessKey
|
|
32
|
+
},
|
|
33
|
+
region
|
|
37
34
|
});
|
|
38
35
|
const command = new GetObjectCommand(params);
|
|
39
36
|
return getSignedUrl(s3, command, {
|
|
@@ -35,14 +35,6 @@
|
|
|
35
35
|
type: 'AwsS3PresignedGetObject request property "key" should be a string.'
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
|
-
public: {
|
|
39
|
-
type: 'boolean',
|
|
40
|
-
default: false,
|
|
41
|
-
description: 'Return a stable, non-expiring public URL instead of a signed URL. The object must be publicly readable (e.g. uploaded with acl "public-read") — public is author-declared and never checked at runtime.',
|
|
42
|
-
errorMessage: {
|
|
43
|
-
type: 'AwsS3PresignedGetObject request property "public" should be a boolean.'
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
38
|
responseContentDisposition: {
|
|
47
39
|
type: 'string',
|
|
48
40
|
description: 'Sets the Content-Disposition header of the response.',
|
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
|
-
*/ import {
|
|
16
|
-
import {
|
|
17
|
-
import createS3Client from '../createS3Client.js';
|
|
15
|
+
*/ import { S3Client } from '@aws-sdk/client-s3';
|
|
16
|
+
import { createPresignedPost } from '@aws-sdk/s3-presigned-post';
|
|
18
17
|
import schema from './schema.js';
|
|
18
|
+
import { type } from '@lowdefy/helpers';
|
|
19
19
|
async function AwsS3PresignedPostPolicy({ request, connection }) {
|
|
20
|
-
const { bucket } = connection;
|
|
20
|
+
const { accessKeyId, secretAccessKey, region, bucket } = connection;
|
|
21
21
|
const { acl, conditions, expires, key, fields = {} } = request;
|
|
22
22
|
const params = {
|
|
23
23
|
Bucket: bucket,
|
|
@@ -34,7 +34,7 @@ async function AwsS3PresignedPostPolicy({ request, connection }) {
|
|
|
34
34
|
params.Fields.acl = acl;
|
|
35
35
|
}
|
|
36
36
|
if (type.isObject(fields) === false) {
|
|
37
|
-
throw new Error(
|
|
37
|
+
throw new Error('properties.fields must be an object.');
|
|
38
38
|
}
|
|
39
39
|
Object.keys(fields).forEach((field)=>{
|
|
40
40
|
if (fields[field]) {
|
|
@@ -44,18 +44,14 @@ async function AwsS3PresignedPostPolicy({ request, connection }) {
|
|
|
44
44
|
params.Fields[field] = field.toLowerCase().startsWith('x-amz-meta-') ? encodeURIComponent(fields[field]) : fields[field];
|
|
45
45
|
}
|
|
46
46
|
});
|
|
47
|
-
const s3 =
|
|
48
|
-
|
|
47
|
+
const s3 = new S3Client({
|
|
48
|
+
credentials: {
|
|
49
|
+
accessKeyId,
|
|
50
|
+
secretAccessKey
|
|
51
|
+
},
|
|
52
|
+
region
|
|
49
53
|
});
|
|
50
|
-
|
|
51
|
-
// Top-level key/bucket/method make the response a standard upload-policy
|
|
52
|
-
// descriptor — the client reads object identity from here, never from fields.
|
|
53
|
-
return {
|
|
54
|
-
...policy,
|
|
55
|
-
bucket,
|
|
56
|
-
key,
|
|
57
|
-
method: 'POST'
|
|
58
|
-
};
|
|
54
|
+
return createPresignedPost(s3, params);
|
|
59
55
|
}
|
|
60
56
|
AwsS3PresignedPostPolicy.schema = schema;
|
|
61
57
|
AwsS3PresignedPostPolicy.meta = {
|
|
@@ -51,27 +51,6 @@
|
|
|
51
51
|
type: 'AwsS3Bucket connection property "bucket" should be a string.'
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
|
-
endpoint: {
|
|
55
|
-
type: 'string',
|
|
56
|
-
description: 'Custom S3 API endpoint for S3-compatible providers (e.g. Cloudflare R2, MinIO, DigitalOcean Spaces, Backblaze B2, Wasabi).',
|
|
57
|
-
errorMessage: {
|
|
58
|
-
type: 'AwsS3Bucket connection property "endpoint" should be a string.'
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
forcePathStyle: {
|
|
62
|
-
type: 'boolean',
|
|
63
|
-
description: 'Force path-style bucket addressing (https://endpoint/bucket/key). Required by MinIO and some self-hosted S3-compatible setups.',
|
|
64
|
-
errorMessage: {
|
|
65
|
-
type: 'AwsS3Bucket connection property "forcePathStyle" should be a boolean.'
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
publicUrlBase: {
|
|
69
|
-
type: 'string',
|
|
70
|
-
description: 'Base URL used to construct stable public object URLs (e.g. a CloudFront or CDN domain in front of the bucket). Used by download requests when the request sets "public: true".',
|
|
71
|
-
errorMessage: {
|
|
72
|
-
type: 'AwsS3Bucket connection property "publicUrlBase" should be a string.'
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
54
|
read: {
|
|
76
55
|
type: 'boolean',
|
|
77
56
|
default: true,
|
package/dist/types.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lowdefy/plugin-aws",
|
|
3
|
-
"version": "0.0.0-experimental-
|
|
3
|
+
"version": "0.0.0-experimental-20260721080548",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "",
|
|
6
6
|
"homepage": "https://lowdefy.com",
|
|
@@ -36,22 +36,20 @@
|
|
|
36
36
|
},
|
|
37
37
|
"type": "module",
|
|
38
38
|
"exports": {
|
|
39
|
-
"
|
|
39
|
+
"./*": "./dist/*",
|
|
40
|
+
"./connections/*": "./dist/connections/*",
|
|
40
41
|
"./blocks": "./dist/blocks.js",
|
|
41
42
|
"./metas": "./dist/metas.js",
|
|
42
|
-
"./types": "./dist/types.js"
|
|
43
|
-
"./connections/*": "./dist/connections/*",
|
|
44
|
-
"./*": "./dist/*"
|
|
43
|
+
"./types": "./dist/types.js"
|
|
45
44
|
},
|
|
46
45
|
"files": [
|
|
47
46
|
"dist/*"
|
|
48
47
|
],
|
|
49
48
|
"dependencies": {
|
|
50
|
-
"@lowdefy/block-utils": "0.0.0-experimental-
|
|
51
|
-
"@lowdefy/blocks-antd": "0.0.0-experimental-
|
|
52
|
-
"@lowdefy/
|
|
53
|
-
"@lowdefy/
|
|
54
|
-
"@lowdefy/helpers": "0.0.0-experimental-20260720072521",
|
|
49
|
+
"@lowdefy/block-utils": "0.0.0-experimental-20260721080548",
|
|
50
|
+
"@lowdefy/blocks-antd": "0.0.0-experimental-20260721080548",
|
|
51
|
+
"@lowdefy/errors": "0.0.0-experimental-20260721080548",
|
|
52
|
+
"@lowdefy/helpers": "0.0.0-experimental-20260721080548",
|
|
55
53
|
"@aws-sdk/client-s3": "3.797.0",
|
|
56
54
|
"@aws-sdk/s3-presigned-post": "3.797.0",
|
|
57
55
|
"@aws-sdk/s3-request-presigner": "3.797.0"
|
|
@@ -62,15 +60,15 @@
|
|
|
62
60
|
"react-dom": ">=18"
|
|
63
61
|
},
|
|
64
62
|
"devDependencies": {
|
|
65
|
-
"@lowdefy/ajv": "0.0.0-experimental-
|
|
66
|
-
"@lowdefy/block-dev": "0.0.0-experimental-
|
|
67
|
-
"@lowdefy/jest-yaml-transform": "0.0.0-experimental-
|
|
68
|
-
"@swc/cli": "0.8.
|
|
69
|
-
"@swc/core": "1.15.
|
|
63
|
+
"@lowdefy/ajv": "0.0.0-experimental-20260721080548",
|
|
64
|
+
"@lowdefy/block-dev": "0.0.0-experimental-20260721080548",
|
|
65
|
+
"@lowdefy/jest-yaml-transform": "0.0.0-experimental-20260721080548",
|
|
66
|
+
"@swc/cli": "0.8.0",
|
|
67
|
+
"@swc/core": "1.15.18",
|
|
70
68
|
"@swc/jest": "0.2.39",
|
|
71
69
|
"@testing-library/dom": "8.19.1",
|
|
72
70
|
"@testing-library/react": "13.4.0",
|
|
73
|
-
"@testing-library/user-event": "14.
|
|
71
|
+
"@testing-library/user-event": "14.4.3",
|
|
74
72
|
"copyfiles": "2.4.1",
|
|
75
73
|
"jest": "28.1.3",
|
|
76
74
|
"jest-environment-jsdom": "28.1.3",
|
|
@@ -82,7 +80,6 @@
|
|
|
82
80
|
"scripts": {
|
|
83
81
|
"build": "swc src --out-dir dist --config-file ../../../../.swcrc --cli-config-file ../../../../.swc-cli.json --copy-files",
|
|
84
82
|
"clean": "rm -rf dist",
|
|
85
|
-
"copyfiles": "copyfiles -u 1 \"./src/**/*\" dist -e \"./src/**/*.js\" -e \"./src/**/*.yaml\" -e \"./src/**/*.snap\""
|
|
86
|
-
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
|
|
83
|
+
"copyfiles": "copyfiles -u 1 \"./src/**/*\" dist -e \"./src/**/*.js\" -e \"./src/**/*.yaml\" -e \"./src/**/*.snap\""
|
|
87
84
|
}
|
|
88
85
|
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
-
|
|
4
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
you may not use this file except in compliance with the License.
|
|
6
|
-
You may obtain a copy of the License at
|
|
7
|
-
|
|
8
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
|
|
10
|
-
Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
See the License for the specific language governing permissions and
|
|
14
|
-
limitations under the License.
|
|
15
|
-
*/ import { GetObjectCommand } from '@aws-sdk/client-s3';
|
|
16
|
-
import { type } from '@lowdefy/helpers';
|
|
17
|
-
import createS3Client from '../createS3Client.js';
|
|
18
|
-
import schema from './schema.js';
|
|
19
|
-
// Server-side read: returns the object content as a base64 string so routine
|
|
20
|
-
// steps can process it with operators and pass it to other steps (base64
|
|
21
|
-
// survives JSON serialization; consumers decode with Buffer.from(content, 'base64')).
|
|
22
|
-
async function AwsS3GetObject({ request, connection }) {
|
|
23
|
-
const { bucket } = connection;
|
|
24
|
-
const { key, versionId } = request;
|
|
25
|
-
const params = {
|
|
26
|
-
Bucket: bucket,
|
|
27
|
-
Key: key
|
|
28
|
-
};
|
|
29
|
-
if (versionId) {
|
|
30
|
-
params.VersionId = versionId;
|
|
31
|
-
}
|
|
32
|
-
const s3 = createS3Client({
|
|
33
|
-
connection
|
|
34
|
-
});
|
|
35
|
-
const response = await s3.send(new GetObjectCommand(params));
|
|
36
|
-
const buffer = Buffer.from(await response.Body.transformToByteArray());
|
|
37
|
-
const result = {
|
|
38
|
-
bucket,
|
|
39
|
-
key,
|
|
40
|
-
content: buffer.toString('base64'),
|
|
41
|
-
// Derived from the returned content so it always matches, byte for byte.
|
|
42
|
-
size: buffer.length
|
|
43
|
-
};
|
|
44
|
-
if (!type.isNone(response.ContentType)) {
|
|
45
|
-
result.contentType = response.ContentType;
|
|
46
|
-
}
|
|
47
|
-
return result;
|
|
48
|
-
}
|
|
49
|
-
AwsS3GetObject.schema = schema;
|
|
50
|
-
AwsS3GetObject.meta = {
|
|
51
|
-
checkRead: true,
|
|
52
|
-
checkWrite: false
|
|
53
|
-
};
|
|
54
|
-
export default AwsS3GetObject;
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
-
|
|
4
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
you may not use this file except in compliance with the License.
|
|
6
|
-
You may obtain a copy of the License at
|
|
7
|
-
|
|
8
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
|
|
10
|
-
Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
See the License for the specific language governing permissions and
|
|
14
|
-
limitations under the License.
|
|
15
|
-
*/ export default {
|
|
16
|
-
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
17
|
-
title: 'Lowdefy Request Schema - AwsS3GetObject',
|
|
18
|
-
type: 'object',
|
|
19
|
-
required: [
|
|
20
|
-
'key'
|
|
21
|
-
],
|
|
22
|
-
properties: {
|
|
23
|
-
key: {
|
|
24
|
-
type: 'string',
|
|
25
|
-
description: 'Key under which the object is stored.',
|
|
26
|
-
errorMessage: {
|
|
27
|
-
type: 'AwsS3GetObject request property "key" should be a string.'
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
versionId: {
|
|
31
|
-
type: 'string',
|
|
32
|
-
description: 'VersionId used to reference a specific version of the object.',
|
|
33
|
-
errorMessage: {
|
|
34
|
-
type: 'AwsS3GetObject request property "versionId" should be a string.'
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
errorMessage: {
|
|
39
|
-
type: 'AwsS3GetObject request properties should be an object.',
|
|
40
|
-
required: 'AwsS3GetObject request should have required property "key".'
|
|
41
|
-
}
|
|
42
|
-
};
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
-
|
|
4
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
you may not use this file except in compliance with the License.
|
|
6
|
-
You may obtain a copy of the License at
|
|
7
|
-
|
|
8
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
|
|
10
|
-
Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
See the License for the specific language governing permissions and
|
|
14
|
-
limitations under the License.
|
|
15
|
-
*/ import { PutObjectCommand } from '@aws-sdk/client-s3';
|
|
16
|
-
import createS3Client from '../createS3Client.js';
|
|
17
|
-
import schema from './schema.js';
|
|
18
|
-
// Server-side write: stores base64 content as an object. Used by API endpoint
|
|
19
|
-
// routines that receive files as endpoint payloads (emitFileContent + CallAPI).
|
|
20
|
-
async function AwsS3PutObject({ request, connection }) {
|
|
21
|
-
const { bucket } = connection;
|
|
22
|
-
const { acl, content, contentType, key } = request;
|
|
23
|
-
const params = {
|
|
24
|
-
Bucket: bucket,
|
|
25
|
-
Key: key,
|
|
26
|
-
Body: Buffer.from(content, 'base64')
|
|
27
|
-
};
|
|
28
|
-
if (contentType) {
|
|
29
|
-
params.ContentType = contentType;
|
|
30
|
-
}
|
|
31
|
-
if (acl) {
|
|
32
|
-
params.ACL = acl;
|
|
33
|
-
}
|
|
34
|
-
const s3 = createS3Client({
|
|
35
|
-
connection
|
|
36
|
-
});
|
|
37
|
-
await s3.send(new PutObjectCommand(params));
|
|
38
|
-
return {
|
|
39
|
-
bucket,
|
|
40
|
-
key
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
AwsS3PutObject.schema = schema;
|
|
44
|
-
AwsS3PutObject.meta = {
|
|
45
|
-
checkRead: false,
|
|
46
|
-
checkWrite: true
|
|
47
|
-
};
|
|
48
|
-
export default AwsS3PutObject;
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
-
|
|
4
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
you may not use this file except in compliance with the License.
|
|
6
|
-
You may obtain a copy of the License at
|
|
7
|
-
|
|
8
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
|
|
10
|
-
Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
See the License for the specific language governing permissions and
|
|
14
|
-
limitations under the License.
|
|
15
|
-
*/ export default {
|
|
16
|
-
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
17
|
-
title: 'Lowdefy Request Schema - AwsS3PutObject',
|
|
18
|
-
type: 'object',
|
|
19
|
-
required: [
|
|
20
|
-
'key',
|
|
21
|
-
'content'
|
|
22
|
-
],
|
|
23
|
-
properties: {
|
|
24
|
-
acl: {
|
|
25
|
-
type: 'string',
|
|
26
|
-
enum: [
|
|
27
|
-
'private',
|
|
28
|
-
'public-read',
|
|
29
|
-
'public-read-write',
|
|
30
|
-
'aws-exec-read',
|
|
31
|
-
'authenticated-read',
|
|
32
|
-
'bucket-owner-read',
|
|
33
|
-
'bucket-owner-full-control'
|
|
34
|
-
],
|
|
35
|
-
description: 'Access control lists used to grant read and write access.',
|
|
36
|
-
errorMessage: {
|
|
37
|
-
type: 'AwsS3PutObject request property "acl" should be a string.',
|
|
38
|
-
enum: 'AwsS3PutObject request property "acl" is not one of "private", "public-read", "public-read-write", "aws-exec-read", "authenticated-read", "bucket-owner-read", "bucket-owner-full-control".'
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
content: {
|
|
42
|
-
type: 'string',
|
|
43
|
-
description: 'Object content as a base64 encoded string.',
|
|
44
|
-
errorMessage: {
|
|
45
|
-
type: 'AwsS3PutObject request property "content" should be a string.'
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
contentType: {
|
|
49
|
-
type: 'string',
|
|
50
|
-
description: 'MIME type of the object (sets the Content-Type of the stored object).',
|
|
51
|
-
errorMessage: {
|
|
52
|
-
type: 'AwsS3PutObject request property "contentType" should be a string.'
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
key: {
|
|
56
|
-
type: 'string',
|
|
57
|
-
description: 'Key under which the object will be stored.',
|
|
58
|
-
errorMessage: {
|
|
59
|
-
type: 'AwsS3PutObject request property "key" should be a string.'
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
errorMessage: {
|
|
64
|
-
type: 'AwsS3PutObject request properties should be an object.',
|
|
65
|
-
required: {
|
|
66
|
-
key: 'AwsS3PutObject request should have required property "key".',
|
|
67
|
-
content: 'AwsS3PutObject request should have required property "content".'
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
};
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
-
|
|
4
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
you may not use this file except in compliance with the License.
|
|
6
|
-
You may obtain a copy of the License at
|
|
7
|
-
|
|
8
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
|
|
10
|
-
Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
See the License for the specific language governing permissions and
|
|
14
|
-
limitations under the License.
|
|
15
|
-
*/ import { S3Client } from '@aws-sdk/client-s3';
|
|
16
|
-
import { type } from '@lowdefy/helpers';
|
|
17
|
-
function createS3Client({ connection }) {
|
|
18
|
-
const { accessKeyId, secretAccessKey, region, endpoint, forcePathStyle } = connection;
|
|
19
|
-
const config = {
|
|
20
|
-
credentials: {
|
|
21
|
-
accessKeyId,
|
|
22
|
-
secretAccessKey
|
|
23
|
-
},
|
|
24
|
-
region
|
|
25
|
-
};
|
|
26
|
-
if (!type.isNone(endpoint)) {
|
|
27
|
-
config.endpoint = endpoint;
|
|
28
|
-
}
|
|
29
|
-
if (!type.isNone(forcePathStyle)) {
|
|
30
|
-
config.forcePathStyle = forcePathStyle;
|
|
31
|
-
}
|
|
32
|
-
return new S3Client(config);
|
|
33
|
-
}
|
|
34
|
-
export default createS3Client;
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
-
|
|
4
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
you may not use this file except in compliance with the License.
|
|
6
|
-
You may obtain a copy of the License at
|
|
7
|
-
|
|
8
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
|
|
10
|
-
Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
See the License for the specific language governing permissions and
|
|
14
|
-
limitations under the License.
|
|
15
|
-
*/ import { type } from '@lowdefy/helpers';
|
|
16
|
-
// Stable, non-expiring URL for a public-read object. Public is author-declared,
|
|
17
|
-
// never runtime-checked — a public URL to a private object 403s.
|
|
18
|
-
function getPublicUrl({ connection, key }) {
|
|
19
|
-
const { bucket, endpoint, publicUrlBase, region } = connection;
|
|
20
|
-
const encodedKey = key.split('/').map(encodeURIComponent).join('/');
|
|
21
|
-
if (!type.isNone(publicUrlBase)) {
|
|
22
|
-
return `${publicUrlBase.replace(/\/+$/, '')}/${encodedKey}`;
|
|
23
|
-
}
|
|
24
|
-
if (!type.isNone(endpoint)) {
|
|
25
|
-
// Custom endpoints (R2, MinIO, Spaces) serve public objects path-style.
|
|
26
|
-
return `${endpoint.replace(/\/+$/, '')}/${bucket}/${encodedKey}`;
|
|
27
|
-
}
|
|
28
|
-
return `https://${bucket}.s3.${region}.amazonaws.com/${encodedKey}`;
|
|
29
|
-
}
|
|
30
|
-
export default getPublicUrl;
|