@payloadcms/plugin-cloud-storage 1.0.14 → 1.0.15
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/.editorconfig +10 -0
- package/.eslintrc.js +14 -14
- package/.gitignore +248 -0
- package/.idea/.gitignore +5 -0
- package/.idea/httpRequests/2023-04-07T152957.206.png +0 -0
- package/.idea/httpRequests/2023-04-07T153025.403.html +10 -0
- package/.idea/httpRequests/2023-04-07T153146.200.png +0 -0
- package/.idea/httpRequests/http-client.cookies +1 -0
- package/.idea/httpRequests/http-requests-log.http +74 -0
- package/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/.idea/jsLinters/eslint.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/plugin-cloud-storage.iml +12 -0
- package/.idea/vcs.xml +6 -0
- package/.idea/workspace.xml +269 -0
- package/.prettierignore +1 -0
- package/.prettierrc.js +8 -0
- package/.vscode/launch.json +28 -0
- package/.vscode/settings.json +9 -0
- package/LICENSE.md +22 -22
- package/README.md +178 -174
- package/azure.d.ts +1 -1
- package/azure.js +1 -1
- package/dev/.env +25 -0
- package/dev/.env.example +21 -0
- package/dev/build/127.d2c2ffcfff69fabfdd1b.js +1 -0
- package/dev/build/16.17dbe03b1d0a96f3e564.js +2 -0
- package/dev/build/16.17dbe03b1d0a96f3e564.js.LICENSE.txt +8 -0
- package/dev/build/171.bbcbae3ea90468ad0cad.js +2 -0
- package/dev/build/171.bbcbae3ea90468ad0cad.js.LICENSE.txt +8 -0
- package/dev/build/18.e50c27edff6716f930d9.js +1 -0
- package/dev/build/205.33c7a29683ba98de93e0.js +1 -0
- package/dev/build/2211c49456cd07331ea9.woff +0 -0
- package/dev/build/234.79395f82c18207c13766.js +1 -0
- package/dev/build/266.9d4a240b3e0985bd7dd5.js +1 -0
- package/dev/build/296.4c5d646257b42c915834.js +1 -0
- package/dev/build/304.40dbe690de322c8f7c0d.js +2 -0
- package/dev/build/304.40dbe690de322c8f7c0d.js.LICENSE.txt +37 -0
- package/dev/build/349.446c12bffd3905085fdb.js +1 -0
- package/dev/build/354.5acd04b85b96a9839125.js +1 -0
- package/dev/build/40ad7515b8674bb854a1.woff2 +0 -0
- package/dev/build/422.086542466cdc9f6a2437.js +2 -0
- package/dev/build/422.086542466cdc9f6a2437.js.LICENSE.txt +6 -0
- package/dev/build/491.0bfe1bb0ecfe383179aa.js +1 -0
- package/dev/build/4d8845b830f4e8e2affb.png +0 -0
- package/dev/build/51922ceb71da289688d3.woff2 +0 -0
- package/dev/build/522443364fda49e9e0ed.woff2 +0 -0
- package/dev/build/531.1c6f53f3b44a3c45b444.js +2 -0
- package/dev/build/531.1c6f53f3b44a3c45b444.js.LICENSE.txt +6 -0
- package/dev/build/570.f2d9b99706765fbf0225.js +1 -0
- package/dev/build/599.570a04990d5806004f61.js +1 -0
- package/dev/build/5b718d9772de251a8c0a.woff2 +0 -0
- package/dev/build/778.41ae26bcd617861ad586.js +1 -0
- package/dev/build/783.0117995f2ff6036d6746.js +1 -0
- package/dev/build/787999a6af6a17efbc7c.woff +0 -0
- package/dev/build/78b8935fb481e11c92ce.woff +0 -0
- package/dev/build/860.7688681d3269f3f16e9a.js +1 -0
- package/dev/build/892.1a4ca5ac67d81038ceec.js +1 -0
- package/dev/build/896.d8cb1160388dc29d6364.js +1 -0
- package/dev/build/8b4ddd0d08500553efde.woff +0 -0
- package/dev/build/8f612153248094525d9d.woff +0 -0
- package/dev/build/995.cc11e738ff81a85821b4.js +1 -0
- package/dev/build/9c7dfd0036f7bd24b053.woff2 +0 -0
- package/dev/build/a1cfdc5b5250b7c4b481.woff2 +0 -0
- package/dev/build/d7aeda9e48ce098e7b48.woff +0 -0
- package/dev/build/e009f21405b4d7e89367.woff2 +0 -0
- package/dev/build/e7caa9e17af6ac87d182.woff +0 -0
- package/dev/build/ebcc1430049fddb274f8.svg +15 -0
- package/dev/build/efe8f6a3b46446cc9135.woff +0 -0
- package/dev/build/f53bb8d4b29adc903703.woff2 +0 -0
- package/dev/build/index.html +1 -0
- package/dev/build/main.a2003d502fbb9aaa3e8d.js +2 -0
- package/dev/build/main.a2003d502fbb9aaa3e8d.js.LICENSE.txt +57 -0
- package/dev/build/styles.css +1 -0
- package/dev/build/styles.fa29d16b0baf5b98a1cf.js +1 -0
- package/dev/nodemon.json +8 -0
- package/dev/package.json +32 -0
- package/dev/src/collections/Media.ts +56 -0
- package/dev/src/collections/Users.ts +23 -0
- package/dev/src/mocks/fsMock.js +1 -0
- package/dev/src/mocks/promisifyMock.js +1 -0
- package/dev/src/payload.config.ts +111 -0
- package/dev/src/server.ts +26 -0
- package/dev/tsconfig.json +20 -0
- package/dist/adapters/azure/fileStub.d.ts +2 -0
- package/dist/adapters/azure/fileStub.js +4 -0
- package/dist/adapters/azure/fileStub.js.map +1 -0
- package/dist/adapters/azure/generateURL.d.ts +7 -7
- package/dist/adapters/azure/generateURL.js +15 -15
- package/dist/adapters/azure/handleDelete.d.ts +9 -9
- package/dist/adapters/azure/handleDelete.js +63 -63
- package/dist/adapters/azure/handleUpload.d.ts +10 -10
- package/dist/adapters/azure/handleUpload.js +80 -65
- package/dist/adapters/azure/handleUpload.js.map +1 -1
- package/dist/adapters/azure/index.d.ts +8 -8
- package/dist/adapters/azure/index.js +42 -42
- package/dist/adapters/azure/mock.d.ts +13 -7
- package/dist/adapters/azure/mock.js +12 -8
- package/dist/adapters/azure/mock.js.map +1 -1
- package/dist/adapters/azure/staticHandler.d.ts +9 -9
- package/dist/adapters/azure/staticHandler.js +81 -77
- package/dist/adapters/azure/staticHandler.js.map +1 -1
- package/dist/adapters/azure/webpack.d.ts +2 -2
- package/dist/adapters/azure/webpack.js +24 -24
- package/dist/adapters/azure/webpack.js.map +1 -1
- package/dist/adapters/gcs/generateURL.d.ts +8 -8
- package/dist/adapters/gcs/generateURL.js +15 -15
- package/dist/adapters/gcs/handleDelete.d.ts +8 -8
- package/dist/adapters/gcs/handleDelete.js +62 -62
- package/dist/adapters/gcs/handleUpload.d.ts +12 -12
- package/dist/adapters/gcs/handleUpload.js +72 -72
- package/dist/adapters/gcs/index.d.ts +8 -8
- package/dist/adapters/gcs/index.js +35 -35
- package/dist/adapters/gcs/mock.d.ts +1 -1
- package/dist/adapters/gcs/mock.js +4 -4
- package/dist/adapters/gcs/staticHandler.d.ts +10 -10
- package/dist/adapters/gcs/staticHandler.js +76 -76
- package/dist/adapters/gcs/webpack.d.ts +2 -2
- package/dist/adapters/gcs/webpack.js +24 -24
- package/dist/adapters/s3/fileStub.d.ts +2 -2
- package/dist/adapters/s3/fileStub.js +3 -3
- package/dist/adapters/s3/generateURL.d.ts +8 -8
- package/dist/adapters/s3/generateURL.js +15 -15
- package/dist/adapters/s3/handleDelete.d.ts +8 -8
- package/dist/adapters/s3/handleDelete.js +63 -63
- package/dist/adapters/s3/handleUpload.d.ts +12 -12
- package/dist/adapters/s3/handleUpload.js +93 -93
- package/dist/adapters/s3/index.d.ts +8 -8
- package/dist/adapters/s3/index.js +59 -59
- package/dist/adapters/s3/mock.d.ts +8 -8
- package/dist/adapters/s3/mock.js +9 -9
- package/dist/adapters/s3/staticHandler.d.ts +10 -10
- package/dist/adapters/s3/staticHandler.js +80 -80
- package/dist/adapters/s3/webpack.d.ts +2 -2
- package/dist/adapters/s3/webpack.js +24 -24
- package/dist/fields/getFields.d.ts +11 -11
- package/dist/fields/getFields.js +118 -118
- package/dist/hooks/afterDelete.d.ts +10 -10
- package/dist/hooks/afterDelete.js +88 -88
- package/dist/hooks/afterRead.d.ts +12 -12
- package/dist/hooks/afterRead.js +79 -79
- package/dist/hooks/beforeChange.d.ts +10 -10
- package/dist/hooks/beforeChange.js +77 -77
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -5
- package/dist/plugin.d.ts +3 -3
- package/dist/plugin.js +124 -124
- package/dist/types.d.ts +62 -62
- package/dist/types.js +2 -2
- package/dist/utilities/getFilePrefix.d.ts +5 -5
- package/dist/utilities/getFilePrefix.js +80 -80
- package/dist/utilities/getIncomingFiles.d.ts +7 -7
- package/dist/utilities/getIncomingFiles.js +37 -37
- package/dist/utilities/getRangeFromHeader.d.ts +6 -0
- package/dist/utilities/getRangeFromHeader.js +67 -0
- package/dist/utilities/getRangeFromHeader.js.map +1 -0
- package/dist/webpack.d.ts +9 -9
- package/dist/webpack.js +39 -39
- package/docs/local-dev.md +47 -0
- package/eslint-config/index.js +15 -0
- package/eslint-config/rules/import.js +38 -0
- package/eslint-config/rules/prettier.js +7 -0
- package/eslint-config/rules/style.js +21 -0
- package/eslint-config/rules/typescript.js +628 -0
- package/gcs.d.ts +1 -1
- package/gcs.js +1 -1
- package/package.json +67 -64
- package/s3.d.ts +1 -1
- package/s3.js +1 -1
- package/src/adapters/azure/emulator/docker-compose.yml +16 -0
- package/src/adapters/azure/fileStub.js +1 -0
- package/src/adapters/azure/generateURL.ts +13 -0
- package/src/adapters/azure/handleDelete.ts +16 -0
- package/src/adapters/azure/handleUpload.ts +41 -0
- package/src/adapters/azure/index.ts +47 -0
- package/src/adapters/azure/mock.js +13 -0
- package/src/adapters/azure/staticHandler.ts +38 -0
- package/src/adapters/azure/webpack.ts +20 -0
- package/src/adapters/gcs/emulator/docker-compose.yml +15 -0
- package/src/adapters/gcs/generateURL.ts +16 -0
- package/src/adapters/gcs/handleDelete.ts +16 -0
- package/src/adapters/gcs/handleUpload.ts +34 -0
- package/src/adapters/gcs/index.ts +37 -0
- package/src/adapters/gcs/mock.js +3 -0
- package/src/adapters/gcs/staticHandler.ts +34 -0
- package/src/adapters/gcs/webpack.ts +17 -0
- package/src/adapters/s3/emulator/docker-compose.yml +15 -0
- package/src/adapters/s3/fileStub.js +1 -0
- package/src/adapters/s3/generateURL.ts +14 -0
- package/src/adapters/s3/handleDelete.ts +17 -0
- package/src/adapters/s3/handleUpload.ts +62 -0
- package/src/adapters/s3/index.ts +38 -0
- package/src/adapters/s3/mock.js +9 -0
- package/src/adapters/s3/staticHandler.ts +40 -0
- package/src/adapters/s3/webpack.ts +19 -0
- package/src/fields/getFields.ts +155 -0
- package/src/hooks/afterDelete.ts +35 -0
- package/src/hooks/afterRead.ts +38 -0
- package/src/hooks/beforeChange.ts +30 -0
- package/src/index.ts +1 -0
- package/src/plugin.ts +94 -0
- package/src/types.ts +73 -0
- package/src/utilities/getFilePrefix.ts +26 -0
- package/src/utilities/getIncomingFiles.ts +44 -0
- package/src/utilities/getRangeFromHeader.ts +27 -0
- package/src/webpack.ts +46 -0
- package/tsconfig.json +23 -0
- package/yarn.lock +8155 -0
package/package.json
CHANGED
|
@@ -1,64 +1,67 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@payloadcms/plugin-cloud-storage",
|
|
3
|
-
"description": "The official cloud storage plugin for Payload CMS",
|
|
4
|
-
"version": "1.0.
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"license": "
|
|
8
|
-
"scripts": {
|
|
9
|
-
"build": "tsc",
|
|
10
|
-
"lint": "eslint src",
|
|
11
|
-
"lint:fix": "eslint --fix --ext .ts,.tsx src",
|
|
12
|
-
"clean": "rimraf dist && rimraf dev/yarn.lock",
|
|
13
|
-
"prepublishOnly": "yarn clean && yarn build"
|
|
14
|
-
},
|
|
15
|
-
"peerDependencies": {
|
|
16
|
-
"@aws-sdk/client-s3": "^3.142.0",
|
|
17
|
-
"@aws-sdk/lib-storage": "^3.267.0",
|
|
18
|
-
"@azure/storage-blob": "^12.11.0",
|
|
19
|
-
"@google-cloud/storage": "^6.4.1",
|
|
20
|
-
"payload": "^1.0.27"
|
|
21
|
-
},
|
|
22
|
-
"peerDependenciesMeta": {
|
|
23
|
-
"@aws-sdk/client-s3": {
|
|
24
|
-
"optional": true
|
|
25
|
-
},
|
|
26
|
-
"@aws-sdk/lib-storage": {
|
|
27
|
-
"optional": true
|
|
28
|
-
},
|
|
29
|
-
"@azure/storage-blob": {
|
|
30
|
-
"optional": true
|
|
31
|
-
},
|
|
32
|
-
"@google-cloud/storage": {
|
|
33
|
-
"optional": true
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
"files": [
|
|
37
|
-
"dist",
|
|
38
|
-
"*.js",
|
|
39
|
-
"*.d.ts",
|
|
40
|
-
"!.prettierrc.js"
|
|
41
|
-
],
|
|
42
|
-
"devDependencies": {
|
|
43
|
-
"@aws-sdk/client-s3": "^3.142.0",
|
|
44
|
-
"@aws-sdk/lib-storage": "^3.267.0",
|
|
45
|
-
"@azure/storage-blob": "^12.11.0",
|
|
46
|
-
"@google-cloud/storage": "^6.4.1",
|
|
47
|
-
"@types/express": "^4.17.9",
|
|
48
|
-
"@typescript-eslint/eslint-plugin": "5.12.1",
|
|
49
|
-
"@typescript-eslint/parser": "5.12.1",
|
|
50
|
-
"cross-env": "^7.0.3",
|
|
51
|
-
"dotenv": "^8.2.0",
|
|
52
|
-
"eslint": "^8.19.0",
|
|
53
|
-
"eslint-config-airbnb-base": "^14.2.1",
|
|
54
|
-
"eslint-config-prettier": "^8.5.0",
|
|
55
|
-
"eslint-plugin-import": "2.25.4",
|
|
56
|
-
"eslint-plugin-prettier": "^4.0.0",
|
|
57
|
-
"nodemon": "^2.0.6",
|
|
58
|
-
"payload": "^1.6.6",
|
|
59
|
-
"prettier": "^2.7.1",
|
|
60
|
-
"rimraf": "^4.1.2",
|
|
61
|
-
"ts-node": "^9.1.1",
|
|
62
|
-
"typescript": "^4.1.3"
|
|
63
|
-
}
|
|
64
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@payloadcms/plugin-cloud-storage",
|
|
3
|
+
"description": "The official cloud storage plugin for Payload CMS",
|
|
4
|
+
"version": "1.0.15",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"lint": "eslint src",
|
|
11
|
+
"lint:fix": "eslint --fix --ext .ts,.tsx src",
|
|
12
|
+
"clean": "rimraf dist && rimraf dev/yarn.lock",
|
|
13
|
+
"prepublishOnly": "yarn clean && yarn build"
|
|
14
|
+
},
|
|
15
|
+
"peerDependencies": {
|
|
16
|
+
"@aws-sdk/client-s3": "^3.142.0",
|
|
17
|
+
"@aws-sdk/lib-storage": "^3.267.0",
|
|
18
|
+
"@azure/storage-blob": "^12.11.0",
|
|
19
|
+
"@google-cloud/storage": "^6.4.1",
|
|
20
|
+
"payload": "^1.0.27"
|
|
21
|
+
},
|
|
22
|
+
"peerDependenciesMeta": {
|
|
23
|
+
"@aws-sdk/client-s3": {
|
|
24
|
+
"optional": true
|
|
25
|
+
},
|
|
26
|
+
"@aws-sdk/lib-storage": {
|
|
27
|
+
"optional": true
|
|
28
|
+
},
|
|
29
|
+
"@azure/storage-blob": {
|
|
30
|
+
"optional": true
|
|
31
|
+
},
|
|
32
|
+
"@google-cloud/storage": {
|
|
33
|
+
"optional": true
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"*.js",
|
|
39
|
+
"*.d.ts",
|
|
40
|
+
"!.prettierrc.js"
|
|
41
|
+
],
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@aws-sdk/client-s3": "^3.142.0",
|
|
44
|
+
"@aws-sdk/lib-storage": "^3.267.0",
|
|
45
|
+
"@azure/storage-blob": "^12.11.0",
|
|
46
|
+
"@google-cloud/storage": "^6.4.1",
|
|
47
|
+
"@types/express": "^4.17.9",
|
|
48
|
+
"@typescript-eslint/eslint-plugin": "5.12.1",
|
|
49
|
+
"@typescript-eslint/parser": "5.12.1",
|
|
50
|
+
"cross-env": "^7.0.3",
|
|
51
|
+
"dotenv": "^8.2.0",
|
|
52
|
+
"eslint": "^8.19.0",
|
|
53
|
+
"eslint-config-airbnb-base": "^14.2.1",
|
|
54
|
+
"eslint-config-prettier": "^8.5.0",
|
|
55
|
+
"eslint-plugin-import": "2.25.4",
|
|
56
|
+
"eslint-plugin-prettier": "^4.0.0",
|
|
57
|
+
"nodemon": "^2.0.6",
|
|
58
|
+
"payload": "^1.6.6",
|
|
59
|
+
"prettier": "^2.7.1",
|
|
60
|
+
"rimraf": "^4.1.2",
|
|
61
|
+
"ts-node": "^9.1.1",
|
|
62
|
+
"typescript": "^4.1.3"
|
|
63
|
+
},
|
|
64
|
+
"dependencies": {
|
|
65
|
+
"range-parser": "^1.2.1"
|
|
66
|
+
}
|
|
67
|
+
}
|
package/s3.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './dist/adapters/s3'
|
|
1
|
+
export * from './dist/adapters/s3'
|
package/s3.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
exports.s3Adapter = require('./dist/adapters/s3').s3Adapter
|
|
1
|
+
exports.s3Adapter = require('./dist/adapters/s3').s3Adapter
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
version: "3"
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
azure-storage:
|
|
5
|
+
image: mcr.microsoft.com/azure-storage/azurite:3.18.0
|
|
6
|
+
restart: always
|
|
7
|
+
command: "azurite --loose --blobHost 0.0.0.0 --tableHost 0.0.0.0 --queueHost 0.0.0.0"
|
|
8
|
+
ports:
|
|
9
|
+
- "10000:10000"
|
|
10
|
+
- "10001:10001"
|
|
11
|
+
- "10002:10002"
|
|
12
|
+
volumes:
|
|
13
|
+
- ./azurestoragedata:/data"
|
|
14
|
+
|
|
15
|
+
volumes:
|
|
16
|
+
azurestoragedata:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default 'file-stub'
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import type { GenerateURL } from '../../types'
|
|
3
|
+
|
|
4
|
+
interface Args {
|
|
5
|
+
containerName: string
|
|
6
|
+
baseURL: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const getGenerateURL =
|
|
10
|
+
({ containerName, baseURL }: Args): GenerateURL =>
|
|
11
|
+
({ filename, prefix = '' }) => {
|
|
12
|
+
return `${baseURL}/${containerName}/${path.posix.join(prefix, filename)}`
|
|
13
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import type { CollectionConfig } from 'payload/types'
|
|
3
|
+
import type { ContainerClient } from '@azure/storage-blob'
|
|
4
|
+
import type { HandleDelete } from '../../types'
|
|
5
|
+
|
|
6
|
+
interface Args {
|
|
7
|
+
collection: CollectionConfig
|
|
8
|
+
getStorageClient: () => ContainerClient
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const getHandleDelete = ({ getStorageClient }: Args): HandleDelete => {
|
|
12
|
+
return async ({ filename, doc: { prefix = '' } }) => {
|
|
13
|
+
const blockBlobClient = getStorageClient().getBlockBlobClient(path.posix.join(prefix, filename))
|
|
14
|
+
await blockBlobClient.deleteIfExists()
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
import { Readable } from 'stream'
|
|
4
|
+
import type { ContainerClient } from '@azure/storage-blob'
|
|
5
|
+
import { AbortController } from '@azure/abort-controller'
|
|
6
|
+
import type { CollectionConfig } from 'payload/types'
|
|
7
|
+
import type { HandleUpload } from '../../types'
|
|
8
|
+
|
|
9
|
+
interface Args {
|
|
10
|
+
collection: CollectionConfig
|
|
11
|
+
getStorageClient: () => ContainerClient
|
|
12
|
+
prefix?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const multipartThreshold = 1024 * 1024 * 50 // 50MB
|
|
16
|
+
export const getHandleUpload = ({ getStorageClient, prefix = '' }: Args): HandleUpload => {
|
|
17
|
+
return async ({ data, file }) => {
|
|
18
|
+
const blockBlobClient = getStorageClient().getBlockBlobClient(
|
|
19
|
+
path.posix.join(prefix, file.filename),
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
// when there are no temp files, or the upload is less than the threshold size, do not stream files
|
|
23
|
+
if (!file.tempFilePath && file.buffer.length > 0 && file.buffer.length < multipartThreshold) {
|
|
24
|
+
await blockBlobClient.upload(file.buffer, file.buffer.byteLength, {
|
|
25
|
+
blobHTTPHeaders: { blobContentType: file.mimeType },
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
return data
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const fileBufferOrStream: Readable = file.tempFilePath
|
|
32
|
+
? fs.createReadStream(file.tempFilePath)
|
|
33
|
+
: Readable.from(file.buffer)
|
|
34
|
+
|
|
35
|
+
await blockBlobClient.uploadStream(fileBufferOrStream, 4 * 1024 * 1024, 4, {
|
|
36
|
+
abortSignal: AbortController.timeout(30 * 60 * 1000),
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
return data
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { BlobServiceClient, ContainerClient } from '@azure/storage-blob'
|
|
2
|
+
import type { Adapter, GeneratedAdapter } from '../../types'
|
|
3
|
+
import { getHandler } from './staticHandler'
|
|
4
|
+
import { getGenerateURL } from './generateURL'
|
|
5
|
+
import { getHandleDelete } from './handleDelete'
|
|
6
|
+
import { getHandleUpload } from './handleUpload'
|
|
7
|
+
import { extendWebpackConfig } from './webpack'
|
|
8
|
+
|
|
9
|
+
export interface Args {
|
|
10
|
+
connectionString: string
|
|
11
|
+
containerName: string
|
|
12
|
+
baseURL: string
|
|
13
|
+
allowContainerCreate: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const azureBlobStorageAdapter = ({
|
|
17
|
+
connectionString,
|
|
18
|
+
allowContainerCreate,
|
|
19
|
+
containerName,
|
|
20
|
+
baseURL,
|
|
21
|
+
}: Args): Adapter => {
|
|
22
|
+
let storageClient: ContainerClient | null = null
|
|
23
|
+
const getStorageClient = () => {
|
|
24
|
+
if (storageClient) return storageClient
|
|
25
|
+
const blobServiceClient = BlobServiceClient.fromConnectionString(connectionString)
|
|
26
|
+
return (storageClient = blobServiceClient.getContainerClient(containerName))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const createContainerIfNotExists = () => {
|
|
30
|
+
getStorageClient().createIfNotExists({ access: 'blob' })
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return ({ collection, prefix }): GeneratedAdapter => {
|
|
34
|
+
return {
|
|
35
|
+
handleUpload: getHandleUpload({
|
|
36
|
+
collection,
|
|
37
|
+
getStorageClient,
|
|
38
|
+
prefix,
|
|
39
|
+
}),
|
|
40
|
+
handleDelete: getHandleDelete({ collection, getStorageClient }),
|
|
41
|
+
generateURL: getGenerateURL({ containerName, baseURL }),
|
|
42
|
+
staticHandler: getHandler({ getStorageClient, collection }),
|
|
43
|
+
webpack: extendWebpackConfig,
|
|
44
|
+
...(allowContainerCreate && { onInit: createContainerIfNotExists }),
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ContainerClient } from '@azure/storage-blob'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import type { CollectionConfig } from 'payload/types'
|
|
4
|
+
import type { StaticHandler } from '../../types'
|
|
5
|
+
import { getFilePrefix } from '../../utilities/getFilePrefix'
|
|
6
|
+
import getRangeFromHeader from '../../utilities/getRangeFromHeader'
|
|
7
|
+
|
|
8
|
+
interface Args {
|
|
9
|
+
getStorageClient: () => ContainerClient
|
|
10
|
+
collection: CollectionConfig
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const getHandler = ({ getStorageClient, collection }: Args): StaticHandler => {
|
|
14
|
+
return async (req, res, next) => {
|
|
15
|
+
try {
|
|
16
|
+
const prefix = await getFilePrefix({ req, collection })
|
|
17
|
+
const blockBlobClient = getStorageClient().getBlockBlobClient(
|
|
18
|
+
path.posix.join(prefix, req.params.filename),
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
const { start, end } = await getRangeFromHeader(blockBlobClient, req.headers.range)
|
|
22
|
+
|
|
23
|
+
const blob = await blockBlobClient.download(start, end)
|
|
24
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
25
|
+
const response = blob._response
|
|
26
|
+
res.header(response.headers.rawHeaders())
|
|
27
|
+
res.status(response.status)
|
|
28
|
+
|
|
29
|
+
if (blob?.readableStreamBody) {
|
|
30
|
+
return blob.readableStreamBody.pipe(res)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return next()
|
|
34
|
+
} catch (err: unknown) {
|
|
35
|
+
return next()
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Configuration as WebpackConfig } from 'webpack'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
export const extendWebpackConfig = (existingWebpackConfig: WebpackConfig): WebpackConfig => {
|
|
5
|
+
const newConfig: WebpackConfig = {
|
|
6
|
+
...existingWebpackConfig,
|
|
7
|
+
resolve: {
|
|
8
|
+
...(existingWebpackConfig.resolve || {}),
|
|
9
|
+
alias: {
|
|
10
|
+
...(existingWebpackConfig.resolve?.alias ? existingWebpackConfig.resolve.alias : {}),
|
|
11
|
+
stream: path.resolve(__dirname, './mock.js'),
|
|
12
|
+
'@azure/storage-blob': path.resolve(__dirname, './mock.js'),
|
|
13
|
+
'@azure/abort-controller': path.resolve(__dirname, './mock.js'),
|
|
14
|
+
fs: path.resolve(__dirname, './fileStub.js'),
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return newConfig
|
|
20
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
version: "3"
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
google-cloud-storage:
|
|
5
|
+
image: fsouza/fake-gcs-server
|
|
6
|
+
restart: always
|
|
7
|
+
command: ["-scheme", "http", "-port", "4443", "-public-host", "http://localhost:4443", "-external-url", "http://localhost:4443", "-backend", "memory"]
|
|
8
|
+
ports:
|
|
9
|
+
- "4443:4443"
|
|
10
|
+
volumes:
|
|
11
|
+
- ./google-cloud-storage/payload-bucket:/data/payload-bucket
|
|
12
|
+
|
|
13
|
+
volumes:
|
|
14
|
+
google-cloud-storage:
|
|
15
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { Storage } from '@google-cloud/storage'
|
|
3
|
+
import type { GenerateURL } from '../../types'
|
|
4
|
+
|
|
5
|
+
interface Args {
|
|
6
|
+
getStorageClient: () => Storage
|
|
7
|
+
bucket: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const getGenerateURL =
|
|
11
|
+
({ getStorageClient, bucket }: Args): GenerateURL =>
|
|
12
|
+
({ filename, prefix = '' }) => {
|
|
13
|
+
return decodeURIComponent(
|
|
14
|
+
getStorageClient().bucket(bucket).file(path.posix.join(prefix, filename)).publicUrl(),
|
|
15
|
+
)
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { Storage } from '@google-cloud/storage'
|
|
3
|
+
import type { HandleDelete } from '../../types'
|
|
4
|
+
|
|
5
|
+
interface Args {
|
|
6
|
+
getStorageClient: () => Storage
|
|
7
|
+
bucket: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const getHandleDelete = ({ getStorageClient, bucket }: Args): HandleDelete => {
|
|
11
|
+
return async ({ filename, doc: { prefix = '' } }) => {
|
|
12
|
+
await getStorageClient().bucket(bucket).file(path.posix.join(prefix, filename)).delete({
|
|
13
|
+
ignoreNotFound: true,
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import type { Storage } from '@google-cloud/storage'
|
|
3
|
+
import type { CollectionConfig } from 'payload/types'
|
|
4
|
+
import type { HandleUpload } from '../../types'
|
|
5
|
+
|
|
6
|
+
interface Args {
|
|
7
|
+
collection: CollectionConfig
|
|
8
|
+
bucket: string
|
|
9
|
+
acl?: 'Private' | 'Public'
|
|
10
|
+
prefix?: string
|
|
11
|
+
getStorageClient: () => Storage
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const getHandleUpload = ({
|
|
15
|
+
getStorageClient,
|
|
16
|
+
bucket,
|
|
17
|
+
acl,
|
|
18
|
+
prefix = '',
|
|
19
|
+
}: Args): HandleUpload => {
|
|
20
|
+
return async ({ data, file }) => {
|
|
21
|
+
const gcsFile = getStorageClient().bucket(bucket).file(path.posix.join(prefix, file.filename))
|
|
22
|
+
await gcsFile.save(file.buffer, {
|
|
23
|
+
metadata: {
|
|
24
|
+
contentType: file.mimeType,
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
if (acl) {
|
|
29
|
+
await gcsFile[`make${acl}`]()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return data
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Storage, StorageOptions } from '@google-cloud/storage'
|
|
2
|
+
import type { Adapter, GeneratedAdapter } from '../../types'
|
|
3
|
+
import { getGenerateURL } from './generateURL'
|
|
4
|
+
import { getHandler } from './staticHandler'
|
|
5
|
+
import { getHandleDelete } from './handleDelete'
|
|
6
|
+
import { getHandleUpload } from './handleUpload'
|
|
7
|
+
import { extendWebpackConfig } from './webpack'
|
|
8
|
+
|
|
9
|
+
export interface Args {
|
|
10
|
+
options: StorageOptions
|
|
11
|
+
bucket: string
|
|
12
|
+
acl?: 'Private' | 'Public'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const gcsAdapter =
|
|
16
|
+
({ options, bucket, acl }: Args): Adapter =>
|
|
17
|
+
({ collection, prefix }): GeneratedAdapter => {
|
|
18
|
+
let storageClient: Storage | null = null
|
|
19
|
+
const getStorageClient = () => {
|
|
20
|
+
if (storageClient) return storageClient
|
|
21
|
+
return (storageClient = new Storage(options))
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
handleUpload: getHandleUpload({
|
|
26
|
+
collection,
|
|
27
|
+
getStorageClient,
|
|
28
|
+
bucket,
|
|
29
|
+
acl,
|
|
30
|
+
prefix,
|
|
31
|
+
}),
|
|
32
|
+
handleDelete: getHandleDelete({ getStorageClient, bucket }),
|
|
33
|
+
generateURL: getGenerateURL({ getStorageClient, bucket }),
|
|
34
|
+
staticHandler: getHandler({ getStorageClient, bucket, collection }),
|
|
35
|
+
webpack: extendWebpackConfig,
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import type { Storage } from '@google-cloud/storage'
|
|
3
|
+
import type { CollectionConfig } from 'payload/types'
|
|
4
|
+
import type { StaticHandler } from '../../types'
|
|
5
|
+
import { getFilePrefix } from '../../utilities/getFilePrefix'
|
|
6
|
+
|
|
7
|
+
interface Args {
|
|
8
|
+
getStorageClient: () => Storage
|
|
9
|
+
bucket: string
|
|
10
|
+
collection: CollectionConfig
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const getHandler = ({ getStorageClient, bucket, collection }: Args): StaticHandler => {
|
|
14
|
+
return async (req, res, next) => {
|
|
15
|
+
try {
|
|
16
|
+
const prefix = await getFilePrefix({ req, collection })
|
|
17
|
+
const file = getStorageClient()
|
|
18
|
+
.bucket(bucket)
|
|
19
|
+
.file(path.posix.join(prefix, req.params.filename))
|
|
20
|
+
|
|
21
|
+
const [metadata] = await file.getMetadata()
|
|
22
|
+
|
|
23
|
+
res.set({
|
|
24
|
+
'Content-Length': metadata.size,
|
|
25
|
+
'Content-Type': metadata.contentType,
|
|
26
|
+
ETag: metadata.etag,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
return file.createReadStream().pipe(res)
|
|
30
|
+
} catch (err: unknown) {
|
|
31
|
+
return next()
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Configuration as WebpackConfig } from 'webpack'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
export const extendWebpackConfig = (existingWebpackConfig: WebpackConfig): WebpackConfig => {
|
|
5
|
+
const newConfig: WebpackConfig = {
|
|
6
|
+
...existingWebpackConfig,
|
|
7
|
+
resolve: {
|
|
8
|
+
...(existingWebpackConfig.resolve || {}),
|
|
9
|
+
alias: {
|
|
10
|
+
...(existingWebpackConfig.resolve?.alias ? existingWebpackConfig.resolve.alias : {}),
|
|
11
|
+
'@google-cloud/storage': path.resolve(__dirname, './mock.js'),
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return newConfig
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
version: '3.2'
|
|
2
|
+
services:
|
|
3
|
+
localstack:
|
|
4
|
+
image: localstack/localstack:latest
|
|
5
|
+
container_name: localstack_demo
|
|
6
|
+
ports:
|
|
7
|
+
- '4563-4599:4563-4599'
|
|
8
|
+
- '8055:8080'
|
|
9
|
+
environment:
|
|
10
|
+
- SERVICES=s3
|
|
11
|
+
- DEBUG=1
|
|
12
|
+
- DATA_DIR=/tmp/localstack/data
|
|
13
|
+
volumes:
|
|
14
|
+
- './.localstack:/var/lib/localstack'
|
|
15
|
+
- '/var/run/docker.sock:/var/run/docker.sock'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default 'file-stub'
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import type * as AWS from '@aws-sdk/client-s3'
|
|
3
|
+
import type { GenerateURL } from '../../types'
|
|
4
|
+
|
|
5
|
+
interface Args {
|
|
6
|
+
config: AWS.S3ClientConfig
|
|
7
|
+
bucket: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const getGenerateURL =
|
|
11
|
+
({ config: { endpoint }, bucket }: Args): GenerateURL =>
|
|
12
|
+
({ filename, prefix = '' }) => {
|
|
13
|
+
return `${endpoint}/${bucket}/${path.posix.join(prefix, filename)}`
|
|
14
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import type * as AWS from '@aws-sdk/client-s3'
|
|
3
|
+
import type { HandleDelete } from '../../types'
|
|
4
|
+
|
|
5
|
+
interface Args {
|
|
6
|
+
getStorageClient: () => AWS.S3
|
|
7
|
+
bucket: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const getHandleDelete = ({ getStorageClient, bucket }: Args): HandleDelete => {
|
|
11
|
+
return async ({ filename, doc: { prefix = '' } }) => {
|
|
12
|
+
await getStorageClient().deleteObject({
|
|
13
|
+
Bucket: bucket,
|
|
14
|
+
Key: path.posix.join(prefix, filename),
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import type * as AWS from '@aws-sdk/client-s3'
|
|
4
|
+
import type { CollectionConfig } from 'payload/types'
|
|
5
|
+
import type stream from 'stream'
|
|
6
|
+
|
|
7
|
+
import { Upload } from '@aws-sdk/lib-storage'
|
|
8
|
+
import type { HandleUpload } from '../../types'
|
|
9
|
+
|
|
10
|
+
interface Args {
|
|
11
|
+
collection: CollectionConfig
|
|
12
|
+
bucket: string
|
|
13
|
+
acl?: 'private' | 'public-read'
|
|
14
|
+
prefix?: string
|
|
15
|
+
getStorageClient: () => AWS.S3
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const multipartThreshold = 1024 * 1024 * 50 // 50MB
|
|
19
|
+
|
|
20
|
+
export const getHandleUpload = ({
|
|
21
|
+
getStorageClient,
|
|
22
|
+
bucket,
|
|
23
|
+
acl,
|
|
24
|
+
prefix = '',
|
|
25
|
+
}: Args): HandleUpload => {
|
|
26
|
+
return async ({ data, file }) => {
|
|
27
|
+
const fileKey = path.posix.join(prefix, file.filename)
|
|
28
|
+
|
|
29
|
+
const fileBufferOrStream: Buffer | stream.Readable = file.tempFilePath
|
|
30
|
+
? fs.createReadStream(file.tempFilePath)
|
|
31
|
+
: file.buffer
|
|
32
|
+
|
|
33
|
+
if (file.buffer.length > 0 && file.buffer.length < multipartThreshold) {
|
|
34
|
+
await getStorageClient().putObject({
|
|
35
|
+
Bucket: bucket,
|
|
36
|
+
Key: fileKey,
|
|
37
|
+
Body: fileBufferOrStream,
|
|
38
|
+
ACL: acl,
|
|
39
|
+
ContentType: file.mimeType,
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
return data
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const parallelUploadS3 = new Upload({
|
|
46
|
+
client: getStorageClient(),
|
|
47
|
+
params: {
|
|
48
|
+
Bucket: bucket,
|
|
49
|
+
Key: fileKey,
|
|
50
|
+
Body: fileBufferOrStream,
|
|
51
|
+
ACL: acl,
|
|
52
|
+
ContentType: file.mimeType,
|
|
53
|
+
},
|
|
54
|
+
queueSize: 4,
|
|
55
|
+
partSize: multipartThreshold,
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
await parallelUploadS3.done()
|
|
59
|
+
|
|
60
|
+
return data
|
|
61
|
+
}
|
|
62
|
+
}
|