@edgestore/server 0.5.0 → 0.5.1
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.
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import { type Provider } from '@edgestore/shared';
|
|
1
|
+
import { type MaybePromise, type Provider, type RequestUploadParams } from '@edgestore/shared';
|
|
2
|
+
type ProviderUploadedFileInfo = RequestUploadParams['fileInfo'];
|
|
3
|
+
export type AWSOverwritePathFnArgs = {
|
|
4
|
+
esBucketName: string;
|
|
5
|
+
fileInfo: ProviderUploadedFileInfo;
|
|
6
|
+
defaultAccessPath: string;
|
|
7
|
+
};
|
|
8
|
+
export type AWSOverwritePathFn = (args: AWSOverwritePathFnArgs) => MaybePromise<string>;
|
|
2
9
|
export type AWSProviderOptions = {
|
|
3
10
|
/**
|
|
4
11
|
* Access key for AWS credentials.
|
|
@@ -49,6 +56,13 @@ export type AWSProviderOptions = {
|
|
|
49
56
|
* It can also be set via the `EDGE_STORE_JWT_SECRET` environment variable.
|
|
50
57
|
*/
|
|
51
58
|
jwtSecret?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Optional function to overwrite the S3 key (object path) for uploads.
|
|
61
|
+
* This function receives the EdgeStore bucket name, fileInfo and the default S3 key
|
|
62
|
+
* and should return the desired S3 key string.
|
|
63
|
+
*/
|
|
64
|
+
overwritePath?: AWSOverwritePathFn;
|
|
52
65
|
};
|
|
53
66
|
export declare function AWSProvider(options?: AWSProviderOptions): Provider;
|
|
67
|
+
export {};
|
|
54
68
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/aws/index.ts"],"names":[],"mappings":"AAOA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/aws/index.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,mBAAmB,EACzB,MAAM,mBAAmB,CAAC;AAK3B,KAAK,wBAAwB,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;AAEhE,MAAM,MAAM,sBAAsB,GAAG;IAEnC,YAAY,EAAE,MAAM,CAAC;IAErB,QAAQ,EAAE,wBAAwB,CAAC;IAEnC,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,CAC/B,IAAI,EAAE,sBAAsB,KACzB,YAAY,CAAC,MAAM,CAAC,CAAC;AAE1B,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,kBAAkB,CAAC;CACpC,CAAC;AAEF,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,QAAQ,CAuIlE"}
|
|
@@ -12,7 +12,7 @@ require('cookie');
|
|
|
12
12
|
require('jose');
|
|
13
13
|
|
|
14
14
|
function AWSProvider(options) {
|
|
15
|
-
const { accessKeyId = shared.getEnv('ES_AWS_ACCESS_KEY_ID'), secretAccessKey = shared.getEnv('ES_AWS_SECRET_ACCESS_KEY'), region = shared.getEnv('ES_AWS_REGION'), bucketName = shared.getEnv('ES_AWS_BUCKET_NAME'), endpoint = shared.getEnv('ES_AWS_ENDPOINT'), forcePathStyle = shared.getEnv('ES_AWS_FORCE_PATH_STYLE') === 'true' } = options ?? {};
|
|
15
|
+
const { accessKeyId = shared.getEnv('ES_AWS_ACCESS_KEY_ID'), secretAccessKey = shared.getEnv('ES_AWS_SECRET_ACCESS_KEY'), region = shared.getEnv('ES_AWS_REGION'), bucketName = shared.getEnv('ES_AWS_BUCKET_NAME'), endpoint = shared.getEnv('ES_AWS_ENDPOINT'), forcePathStyle = shared.getEnv('ES_AWS_FORCE_PATH_STYLE') === 'true', overwritePath } = options ?? {};
|
|
16
16
|
const baseUrl = options?.baseUrl ?? shared.getEnv('EDGE_STORE_BASE_URL') ?? (endpoint ? `${endpoint}/${bucketName}` : `https://${bucketName}.s3.${region}.amazonaws.com`);
|
|
17
17
|
const credentials = accessKeyId && secretAccessKey ? {
|
|
18
18
|
accessKeyId,
|
|
@@ -48,15 +48,27 @@ function AWSProvider(options) {
|
|
|
48
48
|
uploadedAt: LastModified
|
|
49
49
|
};
|
|
50
50
|
},
|
|
51
|
-
async requestUpload (
|
|
51
|
+
async requestUpload (params) {
|
|
52
|
+
const { bucketName: esBucketName, fileInfo } = params;
|
|
53
|
+
if (!bucketName) {
|
|
54
|
+
throw new Error('S3 bucketName is not configured in AWSProviderOptions.');
|
|
55
|
+
}
|
|
56
|
+
// Default S3 key (accessPath) construction logic
|
|
52
57
|
const pathPrefix = `${esBucketName}${fileInfo.isPublic ? '/_public' : ''}`;
|
|
53
58
|
const nameId = uuid.v4();
|
|
54
59
|
const extension = fileInfo.extension ? `.${fileInfo.extension.replace('.', '')}` : '';
|
|
55
|
-
const
|
|
56
|
-
const
|
|
60
|
+
const defaultResolvedFileName = fileInfo.fileName ?? `${nameId}${extension}`;
|
|
61
|
+
const defaultFilePathFromMetadata = fileInfo.path.reduce((acc, item)=>{
|
|
57
62
|
return `${acc}/${item.value}`;
|
|
58
63
|
}, '');
|
|
59
|
-
|
|
64
|
+
let accessPath = `${pathPrefix}${defaultFilePathFromMetadata}/${defaultResolvedFileName}`;
|
|
65
|
+
if (overwritePath) {
|
|
66
|
+
accessPath = await overwritePath({
|
|
67
|
+
esBucketName,
|
|
68
|
+
fileInfo,
|
|
69
|
+
defaultAccessPath: accessPath
|
|
70
|
+
});
|
|
71
|
+
}
|
|
60
72
|
const command = new clientS3.PutObjectCommand({
|
|
61
73
|
Bucket: bucketName,
|
|
62
74
|
Key: accessPath
|
|
@@ -64,10 +76,10 @@ function AWSProvider(options) {
|
|
|
64
76
|
const signedUrl = await s3RequestPresigner.getSignedUrl(s3Client, command, {
|
|
65
77
|
expiresIn: 60 * 60
|
|
66
78
|
});
|
|
67
|
-
const
|
|
79
|
+
const finalAccessUrl = `${baseUrl}/${accessPath.startsWith('/') ? accessPath.substring(1) : accessPath}`;
|
|
68
80
|
return {
|
|
69
81
|
uploadUrl: signedUrl,
|
|
70
|
-
accessUrl:
|
|
82
|
+
accessUrl: finalAccessUrl
|
|
71
83
|
};
|
|
72
84
|
},
|
|
73
85
|
async requestUploadParts () {
|
|
@@ -80,6 +92,9 @@ function AWSProvider(options) {
|
|
|
80
92
|
throw new Error('Not implemented');
|
|
81
93
|
},
|
|
82
94
|
async deleteFile ({ url }) {
|
|
95
|
+
if (!bucketName) {
|
|
96
|
+
throw new Error('S3 bucketName is not configured in AWSProviderOptions for deleteFile.');
|
|
97
|
+
}
|
|
83
98
|
const path = url.replace(`${baseUrl}/`, '');
|
|
84
99
|
await s3Client.send(new clientS3.DeleteObjectCommand({
|
|
85
100
|
Bucket: bucketName,
|
|
@@ -8,7 +8,7 @@ import 'cookie';
|
|
|
8
8
|
import 'jose';
|
|
9
9
|
|
|
10
10
|
function AWSProvider(options) {
|
|
11
|
-
const { accessKeyId = getEnv('ES_AWS_ACCESS_KEY_ID'), secretAccessKey = getEnv('ES_AWS_SECRET_ACCESS_KEY'), region = getEnv('ES_AWS_REGION'), bucketName = getEnv('ES_AWS_BUCKET_NAME'), endpoint = getEnv('ES_AWS_ENDPOINT'), forcePathStyle = getEnv('ES_AWS_FORCE_PATH_STYLE') === 'true' } = options ?? {};
|
|
11
|
+
const { accessKeyId = getEnv('ES_AWS_ACCESS_KEY_ID'), secretAccessKey = getEnv('ES_AWS_SECRET_ACCESS_KEY'), region = getEnv('ES_AWS_REGION'), bucketName = getEnv('ES_AWS_BUCKET_NAME'), endpoint = getEnv('ES_AWS_ENDPOINT'), forcePathStyle = getEnv('ES_AWS_FORCE_PATH_STYLE') === 'true', overwritePath } = options ?? {};
|
|
12
12
|
const baseUrl = options?.baseUrl ?? getEnv('EDGE_STORE_BASE_URL') ?? (endpoint ? `${endpoint}/${bucketName}` : `https://${bucketName}.s3.${region}.amazonaws.com`);
|
|
13
13
|
const credentials = accessKeyId && secretAccessKey ? {
|
|
14
14
|
accessKeyId,
|
|
@@ -44,15 +44,27 @@ function AWSProvider(options) {
|
|
|
44
44
|
uploadedAt: LastModified
|
|
45
45
|
};
|
|
46
46
|
},
|
|
47
|
-
async requestUpload (
|
|
47
|
+
async requestUpload (params) {
|
|
48
|
+
const { bucketName: esBucketName, fileInfo } = params;
|
|
49
|
+
if (!bucketName) {
|
|
50
|
+
throw new Error('S3 bucketName is not configured in AWSProviderOptions.');
|
|
51
|
+
}
|
|
52
|
+
// Default S3 key (accessPath) construction logic
|
|
48
53
|
const pathPrefix = `${esBucketName}${fileInfo.isPublic ? '/_public' : ''}`;
|
|
49
54
|
const nameId = v4();
|
|
50
55
|
const extension = fileInfo.extension ? `.${fileInfo.extension.replace('.', '')}` : '';
|
|
51
|
-
const
|
|
52
|
-
const
|
|
56
|
+
const defaultResolvedFileName = fileInfo.fileName ?? `${nameId}${extension}`;
|
|
57
|
+
const defaultFilePathFromMetadata = fileInfo.path.reduce((acc, item)=>{
|
|
53
58
|
return `${acc}/${item.value}`;
|
|
54
59
|
}, '');
|
|
55
|
-
|
|
60
|
+
let accessPath = `${pathPrefix}${defaultFilePathFromMetadata}/${defaultResolvedFileName}`;
|
|
61
|
+
if (overwritePath) {
|
|
62
|
+
accessPath = await overwritePath({
|
|
63
|
+
esBucketName,
|
|
64
|
+
fileInfo,
|
|
65
|
+
defaultAccessPath: accessPath
|
|
66
|
+
});
|
|
67
|
+
}
|
|
56
68
|
const command = new PutObjectCommand({
|
|
57
69
|
Bucket: bucketName,
|
|
58
70
|
Key: accessPath
|
|
@@ -60,10 +72,10 @@ function AWSProvider(options) {
|
|
|
60
72
|
const signedUrl = await getSignedUrl(s3Client, command, {
|
|
61
73
|
expiresIn: 60 * 60
|
|
62
74
|
});
|
|
63
|
-
const
|
|
75
|
+
const finalAccessUrl = `${baseUrl}/${accessPath.startsWith('/') ? accessPath.substring(1) : accessPath}`;
|
|
64
76
|
return {
|
|
65
77
|
uploadUrl: signedUrl,
|
|
66
|
-
accessUrl:
|
|
78
|
+
accessUrl: finalAccessUrl
|
|
67
79
|
};
|
|
68
80
|
},
|
|
69
81
|
async requestUploadParts () {
|
|
@@ -76,6 +88,9 @@ function AWSProvider(options) {
|
|
|
76
88
|
throw new Error('Not implemented');
|
|
77
89
|
},
|
|
78
90
|
async deleteFile ({ url }) {
|
|
91
|
+
if (!bucketName) {
|
|
92
|
+
throw new Error('S3 bucketName is not configured in AWSProviderOptions for deleteFile.');
|
|
93
|
+
}
|
|
79
94
|
const path = url.replace(`${baseUrl}/`, '');
|
|
80
95
|
await s3Client.send(new DeleteObjectCommand({
|
|
81
96
|
Bucket: bucketName,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edgestore/server",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Upload files with ease from React/Next.js",
|
|
5
5
|
"homepage": "https://edgestore.dev",
|
|
6
6
|
"repository": "https://github.com/edgestorejs/edgestore.git",
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
},
|
|
110
110
|
"license": "MIT",
|
|
111
111
|
"dependencies": {
|
|
112
|
-
"@edgestore/shared": "0.5.
|
|
112
|
+
"@edgestore/shared": "0.5.1",
|
|
113
113
|
"@panva/hkdf": "^1.0.4",
|
|
114
114
|
"cookie": "^0.5.0",
|
|
115
115
|
"jose": "^4.13.1",
|
|
@@ -151,5 +151,5 @@
|
|
|
151
151
|
"typescript": "^5",
|
|
152
152
|
"zod": "3.21.4"
|
|
153
153
|
},
|
|
154
|
-
"gitHead": "
|
|
154
|
+
"gitHead": "0a04ff24dfb62f749cc970794b3d239c75f48fed"
|
|
155
155
|
}
|
|
@@ -5,10 +5,30 @@ import {
|
|
|
5
5
|
S3Client,
|
|
6
6
|
} from '@aws-sdk/client-s3';
|
|
7
7
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
type MaybePromise,
|
|
10
|
+
type Provider,
|
|
11
|
+
type RequestUploadParams,
|
|
12
|
+
} from '@edgestore/shared';
|
|
9
13
|
import { v4 as uuidv4 } from 'uuid';
|
|
10
14
|
import { getEnv } from '../../adapters/shared';
|
|
11
15
|
|
|
16
|
+
// FileInfo type as received by the provider's requestUpload, part of RequestUploadParams
|
|
17
|
+
type ProviderUploadedFileInfo = RequestUploadParams['fileInfo'];
|
|
18
|
+
|
|
19
|
+
export type AWSOverwritePathFnArgs = {
|
|
20
|
+
/* EdgeStore bucket name */
|
|
21
|
+
esBucketName: string;
|
|
22
|
+
/* File info after path generation and metadata by EdgeStore */
|
|
23
|
+
fileInfo: ProviderUploadedFileInfo;
|
|
24
|
+
/* The S3 key that would be generated by default by this provider */
|
|
25
|
+
defaultAccessPath: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type AWSOverwritePathFn = (
|
|
29
|
+
args: AWSOverwritePathFnArgs,
|
|
30
|
+
) => MaybePromise<string>;
|
|
31
|
+
|
|
12
32
|
export type AWSProviderOptions = {
|
|
13
33
|
/**
|
|
14
34
|
* Access key for AWS credentials.
|
|
@@ -59,6 +79,12 @@ export type AWSProviderOptions = {
|
|
|
59
79
|
* It can also be set via the `EDGE_STORE_JWT_SECRET` environment variable.
|
|
60
80
|
*/
|
|
61
81
|
jwtSecret?: string;
|
|
82
|
+
/**
|
|
83
|
+
* Optional function to overwrite the S3 key (object path) for uploads.
|
|
84
|
+
* This function receives the EdgeStore bucket name, fileInfo and the default S3 key
|
|
85
|
+
* and should return the desired S3 key string.
|
|
86
|
+
*/
|
|
87
|
+
overwritePath?: AWSOverwritePathFn;
|
|
62
88
|
};
|
|
63
89
|
|
|
64
90
|
export function AWSProvider(options?: AWSProviderOptions): Provider {
|
|
@@ -69,6 +95,7 @@ export function AWSProvider(options?: AWSProviderOptions): Provider {
|
|
|
69
95
|
bucketName = getEnv('ES_AWS_BUCKET_NAME'),
|
|
70
96
|
endpoint = getEnv('ES_AWS_ENDPOINT'),
|
|
71
97
|
forcePathStyle = getEnv('ES_AWS_FORCE_PATH_STYLE') === 'true',
|
|
98
|
+
overwritePath,
|
|
72
99
|
} = options ?? {};
|
|
73
100
|
|
|
74
101
|
const baseUrl =
|
|
@@ -120,7 +147,16 @@ export function AWSProvider(options?: AWSProviderOptions): Provider {
|
|
|
120
147
|
uploadedAt: LastModified,
|
|
121
148
|
};
|
|
122
149
|
},
|
|
123
|
-
async requestUpload(
|
|
150
|
+
async requestUpload(params: RequestUploadParams) {
|
|
151
|
+
const { bucketName: esBucketName, fileInfo } = params;
|
|
152
|
+
|
|
153
|
+
if (!bucketName) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
'S3 bucketName is not configured in AWSProviderOptions.',
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Default S3 key (accessPath) construction logic
|
|
124
160
|
const pathPrefix = `${esBucketName}${
|
|
125
161
|
fileInfo.isPublic ? '/_public' : ''
|
|
126
162
|
}`;
|
|
@@ -128,11 +164,21 @@ export function AWSProvider(options?: AWSProviderOptions): Provider {
|
|
|
128
164
|
const extension = fileInfo.extension
|
|
129
165
|
? `.${fileInfo.extension.replace('.', '')}`
|
|
130
166
|
: '';
|
|
131
|
-
const
|
|
132
|
-
|
|
167
|
+
const defaultResolvedFileName =
|
|
168
|
+
fileInfo.fileName ?? `${nameId}${extension}`;
|
|
169
|
+
const defaultFilePathFromMetadata = fileInfo.path.reduce((acc, item) => {
|
|
133
170
|
return `${acc}/${item.value}`;
|
|
134
171
|
}, '');
|
|
135
|
-
|
|
172
|
+
|
|
173
|
+
let accessPath = `${pathPrefix}${defaultFilePathFromMetadata}/${defaultResolvedFileName}`;
|
|
174
|
+
|
|
175
|
+
if (overwritePath) {
|
|
176
|
+
accessPath = await overwritePath({
|
|
177
|
+
esBucketName,
|
|
178
|
+
fileInfo,
|
|
179
|
+
defaultAccessPath: accessPath,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
136
182
|
|
|
137
183
|
const command = new PutObjectCommand({
|
|
138
184
|
Bucket: bucketName,
|
|
@@ -143,10 +189,10 @@ export function AWSProvider(options?: AWSProviderOptions): Provider {
|
|
|
143
189
|
expiresIn: 60 * 60, // 1 hour
|
|
144
190
|
});
|
|
145
191
|
|
|
146
|
-
const
|
|
192
|
+
const finalAccessUrl = `${baseUrl}/${accessPath.startsWith('/') ? accessPath.substring(1) : accessPath}`;
|
|
147
193
|
return {
|
|
148
194
|
uploadUrl: signedUrl,
|
|
149
|
-
accessUrl:
|
|
195
|
+
accessUrl: finalAccessUrl,
|
|
150
196
|
};
|
|
151
197
|
},
|
|
152
198
|
async requestUploadParts() {
|
|
@@ -159,6 +205,11 @@ export function AWSProvider(options?: AWSProviderOptions): Provider {
|
|
|
159
205
|
throw new Error('Not implemented');
|
|
160
206
|
},
|
|
161
207
|
async deleteFile({ url }) {
|
|
208
|
+
if (!bucketName) {
|
|
209
|
+
throw new Error(
|
|
210
|
+
'S3 bucketName is not configured in AWSProviderOptions for deleteFile.',
|
|
211
|
+
);
|
|
212
|
+
}
|
|
162
213
|
const path = url.replace(`${baseUrl}/`, '');
|
|
163
214
|
await s3Client.send(
|
|
164
215
|
new DeleteObjectCommand({
|