@lobehub/lobehub 2.0.0-next.144 → 2.0.0-next.146

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.
Files changed (41) hide show
  1. package/.cursor/rules/project-introduce.mdc +1 -1
  2. package/.github/workflows/test.yml +3 -0
  3. package/AGENTS.md +4 -0
  4. package/CHANGELOG.md +50 -0
  5. package/apps/desktop/package.json +2 -0
  6. package/apps/desktop/src/main/controllers/__tests__/UploadFileCtr.test.ts +8 -12
  7. package/apps/desktop/src/main/core/infrastructure/__tests__/ProtocolManager.test.ts +1 -0
  8. package/apps/desktop/src/main/core/ui/__tests__/Tray.test.ts +2 -2
  9. package/apps/desktop/src/main/utils/__tests__/file-system.test.ts +1 -1
  10. package/apps/desktop/src/main/utils/__tests__/logger.test.ts +7 -7
  11. package/apps/desktop/src/main/utils/next-electron-rsc.ts +3 -1
  12. package/apps/desktop/src/preload/invoke.test.ts +4 -2
  13. package/apps/desktop/src/preload/routeInterceptor.test.ts +54 -9
  14. package/apps/desktop/src/preload/streamer.test.ts +32 -31
  15. package/changelog/v1.json +18 -0
  16. package/docs/development/database-schema.dbml +2 -1
  17. package/docs/self-hosting/advanced/auth.mdx +21 -10
  18. package/docs/self-hosting/advanced/auth.zh-CN.mdx +21 -10
  19. package/package.json +4 -3
  20. package/packages/database/migrations/0056_update_agent_slug_index.sql +2 -0
  21. package/packages/database/migrations/meta/0056_snapshot.json +8411 -0
  22. package/packages/database/migrations/meta/_journal.json +7 -0
  23. package/packages/database/src/core/migrations.json +11 -2
  24. package/packages/database/src/schemas/agent.ts +2 -3
  25. package/packages/electron-client-ipc/src/events/system.ts +1 -3
  26. package/packages/electron-client-ipc/src/types/system.ts +1 -0
  27. package/src/envs/email.ts +11 -0
  28. package/src/libs/better-auth/email-templates/magic-link.ts +5 -5
  29. package/src/libs/better-auth/email-templates/reset-password.ts +4 -4
  30. package/src/libs/better-auth/email-templates/verification.ts +4 -4
  31. package/src/libs/mcp/__tests__/__snapshots__/index.test.ts.snap +9 -0
  32. package/src/server/services/email/README.md +19 -0
  33. package/src/server/services/email/impls/index.ts +5 -1
  34. package/src/server/services/email/impls/resend/index.ts +120 -0
  35. package/src/server/services/email/index.test.ts +1 -1
  36. package/src/server/services/email/index.ts +9 -1
  37. package/src/server/services/file/impls/index.ts +3 -3
  38. package/src/server/services/file/impls/local.ts +35 -35
  39. package/src/server/services/file/impls/s3.ts +1 -1
  40. package/src/server/services/file/impls/type.ts +11 -11
  41. package/src/server/services/file/index.ts +12 -12
@@ -12,12 +12,12 @@ import { extractKeyFromUrlOrReturnOriginal } from './utils';
12
12
  const log = debug('lobe-file:desktop-local');
13
13
 
14
14
  /**
15
- * 桌面应用本地文件服务实现
15
+ * Desktop application local file service implementation
16
16
  */
