@lobehub/chat 1.138.4 → 1.138.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/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/src/app/(backend)/trpc/edge/[trpc]/route.ts +0 -2
- package/src/server/routers/edge/index.ts +2 -1
- package/src/server/routers/lambda/index.ts +2 -0
- package/src/server/routers/lambda/upload.ts +16 -0
- package/src/services/__tests__/upload.test.ts +266 -18
- package/src/services/upload.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
### [Version 1.138.5](https://github.com/lobehub/lobe-chat/compare/v1.138.4...v1.138.5)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2025-10-18**</sup>
|
|
8
|
+
|
|
9
|
+
#### ♻ Code Refactoring
|
|
10
|
+
|
|
11
|
+
- **misc**: Refactor upload router into lambda and decide to remove it in V2.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### Code refactoring
|
|
19
|
+
|
|
20
|
+
- **misc**: Refactor upload router into lambda and decide to remove it in V2, closes [#9766](https://github.com/lobehub/lobe-chat/issues/9766) ([d1c7f41](https://github.com/lobehub/lobe-chat/commit/d1c7f41))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
5
30
|
### [Version 1.138.4](https://github.com/lobehub/lobe-chat/compare/v1.138.3...v1.138.4)
|
|
6
31
|
|
|
7
32
|
<sup>Released on **2025-10-18**</sup>
|
package/changelog/v1.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "1.138.
|
|
3
|
+
"version": "1.138.5",
|
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -28,6 +28,7 @@ import { sessionRouter } from './session';
|
|
|
28
28
|
import { sessionGroupRouter } from './sessionGroup';
|
|
29
29
|
import { threadRouter } from './thread';
|
|
30
30
|
import { topicRouter } from './topic';
|
|
31
|
+
import { uploadRouter } from './upload';
|
|
31
32
|
import { userRouter } from './user';
|
|
32
33
|
|
|
33
34
|
export const lambdaRouter = router({
|
|
@@ -57,6 +58,7 @@ export const lambdaRouter = router({
|
|
|
57
58
|
sessionGroup: sessionGroupRouter,
|
|
58
59
|
thread: threadRouter,
|
|
59
60
|
topic: topicRouter,
|
|
61
|
+
upload: uploadRouter,
|
|
60
62
|
user: userRouter,
|
|
61
63
|
});
|
|
62
64
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
import { authedProcedure, router } from '@/libs/trpc/lambda';
|
|
4
|
+
import { S3 } from '@/server/modules/S3';
|
|
5
|
+
|
|
6
|
+
export const uploadRouter = router({
|
|
7
|
+
createS3PreSignedUrl: authedProcedure
|
|
8
|
+
.input(z.object({ pathname: z.string() }))
|
|
9
|
+
.mutation(async ({ input }) => {
|
|
10
|
+
const s3 = new S3();
|
|
11
|
+
|
|
12
|
+
return await s3.createPreSignedUrl(input.pathname);
|
|
13
|
+
}),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export type FileRouter = typeof uploadRouter;
|
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
2
|
|
|
3
3
|
import { fileEnv } from '@/envs/file';
|
|
4
|
-
import {
|
|
4
|
+
import { lambdaClient } from '@/libs/trpc/client';
|
|
5
5
|
import { API_ENDPOINTS } from '@/services/_url';
|
|
6
6
|
import { clientS3Storage } from '@/services/file/ClientS3';
|
|
7
7
|
|
|
8
8
|
import { UPLOAD_NETWORK_ERROR, uploadService } from '../upload';
|
|
9
9
|
|
|
10
10
|
// Mock dependencies
|
|
11
|
+
vi.mock('@lobechat/const', () => ({
|
|
12
|
+
isDesktop: false,
|
|
13
|
+
isServerMode: false,
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
vi.mock('@lobechat/model-runtime', () => ({
|
|
17
|
+
parseDataUri: vi.fn(),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
vi.mock('@lobechat/utils', () => ({
|
|
21
|
+
uuid: () => 'mock-uuid',
|
|
22
|
+
}));
|
|
23
|
+
|
|
11
24
|
vi.mock('@/libs/trpc/client', () => ({
|
|
12
|
-
|
|
25
|
+
lambdaClient: {
|
|
13
26
|
upload: {
|
|
14
27
|
createS3PreSignedUrl: {
|
|
15
28
|
mutate: vi.fn(),
|
|
@@ -24,8 +37,24 @@ vi.mock('@/services/file/ClientS3', () => ({
|
|
|
24
37
|
},
|
|
25
38
|
}));
|
|
26
39
|
|
|
27
|
-
vi.mock('@/
|
|
28
|
-
|
|
40
|
+
vi.mock('@/store/electron', () => ({
|
|
41
|
+
getElectronStoreState: vi.fn(() => ({})),
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
vi.mock('@/store/electron/selectors', () => ({
|
|
45
|
+
electronSyncSelectors: {
|
|
46
|
+
isSyncActive: vi.fn(() => false),
|
|
47
|
+
},
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
vi.mock('@/services/electron/file', () => ({
|
|
51
|
+
desktopFileAPI: {
|
|
52
|
+
uploadFile: vi.fn(),
|
|
53
|
+
},
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
vi.mock('js-sha256', () => ({
|
|
57
|
+
sha256: vi.fn((data) => 'mock-hash-' + data.byteLength),
|
|
29
58
|
}));
|
|
30
59
|
|
|
31
60
|
describe('UploadService', () => {
|
|
@@ -38,23 +67,174 @@ describe('UploadService', () => {
|
|
|
38
67
|
vi.spyOn(Date, 'now').mockImplementation(() => 3600000); // 1 hour in milliseconds
|
|
39
68
|
});
|
|
40
69
|
|
|
41
|
-
describe('
|
|
70
|
+
describe('uploadFileToS3', () => {
|
|
71
|
+
it('should upload to client S3 for non-server mode with image file', async () => {
|
|
72
|
+
const { sha256 } = await import('js-sha256');
|
|
73
|
+
vi.mocked(sha256).mockReturnValue('test-hash');
|
|
74
|
+
vi.mocked(clientS3Storage.putObject).mockResolvedValue(undefined);
|
|
75
|
+
|
|
76
|
+
const result = await uploadService.uploadFileToS3(mockFile, {});
|
|
77
|
+
|
|
78
|
+
expect(result.success).toBe(true);
|
|
79
|
+
expect(result.data).toEqual({
|
|
80
|
+
date: '1',
|
|
81
|
+
dirname: '',
|
|
82
|
+
filename: mockFile.name,
|
|
83
|
+
path: 'client-s3://test-hash',
|
|
84
|
+
});
|
|
85
|
+
expect(clientS3Storage.putObject).toHaveBeenCalledWith('test-hash', mockFile);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should call onNotSupported for non-image/video files', async () => {
|
|
89
|
+
const nonImageFile = new File(['test'], 'test.txt', { type: 'text/plain' });
|
|
90
|
+
const onNotSupported = vi.fn();
|
|
91
|
+
|
|
92
|
+
const result = await uploadService.uploadFileToS3(nonImageFile, {
|
|
93
|
+
onNotSupported,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
expect(result.success).toBe(false);
|
|
97
|
+
expect(onNotSupported).toHaveBeenCalled();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should skip file type check when skipCheckFileType is true', async () => {
|
|
101
|
+
const nonImageFile = new File(['test'], 'test.txt', { type: 'text/plain' });
|
|
102
|
+
const { sha256 } = await import('js-sha256');
|
|
103
|
+
vi.mocked(sha256).mockReturnValue('test-hash');
|
|
104
|
+
vi.mocked(clientS3Storage.putObject).mockResolvedValue(undefined);
|
|
105
|
+
|
|
106
|
+
const result = await uploadService.uploadFileToS3(nonImageFile, {
|
|
107
|
+
skipCheckFileType: true,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
expect(result.success).toBe(true);
|
|
111
|
+
expect(clientS3Storage.putObject).toHaveBeenCalled();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should upload video files', async () => {
|
|
115
|
+
const videoFile = new File(['test'], 'test.mp4', { type: 'video/mp4' });
|
|
116
|
+
const { sha256 } = await import('js-sha256');
|
|
117
|
+
vi.mocked(sha256).mockReturnValue('video-hash');
|
|
118
|
+
vi.mocked(clientS3Storage.putObject).mockResolvedValue(undefined);
|
|
119
|
+
|
|
120
|
+
const result = await uploadService.uploadFileToS3(videoFile, {});
|
|
121
|
+
|
|
122
|
+
expect(result.success).toBe(true);
|
|
123
|
+
expect(clientS3Storage.putObject).toHaveBeenCalledWith('video-hash', videoFile);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('uploadBase64ToS3', () => {
|
|
128
|
+
it('should upload base64 data successfully', async () => {
|
|
129
|
+
const { parseDataUri } = await import('@lobechat/model-runtime');
|
|
130
|
+
vi.mocked(parseDataUri).mockReturnValueOnce({
|
|
131
|
+
base64: 'dGVzdA==', // "test" in base64
|
|
132
|
+
mimeType: 'image/png',
|
|
133
|
+
type: 'base64',
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const { sha256 } = await import('js-sha256');
|
|
137
|
+
vi.mocked(sha256).mockReturnValue('base64-hash');
|
|
138
|
+
vi.mocked(clientS3Storage.putObject).mockResolvedValue(undefined);
|
|
139
|
+
|
|
140
|
+
const base64Data = 'data:image/png;base64,dGVzdA==';
|
|
141
|
+
const result = await uploadService.uploadBase64ToS3(base64Data);
|
|
142
|
+
|
|
143
|
+
expect(result).toMatchObject({
|
|
144
|
+
fileType: 'image/png',
|
|
145
|
+
hash: expect.any(String),
|
|
146
|
+
metadata: expect.objectContaining({
|
|
147
|
+
path: expect.stringContaining('client-s3://'),
|
|
148
|
+
}),
|
|
149
|
+
size: expect.any(Number),
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should throw error for invalid base64 data', async () => {
|
|
154
|
+
const { parseDataUri } = await import('@lobechat/model-runtime');
|
|
155
|
+
vi.mocked(parseDataUri).mockReturnValueOnce({
|
|
156
|
+
base64: null,
|
|
157
|
+
mimeType: null,
|
|
158
|
+
type: 'url',
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const invalidBase64 = 'not-a-base64-string';
|
|
162
|
+
|
|
163
|
+
await expect(uploadService.uploadBase64ToS3(invalidBase64)).rejects.toThrow(
|
|
164
|
+
'Invalid base64 data for image',
|
|
165
|
+
);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should use custom filename when provided', async () => {
|
|
169
|
+
const { parseDataUri } = await import('@lobechat/model-runtime');
|
|
170
|
+
vi.mocked(parseDataUri).mockReturnValueOnce({
|
|
171
|
+
base64: 'dGVzdA==',
|
|
172
|
+
mimeType: 'image/png',
|
|
173
|
+
type: 'base64',
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const { sha256 } = await import('js-sha256');
|
|
177
|
+
vi.mocked(sha256).mockReturnValue('custom-hash');
|
|
178
|
+
vi.mocked(clientS3Storage.putObject).mockResolvedValue(undefined);
|
|
179
|
+
|
|
180
|
+
const base64Data = 'data:image/png;base64,dGVzdA==';
|
|
181
|
+
const result = await uploadService.uploadBase64ToS3(base64Data, {
|
|
182
|
+
filename: 'custom-image',
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
expect(result.metadata.filename).toContain('custom-image');
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe('uploadDataToS3', () => {
|
|
190
|
+
it('should upload JSON data successfully', async () => {
|
|
191
|
+
const { sha256 } = await import('js-sha256');
|
|
192
|
+
vi.mocked(sha256).mockReturnValue('json-hash');
|
|
193
|
+
vi.mocked(clientS3Storage.putObject).mockResolvedValue(undefined);
|
|
194
|
+
|
|
195
|
+
const data = { key: 'value', number: 123 };
|
|
196
|
+
// uploadDataToS3 internally calls uploadFileToS3, which needs skipCheckFileType for JSON
|
|
197
|
+
const result = await uploadService.uploadDataToS3(data, {
|
|
198
|
+
skipCheckFileType: true,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
expect(result.success).toBe(true);
|
|
202
|
+
expect(clientS3Storage.putObject).toHaveBeenCalled();
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('should use custom filename when provided', async () => {
|
|
206
|
+
const { sha256 } = await import('js-sha256');
|
|
207
|
+
vi.mocked(sha256).mockReturnValue('custom-json-hash');
|
|
208
|
+
vi.mocked(clientS3Storage.putObject).mockResolvedValue(undefined);
|
|
209
|
+
|
|
210
|
+
const data = { test: true };
|
|
211
|
+
const result = await uploadService.uploadDataToS3(data, {
|
|
212
|
+
filename: 'custom.json',
|
|
213
|
+
skipCheckFileType: true,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
expect(result.success).toBe(true);
|
|
217
|
+
expect(result.data.filename).toBe('custom.json');
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
describe('uploadToServerS3', () => {
|
|
42
222
|
beforeEach(() => {
|
|
43
223
|
// Mock XMLHttpRequest
|
|
44
224
|
const xhrMock = {
|
|
45
|
-
|
|
46
|
-
addEventListener: vi.fn(),
|
|
47
|
-
},
|
|
225
|
+
addEventListener: vi.fn(),
|
|
48
226
|
open: vi.fn(),
|
|
49
227
|
send: vi.fn(),
|
|
50
228
|
setRequestHeader: vi.fn(),
|
|
51
|
-
addEventListener: vi.fn(),
|
|
52
229
|
status: 200,
|
|
230
|
+
upload: {
|
|
231
|
+
addEventListener: vi.fn(),
|
|
232
|
+
},
|
|
53
233
|
};
|
|
54
234
|
global.XMLHttpRequest = vi.fn(() => xhrMock) as any;
|
|
55
235
|
|
|
56
236
|
// Mock createS3PreSignedUrl
|
|
57
|
-
(
|
|
237
|
+
vi.mocked(lambdaClient.upload.createS3PreSignedUrl.mutate).mockResolvedValue(mockPreSignUrl);
|
|
58
238
|
});
|
|
59
239
|
|
|
60
240
|
it('should upload file successfully with progress', async () => {
|
|
@@ -64,7 +244,7 @@ describe('UploadService', () => {
|
|
|
64
244
|
// Simulate successful upload
|
|
65
245
|
vi.spyOn(xhr, 'addEventListener').mockImplementation((event, handler) => {
|
|
66
246
|
if (event === 'load') {
|
|
67
|
-
// @ts-
|
|
247
|
+
// @ts-expect-error - mock implementation
|
|
68
248
|
handler({ target: { status: 200 } });
|
|
69
249
|
}
|
|
70
250
|
});
|
|
@@ -79,6 +259,41 @@ describe('UploadService', () => {
|
|
|
79
259
|
});
|
|
80
260
|
});
|
|
81
261
|
|
|
262
|
+
it('should report progress during upload', async () => {
|
|
263
|
+
const onProgress = vi.fn();
|
|
264
|
+
const xhr = new XMLHttpRequest();
|
|
265
|
+
|
|
266
|
+
// Simulate progress events
|
|
267
|
+
vi.spyOn(xhr.upload, 'addEventListener').mockImplementation((event, handler) => {
|
|
268
|
+
if (event === 'progress') {
|
|
269
|
+
// @ts-expect-error - mock implementation
|
|
270
|
+
handler({
|
|
271
|
+
lengthComputable: true,
|
|
272
|
+
loaded: 500,
|
|
273
|
+
total: 1000,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
vi.spyOn(xhr, 'addEventListener').mockImplementation((event, handler) => {
|
|
279
|
+
if (event === 'load') {
|
|
280
|
+
// @ts-expect-error - mock implementation
|
|
281
|
+
handler({ target: { status: 200 } });
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
await uploadService.uploadToServerS3(mockFile, { onProgress });
|
|
286
|
+
|
|
287
|
+
expect(onProgress).toHaveBeenCalledWith(
|
|
288
|
+
'uploading',
|
|
289
|
+
expect.objectContaining({
|
|
290
|
+
progress: expect.any(Number),
|
|
291
|
+
restTime: expect.any(Number),
|
|
292
|
+
speed: expect.any(Number),
|
|
293
|
+
}),
|
|
294
|
+
);
|
|
295
|
+
});
|
|
296
|
+
|
|
82
297
|
it('should handle network error', async () => {
|
|
83
298
|
const xhr = new XMLHttpRequest();
|
|
84
299
|
|
|
@@ -86,7 +301,7 @@ describe('UploadService', () => {
|
|
|
86
301
|
vi.spyOn(xhr, 'addEventListener').mockImplementation((event, handler) => {
|
|
87
302
|
if (event === 'error') {
|
|
88
303
|
Object.assign(xhr, { status: 0 });
|
|
89
|
-
// @ts-
|
|
304
|
+
// @ts-expect-error - mock implementation
|
|
90
305
|
handler({});
|
|
91
306
|
}
|
|
92
307
|
});
|
|
@@ -102,13 +317,46 @@ describe('UploadService', () => {
|
|
|
102
317
|
if (event === 'load') {
|
|
103
318
|
Object.assign(xhr, { status: 400, statusText: 'Bad Request' });
|
|
104
319
|
|
|
105
|
-
// @ts-
|
|
320
|
+
// @ts-expect-error - mock implementation
|
|
106
321
|
handler({});
|
|
107
322
|
}
|
|
108
323
|
});
|
|
109
324
|
|
|
110
325
|
await expect(uploadService.uploadToServerS3(mockFile, {})).rejects.toBe('Bad Request');
|
|
111
326
|
});
|
|
327
|
+
|
|
328
|
+
it('should use custom directory when provided', async () => {
|
|
329
|
+
const xhr = new XMLHttpRequest();
|
|
330
|
+
vi.spyOn(xhr, 'addEventListener').mockImplementation((event, handler) => {
|
|
331
|
+
if (event === 'load') {
|
|
332
|
+
// @ts-expect-error - mock implementation
|
|
333
|
+
handler({ target: { status: 200 } });
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
const result = await uploadService.uploadToServerS3(mockFile, {
|
|
338
|
+
directory: 'custom/dir',
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
expect(result.dirname).toContain('custom/dir');
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it('should use custom pathname when provided', async () => {
|
|
345
|
+
const xhr = new XMLHttpRequest();
|
|
346
|
+
vi.spyOn(xhr, 'addEventListener').mockImplementation((event, handler) => {
|
|
347
|
+
if (event === 'load') {
|
|
348
|
+
// @ts-expect-error - mock implementation
|
|
349
|
+
handler({ target: { status: 200 } });
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
const customPath = 'custom/path/file.png';
|
|
354
|
+
const result = await uploadService.uploadToServerS3(mockFile, {
|
|
355
|
+
pathname: customPath,
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
expect(result.path).toBe(customPath);
|
|
359
|
+
});
|
|
112
360
|
});
|
|
113
361
|
|
|
114
362
|
describe('uploadToClientS3', () => {
|
|
@@ -121,7 +369,7 @@ describe('UploadService', () => {
|
|
|
121
369
|
path: `client-s3://${hash}`,
|
|
122
370
|
};
|
|
123
371
|
|
|
124
|
-
(clientS3Storage.putObject
|
|
372
|
+
vi.mocked(clientS3Storage.putObject).mockResolvedValue(undefined);
|
|
125
373
|
|
|
126
374
|
const result = await uploadService['uploadToClientS3'](hash, mockFile);
|
|
127
375
|
|
|
@@ -140,9 +388,9 @@ describe('UploadService', () => {
|
|
|
140
388
|
const filename = 'test.png';
|
|
141
389
|
const mockArrayBuffer = new ArrayBuffer(8);
|
|
142
390
|
|
|
143
|
-
(global.fetch
|
|
391
|
+
vi.mocked(global.fetch).mockResolvedValue({
|
|
144
392
|
arrayBuffer: () => Promise.resolve(mockArrayBuffer),
|
|
145
|
-
});
|
|
393
|
+
} as Response);
|
|
146
394
|
|
|
147
395
|
const result = await uploadService.getImageFileByUrlWithCORS(url, filename);
|
|
148
396
|
|
|
@@ -161,9 +409,9 @@ describe('UploadService', () => {
|
|
|
161
409
|
const fileType = 'image/jpeg';
|
|
162
410
|
const mockArrayBuffer = new ArrayBuffer(8);
|
|
163
411
|
|
|
164
|
-
(global.fetch
|
|
412
|
+
vi.mocked(global.fetch).mockResolvedValue({
|
|
165
413
|
arrayBuffer: () => Promise.resolve(mockArrayBuffer),
|
|
166
|
-
});
|
|
414
|
+
} as Response);
|
|
167
415
|
|
|
168
416
|
const result = await uploadService.getImageFileByUrlWithCORS(url, filename, fileType);
|
|
169
417
|
|
package/src/services/upload.ts
CHANGED
|
@@ -5,7 +5,7 @@ import dayjs from 'dayjs';
|
|
|
5
5
|
import { sha256 } from 'js-sha256';
|
|
6
6
|
|
|
7
7
|
import { fileEnv } from '@/envs/file';
|
|
8
|
-
import {
|
|
8
|
+
import { lambdaClient } from '@/libs/trpc/client';
|
|
9
9
|
import { API_ENDPOINTS } from '@/services/_url';
|
|
10
10
|
import { clientS3Storage } from '@/services/file/ClientS3';
|
|
11
11
|
import { FileMetadata, UploadBase64ToS3Result } from '@/types/files';
|
|
@@ -264,7 +264,7 @@ class UploadService {
|
|
|
264
264
|
// 生成文件路径元数据
|
|
265
265
|
const { date, dirname, filename, pathname } = generateFilePathMetadata(file.name, options);
|
|
266
266
|
|
|
267
|
-
const preSignUrl = await
|
|
267
|
+
const preSignUrl = await lambdaClient.upload.createS3PreSignedUrl.mutate({ pathname });
|
|
268
268
|
|
|
269
269
|
return {
|
|
270
270
|
date,
|