@edgestore/server 0.1.5-alpha.3 → 0.1.5-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/next/app/index.d.ts +5 -3
- package/dist/adapters/next/app/index.d.ts.map +1 -1
- package/dist/adapters/next/app/index.js +38 -15
- package/dist/adapters/next/app/index.mjs +37 -14
- package/dist/adapters/next/pages/index.d.ts +5 -3
- package/dist/adapters/next/pages/index.d.ts.map +1 -1
- package/dist/adapters/next/pages/index.js +32 -12
- package/dist/adapters/next/pages/index.mjs +31 -11
- package/dist/adapters/shared.d.ts.map +1 -1
- package/dist/core/client/index.d.ts +56 -2
- package/dist/core/client/index.d.ts.map +1 -1
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +13 -3
- package/dist/core/index.mjs +14 -5
- package/dist/core/sdk/index.d.ts.map +1 -1
- package/dist/{index-50ab9e08.js → index-7cb3a3f3.mjs} +41 -5
- package/dist/{index-f33a00fb.js → index-9eb6248c.js} +37 -2
- package/dist/{index-30a3741e.mjs → index-e25c8209.js} +48 -2
- package/dist/libs/errors/EdgeStoreApiClientError.d.ts +8 -0
- package/dist/libs/errors/EdgeStoreApiClientError.d.ts.map +1 -0
- package/dist/libs/errors/EdgeStoreError.d.ts +36 -4
- package/dist/libs/errors/EdgeStoreError.d.ts.map +1 -1
- package/dist/libs/logger.d.ts +13 -0
- package/dist/libs/logger.d.ts.map +1 -0
- package/dist/logger-0f08f252.mjs +40 -0
- package/dist/logger-623f2833.js +42 -0
- package/dist/logger-8f098618.js +33 -0
- package/dist/providers/azure/index.d.ts +20 -0
- package/dist/providers/azure/index.d.ts.map +1 -0
- package/dist/providers/azure/index.js +61 -0
- package/dist/providers/azure/index.mjs +57 -0
- package/dist/providers/edgestore/index.d.ts.map +1 -1
- package/dist/providers/edgestore/index.js +10 -3
- package/dist/providers/edgestore/index.mjs +10 -3
- package/dist/{shared-5d1e7f43.js → shared-53cb59dd.js} +72 -51
- package/dist/{shared-30b7a2ab.mjs → shared-b14a84ee.mjs} +65 -42
- package/dist/{shared-88655ba7.js → shared-f8ddbf7c.js} +62 -36
- package/package.json +12 -2
- package/providers/azure/index.d.ts +1 -0
- package/providers/azure/index.js +1 -0
- package/src/adapters/next/app/index.ts +52 -19
- package/src/adapters/next/pages/index.ts +43 -14
- package/src/adapters/shared.ts +62 -29
- package/src/core/client/index.ts +57 -2
- package/src/core/index.ts +6 -0
- package/src/core/sdk/index.ts +7 -1
- package/src/libs/errors/EdgeStoreApiClientError.ts +14 -0
- package/src/libs/errors/EdgeStoreError.ts +76 -7
- package/src/libs/logger.ts +44 -0
- package/src/providers/azure/index.ts +89 -0
- package/src/providers/edgestore/index.ts +9 -2
|
@@ -2,19 +2,7 @@ import { hkdf } from '@panva/hkdf';
|
|
|
2
2
|
import { serialize } from 'cookie';
|
|
3
3
|
import { EncryptJWT, jwtDecrypt } from 'jose';
|
|
4
4
|
import { v4 } from 'uuid';
|
|
5
|
-
|
|
6
|
-
const EDGE_STORE_ERROR_CODES = {
|
|
7
|
-
BAD_REQUEST: 400,
|
|
8
|
-
UNAUTHORIZED: 401,
|
|
9
|
-
};
|
|
10
|
-
class EdgeStoreError extends Error {
|
|
11
|
-
constructor(opts) {
|
|
12
|
-
super(opts.message);
|
|
13
|
-
this.name = 'EdgeStoreError';
|
|
14
|
-
this.code = opts.code;
|
|
15
|
-
this.cause = opts.cause;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
5
|
+
import { E as EdgeStoreError } from './index-9eb6248c.js';
|
|
18
6
|
|
|
19
7
|
const IMAGE_MIME_TYPES = [
|
|
20
8
|
'image/jpeg',
|
|
@@ -66,7 +54,10 @@ async function requestUpload(params) {
|
|
|
66
54
|
const ctx = await getContext(ctxToken);
|
|
67
55
|
const bucket = router.buckets[bucketName];
|
|
68
56
|
if (!bucket) {
|
|
69
|
-
throw new
|
|
57
|
+
throw new EdgeStoreError({
|
|
58
|
+
message: `Bucket ${bucketName} not found`,
|
|
59
|
+
code: 'BAD_REQUEST',
|
|
60
|
+
});
|
|
70
61
|
}
|
|
71
62
|
if (bucket._def.beforeUpload) {
|
|
72
63
|
const canUpload = await bucket._def.beforeUpload?.({
|
|
@@ -82,22 +73,33 @@ async function requestUpload(params) {
|
|
|
82
73
|
},
|
|
83
74
|
});
|
|
84
75
|
if (!canUpload) {
|
|
85
|
-
throw new
|
|
76
|
+
throw new EdgeStoreError({
|
|
77
|
+
message: 'Upload not allowed for the current context',
|
|
78
|
+
code: 'UPLOAD_NOT_ALLOWED',
|
|
79
|
+
});
|
|
86
80
|
}
|
|
87
81
|
}
|
|
88
82
|
if (bucket._def.type === 'IMAGE') {
|
|
89
83
|
if (!IMAGE_MIME_TYPES.includes(fileInfo.type)) {
|
|
90
84
|
throw new EdgeStoreError({
|
|
91
|
-
code: '
|
|
85
|
+
code: 'MIME_TYPE_NOT_ALLOWED',
|
|
92
86
|
message: 'Only images are allowed in this bucket',
|
|
87
|
+
details: {
|
|
88
|
+
allowedMimeTypes: IMAGE_MIME_TYPES,
|
|
89
|
+
mimeType: fileInfo.type,
|
|
90
|
+
},
|
|
93
91
|
});
|
|
94
92
|
}
|
|
95
93
|
}
|
|
96
94
|
if (bucket._def.bucketConfig?.maxSize) {
|
|
97
95
|
if (fileInfo.size > bucket._def.bucketConfig.maxSize) {
|
|
98
96
|
throw new EdgeStoreError({
|
|
99
|
-
code: '
|
|
97
|
+
code: 'FILE_TOO_LARGE',
|
|
100
98
|
message: `File size is too big. Max size is ${bucket._def.bucketConfig.maxSize}`,
|
|
99
|
+
details: {
|
|
100
|
+
maxFileSize: bucket._def.bucketConfig.maxSize,
|
|
101
|
+
fileSize: fileInfo.size,
|
|
102
|
+
},
|
|
101
103
|
});
|
|
102
104
|
}
|
|
103
105
|
}
|
|
@@ -119,8 +121,12 @@ async function requestUpload(params) {
|
|
|
119
121
|
}
|
|
120
122
|
if (!accepted) {
|
|
121
123
|
throw new EdgeStoreError({
|
|
122
|
-
code: '
|
|
124
|
+
code: 'MIME_TYPE_NOT_ALLOWED',
|
|
123
125
|
message: `"${fileInfo.type}" is not allowed. Accepted types are ${JSON.stringify(accept)}`,
|
|
126
|
+
details: {
|
|
127
|
+
allowedMimeTypes: accept,
|
|
128
|
+
mimeType: fileInfo.type,
|
|
129
|
+
},
|
|
124
130
|
});
|
|
125
131
|
}
|
|
126
132
|
}
|
|
@@ -152,7 +158,7 @@ async function requestUpload(params) {
|
|
|
152
158
|
};
|
|
153
159
|
}
|
|
154
160
|
async function requestUploadParts(params) {
|
|
155
|
-
const { provider,
|
|
161
|
+
const { provider, ctxToken, body: { multipart, path }, } = params;
|
|
156
162
|
if (!ctxToken) {
|
|
157
163
|
throw new EdgeStoreError({
|
|
158
164
|
message: 'Missing edgestore-ctx cookie',
|
|
@@ -160,10 +166,6 @@ async function requestUploadParts(params) {
|
|
|
160
166
|
});
|
|
161
167
|
}
|
|
162
168
|
await getContext(ctxToken); // just to check if the token is valid
|
|
163
|
-
const bucket = router.buckets[multipart.uploadId];
|
|
164
|
-
if (!bucket) {
|
|
165
|
-
throw new Error(`Bucket ${multipart.uploadId} not found`);
|
|
166
|
-
}
|
|
167
169
|
return await provider.requestUploadParts({
|
|
168
170
|
multipart,
|
|
169
171
|
path,
|
|
@@ -180,7 +182,10 @@ async function completeMultipartUpload(params) {
|
|
|
180
182
|
await getContext(ctxToken); // just to check if the token is valid
|
|
181
183
|
const bucket = router.buckets[bucketName];
|
|
182
184
|
if (!bucket) {
|
|
183
|
-
throw new
|
|
185
|
+
throw new EdgeStoreError({
|
|
186
|
+
message: `Bucket ${bucketName} not found`,
|
|
187
|
+
code: 'BAD_REQUEST',
|
|
188
|
+
});
|
|
184
189
|
}
|
|
185
190
|
return await provider.completeMultipartUpload({
|
|
186
191
|
uploadId,
|
|
@@ -199,7 +204,10 @@ async function confirmUpload(params) {
|
|
|
199
204
|
await getContext(ctxToken); // just to check if the token is valid
|
|
200
205
|
const bucket = router.buckets[bucketName];
|
|
201
206
|
if (!bucket) {
|
|
202
|
-
throw new
|
|
207
|
+
throw new EdgeStoreError({
|
|
208
|
+
message: `Bucket ${bucketName} not found`,
|
|
209
|
+
code: 'BAD_REQUEST',
|
|
210
|
+
});
|
|
203
211
|
}
|
|
204
212
|
return await provider.confirmUpload({
|
|
205
213
|
bucket,
|
|
@@ -217,10 +225,16 @@ async function deleteFile(params) {
|
|
|
217
225
|
const ctx = await getContext(ctxToken);
|
|
218
226
|
const bucket = router.buckets[bucketName];
|
|
219
227
|
if (!bucket) {
|
|
220
|
-
throw new
|
|
228
|
+
throw new EdgeStoreError({
|
|
229
|
+
message: `Bucket ${bucketName} not found`,
|
|
230
|
+
code: 'BAD_REQUEST',
|
|
231
|
+
});
|
|
221
232
|
}
|
|
222
233
|
if (!bucket._def.beforeDelete) {
|
|
223
|
-
throw new
|
|
234
|
+
throw new EdgeStoreError({
|
|
235
|
+
message: 'You need to define beforeDelete if you want to delete files directly from the frontend.',
|
|
236
|
+
code: 'SERVER_ERROR',
|
|
237
|
+
});
|
|
224
238
|
}
|
|
225
239
|
const fileInfo = await provider.getFile({
|
|
226
240
|
url,
|
|
@@ -230,7 +244,10 @@ async function deleteFile(params) {
|
|
|
230
244
|
fileInfo,
|
|
231
245
|
});
|
|
232
246
|
if (!canDelete) {
|
|
233
|
-
throw new
|
|
247
|
+
throw new EdgeStoreError({
|
|
248
|
+
message: 'Delete not allowed for the current context',
|
|
249
|
+
code: 'DELETE_NOT_ALLOWED',
|
|
250
|
+
});
|
|
234
251
|
}
|
|
235
252
|
return await provider.deleteFile({
|
|
236
253
|
bucket,
|
|
@@ -240,7 +257,10 @@ async function deleteFile(params) {
|
|
|
240
257
|
async function encryptJWT(ctx) {
|
|
241
258
|
const secret = process.env.EDGE_STORE_JWT_SECRET ?? process.env.EDGE_STORE_SECRET_KEY;
|
|
242
259
|
if (!secret) {
|
|
243
|
-
throw new
|
|
260
|
+
throw new EdgeStoreError({
|
|
261
|
+
message: 'EDGE_STORE_JWT_SECRET or EDGE_STORE_SECRET_KEY is not defined',
|
|
262
|
+
code: 'SERVER_ERROR',
|
|
263
|
+
});
|
|
244
264
|
}
|
|
245
265
|
const encryptionSecret = await getDerivedEncryptionKey(secret);
|
|
246
266
|
return await new EncryptJWT(ctx)
|
|
@@ -253,7 +273,10 @@ async function encryptJWT(ctx) {
|
|
|
253
273
|
async function decryptJWT(token) {
|
|
254
274
|
const secret = process.env.EDGE_STORE_JWT_SECRET ?? process.env.EDGE_STORE_SECRET_KEY;
|
|
255
275
|
if (!secret) {
|
|
256
|
-
throw new
|
|
276
|
+
throw new EdgeStoreError({
|
|
277
|
+
message: 'EDGE_STORE_JWT_SECRET or EDGE_STORE_SECRET_KEY is not defined',
|
|
278
|
+
code: 'SERVER_ERROR',
|
|
279
|
+
});
|
|
257
280
|
}
|
|
258
281
|
const encryptionSecret = await getDerivedEncryptionKey(secret);
|
|
259
282
|
const { payload } = await jwtDecrypt(token, encryptionSecret, {
|
|
@@ -270,7 +293,10 @@ function buildPath(params) {
|
|
|
270
293
|
const path = pathParams.map((param) => {
|
|
271
294
|
const paramEntries = Object.entries(param);
|
|
272
295
|
if (paramEntries[0] === undefined) {
|
|
273
|
-
throw new
|
|
296
|
+
throw new EdgeStoreError({
|
|
297
|
+
message: `Empty path param found in: ${JSON.stringify(pathParams)}`,
|
|
298
|
+
code: 'SERVER_ERROR',
|
|
299
|
+
});
|
|
274
300
|
}
|
|
275
301
|
const [key, value] = paramEntries[0];
|
|
276
302
|
// this is a string like: "ctx.xxx" or "input.yyy.zzz"
|
|
@@ -278,7 +304,10 @@ function buildPath(params) {
|
|
|
278
304
|
.split('.')
|
|
279
305
|
.reduce((acc2, key) => {
|
|
280
306
|
if (acc2[key] === undefined) {
|
|
281
|
-
throw new
|
|
307
|
+
throw new EdgeStoreError({
|
|
308
|
+
message: `Missing key ${key} in ${JSON.stringify(acc2)}`,
|
|
309
|
+
code: 'BAD_REQUEST',
|
|
310
|
+
});
|
|
282
311
|
}
|
|
283
312
|
return acc2[key];
|
|
284
313
|
}, params.pathAttrs);
|
|
@@ -301,9 +330,6 @@ function parsePath(path) {
|
|
|
301
330
|
};
|
|
302
331
|
}
|
|
303
332
|
async function getContext(token) {
|
|
304
|
-
if (!token) {
|
|
305
|
-
throw new Error('No token');
|
|
306
|
-
}
|
|
307
333
|
return await decryptJWT(token);
|
|
308
334
|
}
|
|
309
335
|
/**
|
|
@@ -324,4 +350,4 @@ function unproxyUrl(url) {
|
|
|
324
350
|
return url;
|
|
325
351
|
}
|
|
326
352
|
|
|
327
|
-
export {
|
|
353
|
+
export { requestUploadParts as a, buildPath as b, completeMultipartUpload as c, confirmUpload as d, deleteFile as e, init as i, parsePath as p, requestUpload as r };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edgestore/server",
|
|
3
|
-
"version": "0.1.5-alpha.
|
|
3
|
+
"version": "0.1.5-alpha.5",
|
|
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",
|
|
@@ -57,6 +57,11 @@
|
|
|
57
57
|
"require": "./dist/providers/aws/index.js",
|
|
58
58
|
"default": "./dist/providers/aws/index.js"
|
|
59
59
|
},
|
|
60
|
+
"./providers/azure": {
|
|
61
|
+
"import": "./dist/providers/azure/index.mjs",
|
|
62
|
+
"require": "./dist/providers/azure/index.js",
|
|
63
|
+
"default": "./dist/providers/azure/index.js"
|
|
64
|
+
},
|
|
60
65
|
"./providers/edgestore": {
|
|
61
66
|
"import": "./dist/providers/edgestore/index.mjs",
|
|
62
67
|
"require": "./dist/providers/edgestore/index.js",
|
|
@@ -92,6 +97,7 @@
|
|
|
92
97
|
"peerDependencies": {
|
|
93
98
|
"@aws-sdk/client-s3": ">=3.0.0",
|
|
94
99
|
"@aws-sdk/s3-request-presigner": ">=3.0.0",
|
|
100
|
+
"@azure/storage-blob": "^12.17.0",
|
|
95
101
|
"zod": ">=3.0.0"
|
|
96
102
|
},
|
|
97
103
|
"peerDependenciesMeta": {
|
|
@@ -103,11 +109,15 @@
|
|
|
103
109
|
},
|
|
104
110
|
"@aws-sdk/s3-request-presigner": {
|
|
105
111
|
"optional": true
|
|
112
|
+
},
|
|
113
|
+
"@azure/storage-blob": {
|
|
114
|
+
"optional": true
|
|
106
115
|
}
|
|
107
116
|
},
|
|
108
117
|
"devDependencies": {
|
|
109
118
|
"@aws-sdk/client-s3": "^3.420.0",
|
|
110
119
|
"@aws-sdk/s3-request-presigner": "^3.420.0",
|
|
120
|
+
"@azure/storage-blob": "^12.17.0",
|
|
111
121
|
"@types/cookie": "^0.5.1",
|
|
112
122
|
"@types/node": "^18.11.18",
|
|
113
123
|
"@types/uuid": "^9.0.1",
|
|
@@ -117,5 +127,5 @@
|
|
|
117
127
|
"typescript": "^5.1.6",
|
|
118
128
|
"zod": "^3.21.4"
|
|
119
129
|
},
|
|
120
|
-
"gitHead": "
|
|
130
|
+
"gitHead": "eb9944bf86d040a4bcbe8753cb428be5cf3e0cf7"
|
|
121
131
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../../dist/providers/azure';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('../../dist/providers/azure');
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/ban-types */
|
|
1
2
|
import { type NextRequest } from 'next/server';
|
|
2
3
|
import { type EdgeStoreRouter } from '../../../core/internals/bucketBuilder';
|
|
3
4
|
import EdgeStoreError, {
|
|
4
5
|
EDGE_STORE_ERROR_CODES,
|
|
6
|
+
type EdgeStoreErrorCodeKey,
|
|
5
7
|
} from '../../../libs/errors/EdgeStoreError';
|
|
8
|
+
import Logger, { type LogLevel } from '../../../libs/logger';
|
|
6
9
|
import { EdgeStoreProvider } from '../../../providers/edgestore';
|
|
7
10
|
import { type Provider } from '../../../providers/types';
|
|
8
11
|
import { type MaybePromise } from '../../../types';
|
|
@@ -24,26 +27,43 @@ export type CreateContextOptions = {
|
|
|
24
27
|
req: NextRequest;
|
|
25
28
|
};
|
|
26
29
|
|
|
27
|
-
export type Config<TCtx> =
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
export type Config<TCtx> = {
|
|
31
|
+
provider?: Provider;
|
|
32
|
+
router: EdgeStoreRouter<TCtx>;
|
|
33
|
+
logLevel?: LogLevel;
|
|
34
|
+
} & (TCtx extends Record<string, never>
|
|
35
|
+
? {}
|
|
32
36
|
: {
|
|
33
37
|
provider?: Provider;
|
|
34
38
|
router: EdgeStoreRouter<TCtx>;
|
|
35
39
|
createContext: (opts: CreateContextOptions) => MaybePromise<TCtx>;
|
|
36
|
-
};
|
|
40
|
+
});
|
|
37
41
|
|
|
38
42
|
export function createEdgeStoreNextHandler<TCtx>(config: Config<TCtx>) {
|
|
39
43
|
const { provider = EdgeStoreProvider() } = config;
|
|
44
|
+
const log = new Logger(config.logLevel);
|
|
40
45
|
return async (req: NextRequest) => {
|
|
41
46
|
try {
|
|
47
|
+
if (!('nextUrl' in req))
|
|
48
|
+
throw new EdgeStoreError({
|
|
49
|
+
message:
|
|
50
|
+
'Error running the app adapter. Make sure you are importing the correct adapter in your router configuration',
|
|
51
|
+
code: 'SERVER_ERROR',
|
|
52
|
+
});
|
|
42
53
|
if (req.nextUrl.pathname.endsWith('/init')) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
54
|
+
let ctx = {} as TCtx;
|
|
55
|
+
try {
|
|
56
|
+
ctx =
|
|
57
|
+
'createContext' in config
|
|
58
|
+
? await config.createContext({ req })
|
|
59
|
+
: ({} as TCtx);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
throw new EdgeStoreError({
|
|
62
|
+
message: 'Error creating context',
|
|
63
|
+
code: 'CREATE_CONTEXT_ERROR',
|
|
64
|
+
cause: err instanceof Error ? err : undefined,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
47
67
|
const { newCookies, token, baseUrl } = await init({
|
|
48
68
|
ctx,
|
|
49
69
|
provider,
|
|
@@ -157,17 +177,30 @@ export function createEdgeStoreNextHandler<TCtx>(config: Config<TCtx>) {
|
|
|
157
177
|
}
|
|
158
178
|
} catch (err) {
|
|
159
179
|
if (err instanceof EdgeStoreError) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
180
|
+
log[err.level](err.formattedMessage());
|
|
181
|
+
if (err.cause) log[err.level](err.cause);
|
|
182
|
+
return new Response(JSON.stringify(err.formattedJson()), {
|
|
183
|
+
status: EDGE_STORE_ERROR_CODES[err.code as EdgeStoreErrorCodeKey],
|
|
184
|
+
headers: {
|
|
185
|
+
'Content-Type': 'application/json',
|
|
186
|
+
},
|
|
166
187
|
});
|
|
167
188
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
189
|
+
log.error(err);
|
|
190
|
+
return new Response(
|
|
191
|
+
JSON.stringify(
|
|
192
|
+
new EdgeStoreError({
|
|
193
|
+
message: 'Internal server error',
|
|
194
|
+
code: 'SERVER_ERROR',
|
|
195
|
+
}).formattedJson(),
|
|
196
|
+
),
|
|
197
|
+
{
|
|
198
|
+
status: 500,
|
|
199
|
+
headers: {
|
|
200
|
+
'Content-Type': 'application/json',
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
);
|
|
171
204
|
}
|
|
172
205
|
};
|
|
173
206
|
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/ban-types */
|
|
1
2
|
import { type NextApiRequest, type NextApiResponse } from 'next/types';
|
|
2
3
|
import { type EdgeStoreRouter } from '../../../core/internals/bucketBuilder';
|
|
3
4
|
import EdgeStoreError, {
|
|
4
5
|
EDGE_STORE_ERROR_CODES,
|
|
6
|
+
type EdgeStoreErrorCodeKey,
|
|
5
7
|
} from '../../../libs/errors/EdgeStoreError';
|
|
8
|
+
import Logger, { type LogLevel } from '../../../libs/logger';
|
|
6
9
|
import { EdgeStoreProvider } from '../../../providers/edgestore';
|
|
7
10
|
import { type Provider } from '../../../providers/types';
|
|
8
11
|
import { type MaybePromise } from '../../../types';
|
|
@@ -25,26 +28,43 @@ export type CreateContextOptions = {
|
|
|
25
28
|
res: NextApiResponse;
|
|
26
29
|
};
|
|
27
30
|
|
|
28
|
-
export type Config<TCtx> =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
export type Config<TCtx> = {
|
|
32
|
+
provider?: Provider;
|
|
33
|
+
router: EdgeStoreRouter<TCtx>;
|
|
34
|
+
logLevel?: LogLevel;
|
|
35
|
+
} & (TCtx extends Record<string, never>
|
|
36
|
+
? {}
|
|
33
37
|
: {
|
|
34
38
|
provider?: Provider;
|
|
35
39
|
router: EdgeStoreRouter<TCtx>;
|
|
36
40
|
createContext: (opts: CreateContextOptions) => MaybePromise<TCtx>;
|
|
37
|
-
};
|
|
41
|
+
});
|
|
38
42
|
|
|
39
43
|
export function createEdgeStoreNextHandler<TCtx>(config: Config<TCtx>) {
|
|
40
44
|
const { provider = EdgeStoreProvider() } = config;
|
|
45
|
+
const log = new Logger(config.logLevel);
|
|
41
46
|
return async (req: NextApiRequest, res: NextApiResponse) => {
|
|
42
47
|
try {
|
|
48
|
+
if (!('json' in res))
|
|
49
|
+
throw new EdgeStoreError({
|
|
50
|
+
message:
|
|
51
|
+
'Error running the pages adapter. Make sure you are importing the correct adapter in your router configuration',
|
|
52
|
+
code: 'SERVER_ERROR',
|
|
53
|
+
});
|
|
43
54
|
if (req.url?.includes?.('/init')) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
55
|
+
let ctx = {} as TCtx;
|
|
56
|
+
try {
|
|
57
|
+
ctx =
|
|
58
|
+
'createContext' in config
|
|
59
|
+
? await config.createContext({ req, res })
|
|
60
|
+
: ({} as TCtx);
|
|
61
|
+
} catch (err) {
|
|
62
|
+
throw new EdgeStoreError({
|
|
63
|
+
message: 'Error creating context',
|
|
64
|
+
code: 'CREATE_CONTEXT_ERROR',
|
|
65
|
+
cause: err instanceof Error ? err : undefined,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
48
68
|
const { newCookies, token, baseUrl } = await init({
|
|
49
69
|
ctx,
|
|
50
70
|
provider,
|
|
@@ -123,11 +143,20 @@ export function createEdgeStoreNextHandler<TCtx>(config: Config<TCtx>) {
|
|
|
123
143
|
}
|
|
124
144
|
} catch (err) {
|
|
125
145
|
if (err instanceof EdgeStoreError) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
res
|
|
146
|
+
log[err.level](err.formattedMessage());
|
|
147
|
+
if (err.cause) log[err.level](err.cause);
|
|
148
|
+
res
|
|
149
|
+
.status(EDGE_STORE_ERROR_CODES[err.code as EdgeStoreErrorCodeKey])
|
|
150
|
+
.json(err.formattedJson());
|
|
151
|
+
} else {
|
|
152
|
+
log.error(err);
|
|
153
|
+
res.status(500).send(
|
|
154
|
+
new EdgeStoreError({
|
|
155
|
+
message: 'Internal Server Error',
|
|
156
|
+
code: 'SERVER_ERROR',
|
|
157
|
+
}).formattedJson(),
|
|
158
|
+
);
|
|
129
159
|
}
|
|
130
|
-
res.status(500).send('Internal server error');
|
|
131
160
|
}
|
|
132
161
|
};
|
|
133
162
|
}
|
package/src/adapters/shared.ts
CHANGED
|
@@ -82,7 +82,10 @@ export async function requestUpload<TCtx>(params: {
|
|
|
82
82
|
const ctx = await getContext(ctxToken);
|
|
83
83
|
const bucket = router.buckets[bucketName];
|
|
84
84
|
if (!bucket) {
|
|
85
|
-
throw new
|
|
85
|
+
throw new EdgeStoreError({
|
|
86
|
+
message: `Bucket ${bucketName} not found`,
|
|
87
|
+
code: 'BAD_REQUEST',
|
|
88
|
+
});
|
|
86
89
|
}
|
|
87
90
|
if (bucket._def.beforeUpload) {
|
|
88
91
|
const canUpload = await bucket._def.beforeUpload?.({
|
|
@@ -98,15 +101,22 @@ export async function requestUpload<TCtx>(params: {
|
|
|
98
101
|
},
|
|
99
102
|
});
|
|
100
103
|
if (!canUpload) {
|
|
101
|
-
throw new
|
|
104
|
+
throw new EdgeStoreError({
|
|
105
|
+
message: 'Upload not allowed for the current context',
|
|
106
|
+
code: 'UPLOAD_NOT_ALLOWED',
|
|
107
|
+
});
|
|
102
108
|
}
|
|
103
109
|
}
|
|
104
110
|
|
|
105
111
|
if (bucket._def.type === 'IMAGE') {
|
|
106
112
|
if (!IMAGE_MIME_TYPES.includes(fileInfo.type)) {
|
|
107
113
|
throw new EdgeStoreError({
|
|
108
|
-
code: '
|
|
114
|
+
code: 'MIME_TYPE_NOT_ALLOWED',
|
|
109
115
|
message: 'Only images are allowed in this bucket',
|
|
116
|
+
details: {
|
|
117
|
+
allowedMimeTypes: IMAGE_MIME_TYPES,
|
|
118
|
+
mimeType: fileInfo.type,
|
|
119
|
+
},
|
|
110
120
|
});
|
|
111
121
|
}
|
|
112
122
|
}
|
|
@@ -114,8 +124,12 @@ export async function requestUpload<TCtx>(params: {
|
|
|
114
124
|
if (bucket._def.bucketConfig?.maxSize) {
|
|
115
125
|
if (fileInfo.size > bucket._def.bucketConfig.maxSize) {
|
|
116
126
|
throw new EdgeStoreError({
|
|
117
|
-
code: '
|
|
127
|
+
code: 'FILE_TOO_LARGE',
|
|
118
128
|
message: `File size is too big. Max size is ${bucket._def.bucketConfig.maxSize}`,
|
|
129
|
+
details: {
|
|
130
|
+
maxFileSize: bucket._def.bucketConfig.maxSize,
|
|
131
|
+
fileSize: fileInfo.size,
|
|
132
|
+
},
|
|
119
133
|
});
|
|
120
134
|
}
|
|
121
135
|
}
|
|
@@ -137,10 +151,14 @@ export async function requestUpload<TCtx>(params: {
|
|
|
137
151
|
}
|
|
138
152
|
if (!accepted) {
|
|
139
153
|
throw new EdgeStoreError({
|
|
140
|
-
code: '
|
|
154
|
+
code: 'MIME_TYPE_NOT_ALLOWED',
|
|
141
155
|
message: `"${
|
|
142
156
|
fileInfo.type
|
|
143
157
|
}" is not allowed. Accepted types are ${JSON.stringify(accept)}`,
|
|
158
|
+
details: {
|
|
159
|
+
allowedMimeTypes: accept,
|
|
160
|
+
mimeType: fileInfo.type,
|
|
161
|
+
},
|
|
144
162
|
});
|
|
145
163
|
}
|
|
146
164
|
}
|
|
@@ -190,7 +208,6 @@ export async function requestUploadParts<TCtx>(params: {
|
|
|
190
208
|
}) {
|
|
191
209
|
const {
|
|
192
210
|
provider,
|
|
193
|
-
router,
|
|
194
211
|
ctxToken,
|
|
195
212
|
body: { multipart, path },
|
|
196
213
|
} = params;
|
|
@@ -201,10 +218,6 @@ export async function requestUploadParts<TCtx>(params: {
|
|
|
201
218
|
});
|
|
202
219
|
}
|
|
203
220
|
await getContext(ctxToken); // just to check if the token is valid
|
|
204
|
-
const bucket = router.buckets[multipart.uploadId];
|
|
205
|
-
if (!bucket) {
|
|
206
|
-
throw new Error(`Bucket ${multipart.uploadId} not found`);
|
|
207
|
-
}
|
|
208
221
|
return await provider.requestUploadParts({
|
|
209
222
|
multipart,
|
|
210
223
|
path,
|
|
@@ -242,7 +255,10 @@ export async function completeMultipartUpload<TCtx>(params: {
|
|
|
242
255
|
await getContext(ctxToken); // just to check if the token is valid
|
|
243
256
|
const bucket = router.buckets[bucketName];
|
|
244
257
|
if (!bucket) {
|
|
245
|
-
throw new
|
|
258
|
+
throw new EdgeStoreError({
|
|
259
|
+
message: `Bucket ${bucketName} not found`,
|
|
260
|
+
code: 'BAD_REQUEST',
|
|
261
|
+
});
|
|
246
262
|
}
|
|
247
263
|
return await provider.completeMultipartUpload({
|
|
248
264
|
uploadId,
|
|
@@ -278,7 +294,10 @@ export async function confirmUpload<TCtx>(params: {
|
|
|
278
294
|
await getContext(ctxToken); // just to check if the token is valid
|
|
279
295
|
const bucket = router.buckets[bucketName];
|
|
280
296
|
if (!bucket) {
|
|
281
|
-
throw new
|
|
297
|
+
throw new EdgeStoreError({
|
|
298
|
+
message: `Bucket ${bucketName} not found`,
|
|
299
|
+
code: 'BAD_REQUEST',
|
|
300
|
+
});
|
|
282
301
|
}
|
|
283
302
|
|
|
284
303
|
return await provider.confirmUpload({
|
|
@@ -314,24 +333,33 @@ export async function deleteFile<TCtx>(params: {
|
|
|
314
333
|
const ctx = await getContext(ctxToken);
|
|
315
334
|
const bucket = router.buckets[bucketName];
|
|
316
335
|
if (!bucket) {
|
|
317
|
-
throw new
|
|
336
|
+
throw new EdgeStoreError({
|
|
337
|
+
message: `Bucket ${bucketName} not found`,
|
|
338
|
+
code: 'BAD_REQUEST',
|
|
339
|
+
});
|
|
318
340
|
}
|
|
319
341
|
|
|
320
342
|
if (!bucket._def.beforeDelete) {
|
|
321
|
-
throw new
|
|
322
|
-
|
|
323
|
-
|
|
343
|
+
throw new EdgeStoreError({
|
|
344
|
+
message:
|
|
345
|
+
'You need to define beforeDelete if you want to delete files directly from the frontend.',
|
|
346
|
+
code: 'SERVER_ERROR',
|
|
347
|
+
});
|
|
324
348
|
}
|
|
325
349
|
|
|
326
350
|
const fileInfo = await provider.getFile({
|
|
327
351
|
url,
|
|
328
352
|
});
|
|
353
|
+
|
|
329
354
|
const canDelete = await bucket._def.beforeDelete({
|
|
330
355
|
ctx,
|
|
331
356
|
fileInfo,
|
|
332
357
|
});
|
|
333
358
|
if (!canDelete) {
|
|
334
|
-
throw new
|
|
359
|
+
throw new EdgeStoreError({
|
|
360
|
+
message: 'Delete not allowed for the current context',
|
|
361
|
+
code: 'DELETE_NOT_ALLOWED',
|
|
362
|
+
});
|
|
335
363
|
}
|
|
336
364
|
return await provider.deleteFile({
|
|
337
365
|
bucket,
|
|
@@ -343,9 +371,10 @@ async function encryptJWT(ctx: any) {
|
|
|
343
371
|
const secret =
|
|
344
372
|
process.env.EDGE_STORE_JWT_SECRET ?? process.env.EDGE_STORE_SECRET_KEY;
|
|
345
373
|
if (!secret) {
|
|
346
|
-
throw new
|
|
347
|
-
'EDGE_STORE_JWT_SECRET or EDGE_STORE_SECRET_KEY is not defined',
|
|
348
|
-
|
|
374
|
+
throw new EdgeStoreError({
|
|
375
|
+
message: 'EDGE_STORE_JWT_SECRET or EDGE_STORE_SECRET_KEY is not defined',
|
|
376
|
+
code: 'SERVER_ERROR',
|
|
377
|
+
});
|
|
349
378
|
}
|
|
350
379
|
const encryptionSecret = await getDerivedEncryptionKey(secret);
|
|
351
380
|
return await new EncryptJWT(ctx)
|
|
@@ -360,9 +389,10 @@ async function decryptJWT(token: string) {
|
|
|
360
389
|
const secret =
|
|
361
390
|
process.env.EDGE_STORE_JWT_SECRET ?? process.env.EDGE_STORE_SECRET_KEY;
|
|
362
391
|
if (!secret) {
|
|
363
|
-
throw new
|
|
364
|
-
'EDGE_STORE_JWT_SECRET or EDGE_STORE_SECRET_KEY is not
|
|
365
|
-
|
|
392
|
+
throw new EdgeStoreError({
|
|
393
|
+
message: 'EDGE_STORE_JWT_SECRET or EDGE_STORE_SECRET_KEY is not defined',
|
|
394
|
+
code: 'SERVER_ERROR',
|
|
395
|
+
});
|
|
366
396
|
}
|
|
367
397
|
const encryptionSecret = await getDerivedEncryptionKey(secret);
|
|
368
398
|
const { payload } = await jwtDecrypt(token, encryptionSecret, {
|
|
@@ -394,7 +424,10 @@ export function buildPath(params: {
|
|
|
394
424
|
const path = pathParams.map((param) => {
|
|
395
425
|
const paramEntries = Object.entries(param);
|
|
396
426
|
if (paramEntries[0] === undefined) {
|
|
397
|
-
throw new
|
|
427
|
+
throw new EdgeStoreError({
|
|
428
|
+
message: `Empty path param found in: ${JSON.stringify(pathParams)}`,
|
|
429
|
+
code: 'SERVER_ERROR',
|
|
430
|
+
});
|
|
398
431
|
}
|
|
399
432
|
const [key, value] = paramEntries[0];
|
|
400
433
|
// this is a string like: "ctx.xxx" or "input.yyy.zzz"
|
|
@@ -402,7 +435,10 @@ export function buildPath(params: {
|
|
|
402
435
|
.split('.')
|
|
403
436
|
.reduce((acc2: any, key: string) => {
|
|
404
437
|
if (acc2[key] === undefined) {
|
|
405
|
-
throw new
|
|
438
|
+
throw new EdgeStoreError({
|
|
439
|
+
message: `Missing key ${key} in ${JSON.stringify(acc2)}`,
|
|
440
|
+
code: 'BAD_REQUEST',
|
|
441
|
+
});
|
|
406
442
|
}
|
|
407
443
|
return acc2[key];
|
|
408
444
|
}, params.pathAttrs as any) as string;
|
|
@@ -426,10 +462,7 @@ export function parsePath(path: { key: string; value: string }[]) {
|
|
|
426
462
|
};
|
|
427
463
|
}
|
|
428
464
|
|
|
429
|
-
async function getContext(token
|
|
430
|
-
if (!token) {
|
|
431
|
-
throw new Error('No token');
|
|
432
|
-
}
|
|
465
|
+
async function getContext(token: string) {
|
|
433
466
|
return await decryptJWT(token);
|
|
434
467
|
}
|
|
435
468
|
|