@edgestore/server 0.5.0 → 0.5.2

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,EAAE,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAIlD,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;CACpB,CAAC;AAEF,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,QAAQ,CA8GlE"}
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 ({ bucketName: esBucketName, fileInfo }) {
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 fileName = fileInfo.fileName ?? `${nameId}${extension}`;
56
- const filePath = fileInfo.path.reduce((acc, item)=>{
60
+ const defaultResolvedFileName = fileInfo.fileName ?? `${nameId}${extension}`;
61
+ const defaultFilePathFromMetadata = fileInfo.path.reduce((acc, item)=>{
57
62
  return `${acc}/${item.value}`;
58
63
  }, '');
59
- const accessPath = `${pathPrefix}${filePath}/${fileName}`;
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 url = `${baseUrl}/${accessPath}`;
79
+ const finalAccessUrl = `${baseUrl}/${accessPath.startsWith('/') ? accessPath.substring(1) : accessPath}`;
68
80
  return {
69
81
  uploadUrl: signedUrl,
70
- accessUrl: url
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 ({ bucketName: esBucketName, fileInfo }) {
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 fileName = fileInfo.fileName ?? `${nameId}${extension}`;
52
- const filePath = fileInfo.path.reduce((acc, item)=>{
56
+ const defaultResolvedFileName = fileInfo.fileName ?? `${nameId}${extension}`;
57
+ const defaultFilePathFromMetadata = fileInfo.path.reduce((acc, item)=>{
53
58
  return `${acc}/${item.value}`;
54
59
  }, '');
55
- const accessPath = `${pathPrefix}${filePath}/${fileName}`;
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 url = `${baseUrl}/${accessPath}`;
75
+ const finalAccessUrl = `${baseUrl}/${accessPath.startsWith('/') ? accessPath.substring(1) : accessPath}`;
64
76
  return {
65
77
  uploadUrl: signedUrl,
66
- accessUrl: url
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.0",
3
+ "version": "0.5.2",
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.0",
112
+ "@edgestore/shared": "0.5.2",
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": "1bc997b2797e026d6661b9964e74c6e117dba352"
154
+ "gitHead": "97cb861d3f56e4e04c83acc6808d56771433df29"
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 { type Provider } from '@edgestore/shared';
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({ bucketName: esBucketName, fileInfo }) {
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 fileName = fileInfo.fileName ?? `${nameId}${extension}`;
132
- const filePath = fileInfo.path.reduce((acc, item) => {
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
- const accessPath = `${pathPrefix}${filePath}/${fileName}`;
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 url = `${baseUrl}/${accessPath}`;
192
+ const finalAccessUrl = `${baseUrl}/${accessPath.startsWith('/') ? accessPath.substring(1) : accessPath}`;
147
193
  return {
148
194
  uploadUrl: signedUrl,
149
- accessUrl: url,
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({