@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.
@@ -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 { Download } from '@lowdefy/blocks-files/blocks';
17
- // Deprecated alias for the generic Download block in @lowdefy/blocks-files.
18
- // Maps the legacy s3GetPolicyRequestId property onto downloadPolicyRequestId.
19
- const S3Download = (props)=>{
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
- console.warn('The S3Download block is deprecated. Use the Download block with "downloadPolicyRequestId" instead.');
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 { s3GetPolicyRequestId, ...properties } = props.properties ?? {};
24
- return /*#__PURE__*/ React.createElement(Download, {
25
- ...props,
26
- properties: {
27
- downloadPolicyRequestId: s3GetPolicyRequestId,
28
- ...properties
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 { Upload } from '@lowdefy/blocks-files/blocks';
18
- // Deprecated alias for the generic Upload block in @lowdefy/blocks-files.
19
- // Maps the legacy s3PostPolicyRequestId property onto uploadPolicyRequestId
20
- // and keeps the legacy element class so existing app CSS targeting the S3
21
- // block name keeps matching.
22
- const S3UploadButton = (props)=>{
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
- console.warn('The S3UploadButton block is deprecated. Use the Upload block with "uploadPolicyRequestId" instead.');
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
- const { s3PostPolicyRequestId, ...properties } = props.properties ?? {};
27
- const classNames = props.classNames ?? {};
28
- return /*#__PURE__*/ React.createElement(Upload, {
29
- ...props,
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
- ...classNames,
32
- element: cn('lf-s3-upload-button', classNames.element)
65
+ trigger: classNames.trigger,
66
+ list: classNames.list,
67
+ item: classNames.item
33
68
  },
34
- properties: {
35
- uploadPolicyRequestId: s3PostPolicyRequestId,
36
- ...properties
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 S3UploadButton;
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 { cn } from '@lowdefy/block-utils';
17
- import { UploadDragger } from '@lowdefy/blocks-files/blocks';
18
- // Deprecated alias for the generic UploadDragger block in @lowdefy/blocks-files.
19
- // Maps the legacy s3PostPolicyRequestId property onto uploadPolicyRequestId
20
- // and keeps the legacy element class so existing app CSS targeting the S3
21
- // block name keeps matching.
22
- const S3UploadDragger = (props)=>{
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
- console.warn('The S3UploadDragger block is deprecated. Use the UploadDragger block with "uploadPolicyRequestId" instead.');
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
- const { s3PostPolicyRequestId, ...properties } = props.properties ?? {};
27
- const classNames = props.classNames ?? {};
28
- return /*#__PURE__*/ React.createElement(UploadDragger, {
29
- ...props,
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
- ...classNames,
32
- element: cn('lf-s3-upload-dragger', classNames.element)
81
+ trigger: classNames.trigger,
82
+ list: classNames.list,
83
+ item: classNames.item
33
84
  },
34
- properties: {
35
- uploadPolicyRequestId: s3PostPolicyRequestId,
36
- ...properties
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 { UploadPhoto } from '@lowdefy/blocks-files/blocks';
18
- // Deprecated alias for the generic UploadPhoto block in @lowdefy/blocks-files.
19
- // Maps the legacy s3PostPolicyRequestId property onto uploadPolicyRequestId
20
- // and keeps the legacy element class so existing app CSS targeting the S3
21
- // block name keeps matching.
22
- const S3UploadPhoto = (props)=>{
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
- console.warn('The S3UploadPhoto block is deprecated. Use the UploadPhoto block with "uploadPolicyRequestId" instead.');
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
- const { s3PostPolicyRequestId, ...properties } = props.properties ?? {};
27
- const classNames = props.classNames ?? {};
28
- return /*#__PURE__*/ React.createElement(UploadPhoto, {
29
- ...props,
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
- ...classNames,
32
- element: cn('lf-s3-upload-photo', classNames.element)
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
- uploadPolicyRequestId: s3PostPolicyRequestId,
36
- ...properties
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 AwsS3GetObject from './AwsS3GetObject/AwsS3GetObject.js';
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 = createS3Client({
36
- connection
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 { createPresignedPost } from '@aws-sdk/s3-presigned-post';
16
- import { type } from '@lowdefy/helpers';
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(`properties.fields must be an object. Received ${JSON.stringify(fields)}.`);
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 = createS3Client({
48
- connection
47
+ const s3 = new S3Client({
48
+ credentials: {
49
+ accessKeyId,
50
+ secretAccessKey
51
+ },
52
+ region
49
53
  });
50
- const policy = await createPresignedPost(s3, params);
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
@@ -20,9 +20,7 @@ export default {
20
20
  'AwsS3Bucket'
21
21
  ],
22
22
  requests: [
23
- 'AwsS3GetObject',
24
23
  'AwsS3PresignedGetObject',
25
- 'AwsS3PresignedPostPolicy',
26
- 'AwsS3PutObject'
24
+ 'AwsS3PresignedPostPolicy'
27
25
  ]
28
26
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lowdefy/plugin-aws",
3
- "version": "0.0.0-experimental-20260720072521",
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
- "./connections": "./dist/connections.js",
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-20260720072521",
51
- "@lowdefy/blocks-antd": "0.0.0-experimental-20260720072521",
52
- "@lowdefy/blocks-files": "0.0.0-experimental-20260720072521",
53
- "@lowdefy/errors": "0.0.0-experimental-20260720072521",
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-20260720072521",
66
- "@lowdefy/block-dev": "0.0.0-experimental-20260720072521",
67
- "@lowdefy/jest-yaml-transform": "0.0.0-experimental-20260720072521",
68
- "@swc/cli": "0.8.1",
69
- "@swc/core": "1.15.32",
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.6.1",
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;