@edgestore/server 0.4.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.
- package/adapters/astro/index.d.ts +1 -0
- package/adapters/astro/index.js +1 -0
- package/adapters/hono/index.d.ts +1 -0
- package/adapters/hono/index.js +1 -0
- package/adapters/remix/index.d.ts +1 -0
- package/adapters/remix/index.js +1 -0
- package/dist/adapters/astro/index.d.ts +14 -0
- package/dist/adapters/astro/index.d.ts.map +1 -0
- package/dist/adapters/astro/index.js +183 -0
- package/dist/adapters/astro/index.mjs +179 -0
- package/dist/adapters/express/index.js +3 -3
- package/dist/adapters/express/index.mjs +3 -3
- package/dist/adapters/fastify/index.js +3 -3
- package/dist/adapters/fastify/index.mjs +3 -3
- package/dist/adapters/hono/index.d.ts +82 -0
- package/dist/adapters/hono/index.d.ts.map +1 -0
- package/dist/adapters/hono/index.js +137 -0
- package/dist/adapters/hono/index.mjs +133 -0
- package/dist/adapters/next/app/index.js +3 -3
- package/dist/adapters/next/app/index.mjs +3 -3
- package/dist/adapters/next/pages/index.js +3 -3
- package/dist/adapters/next/pages/index.mjs +3 -3
- package/dist/adapters/remix/index.d.ts +18 -0
- package/dist/adapters/remix/index.d.ts.map +1 -0
- package/dist/adapters/remix/index.js +158 -0
- package/dist/adapters/remix/index.mjs +154 -0
- package/dist/adapters/shared.d.ts +2 -0
- package/dist/adapters/shared.d.ts.map +1 -1
- package/dist/adapters/start/index.js +3 -3
- package/dist/adapters/start/index.mjs +3 -3
- package/dist/core/index.js +3 -3
- package/dist/core/index.mjs +4 -4
- package/dist/core/sdk/index.d.ts.map +1 -1
- package/dist/{index-a7cc3cd3.mjs → index-2848cb40.mjs} +3 -2
- package/dist/{index-474a21c4.js → index-421c502f.js} +3 -2
- package/dist/{index-b689bf59.js → index-7b259533.js} +5 -4
- package/dist/libs/logger.d.ts +1 -1
- package/dist/libs/logger.d.ts.map +1 -1
- package/dist/providers/aws/index.d.ts +15 -1
- package/dist/providers/aws/index.d.ts.map +1 -1
- package/dist/providers/aws/index.js +28 -8
- package/dist/providers/aws/index.mjs +28 -8
- package/dist/providers/azure/index.d.ts.map +1 -1
- package/dist/providers/azure/index.js +6 -1
- package/dist/providers/azure/index.mjs +6 -1
- package/dist/providers/edgestore/index.d.ts.map +1 -1
- package/dist/providers/edgestore/index.js +13 -6
- package/dist/providers/edgestore/index.mjs +10 -3
- package/dist/{shared-d27101a7.js → shared-25dbfab4.js} +17 -4
- package/dist/{shared-c9442cbb.mjs → shared-4b199b96.mjs} +16 -4
- package/dist/{shared-6f6fd0bd.js → shared-685c8a0c.js} +17 -3
- package/dist/{utils-5819d5e1.js → utils-0aab6e3b.js} +3 -1
- package/dist/{utils-f6f56d38.mjs → utils-7349adab.mjs} +3 -1
- package/dist/{utils-461a2e3b.js → utils-b3d35894.js} +3 -2
- package/package.json +20 -3
- package/src/adapters/astro/index.ts +222 -0
- package/src/adapters/hono/index.ts +195 -0
- package/src/adapters/remix/index.ts +201 -0
- package/src/adapters/shared.ts +20 -3
- package/src/core/client/index.ts +2 -2
- package/src/core/sdk/index.ts +4 -3
- package/src/libs/logger.ts +4 -3
- package/src/providers/aws/index.ts +66 -14
- package/src/providers/azure/index.ts +5 -4
- package/src/providers/edgestore/index.ts +8 -3
- package/adapters/index.d.ts +0 -1
- package/adapters/index.js +0 -1
- package/providers/index.d.ts +0 -1
- package/providers/index.js +0 -1
|
@@ -321,7 +321,7 @@ async function deleteFile(params) {
|
|
|
321
321
|
return res;
|
|
322
322
|
}
|
|
323
323
|
async function encryptJWT(ctx) {
|
|
324
|
-
const secret =
|
|
324
|
+
const secret = getEnv('EDGE_STORE_JWT_SECRET') ?? getEnv('EDGE_STORE_SECRET_KEY');
|
|
325
325
|
if (!secret) {
|
|
326
326
|
throw new shared.EdgeStoreError({
|
|
327
327
|
message: 'EDGE_STORE_JWT_SECRET or EDGE_STORE_SECRET_KEY is not defined',
|
|
@@ -335,7 +335,7 @@ async function encryptJWT(ctx) {
|
|
|
335
335
|
}).setIssuedAt().setExpirationTime(Date.now() / 1000 + DEFAULT_MAX_AGE).setJti(uuid.v4()).encrypt(encryptionSecret);
|
|
336
336
|
}
|
|
337
337
|
async function decryptJWT(token) {
|
|
338
|
-
const secret =
|
|
338
|
+
const secret = getEnv('EDGE_STORE_JWT_SECRET') ?? getEnv('EDGE_STORE_SECRET_KEY');
|
|
339
339
|
if (!secret) {
|
|
340
340
|
throw new shared.EdgeStoreError({
|
|
341
341
|
message: 'EDGE_STORE_JWT_SECRET or EDGE_STORE_SECRET_KEY is not defined',
|
|
@@ -401,7 +401,7 @@ async function getContext(token) {
|
|
|
401
401
|
* This function is used to get the original URL,
|
|
402
402
|
* so that we can delete or confirm the upload.
|
|
403
403
|
*/ function unproxyUrl(url) {
|
|
404
|
-
if (
|
|
404
|
+
if (isDev() && url.startsWith('http://')) {
|
|
405
405
|
// get the url param from the query string
|
|
406
406
|
const urlParam = new URL(url).searchParams.get('url');
|
|
407
407
|
if (urlParam) {
|
|
@@ -410,12 +410,26 @@ async function getContext(token) {
|
|
|
410
410
|
}
|
|
411
411
|
return url;
|
|
412
412
|
}
|
|
413
|
+
function getEnv(key) {
|
|
414
|
+
if (typeof process !== 'undefined' && process.env) {
|
|
415
|
+
// @ts-expect-error - In Vite/Astro, the env variables are available on `import.meta`.
|
|
416
|
+
return process.env[key] ?? undefined?.[key];
|
|
417
|
+
}
|
|
418
|
+
// @ts-expect-error - In Vite/Astro, the env variables are available on `import.meta`.
|
|
419
|
+
return undefined?.[key];
|
|
420
|
+
}
|
|
421
|
+
function isDev() {
|
|
422
|
+
return process?.env?.NODE_ENV === 'development' || // @ts-expect-error - In Vite/Astro, the env variables are available on `import.meta`.
|
|
423
|
+
undefined?.DEV;
|
|
424
|
+
}
|
|
413
425
|
|
|
414
426
|
exports.buildPath = buildPath;
|
|
415
427
|
exports.completeMultipartUpload = completeMultipartUpload;
|
|
416
428
|
exports.confirmUpload = confirmUpload;
|
|
417
429
|
exports.deleteFile = deleteFile;
|
|
430
|
+
exports.getEnv = getEnv;
|
|
418
431
|
exports.init = init;
|
|
432
|
+
exports.isDev = isDev;
|
|
419
433
|
exports.parsePath = parsePath;
|
|
420
434
|
exports.requestUpload = requestUpload;
|
|
421
435
|
exports.requestUploadParts = requestUploadParts;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var shared = require('./shared-685c8a0c.js');
|
|
4
|
+
|
|
3
5
|
/* eslint-disable no-console */ function _define_property(obj, key, value) {
|
|
4
6
|
if (key in obj) {
|
|
5
7
|
Object.defineProperty(obj, key, {
|
|
@@ -46,7 +48,7 @@ class Logger {
|
|
|
46
48
|
}
|
|
47
49
|
constructor(logLevel){
|
|
48
50
|
_define_property(this, "logLevel", void 0);
|
|
49
|
-
this.logLevel = logLevel ?? (
|
|
51
|
+
this.logLevel = logLevel ?? (shared.isDev() ? 'info' : 'error');
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { i as isDev } from './shared-4b199b96.mjs';
|
|
2
|
+
|
|
1
3
|
/* eslint-disable no-console */ function _define_property(obj, key, value) {
|
|
2
4
|
if (key in obj) {
|
|
3
5
|
Object.defineProperty(obj, key, {
|
|
@@ -44,7 +46,7 @@ class Logger {
|
|
|
44
46
|
}
|
|
45
47
|
constructor(logLevel){
|
|
46
48
|
_define_property(this, "logLevel", void 0);
|
|
47
|
-
this.logLevel = logLevel ?? (
|
|
49
|
+
this.logLevel = logLevel ?? (isDev() ? 'info' : 'error');
|
|
48
50
|
}
|
|
49
51
|
}
|
|
50
52
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { i as isDev } from './shared-25dbfab4.js';
|
|
2
|
+
|
|
1
3
|
/* eslint-disable no-console */
|
|
2
4
|
const logLevel = ['debug', 'info', 'warn', 'error', 'none'];
|
|
3
5
|
class Logger {
|
|
4
6
|
constructor(logLevel) {
|
|
5
|
-
this.logLevel =
|
|
6
|
-
logLevel ?? (process.env.NODE_ENV === 'production' ? 'error' : 'info');
|
|
7
|
+
this.logLevel = logLevel ?? (isDev() ? 'info' : 'error');
|
|
7
8
|
}
|
|
8
9
|
shouldLog(level) {
|
|
9
10
|
return logLevel.indexOf(level) >= logLevel.indexOf(this.logLevel);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edgestore/server",
|
|
3
|
-
"version": "0.
|
|
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",
|
|
@@ -37,6 +37,11 @@
|
|
|
37
37
|
"require": "./dist/core/index.js",
|
|
38
38
|
"default": "./dist/core/index.js"
|
|
39
39
|
},
|
|
40
|
+
"./adapters/astro": {
|
|
41
|
+
"import": "./dist/adapters/astro/index.mjs",
|
|
42
|
+
"require": "./dist/adapters/astro/index.js",
|
|
43
|
+
"default": "./dist/adapters/astro/index.js"
|
|
44
|
+
},
|
|
40
45
|
"./adapters/express": {
|
|
41
46
|
"import": "./dist/adapters/express/index.mjs",
|
|
42
47
|
"require": "./dist/adapters/express/index.js",
|
|
@@ -47,6 +52,11 @@
|
|
|
47
52
|
"require": "./dist/adapters/fastify/index.js",
|
|
48
53
|
"default": "./dist/adapters/fastify/index.js"
|
|
49
54
|
},
|
|
55
|
+
"./adapters/hono": {
|
|
56
|
+
"import": "./dist/adapters/hono/index.mjs",
|
|
57
|
+
"require": "./dist/adapters/hono/index.js",
|
|
58
|
+
"default": "./dist/adapters/hono/index.js"
|
|
59
|
+
},
|
|
50
60
|
"./adapters/next/pages": {
|
|
51
61
|
"import": "./dist/adapters/next/pages/index.mjs",
|
|
52
62
|
"require": "./dist/adapters/next/pages/index.js",
|
|
@@ -57,6 +67,11 @@
|
|
|
57
67
|
"require": "./dist/adapters/next/app/index.js",
|
|
58
68
|
"default": "./dist/adapters/next/app/index.js"
|
|
59
69
|
},
|
|
70
|
+
"./adapters/remix": {
|
|
71
|
+
"import": "./dist/adapters/remix/index.mjs",
|
|
72
|
+
"require": "./dist/adapters/remix/index.js",
|
|
73
|
+
"default": "./dist/adapters/remix/index.js"
|
|
74
|
+
},
|
|
60
75
|
"./adapters/start": {
|
|
61
76
|
"import": "./dist/adapters/start/index.mjs",
|
|
62
77
|
"require": "./dist/adapters/start/index.js",
|
|
@@ -94,7 +109,7 @@
|
|
|
94
109
|
},
|
|
95
110
|
"license": "MIT",
|
|
96
111
|
"dependencies": {
|
|
97
|
-
"@edgestore/shared": "0.
|
|
112
|
+
"@edgestore/shared": "0.5.1",
|
|
98
113
|
"@panva/hkdf": "^1.0.4",
|
|
99
114
|
"cookie": "^0.5.0",
|
|
100
115
|
"jose": "^4.13.1",
|
|
@@ -127,12 +142,14 @@
|
|
|
127
142
|
"@types/cookie": "^0.5.1",
|
|
128
143
|
"@types/node": "^20",
|
|
129
144
|
"@types/uuid": "^9.0.1",
|
|
145
|
+
"astro": "^5.7.12",
|
|
130
146
|
"fastify": "^5.3.2",
|
|
147
|
+
"hono": "^4.7.9",
|
|
131
148
|
"next": "^15.2.4",
|
|
132
149
|
"react": "^19.0.0",
|
|
133
150
|
"react-dom": "^19.0.0",
|
|
134
151
|
"typescript": "^5",
|
|
135
152
|
"zod": "3.21.4"
|
|
136
153
|
},
|
|
137
|
-
"gitHead": "
|
|
154
|
+
"gitHead": "0a04ff24dfb62f749cc970794b3d239c75f48fed"
|
|
138
155
|
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EDGE_STORE_ERROR_CODES,
|
|
3
|
+
EdgeStoreError,
|
|
4
|
+
type EdgeStoreErrorCodeKey,
|
|
5
|
+
type EdgeStoreRouter,
|
|
6
|
+
type MaybePromise,
|
|
7
|
+
type Provider,
|
|
8
|
+
} from '@edgestore/shared';
|
|
9
|
+
import type { APIContext } from 'astro';
|
|
10
|
+
import Logger, { type LogLevel } from '../../libs/logger';
|
|
11
|
+
import { matchPath } from '../../libs/utils';
|
|
12
|
+
import { EdgeStoreProvider } from '../../providers/edgestore';
|
|
13
|
+
import {
|
|
14
|
+
completeMultipartUpload,
|
|
15
|
+
confirmUpload,
|
|
16
|
+
deleteFile,
|
|
17
|
+
init,
|
|
18
|
+
requestUpload,
|
|
19
|
+
requestUploadParts,
|
|
20
|
+
type CompleteMultipartUploadBody,
|
|
21
|
+
type ConfirmUploadBody,
|
|
22
|
+
type DeleteFileBody,
|
|
23
|
+
type RequestUploadBody,
|
|
24
|
+
type RequestUploadPartsParams,
|
|
25
|
+
} from '../shared';
|
|
26
|
+
|
|
27
|
+
export type Config<TCtx> = {
|
|
28
|
+
provider?: Provider;
|
|
29
|
+
router: EdgeStoreRouter<TCtx>;
|
|
30
|
+
logLevel?: LogLevel;
|
|
31
|
+
} & (TCtx extends Record<string, never>
|
|
32
|
+
? object
|
|
33
|
+
: {
|
|
34
|
+
provider?: Provider;
|
|
35
|
+
router: EdgeStoreRouter<TCtx>;
|
|
36
|
+
createContext: (opts: APIContext) => MaybePromise<TCtx>;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
declare const globalThis: {
|
|
40
|
+
_EDGE_STORE_LOGGER: Logger;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Helper to safely get cookies from Astro request
|
|
44
|
+
function getCookie(request: Request, name: string): string | undefined {
|
|
45
|
+
const cookieHeader = request.headers.get('cookie');
|
|
46
|
+
if (!cookieHeader) return undefined;
|
|
47
|
+
|
|
48
|
+
const cookies = cookieHeader
|
|
49
|
+
.split(';')
|
|
50
|
+
.reduce<Record<string, string>>((acc, cookie) => {
|
|
51
|
+
const [key, value] = cookie.trim().split('=');
|
|
52
|
+
if (key && value) acc[key] = value;
|
|
53
|
+
return acc;
|
|
54
|
+
}, {});
|
|
55
|
+
|
|
56
|
+
return cookies[name];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function createEdgeStoreAstroHandler<TCtx>(config: Config<TCtx>) {
|
|
60
|
+
const { provider = EdgeStoreProvider() } = config;
|
|
61
|
+
const log = new Logger(config.logLevel);
|
|
62
|
+
globalThis._EDGE_STORE_LOGGER = log;
|
|
63
|
+
log.debug('Creating EdgeStore Astro handler');
|
|
64
|
+
|
|
65
|
+
return async (context: APIContext) => {
|
|
66
|
+
try {
|
|
67
|
+
const { request } = context;
|
|
68
|
+
const url = new URL(request.url);
|
|
69
|
+
|
|
70
|
+
if (matchPath(url.pathname, 'health')) {
|
|
71
|
+
return new Response('OK');
|
|
72
|
+
} else if (matchPath(url.pathname, 'init')) {
|
|
73
|
+
let ctx = {} as TCtx;
|
|
74
|
+
try {
|
|
75
|
+
ctx =
|
|
76
|
+
'createContext' in config
|
|
77
|
+
? await config.createContext(context)
|
|
78
|
+
: ({} as TCtx);
|
|
79
|
+
} catch (err) {
|
|
80
|
+
throw new EdgeStoreError({
|
|
81
|
+
message: 'Error creating context',
|
|
82
|
+
code: 'CREATE_CONTEXT_ERROR',
|
|
83
|
+
cause: err instanceof Error ? err : undefined,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const { newCookies, token, baseUrl } = await init({
|
|
87
|
+
ctx,
|
|
88
|
+
provider,
|
|
89
|
+
router: config.router,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const headers = new Headers();
|
|
93
|
+
headers.set('Content-Type', 'application/json');
|
|
94
|
+
|
|
95
|
+
// Set cookies
|
|
96
|
+
if (Array.isArray(newCookies)) {
|
|
97
|
+
for (const cookie of newCookies) {
|
|
98
|
+
headers.append('Set-Cookie', cookie);
|
|
99
|
+
}
|
|
100
|
+
} else if (newCookies) {
|
|
101
|
+
headers.append('Set-Cookie', newCookies);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return new Response(
|
|
105
|
+
JSON.stringify({
|
|
106
|
+
token,
|
|
107
|
+
baseUrl,
|
|
108
|
+
}),
|
|
109
|
+
{ headers },
|
|
110
|
+
);
|
|
111
|
+
} else if (matchPath(url.pathname, 'request-upload')) {
|
|
112
|
+
const body = (await request.json()) as RequestUploadBody;
|
|
113
|
+
const result = await requestUpload({
|
|
114
|
+
provider,
|
|
115
|
+
router: config.router,
|
|
116
|
+
body,
|
|
117
|
+
ctxToken: getCookie(request, 'edgestore-ctx'),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return new Response(JSON.stringify(result), {
|
|
121
|
+
headers: { 'Content-Type': 'application/json' },
|
|
122
|
+
});
|
|
123
|
+
} else if (matchPath(url.pathname, 'request-upload-parts')) {
|
|
124
|
+
const body = (await request.json()) as RequestUploadPartsParams;
|
|
125
|
+
const result = await requestUploadParts({
|
|
126
|
+
provider,
|
|
127
|
+
router: config.router,
|
|
128
|
+
body,
|
|
129
|
+
ctxToken: getCookie(request, 'edgestore-ctx'),
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
return new Response(JSON.stringify(result), {
|
|
133
|
+
headers: { 'Content-Type': 'application/json' },
|
|
134
|
+
});
|
|
135
|
+
} else if (matchPath(url.pathname, 'complete-multipart-upload')) {
|
|
136
|
+
const body = (await request.json()) as CompleteMultipartUploadBody;
|
|
137
|
+
await completeMultipartUpload({
|
|
138
|
+
provider,
|
|
139
|
+
router: config.router,
|
|
140
|
+
body,
|
|
141
|
+
ctxToken: getCookie(request, 'edgestore-ctx'),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
return new Response(null, { status: 200 });
|
|
145
|
+
} else if (matchPath(url.pathname, 'confirm-upload')) {
|
|
146
|
+
const body = (await request.json()) as ConfirmUploadBody;
|
|
147
|
+
const result = await confirmUpload({
|
|
148
|
+
provider,
|
|
149
|
+
router: config.router,
|
|
150
|
+
body,
|
|
151
|
+
ctxToken: getCookie(request, 'edgestore-ctx'),
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
return new Response(JSON.stringify(result), {
|
|
155
|
+
headers: { 'Content-Type': 'application/json' },
|
|
156
|
+
});
|
|
157
|
+
} else if (matchPath(url.pathname, 'delete-file')) {
|
|
158
|
+
const body = (await request.json()) as DeleteFileBody;
|
|
159
|
+
const result = await deleteFile({
|
|
160
|
+
provider,
|
|
161
|
+
router: config.router,
|
|
162
|
+
body,
|
|
163
|
+
ctxToken: getCookie(request, 'edgestore-ctx'),
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
return new Response(JSON.stringify(result), {
|
|
167
|
+
headers: { 'Content-Type': 'application/json' },
|
|
168
|
+
});
|
|
169
|
+
} else if (matchPath(url.pathname, 'proxy-file')) {
|
|
170
|
+
const url = new URL(request.url).searchParams.get('url');
|
|
171
|
+
|
|
172
|
+
if (typeof url === 'string') {
|
|
173
|
+
const cookieHeader = request.headers.get('cookie') ?? '';
|
|
174
|
+
|
|
175
|
+
const proxyRes = await fetch(url, {
|
|
176
|
+
headers: {
|
|
177
|
+
cookie: cookieHeader,
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const data = await proxyRes.arrayBuffer();
|
|
182
|
+
const headers = new Headers();
|
|
183
|
+
headers.set(
|
|
184
|
+
'Content-Type',
|
|
185
|
+
proxyRes.headers.get('Content-Type') ?? 'application/octet-stream',
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
return new Response(data, { headers });
|
|
189
|
+
} else {
|
|
190
|
+
return new Response(null, { status: 400 });
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
return new Response(null, { status: 404 });
|
|
194
|
+
}
|
|
195
|
+
} catch (err) {
|
|
196
|
+
if (err instanceof EdgeStoreError) {
|
|
197
|
+
log[err.level](err.formattedMessage());
|
|
198
|
+
if (err.cause) log[err.level](err.cause);
|
|
199
|
+
|
|
200
|
+
return new Response(JSON.stringify(err.formattedJson()), {
|
|
201
|
+
status: EDGE_STORE_ERROR_CODES[err.code as EdgeStoreErrorCodeKey],
|
|
202
|
+
headers: { 'Content-Type': 'application/json' },
|
|
203
|
+
});
|
|
204
|
+
} else {
|
|
205
|
+
log.error(err);
|
|
206
|
+
|
|
207
|
+
return new Response(
|
|
208
|
+
JSON.stringify(
|
|
209
|
+
new EdgeStoreError({
|
|
210
|
+
message: 'Internal Server Error',
|
|
211
|
+
code: 'SERVER_ERROR',
|
|
212
|
+
}).formattedJson(),
|
|
213
|
+
),
|
|
214
|
+
{
|
|
215
|
+
status: 500,
|
|
216
|
+
headers: { 'Content-Type': 'application/json' },
|
|
217
|
+
},
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EDGE_STORE_ERROR_CODES,
|
|
3
|
+
EdgeStoreError,
|
|
4
|
+
type EdgeStoreErrorCodeKey,
|
|
5
|
+
type EdgeStoreRouter,
|
|
6
|
+
type MaybePromise,
|
|
7
|
+
type Provider,
|
|
8
|
+
} from '@edgestore/shared';
|
|
9
|
+
import { type Context } from 'hono';
|
|
10
|
+
import Logger, { type LogLevel } from '../../libs/logger';
|
|
11
|
+
import { matchPath } from '../../libs/utils';
|
|
12
|
+
import { EdgeStoreProvider } from '../../providers/edgestore';
|
|
13
|
+
import {
|
|
14
|
+
completeMultipartUpload,
|
|
15
|
+
confirmUpload,
|
|
16
|
+
deleteFile,
|
|
17
|
+
init,
|
|
18
|
+
requestUpload,
|
|
19
|
+
requestUploadParts,
|
|
20
|
+
type CompleteMultipartUploadBody,
|
|
21
|
+
type ConfirmUploadBody,
|
|
22
|
+
type DeleteFileBody,
|
|
23
|
+
type RequestUploadBody,
|
|
24
|
+
type RequestUploadPartsParams,
|
|
25
|
+
} from '../shared';
|
|
26
|
+
|
|
27
|
+
export type CreateContextOptions = {
|
|
28
|
+
c: Context;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type Config<TCtx> = {
|
|
32
|
+
provider?: Provider;
|
|
33
|
+
router: EdgeStoreRouter<TCtx>;
|
|
34
|
+
logLevel?: LogLevel;
|
|
35
|
+
} & (TCtx extends Record<string, never>
|
|
36
|
+
? object
|
|
37
|
+
: {
|
|
38
|
+
provider?: Provider;
|
|
39
|
+
router: EdgeStoreRouter<TCtx>;
|
|
40
|
+
createContext: (opts: CreateContextOptions) => MaybePromise<TCtx>;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
declare const globalThis: {
|
|
44
|
+
_EDGE_STORE_LOGGER: Logger;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Helper to get a cookie value from Hono Context
|
|
48
|
+
function getCookie(c: Context, name: string): string | undefined {
|
|
49
|
+
const cookies = c.req.header('cookie');
|
|
50
|
+
if (!cookies) return undefined;
|
|
51
|
+
|
|
52
|
+
const match = new RegExp(`${name}=([^;]+)`).exec(cookies);
|
|
53
|
+
return match ? match[1] : undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function createEdgeStoreHonoHandler<TCtx>(config: Config<TCtx>) {
|
|
57
|
+
const { provider = EdgeStoreProvider() } = config;
|
|
58
|
+
const log = new Logger(config.logLevel);
|
|
59
|
+
globalThis._EDGE_STORE_LOGGER = log;
|
|
60
|
+
log.debug('Creating EdgeStore Hono handler');
|
|
61
|
+
|
|
62
|
+
return async (c: Context) => {
|
|
63
|
+
try {
|
|
64
|
+
const pathname = new URL(c.req.url).pathname;
|
|
65
|
+
|
|
66
|
+
if (matchPath(pathname, '/health')) {
|
|
67
|
+
return c.text('OK');
|
|
68
|
+
} else if (matchPath(pathname, '/init')) {
|
|
69
|
+
let ctx = {} as TCtx;
|
|
70
|
+
try {
|
|
71
|
+
ctx =
|
|
72
|
+
'createContext' in config
|
|
73
|
+
? await config.createContext({ c })
|
|
74
|
+
: ({} as TCtx);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
throw new EdgeStoreError({
|
|
77
|
+
message: 'Error creating context',
|
|
78
|
+
code: 'CREATE_CONTEXT_ERROR',
|
|
79
|
+
cause: err instanceof Error ? err : undefined,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const { newCookies, token, baseUrl } = await init({
|
|
83
|
+
ctx,
|
|
84
|
+
provider,
|
|
85
|
+
router: config.router,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Set cookies
|
|
89
|
+
if (Array.isArray(newCookies)) {
|
|
90
|
+
for (const cookie of newCookies) {
|
|
91
|
+
c.header('Set-Cookie', cookie);
|
|
92
|
+
}
|
|
93
|
+
} else if (newCookies) {
|
|
94
|
+
c.header('Set-Cookie', newCookies);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return c.json({
|
|
98
|
+
token,
|
|
99
|
+
baseUrl,
|
|
100
|
+
});
|
|
101
|
+
} else if (matchPath(pathname, '/request-upload')) {
|
|
102
|
+
const body = await c.req.json<RequestUploadBody>();
|
|
103
|
+
return c.json(
|
|
104
|
+
await requestUpload({
|
|
105
|
+
provider,
|
|
106
|
+
router: config.router,
|
|
107
|
+
body,
|
|
108
|
+
ctxToken: getCookie(c, 'edgestore-ctx'),
|
|
109
|
+
}),
|
|
110
|
+
);
|
|
111
|
+
} else if (matchPath(pathname, '/request-upload-parts')) {
|
|
112
|
+
const body = await c.req.json<RequestUploadPartsParams>();
|
|
113
|
+
return c.json(
|
|
114
|
+
await requestUploadParts({
|
|
115
|
+
provider,
|
|
116
|
+
router: config.router,
|
|
117
|
+
body,
|
|
118
|
+
ctxToken: getCookie(c, 'edgestore-ctx'),
|
|
119
|
+
}),
|
|
120
|
+
);
|
|
121
|
+
} else if (matchPath(pathname, '/complete-multipart-upload')) {
|
|
122
|
+
const body = await c.req.json<CompleteMultipartUploadBody>();
|
|
123
|
+
await completeMultipartUpload({
|
|
124
|
+
provider,
|
|
125
|
+
router: config.router,
|
|
126
|
+
body,
|
|
127
|
+
ctxToken: getCookie(c, 'edgestore-ctx'),
|
|
128
|
+
});
|
|
129
|
+
return c.body(null, 200);
|
|
130
|
+
} else if (matchPath(pathname, '/confirm-upload')) {
|
|
131
|
+
const body = await c.req.json<ConfirmUploadBody>();
|
|
132
|
+
return c.json(
|
|
133
|
+
await confirmUpload({
|
|
134
|
+
provider,
|
|
135
|
+
router: config.router,
|
|
136
|
+
body,
|
|
137
|
+
ctxToken: getCookie(c, 'edgestore-ctx'),
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
} else if (matchPath(pathname, '/delete-file')) {
|
|
141
|
+
const body = await c.req.json<DeleteFileBody>();
|
|
142
|
+
return c.json(
|
|
143
|
+
await deleteFile({
|
|
144
|
+
provider,
|
|
145
|
+
router: config.router,
|
|
146
|
+
body,
|
|
147
|
+
ctxToken: getCookie(c, 'edgestore-ctx'),
|
|
148
|
+
}),
|
|
149
|
+
);
|
|
150
|
+
} else if (matchPath(pathname, '/proxy-file')) {
|
|
151
|
+
const url = c.req.query('url');
|
|
152
|
+
|
|
153
|
+
if (typeof url === 'string') {
|
|
154
|
+
const cookieHeader = c.req.header('cookie') ?? '';
|
|
155
|
+
|
|
156
|
+
const proxyRes = await fetch(url, {
|
|
157
|
+
headers: {
|
|
158
|
+
cookie: cookieHeader,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const data = await proxyRes.arrayBuffer();
|
|
163
|
+
c.header(
|
|
164
|
+
'Content-Type',
|
|
165
|
+
proxyRes.headers.get('Content-Type') ?? 'application/octet-stream',
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
return c.body(Buffer.from(data));
|
|
169
|
+
} else {
|
|
170
|
+
return c.body(null, 400);
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
return c.body(null, 404);
|
|
174
|
+
}
|
|
175
|
+
} catch (err) {
|
|
176
|
+
if (err instanceof EdgeStoreError) {
|
|
177
|
+
log[err.level](err.formattedMessage());
|
|
178
|
+
if (err.cause) log[err.level](err.cause);
|
|
179
|
+
return c.json(
|
|
180
|
+
err.formattedJson(),
|
|
181
|
+
EDGE_STORE_ERROR_CODES[err.code as EdgeStoreErrorCodeKey],
|
|
182
|
+
);
|
|
183
|
+
} else {
|
|
184
|
+
log.error(err);
|
|
185
|
+
return c.json(
|
|
186
|
+
new EdgeStoreError({
|
|
187
|
+
message: 'Internal Server Error',
|
|
188
|
+
code: 'SERVER_ERROR',
|
|
189
|
+
}).formattedJson(),
|
|
190
|
+
500,
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|