17
17
  export class DesktopLocalFileImpl implements FileServiceImpl {
18
18
  /**
19
- * 获取本地文件的URL
20
- * 通过 IPC 从主进程获取 HTTP URL
19
+ * Get local file URL
20
+ * Retrieve HTTP URL from main process via IPC
21
21
  */
22
22
  private async getLocalFileUrl(key: string): Promise<string> {
23
23
  try {
@@ -29,16 +29,16 @@ export class DesktopLocalFileImpl implements FileServiceImpl {
29
29
  }
30
30
 
31
31
  /**
32
- * 创建预签名上传URL(本地版实际上是直接返回文件路径,可能需要进一步扩展)
32
+ * Create pre-signed upload URL (local version directly returns file path, may need further extension)
33
33
  */
34
34
  async createPreSignedUrl(key: string): Promise<string> {
35
- // 在桌面应用本地文件实现中,不需要预签名URL
36
- // 直接返回文件路径
35
+ // In desktop application local file implementation, pre-signed URL is not needed
36
+ // Directly return the file path
37
37
  return key;
38
38
  }
39
39
 
40
40
  /**
41
- * 创建预签名预览URL(本地版是通过HTTP路径访问本地文件)
41
+ * Create pre-signed preview URL (local version accesses local files via HTTP path)
42
42
  */
43
43
  async createPreSignedUrlForPreview(key: string): Promise<string> {
44
44
  return this.getLocalFileUrl(key);
@@ -49,13 +49,13 @@ export class DesktopLocalFileImpl implements FileServiceImpl {
49
49
  }
50
50
 
51
51
  /**
52
- * 批量删除文件
52
+ * Delete files in batch
53
53
  */
54
54
  async deleteFiles(keys: string[]): Promise<any> {
55
55
  try {
56
56
  if (!keys || keys.length === 0) return { success: true };
57
57
 
58
- // 确保所有路径都是合法的desktop://路径
58
+ // Ensure all paths are valid desktop:// paths
59
59
  const invalidKeys = keys.filter((key) => !key.startsWith('desktop://'));
60
60
  if (invalidKeys.length > 0) {
61
61
  console.error('Invalid desktop file paths:', invalidKeys);
@@ -65,7 +65,7 @@ export class DesktopLocalFileImpl implements FileServiceImpl {
65
65
  };
66
66
  }
67
67
 
68
- // 使用electronIpcClient的专用方法
68
+ // Use electronIpcClient's dedicated method
69
69
  return await electronIpcClient.deleteFiles(keys);
70
70
  } catch (error) {
71
71
  console.error('Failed to delete files:', error);
@@ -82,20 +82,20 @@ export class DesktopLocalFileImpl implements FileServiceImpl {
82
82
  }
83
83
 
84
84
  /**
85
- * 获取文件字节数组
85
+ * Get file byte array
86
86
  */
87
87
  async getFileByteArray(key: string): Promise<Uint8Array> {
88
88
  try {
89
- // Electron获取文件的绝对路径
89
+ // Get absolute file path from Electron
90
90
  const filePath = await electronIpcClient.getFilePathById(key);
91
91
 
92
- // 检查文件是否存在
92
+ // Check if file exists
93
93
  if (!existsSync(filePath)) {
94
94
  console.error(`File not found: ${filePath}`);
95
95
  return new Uint8Array();
96
96
  }
97
97
 
98
- // 读取文件内容并转换为Uint8Array
98
+ // Read file content and convert to Uint8Array
99
99
  const buffer = readFileSync(filePath);
100
100
  return new Uint8Array(buffer);
101
101
  } catch (e) {
@@ -105,20 +105,20 @@ export class DesktopLocalFileImpl implements FileServiceImpl {
105
105
  }
106
106
 
107
107
  /**
108
- * 获取文件内容
108
+ * Get file content
109
109
  */
110
110
  async getFileContent(key: string): Promise<string> {
111
111
  try {
112
- // Electron获取文件的绝对路径
112
+ // Get absolute file path from Electron
113
113
  const filePath = await electronIpcClient.getFilePathById(key);
114
114
 
115
- // 检查文件是否存在
115
+ // Check if file exists
116
116
  if (!existsSync(filePath)) {
117
117
  console.error(`File not found: ${filePath}`);
118
118
  return '';
119
119
  }
120
120
 
121
- // 读取文件内容并转换为字符串
121
+ // Read file content and convert to string
122
122
  return readFileSync(filePath, 'utf8');
123
123
  } catch (e) {
124
124
  console.error('Failed to get file content:', e);
@@ -127,7 +127,7 @@ export class DesktopLocalFileImpl implements FileServiceImpl {
127
127
  }
128
128
 
129
129
  /**
130
- * 获取完整文件URL
130
+ * Get full file URL
131
131
  */
132
132
  async getFullFileUrl(url?: string | null): Promise<string> {
133
133
  if (!url) return '';
@@ -139,32 +139,32 @@ export class DesktopLocalFileImpl implements FileServiceImpl {
139
139
  }
140
140
 
141
141
  /**
142
- * 上传内容
143
- * 注意:这个功能可能需要扩展Electron IPC接口
142
+ * Upload content
143
+ * Note: This feature may require extension of Electron IPC interface
144
144
  */
145
145
  async uploadContent(filePath: string, content: string): Promise<any> {
146
- // 这里需要扩展electronIpcClient以支持上传文件内容
147
- // 例如: return electronIpcClient.uploadContent(filePath, content);
146
+ // Need to extend electronIpcClient to support uploading file content
147
+ // For example: return electronIpcClient.uploadContent(filePath, content);
148
148
  console.warn('uploadContent not implemented for Desktop local file service', filePath, content);
149
149
  return;
150
150
  }
151
151
 
152
152
  /**
153
- * 从完整URL中提取key
154
- * HTTP URL 中提取 desktop:// 格式的路径
153
+ * Extract key from full URL
154
+ * Extract desktop:// format path from HTTP URL
155
155
  */
156
156
  getKeyFromFullUrl(url: string): string {
157
157
  try {
158
158
  const urlObj = new URL(url);
159
159
  const pathSegments = urlObj.pathname.split('/').filter((segment) => segment !== '');
160
160
 
161
- // 移除第一个路径段(desktop-file
161
+ // Remove first path segment (desktop-file)
162
162
  pathSegments.shift();
163
163
 
164
- // 重新组合剩余的路径段
164
+ // Recombine remaining path segments
165
165
  const filePath = pathSegments.join('/');
166
166
 
167
- // 返回 desktop:// 格式的路径
167
+ // Return desktop:// format path
168
168
  return `desktop://${filePath}`;
169
169
  } catch (e) {
170
170
  console.error('[DesktopLocalFileImpl] Failed to extract key from URL:', e);
@@ -173,23 +173,23 @@ export class DesktopLocalFileImpl implements FileServiceImpl {
173
173
  }
174
174
 
175
175
  /**
176
- * 上传媒体文件
176
+ * Upload media file
177
177
  */
178
178
  async uploadMedia(key: string, buffer: Buffer): Promise<{ key: string }> {
179
179
  try {
180
- // Buffer 转换为 Base64 字符串
180
+ // Convert Buffer to Base64 string
181
181
  const content = buffer.toString('base64');
182
182
 
183
- // key 中提取文件名
183
+ // Extract filename from key
184
184
  const filename = path.basename(key);
185
185
 
186
- // 计算文件的 SHA256 hash
186
+ // Calculate SHA256 hash of the file
187
187
  const hash = sha256(buffer);
188
188
 
189
- // 根据文件URL推断 MIME 类型
189
+ // Infer MIME type from file URL
190
190
  const type = inferContentTypeFromImageUrl(key)!;
191
191
 
192
- // 构造上传参数
192
+ // Construct upload parameters
193
193
  const uploadParams = {
194
194
  content,
195
195
  filename,
@@ -198,7 +198,7 @@ export class DesktopLocalFileImpl implements FileServiceImpl {
198
198
  type,
199
199
  };
200
200
 
201
- // 调用 electronIpcClient 上传文件
201
+ // Call electronIpcClient to upload file
202
202
  const result = await electronIpcClient.createFile(uploadParams);
203
203
 
204
204
  if (!result.success) {
@@ -7,7 +7,7 @@ import { FileServiceImpl } from './type';
7
7
  import { extractKeyFromUrlOrReturnOriginal } from './utils';
8
8
 
9
9
  /**
10
- * 基于S3的文件服务实现
10
+ * S3-based file service implementation
11
11
  */
12
12
  export class S3StaticFileImpl implements FileServiceImpl {
13
13
  private readonly s3: S3;
@@ -1,54 +1,54 @@
1
1
  /**
2
- * S3文件服务实现
2
+ * File service implementation interface
3
3
  */
4
4
  export interface FileServiceImpl {
5
5
  /**
6
- * 创建预签名上传URL
6
+ * Create pre-signed upload URL
7
7
  */
8
8
  createPreSignedUrl(key: string): Promise<string>;
9
9
 
10
10
  /**
11
- * 创建预签名预览URL
11
+ * Create pre-signed preview URL
12
12
  */
13
13
  createPreSignedUrlForPreview(key: string, expiresIn?: number): Promise<string>;
14
14
 
15
15
  /**
16
- * 删除文件
16
+ * Delete file
17
17
  */
18
18
  deleteFile(key: string): Promise<any>;
19
19
 
20
20
  /**
21
- * 批量删除文件
21
+ * Delete files in batch
22
22
  */
23
23
  deleteFiles(keys: string[]): Promise<any>;
24
24
 
25
25
  /**
26
- * 获取文件字节数组
26
+ * Get file byte array
27
27
  */
28
28
  getFileByteArray(key: string): Promise<Uint8Array>;
29
29
 
30
30
  /**
31
- * 获取文件内容
31
+ * Get file content
32
32
  */
33
33
  getFileContent(key: string): Promise<string>;
34
34
 
35
35
  /**
36
- * 获取完整文件URL
36
+ * Get full file URL
37
37
  */
38
38
  getFullFileUrl(url?: string | null, expiresIn?: number): Promise<string>;
39
39
 
40
40
  /**
41
- * 从完整URL中提取key
41
+ * Extract key from full URL
42
42
  */
43
43
  getKeyFromFullUrl(url: string): string;
44
44
 
45
45
  /**
46
- * 上传内容
46
+ * Upload content
47
47
  */
48
48
  uploadContent(path: string, content: string): Promise<any>;
49
49
 
50
50
  /**
51
- * 上传媒体文件
51
+ * Upload media file
52
52
  */
53
53
  uploadMedia(key: string, buffer: Buffer): Promise<{ key: string }>;
54
54
  }
@@ -11,8 +11,8 @@ import { TempFileManager } from '@/server/utils/tempFileManager';
11
11
  import { FileServiceImpl, createFileServiceModule } from './impls';
12
12
 
13
13
  /**
14
- * 文件服务类
15
- * 使用模块化实现方式,提供文件操作服务
14
+ * File service class
15
+ * Provides file operation services using a modular implementation approach
16
16
  */
17
17
  export class FileService {
18
18
  private userId: string;
@@ -26,70 +26,70 @@ export class FileService {
26
26
  }
27
27
 
28
28
  /**
29
- * 删除文件
29
+ * Delete file
30
30
  */
31
31
  public async deleteFile(key: string) {
32
32
  return this.impl.deleteFile(key);
33
33
  }
34
34
 
35
35
  /**
36
- * 批量删除文件
36
+ * Delete files in batch
37
37
  */
38
38
  public async deleteFiles(keys: string[]) {
39
39
  return this.impl.deleteFiles(keys);
40
40
  }
41
41
 
42
42
  /**
43
- * 获取文件内容
43
+ * Get file content
44
44
  */
45
45
  public async getFileContent(key: string): Promise<string> {
46
46
  return this.impl.getFileContent(key);
47
47
  }
48
48
 
49
49
  /**
50
- * 获取文件字节数组
50
+ * Get file byte array
51
51
  */
52
52
  public async getFileByteArray(key: string): Promise<Uint8Array> {
53
53
  return this.impl.getFileByteArray(key);
54
54
  }
55
55
 
56
56
  /**
57
- * 创建预签名上传URL
57
+ * Create pre-signed upload URL
58
58
  */
59
59
  public async createPreSignedUrl(key: string): Promise<string> {
60
60
  return this.impl.createPreSignedUrl(key);
61
61
  }
62
62
 
63
63
  /**
64
- * 创建预签名预览URL
64
+ * Create pre-signed preview URL
65
65
  */
66
66
  public async createPreSignedUrlForPreview(key: string, expiresIn?: number): Promise<string> {
67
67
  return this.impl.createPreSignedUrlForPreview(key, expiresIn);
68
68
  }
69
69
 
70
70
  /**
71
- * 上传内容
71
+ * Upload content
72
72
  */
73
73
  public async uploadContent(path: string, content: string) {
74
74
  return this.impl.uploadContent(path, content);
75
75
  }
76
76
 
77
77
  /**
78
- * 获取完整文件URL
78
+ * Get full file URL
79
79
  */
80
80
  public async getFullFileUrl(url?: string | null, expiresIn?: number): Promise<string> {
81
81
  return this.impl.getFullFileUrl(url, expiresIn);
82
82
  }
83
83
 
84
84
  /**
85
- * 从完整 URL中 提取 key
85
+ * Extract key from full URL
86
86
  */
87
87
  public getKeyFromFullUrl(url: string): string {
88
88
  return this.impl.getKeyFromFullUrl(url);
89
89
  }
90
90
 
91
91
  /**
92
- * 上传媒体文件
92
+ * Upload media file
93
93
  */
94
94
  public async uploadMedia(key: string, buffer: Buffer): Promise<{ key: string }> {
95
95
  return this.impl.uploadMedia(key, buffer);