@edgestore/server 0.3.1 → 0.3.3
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/start/index.d.ts +18 -0
- package/dist/adapters/start/index.d.ts.map +1 -0
- package/dist/adapters/start/index.js +190 -0
- package/dist/adapters/start/index.mjs +186 -0
- package/dist/providers/aws/index.d.ts +11 -0
- package/dist/providers/aws/index.d.ts.map +1 -1
- package/dist/providers/aws/index.js +5 -3
- package/dist/providers/aws/index.mjs +5 -3
- package/package.json +8 -3
- package/src/adapters/start/index.ts +208 -0
- package/src/providers/aws/index.ts +22 -2
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type EdgeStoreRouter, type MaybePromise, type Provider } from '@edgestore/shared';
|
|
2
|
+
import { type LogLevel } from '../../libs/logger';
|
|
3
|
+
export type CreateContextOptions = {
|
|
4
|
+
req: Request;
|
|
5
|
+
};
|
|
6
|
+
export type Config<TCtx> = {
|
|
7
|
+
provider?: Provider;
|
|
8
|
+
router: EdgeStoreRouter<TCtx>;
|
|
9
|
+
logLevel?: LogLevel;
|
|
10
|
+
} & (TCtx extends Record<string, never> ? object : {
|
|
11
|
+
provider?: Provider;
|
|
12
|
+
router: EdgeStoreRouter<TCtx>;
|
|
13
|
+
createContext: (opts: CreateContextOptions) => MaybePromise<TCtx>;
|
|
14
|
+
});
|
|
15
|
+
export declare function createEdgeStoreStartHandler<TCtx>(config: Config<TCtx>): ({ request }: {
|
|
16
|
+
request: Request;
|
|
17
|
+
}) => Promise<Response>;
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/start/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,QAAQ,EACd,MAAM,mBAAmB,CAAC;AAC3B,OAAe,EAAE,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAiB1D,MAAM,MAAM,oBAAoB,GAAG;IACjC,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,MAAM,CAAC,IAAI,IAAI;IACzB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,MAAM,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,GAAG,CAAC,IAAI,SAAS,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GACnC,MAAM,GACN;IACE,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,MAAM,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC;IAC9B,aAAa,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,YAAY,CAAC,IAAI,CAAC,CAAC;CACnE,CAAC,CAAC;AAsBP,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC;aAM9B,OAAO;wBA4I9C"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var shared = require('@edgestore/shared');
|
|
6
|
+
var utils = require('../../utils-5819d5e1.js');
|
|
7
|
+
var providers_edgestore_index = require('../../providers/edgestore/index.js');
|
|
8
|
+
var shared$1 = require('../../shared-7c700083.js');
|
|
9
|
+
require('../../index-4491caf0.js');
|
|
10
|
+
require('@panva/hkdf');
|
|
11
|
+
require('cookie');
|
|
12
|
+
require('jose');
|
|
13
|
+
require('uuid');
|
|
14
|
+
|
|
15
|
+
// Helper to extract a cookie from the request's cookie header
|
|
16
|
+
function getCookie(req, cookieName) {
|
|
17
|
+
const cookieHeader = req.headers.get('cookie');
|
|
18
|
+
if (!cookieHeader) return undefined;
|
|
19
|
+
return cookieHeader.split(';').map((cookieStr)=>cookieStr.trim()).reduce((acc, cookieStr)=>{
|
|
20
|
+
const [name, ...rest] = cookieStr.split('=');
|
|
21
|
+
if (name && rest.length > 0) {
|
|
22
|
+
acc[name] = rest.join('=');
|
|
23
|
+
}
|
|
24
|
+
return acc;
|
|
25
|
+
}, {})[cookieName];
|
|
26
|
+
}
|
|
27
|
+
function createEdgeStoreStartHandler(config) {
|
|
28
|
+
const { provider = providers_edgestore_index.EdgeStoreProvider() } = config;
|
|
29
|
+
const log = new utils.Logger(config.logLevel);
|
|
30
|
+
globalThis._EDGE_STORE_LOGGER = log;
|
|
31
|
+
log.debug('Creating Edge Store TanStack Start handler');
|
|
32
|
+
return async ({ request })=>{
|
|
33
|
+
try {
|
|
34
|
+
const { pathname } = new URL(request.url);
|
|
35
|
+
if (utils.matchPath(pathname, '/health')) {
|
|
36
|
+
return new Response('OK', {
|
|
37
|
+
status: 200
|
|
38
|
+
});
|
|
39
|
+
} else if (utils.matchPath(pathname, '/init')) {
|
|
40
|
+
let ctx = {};
|
|
41
|
+
try {
|
|
42
|
+
ctx = 'createContext' in config ? await config.createContext({
|
|
43
|
+
req: request
|
|
44
|
+
}) : {};
|
|
45
|
+
} catch (err) {
|
|
46
|
+
throw new shared.EdgeStoreError({
|
|
47
|
+
message: 'Error creating context',
|
|
48
|
+
code: 'CREATE_CONTEXT_ERROR',
|
|
49
|
+
cause: err instanceof Error ? err : undefined
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const { newCookies, token, baseUrl } = await shared$1.init({
|
|
53
|
+
ctx,
|
|
54
|
+
provider,
|
|
55
|
+
router: config.router
|
|
56
|
+
});
|
|
57
|
+
const headers = new Headers();
|
|
58
|
+
newCookies.forEach((cookie)=>headers.append('Set-Cookie', cookie));
|
|
59
|
+
headers.set('Content-Type', 'application/json');
|
|
60
|
+
return new Response(JSON.stringify({
|
|
61
|
+
token,
|
|
62
|
+
baseUrl
|
|
63
|
+
}), {
|
|
64
|
+
status: 200,
|
|
65
|
+
headers
|
|
66
|
+
});
|
|
67
|
+
} else if (utils.matchPath(pathname, '/request-upload')) {
|
|
68
|
+
const body = await request.json();
|
|
69
|
+
const ctxToken = getCookie(request, 'edgestore-ctx');
|
|
70
|
+
const result = await shared$1.requestUpload({
|
|
71
|
+
provider,
|
|
72
|
+
router: config.router,
|
|
73
|
+
body: body,
|
|
74
|
+
ctxToken
|
|
75
|
+
});
|
|
76
|
+
return new Response(JSON.stringify(result), {
|
|
77
|
+
status: 200,
|
|
78
|
+
headers: {
|
|
79
|
+
'Content-Type': 'application/json'
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
} else if (utils.matchPath(pathname, '/request-upload-parts')) {
|
|
83
|
+
const body = await request.json();
|
|
84
|
+
const ctxToken = getCookie(request, 'edgestore-ctx');
|
|
85
|
+
const result = await shared$1.requestUploadParts({
|
|
86
|
+
provider,
|
|
87
|
+
router: config.router,
|
|
88
|
+
body: body,
|
|
89
|
+
ctxToken
|
|
90
|
+
});
|
|
91
|
+
return new Response(JSON.stringify(result), {
|
|
92
|
+
status: 200,
|
|
93
|
+
headers: {
|
|
94
|
+
'Content-Type': 'application/json'
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
} else if (utils.matchPath(pathname, '/complete-multipart-upload')) {
|
|
98
|
+
const body = await request.json();
|
|
99
|
+
const ctxToken = getCookie(request, 'edgestore-ctx');
|
|
100
|
+
await shared$1.completeMultipartUpload({
|
|
101
|
+
provider,
|
|
102
|
+
router: config.router,
|
|
103
|
+
body: body,
|
|
104
|
+
ctxToken
|
|
105
|
+
});
|
|
106
|
+
return new Response(null, {
|
|
107
|
+
status: 200
|
|
108
|
+
});
|
|
109
|
+
} else if (utils.matchPath(pathname, '/confirm-upload')) {
|
|
110
|
+
const body = await request.json();
|
|
111
|
+
const ctxToken = getCookie(request, 'edgestore-ctx');
|
|
112
|
+
const result = await shared$1.confirmUpload({
|
|
113
|
+
provider,
|
|
114
|
+
router: config.router,
|
|
115
|
+
body: body,
|
|
116
|
+
ctxToken
|
|
117
|
+
});
|
|
118
|
+
return new Response(JSON.stringify(result), {
|
|
119
|
+
status: 200,
|
|
120
|
+
headers: {
|
|
121
|
+
'Content-Type': 'application/json'
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
} else if (utils.matchPath(pathname, '/delete-file')) {
|
|
125
|
+
const body = await request.json();
|
|
126
|
+
const ctxToken = getCookie(request, 'edgestore-ctx');
|
|
127
|
+
const result = await shared$1.deleteFile({
|
|
128
|
+
provider,
|
|
129
|
+
router: config.router,
|
|
130
|
+
body: body,
|
|
131
|
+
ctxToken
|
|
132
|
+
});
|
|
133
|
+
return new Response(JSON.stringify(result), {
|
|
134
|
+
status: 200,
|
|
135
|
+
headers: {
|
|
136
|
+
'Content-Type': 'application/json'
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
} else if (utils.matchPath(pathname, '/proxy-file')) {
|
|
140
|
+
const urlParam = new URL(request.url).searchParams.get('url');
|
|
141
|
+
if (typeof urlParam === 'string') {
|
|
142
|
+
const proxyRes = await fetch(urlParam, {
|
|
143
|
+
headers: {
|
|
144
|
+
cookie: request.headers.get('cookie') ?? ''
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
const data = await proxyRes.arrayBuffer();
|
|
148
|
+
const headers = new Headers();
|
|
149
|
+
headers.set('Content-Type', proxyRes.headers.get('Content-Type') ?? 'application/octet-stream');
|
|
150
|
+
return new Response(data, {
|
|
151
|
+
status: proxyRes.status,
|
|
152
|
+
headers
|
|
153
|
+
});
|
|
154
|
+
} else {
|
|
155
|
+
return new Response(null, {
|
|
156
|
+
status: 400
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
return new Response(null, {
|
|
161
|
+
status: 404
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
} catch (err) {
|
|
165
|
+
if (err instanceof shared.EdgeStoreError) {
|
|
166
|
+
log[err.level](err.formattedMessage());
|
|
167
|
+
if (err.cause) log[err.level](err.cause);
|
|
168
|
+
const status = shared.EDGE_STORE_ERROR_CODES[err.code] || 500;
|
|
169
|
+
return new Response(JSON.stringify(err.formattedJson()), {
|
|
170
|
+
status,
|
|
171
|
+
headers: {
|
|
172
|
+
'Content-Type': 'application/json'
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
log.error(err);
|
|
177
|
+
return new Response(JSON.stringify(new shared.EdgeStoreError({
|
|
178
|
+
message: 'Internal server error',
|
|
179
|
+
code: 'SERVER_ERROR'
|
|
180
|
+
}).formattedJson()), {
|
|
181
|
+
status: 500,
|
|
182
|
+
headers: {
|
|
183
|
+
'Content-Type': 'application/json'
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
exports.createEdgeStoreStartHandler = createEdgeStoreStartHandler;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { EdgeStoreError, EDGE_STORE_ERROR_CODES } from '@edgestore/shared';
|
|
2
|
+
import { L as Logger, m as matchPath } from '../../utils-f6f56d38.mjs';
|
|
3
|
+
import { EdgeStoreProvider } from '../../providers/edgestore/index.mjs';
|
|
4
|
+
import { i as init, r as requestUpload, a as requestUploadParts, c as completeMultipartUpload, d as confirmUpload, e as deleteFile } from '../../shared-039276af.mjs';
|
|
5
|
+
import '../../index-28efdacf.mjs';
|
|
6
|
+
import '@panva/hkdf';
|
|
7
|
+
import 'cookie';
|
|
8
|
+
import 'jose';
|
|
9
|
+
import 'uuid';
|
|
10
|
+
|
|
11
|
+
// Helper to extract a cookie from the request's cookie header
|
|
12
|
+
function getCookie(req, cookieName) {
|
|
13
|
+
const cookieHeader = req.headers.get('cookie');
|
|
14
|
+
if (!cookieHeader) return undefined;
|
|
15
|
+
return cookieHeader.split(';').map((cookieStr)=>cookieStr.trim()).reduce((acc, cookieStr)=>{
|
|
16
|
+
const [name, ...rest] = cookieStr.split('=');
|
|
17
|
+
if (name && rest.length > 0) {
|
|
18
|
+
acc[name] = rest.join('=');
|
|
19
|
+
}
|
|
20
|
+
return acc;
|
|
21
|
+
}, {})[cookieName];
|
|
22
|
+
}
|
|
23
|
+
function createEdgeStoreStartHandler(config) {
|
|
24
|
+
const { provider = EdgeStoreProvider() } = config;
|
|
25
|
+
const log = new Logger(config.logLevel);
|
|
26
|
+
globalThis._EDGE_STORE_LOGGER = log;
|
|
27
|
+
log.debug('Creating Edge Store TanStack Start handler');
|
|
28
|
+
return async ({ request })=>{
|
|
29
|
+
try {
|
|
30
|
+
const { pathname } = new URL(request.url);
|
|
31
|
+
if (matchPath(pathname, '/health')) {
|
|
32
|
+
return new Response('OK', {
|
|
33
|
+
status: 200
|
|
34
|
+
});
|
|
35
|
+
} else if (matchPath(pathname, '/init')) {
|
|
36
|
+
let ctx = {};
|
|
37
|
+
try {
|
|
38
|
+
ctx = 'createContext' in config ? await config.createContext({
|
|
39
|
+
req: request
|
|
40
|
+
}) : {};
|
|
41
|
+
} catch (err) {
|
|
42
|
+
throw new EdgeStoreError({
|
|
43
|
+
message: 'Error creating context',
|
|
44
|
+
code: 'CREATE_CONTEXT_ERROR',
|
|
45
|
+
cause: err instanceof Error ? err : undefined
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const { newCookies, token, baseUrl } = await init({
|
|
49
|
+
ctx,
|
|
50
|
+
provider,
|
|
51
|
+
router: config.router
|
|
52
|
+
});
|
|
53
|
+
const headers = new Headers();
|
|
54
|
+
newCookies.forEach((cookie)=>headers.append('Set-Cookie', cookie));
|
|
55
|
+
headers.set('Content-Type', 'application/json');
|
|
56
|
+
return new Response(JSON.stringify({
|
|
57
|
+
token,
|
|
58
|
+
baseUrl
|
|
59
|
+
}), {
|
|
60
|
+
status: 200,
|
|
61
|
+
headers
|
|
62
|
+
});
|
|
63
|
+
} else if (matchPath(pathname, '/request-upload')) {
|
|
64
|
+
const body = await request.json();
|
|
65
|
+
const ctxToken = getCookie(request, 'edgestore-ctx');
|
|
66
|
+
const result = await requestUpload({
|
|
67
|
+
provider,
|
|
68
|
+
router: config.router,
|
|
69
|
+
body: body,
|
|
70
|
+
ctxToken
|
|
71
|
+
});
|
|
72
|
+
return new Response(JSON.stringify(result), {
|
|
73
|
+
status: 200,
|
|
74
|
+
headers: {
|
|
75
|
+
'Content-Type': 'application/json'
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
} else if (matchPath(pathname, '/request-upload-parts')) {
|
|
79
|
+
const body = await request.json();
|
|
80
|
+
const ctxToken = getCookie(request, 'edgestore-ctx');
|
|
81
|
+
const result = await requestUploadParts({
|
|
82
|
+
provider,
|
|
83
|
+
router: config.router,
|
|
84
|
+
body: body,
|
|
85
|
+
ctxToken
|
|
86
|
+
});
|
|
87
|
+
return new Response(JSON.stringify(result), {
|
|
88
|
+
status: 200,
|
|
89
|
+
headers: {
|
|
90
|
+
'Content-Type': 'application/json'
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
} else if (matchPath(pathname, '/complete-multipart-upload')) {
|
|
94
|
+
const body = await request.json();
|
|
95
|
+
const ctxToken = getCookie(request, 'edgestore-ctx');
|
|
96
|
+
await completeMultipartUpload({
|
|
97
|
+
provider,
|
|
98
|
+
router: config.router,
|
|
99
|
+
body: body,
|
|
100
|
+
ctxToken
|
|
101
|
+
});
|
|
102
|
+
return new Response(null, {
|
|
103
|
+
status: 200
|
|
104
|
+
});
|
|
105
|
+
} else if (matchPath(pathname, '/confirm-upload')) {
|
|
106
|
+
const body = await request.json();
|
|
107
|
+
const ctxToken = getCookie(request, 'edgestore-ctx');
|
|
108
|
+
const result = await confirmUpload({
|
|
109
|
+
provider,
|
|
110
|
+
router: config.router,
|
|
111
|
+
body: body,
|
|
112
|
+
ctxToken
|
|
113
|
+
});
|
|
114
|
+
return new Response(JSON.stringify(result), {
|
|
115
|
+
status: 200,
|
|
116
|
+
headers: {
|
|
117
|
+
'Content-Type': 'application/json'
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
} else if (matchPath(pathname, '/delete-file')) {
|
|
121
|
+
const body = await request.json();
|
|
122
|
+
const ctxToken = getCookie(request, 'edgestore-ctx');
|
|
123
|
+
const result = await deleteFile({
|
|
124
|
+
provider,
|
|
125
|
+
router: config.router,
|
|
126
|
+
body: body,
|
|
127
|
+
ctxToken
|
|
128
|
+
});
|
|
129
|
+
return new Response(JSON.stringify(result), {
|
|
130
|
+
status: 200,
|
|
131
|
+
headers: {
|
|
132
|
+
'Content-Type': 'application/json'
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
} else if (matchPath(pathname, '/proxy-file')) {
|
|
136
|
+
const urlParam = new URL(request.url).searchParams.get('url');
|
|
137
|
+
if (typeof urlParam === 'string') {
|
|
138
|
+
const proxyRes = await fetch(urlParam, {
|
|
139
|
+
headers: {
|
|
140
|
+
cookie: request.headers.get('cookie') ?? ''
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
const data = await proxyRes.arrayBuffer();
|
|
144
|
+
const headers = new Headers();
|
|
145
|
+
headers.set('Content-Type', proxyRes.headers.get('Content-Type') ?? 'application/octet-stream');
|
|
146
|
+
return new Response(data, {
|
|
147
|
+
status: proxyRes.status,
|
|
148
|
+
headers
|
|
149
|
+
});
|
|
150
|
+
} else {
|
|
151
|
+
return new Response(null, {
|
|
152
|
+
status: 400
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
return new Response(null, {
|
|
157
|
+
status: 404
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
} catch (err) {
|
|
161
|
+
if (err instanceof EdgeStoreError) {
|
|
162
|
+
log[err.level](err.formattedMessage());
|
|
163
|
+
if (err.cause) log[err.level](err.cause);
|
|
164
|
+
const status = EDGE_STORE_ERROR_CODES[err.code] || 500;
|
|
165
|
+
return new Response(JSON.stringify(err.formattedJson()), {
|
|
166
|
+
status,
|
|
167
|
+
headers: {
|
|
168
|
+
'Content-Type': 'application/json'
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
log.error(err);
|
|
173
|
+
return new Response(JSON.stringify(new EdgeStoreError({
|
|
174
|
+
message: 'Internal server error',
|
|
175
|
+
code: 'SERVER_ERROR'
|
|
176
|
+
}).formattedJson()), {
|
|
177
|
+
status: 500,
|
|
178
|
+
headers: {
|
|
179
|
+
'Content-Type': 'application/json'
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export { createEdgeStoreStartHandler };
|
|
@@ -24,6 +24,17 @@ export type AWSProviderOptions = {
|
|
|
24
24
|
* Can also be set via the `ES_AWS_BUCKET_NAME` environment variable.
|
|
25
25
|
*/
|
|
26
26
|
bucketName?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Custom endpoint for S3-compatible storage providers (e.g., MinIO).
|
|
29
|
+
* Can also be set via the `ES_AWS_ENDPOINT` environment variable.
|
|
30
|
+
*/
|
|
31
|
+
endpoint?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Force path style for S3-compatible storage providers.
|
|
34
|
+
* Can also be set via the `ES_AWS_FORCE_PATH_STYLE` environment variable.
|
|
35
|
+
* Defaults to false for AWS S3, but should be true for most S3-compatible providers.
|
|
36
|
+
*/
|
|
37
|
+
forcePathStyle?: boolean;
|
|
27
38
|
/**
|
|
28
39
|
* Base URL to use for accessing files.
|
|
29
40
|
* Only needed if you are using a custom domain or cloudfront.
|
|
@@ -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;AAGlD,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;;;;;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,
|
|
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;AAGlD,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"}
|
|
@@ -7,15 +7,17 @@ var s3RequestPresigner = require('@aws-sdk/s3-request-presigner');
|
|
|
7
7
|
var uuid = require('uuid');
|
|
8
8
|
|
|
9
9
|
function AWSProvider(options) {
|
|
10
|
-
const { accessKeyId = process.env.ES_AWS_ACCESS_KEY_ID, secretAccessKey = process.env.ES_AWS_SECRET_ACCESS_KEY, region = process.env.ES_AWS_REGION, bucketName = process.env.ES_AWS_BUCKET_NAME } = options ?? {};
|
|
11
|
-
const baseUrl = options?.baseUrl ?? process.env.EDGE_STORE_BASE_URL ?? `https://${bucketName}.s3.${region}.amazonaws.com
|
|
10
|
+
const { accessKeyId = process.env.ES_AWS_ACCESS_KEY_ID, secretAccessKey = process.env.ES_AWS_SECRET_ACCESS_KEY, region = process.env.ES_AWS_REGION, bucketName = process.env.ES_AWS_BUCKET_NAME, endpoint = process.env.ES_AWS_ENDPOINT, forcePathStyle = process.env.ES_AWS_FORCE_PATH_STYLE === 'true' } = options ?? {};
|
|
11
|
+
const baseUrl = options?.baseUrl ?? process.env.EDGE_STORE_BASE_URL ?? (endpoint ? `${endpoint}/${bucketName}` : `https://${bucketName}.s3.${region}.amazonaws.com`);
|
|
12
12
|
const credentials = accessKeyId && secretAccessKey ? {
|
|
13
13
|
accessKeyId,
|
|
14
14
|
secretAccessKey
|
|
15
15
|
} : undefined;
|
|
16
16
|
const s3Client = new clientS3.S3Client({
|
|
17
17
|
region,
|
|
18
|
-
credentials
|
|
18
|
+
credentials,
|
|
19
|
+
endpoint,
|
|
20
|
+
forcePathStyle
|
|
19
21
|
});
|
|
20
22
|
return {
|
|
21
23
|
async init () {
|
|
@@ -3,15 +3,17 @@ import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
|
3
3
|
import { v4 } from 'uuid';
|
|
4
4
|
|
|
5
5
|
function AWSProvider(options) {
|
|
6
|
-
const { accessKeyId = process.env.ES_AWS_ACCESS_KEY_ID, secretAccessKey = process.env.ES_AWS_SECRET_ACCESS_KEY, region = process.env.ES_AWS_REGION, bucketName = process.env.ES_AWS_BUCKET_NAME } = options ?? {};
|
|
7
|
-
const baseUrl = options?.baseUrl ?? process.env.EDGE_STORE_BASE_URL ?? `https://${bucketName}.s3.${region}.amazonaws.com
|
|
6
|
+
const { accessKeyId = process.env.ES_AWS_ACCESS_KEY_ID, secretAccessKey = process.env.ES_AWS_SECRET_ACCESS_KEY, region = process.env.ES_AWS_REGION, bucketName = process.env.ES_AWS_BUCKET_NAME, endpoint = process.env.ES_AWS_ENDPOINT, forcePathStyle = process.env.ES_AWS_FORCE_PATH_STYLE === 'true' } = options ?? {};
|
|
7
|
+
const baseUrl = options?.baseUrl ?? process.env.EDGE_STORE_BASE_URL ?? (endpoint ? `${endpoint}/${bucketName}` : `https://${bucketName}.s3.${region}.amazonaws.com`);
|
|
8
8
|
const credentials = accessKeyId && secretAccessKey ? {
|
|
9
9
|
accessKeyId,
|
|
10
10
|
secretAccessKey
|
|
11
11
|
} : undefined;
|
|
12
12
|
const s3Client = new S3Client({
|
|
13
13
|
region,
|
|
14
|
-
credentials
|
|
14
|
+
credentials,
|
|
15
|
+
endpoint,
|
|
16
|
+
forcePathStyle
|
|
15
17
|
});
|
|
16
18
|
return {
|
|
17
19
|
async init () {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edgestore/server",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
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",
|
|
@@ -52,6 +52,11 @@
|
|
|
52
52
|
"require": "./dist/adapters/next/app/index.js",
|
|
53
53
|
"default": "./dist/adapters/next/app/index.js"
|
|
54
54
|
},
|
|
55
|
+
"./adapters/start": {
|
|
56
|
+
"import": "./dist/adapters/start/index.mjs",
|
|
57
|
+
"require": "./dist/adapters/start/index.js",
|
|
58
|
+
"default": "./dist/adapters/start/index.js"
|
|
59
|
+
},
|
|
55
60
|
"./providers/aws": {
|
|
56
61
|
"import": "./dist/providers/aws/index.mjs",
|
|
57
62
|
"require": "./dist/providers/aws/index.js",
|
|
@@ -84,7 +89,7 @@
|
|
|
84
89
|
},
|
|
85
90
|
"license": "MIT",
|
|
86
91
|
"dependencies": {
|
|
87
|
-
"@edgestore/shared": "0.3.
|
|
92
|
+
"@edgestore/shared": "0.3.3",
|
|
88
93
|
"@panva/hkdf": "^1.0.4",
|
|
89
94
|
"cookie": "^0.5.0",
|
|
90
95
|
"jose": "^4.13.1",
|
|
@@ -123,5 +128,5 @@
|
|
|
123
128
|
"typescript": "^5.1.6",
|
|
124
129
|
"zod": "3.21.4"
|
|
125
130
|
},
|
|
126
|
-
"gitHead": "
|
|
131
|
+
"gitHead": "9bc2c36ee2463f79c26cb39cfc69dc77b7569a21"
|
|
127
132
|
}
|
|
@@ -0,0 +1,208 @@
|
|
|
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 Logger, { type LogLevel } from '../../libs/logger';
|
|
10
|
+
import { matchPath } from '../../libs/utils';
|
|
11
|
+
import { EdgeStoreProvider } from '../../providers/edgestore';
|
|
12
|
+
import {
|
|
13
|
+
completeMultipartUpload,
|
|
14
|
+
confirmUpload,
|
|
15
|
+
deleteFile,
|
|
16
|
+
init,
|
|
17
|
+
requestUpload,
|
|
18
|
+
requestUploadParts,
|
|
19
|
+
type CompleteMultipartUploadBody,
|
|
20
|
+
type ConfirmUploadBody,
|
|
21
|
+
type DeleteFileBody,
|
|
22
|
+
type RequestUploadBody,
|
|
23
|
+
type RequestUploadPartsParams,
|
|
24
|
+
} from '../shared';
|
|
25
|
+
|
|
26
|
+
export type CreateContextOptions = {
|
|
27
|
+
req: Request;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type Config<TCtx> = {
|
|
31
|
+
provider?: Provider;
|
|
32
|
+
router: EdgeStoreRouter<TCtx>;
|
|
33
|
+
logLevel?: LogLevel;
|
|
34
|
+
} & (TCtx extends Record<string, never>
|
|
35
|
+
? object
|
|
36
|
+
: {
|
|
37
|
+
provider?: Provider;
|
|
38
|
+
router: EdgeStoreRouter<TCtx>;
|
|
39
|
+
createContext: (opts: CreateContextOptions) => MaybePromise<TCtx>;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
declare const globalThis: {
|
|
43
|
+
_EDGE_STORE_LOGGER: Logger;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Helper to extract a cookie from the request's cookie header
|
|
47
|
+
function getCookie(req: Request, cookieName: string): string | undefined {
|
|
48
|
+
const cookieHeader = req.headers.get('cookie');
|
|
49
|
+
if (!cookieHeader) return undefined;
|
|
50
|
+
return cookieHeader
|
|
51
|
+
.split(';')
|
|
52
|
+
.map((cookieStr) => cookieStr.trim())
|
|
53
|
+
.reduce((acc: Record<string, string>, cookieStr) => {
|
|
54
|
+
const [name, ...rest] = cookieStr.split('=');
|
|
55
|
+
if (name && rest.length > 0) {
|
|
56
|
+
acc[name] = rest.join('=');
|
|
57
|
+
}
|
|
58
|
+
return acc;
|
|
59
|
+
}, {})[cookieName];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function createEdgeStoreStartHandler<TCtx>(config: Config<TCtx>) {
|
|
63
|
+
const { provider = EdgeStoreProvider() } = config;
|
|
64
|
+
const log = new Logger(config.logLevel);
|
|
65
|
+
globalThis._EDGE_STORE_LOGGER = log;
|
|
66
|
+
log.debug('Creating Edge Store TanStack Start handler');
|
|
67
|
+
|
|
68
|
+
return async ({ request }: { request: Request }) => {
|
|
69
|
+
try {
|
|
70
|
+
const { pathname } = new URL(request.url);
|
|
71
|
+
if (matchPath(pathname, '/health')) {
|
|
72
|
+
return new Response('OK', { status: 200 });
|
|
73
|
+
} else if (matchPath(pathname, '/init')) {
|
|
74
|
+
let ctx = {} as TCtx;
|
|
75
|
+
try {
|
|
76
|
+
ctx =
|
|
77
|
+
'createContext' in config
|
|
78
|
+
? await config.createContext({ req: request })
|
|
79
|
+
: ({} as TCtx);
|
|
80
|
+
} catch (err) {
|
|
81
|
+
throw new EdgeStoreError({
|
|
82
|
+
message: 'Error creating context',
|
|
83
|
+
code: 'CREATE_CONTEXT_ERROR',
|
|
84
|
+
cause: err instanceof Error ? err : undefined,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const { newCookies, token, baseUrl } = await init({
|
|
88
|
+
ctx,
|
|
89
|
+
provider,
|
|
90
|
+
router: config.router,
|
|
91
|
+
});
|
|
92
|
+
const headers = new Headers();
|
|
93
|
+
newCookies.forEach((cookie) => headers.append('Set-Cookie', cookie));
|
|
94
|
+
headers.set('Content-Type', 'application/json');
|
|
95
|
+
return new Response(JSON.stringify({ token, baseUrl }), {
|
|
96
|
+
status: 200,
|
|
97
|
+
headers,
|
|
98
|
+
});
|
|
99
|
+
} else if (matchPath(pathname, '/request-upload')) {
|
|
100
|
+
const body = await request.json();
|
|
101
|
+
const ctxToken = getCookie(request, 'edgestore-ctx');
|
|
102
|
+
const result = await requestUpload({
|
|
103
|
+
provider,
|
|
104
|
+
router: config.router,
|
|
105
|
+
body: body as RequestUploadBody,
|
|
106
|
+
ctxToken,
|
|
107
|
+
});
|
|
108
|
+
return new Response(JSON.stringify(result), {
|
|
109
|
+
status: 200,
|
|
110
|
+
headers: { 'Content-Type': 'application/json' },
|
|
111
|
+
});
|
|
112
|
+
} else if (matchPath(pathname, '/request-upload-parts')) {
|
|
113
|
+
const body = await request.json();
|
|
114
|
+
const ctxToken = getCookie(request, 'edgestore-ctx');
|
|
115
|
+
const result = await requestUploadParts({
|
|
116
|
+
provider,
|
|
117
|
+
router: config.router,
|
|
118
|
+
body: body as RequestUploadPartsParams,
|
|
119
|
+
ctxToken,
|
|
120
|
+
});
|
|
121
|
+
return new Response(JSON.stringify(result), {
|
|
122
|
+
status: 200,
|
|
123
|
+
headers: { 'Content-Type': 'application/json' },
|
|
124
|
+
});
|
|
125
|
+
} else if (matchPath(pathname, '/complete-multipart-upload')) {
|
|
126
|
+
const body = await request.json();
|
|
127
|
+
const ctxToken = getCookie(request, 'edgestore-ctx');
|
|
128
|
+
await completeMultipartUpload({
|
|
129
|
+
provider,
|
|
130
|
+
router: config.router,
|
|
131
|
+
body: body as CompleteMultipartUploadBody,
|
|
132
|
+
ctxToken,
|
|
133
|
+
});
|
|
134
|
+
return new Response(null, { status: 200 });
|
|
135
|
+
} else if (matchPath(pathname, '/confirm-upload')) {
|
|
136
|
+
const body = await request.json();
|
|
137
|
+
const ctxToken = getCookie(request, 'edgestore-ctx');
|
|
138
|
+
const result = await confirmUpload({
|
|
139
|
+
provider,
|
|
140
|
+
router: config.router,
|
|
141
|
+
body: body as ConfirmUploadBody,
|
|
142
|
+
ctxToken,
|
|
143
|
+
});
|
|
144
|
+
return new Response(JSON.stringify(result), {
|
|
145
|
+
status: 200,
|
|
146
|
+
headers: { 'Content-Type': 'application/json' },
|
|
147
|
+
});
|
|
148
|
+
} else if (matchPath(pathname, '/delete-file')) {
|
|
149
|
+
const body = await request.json();
|
|
150
|
+
const ctxToken = getCookie(request, 'edgestore-ctx');
|
|
151
|
+
const result = await deleteFile({
|
|
152
|
+
provider,
|
|
153
|
+
router: config.router,
|
|
154
|
+
body: body as DeleteFileBody,
|
|
155
|
+
ctxToken,
|
|
156
|
+
});
|
|
157
|
+
return new Response(JSON.stringify(result), {
|
|
158
|
+
status: 200,
|
|
159
|
+
headers: { 'Content-Type': 'application/json' },
|
|
160
|
+
});
|
|
161
|
+
} else if (matchPath(pathname, '/proxy-file')) {
|
|
162
|
+
const urlParam = new URL(request.url).searchParams.get('url');
|
|
163
|
+
if (typeof urlParam === 'string') {
|
|
164
|
+
const proxyRes = await fetch(urlParam, {
|
|
165
|
+
headers: {
|
|
166
|
+
cookie: request.headers.get('cookie') ?? '',
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
const data = await proxyRes.arrayBuffer();
|
|
170
|
+
const headers = new Headers();
|
|
171
|
+
headers.set(
|
|
172
|
+
'Content-Type',
|
|
173
|
+
proxyRes.headers.get('Content-Type') ?? 'application/octet-stream',
|
|
174
|
+
);
|
|
175
|
+
return new Response(data, { status: proxyRes.status, headers });
|
|
176
|
+
} else {
|
|
177
|
+
return new Response(null, { status: 400 });
|
|
178
|
+
}
|
|
179
|
+
} else {
|
|
180
|
+
return new Response(null, { status: 404 });
|
|
181
|
+
}
|
|
182
|
+
} catch (err) {
|
|
183
|
+
if (err instanceof EdgeStoreError) {
|
|
184
|
+
log[err.level](err.formattedMessage());
|
|
185
|
+
if (err.cause) log[err.level](err.cause);
|
|
186
|
+
const status =
|
|
187
|
+
EDGE_STORE_ERROR_CODES[err.code as EdgeStoreErrorCodeKey] || 500;
|
|
188
|
+
return new Response(JSON.stringify(err.formattedJson()), {
|
|
189
|
+
status,
|
|
190
|
+
headers: { 'Content-Type': 'application/json' },
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
log.error(err);
|
|
194
|
+
return new Response(
|
|
195
|
+
JSON.stringify(
|
|
196
|
+
new EdgeStoreError({
|
|
197
|
+
message: 'Internal server error',
|
|
198
|
+
code: 'SERVER_ERROR',
|
|
199
|
+
}).formattedJson(),
|
|
200
|
+
),
|
|
201
|
+
{
|
|
202
|
+
status: 500,
|
|
203
|
+
headers: { 'Content-Type': 'application/json' },
|
|
204
|
+
},
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
@@ -33,6 +33,17 @@ export type AWSProviderOptions = {
|
|
|
33
33
|
* Can also be set via the `ES_AWS_BUCKET_NAME` environment variable.
|
|
34
34
|
*/
|
|
35
35
|
bucketName?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Custom endpoint for S3-compatible storage providers (e.g., MinIO).
|
|
38
|
+
* Can also be set via the `ES_AWS_ENDPOINT` environment variable.
|
|
39
|
+
*/
|
|
40
|
+
endpoint?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Force path style for S3-compatible storage providers.
|
|
43
|
+
* Can also be set via the `ES_AWS_FORCE_PATH_STYLE` environment variable.
|
|
44
|
+
* Defaults to false for AWS S3, but should be true for most S3-compatible providers.
|
|
45
|
+
*/
|
|
46
|
+
forcePathStyle?: boolean;
|
|
36
47
|
/**
|
|
37
48
|
* Base URL to use for accessing files.
|
|
38
49
|
* Only needed if you are using a custom domain or cloudfront.
|
|
@@ -55,12 +66,16 @@ export function AWSProvider(options?: AWSProviderOptions): Provider {
|
|
|
55
66
|
secretAccessKey = process.env.ES_AWS_SECRET_ACCESS_KEY,
|
|
56
67
|
region = process.env.ES_AWS_REGION,
|
|
57
68
|
bucketName = process.env.ES_AWS_BUCKET_NAME,
|
|
69
|
+
endpoint = process.env.ES_AWS_ENDPOINT,
|
|
70
|
+
forcePathStyle = process.env.ES_AWS_FORCE_PATH_STYLE === 'true',
|
|
58
71
|
} = options ?? {};
|
|
59
72
|
|
|
60
73
|
const baseUrl =
|
|
61
74
|
options?.baseUrl ??
|
|
62
75
|
process.env.EDGE_STORE_BASE_URL ??
|
|
63
|
-
|
|
76
|
+
(endpoint
|
|
77
|
+
? `${endpoint}/${bucketName}`
|
|
78
|
+
: `https://${bucketName}.s3.${region}.amazonaws.com`);
|
|
64
79
|
|
|
65
80
|
const credentials =
|
|
66
81
|
accessKeyId && secretAccessKey
|
|
@@ -69,7 +84,12 @@ export function AWSProvider(options?: AWSProviderOptions): Provider {
|
|
|
69
84
|
secretAccessKey,
|
|
70
85
|
}
|
|
71
86
|
: undefined;
|
|
72
|
-
const s3Client = new S3Client({
|
|
87
|
+
const s3Client = new S3Client({
|
|
88
|
+
region,
|
|
89
|
+
credentials,
|
|
90
|
+
endpoint,
|
|
91
|
+
forcePathStyle,
|
|
92
|
+
});
|
|
73
93
|
|
|
74
94
|
return {
|
|
75
95
|
async init() {
|