@lobehub/lobehub 2.0.0-next.91 → 2.0.0-next.93
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 +50 -0
- package/changelog/v1.json +18 -0
- package/locales/ar/common.json +1 -0
- package/locales/bg-BG/common.json +1 -0
- package/locales/de-DE/common.json +1 -0
- package/locales/es-ES/common.json +1 -0
- package/locales/fa-IR/common.json +1 -0
- package/locales/fr-FR/common.json +1 -0
- package/locales/it-IT/common.json +1 -0
- package/locales/ja-JP/common.json +1 -0
- package/locales/ko-KR/common.json +1 -0
- package/locales/nl-NL/common.json +1 -0
- package/locales/pl-PL/common.json +1 -0
- package/locales/pt-BR/common.json +1 -0
- package/locales/ru-RU/common.json +1 -0
- package/locales/tr-TR/common.json +1 -0
- package/locales/vi-VN/common.json +1 -0
- package/locales/zh-TW/common.json +1 -0
- package/package.json +1 -1
- package/packages/database/src/client/pglite.ts +1 -1
- package/packages/database/src/core/db-adaptor.ts +4 -4
- package/packages/database/src/models/file.ts +32 -32
- package/packages/database/src/models/message.ts +7 -7
- package/packages/database/src/schemas/file.ts +12 -12
- package/packages/database/src/schemas/message.ts +4 -4
- package/packages/database/src/schemas/rag.ts +2 -2
- package/src/app/[variants]/page.tsx +1 -3
- package/src/app/[variants]/(main)/profile/(home)/[[...slugs]]/page.tsx +0 -40
- package/src/app/[variants]/(main)/settings/error.tsx +0 -3
- package/src/app/[variants]/(main)/settings/not-found.tsx +0 -1
- package/src/app/[variants]/(main)/settings/page.tsx +0 -41
- /package/src/app/[variants]/{(main)/settings/loading.tsx → loading.tsx} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.93](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.92...v2.0.0-next.93)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2025-11-20**</sup>
|
|
8
|
+
|
|
9
|
+
#### 💄 Styles
|
|
10
|
+
|
|
11
|
+
- **misc**: Update i18n.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### Styles
|
|
19
|
+
|
|
20
|
+
- **misc**: Update i18n, closes [#10317](https://github.com/lobehub/lobe-chat/issues/10317) ([8fb9890](https://github.com/lobehub/lobe-chat/commit/8fb9890))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
## [Version 2.0.0-next.92](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.91...v2.0.0-next.92)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2025-11-19**</sup>
|
|
33
|
+
|
|
34
|
+
#### 💄 Styles
|
|
35
|
+
|
|
36
|
+
- **misc**: Remove debug console logs and add loading state.
|
|
37
|
+
|
|
38
|
+
<br/>
|
|
39
|
+
|
|
40
|
+
<details>
|
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
42
|
+
|
|
43
|
+
#### Styles
|
|
44
|
+
|
|
45
|
+
- **misc**: Remove debug console logs and add loading state, closes [#10314](https://github.com/lobehub/lobe-chat/issues/10314) ([094cdff](https://github.com/lobehub/lobe-chat/commit/094cdff))
|
|
46
|
+
|
|
47
|
+
</details>
|
|
48
|
+
|
|
49
|
+
<div align="right">
|
|
50
|
+
|
|
51
|
+
[](#readme-top)
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
|
|
5
55
|
## [Version 2.0.0-next.91](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.90...v2.0.0-next.91)
|
|
6
56
|
|
|
7
57
|
<sup>Released on **2025-11-19**</sup>
|
package/changelog/v1.json
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"children": {
|
|
4
|
+
"improvements": [
|
|
5
|
+
"Update i18n."
|
|
6
|
+
]
|
|
7
|
+
},
|
|
8
|
+
"date": "2025-11-20",
|
|
9
|
+
"version": "2.0.0-next.93"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"children": {
|
|
13
|
+
"improvements": [
|
|
14
|
+
"Remove debug console logs and add loading state."
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"date": "2025-11-19",
|
|
18
|
+
"version": "2.0.0-next.92"
|
|
19
|
+
},
|
|
2
20
|
{
|
|
3
21
|
"children": {
|
|
4
22
|
"fixes": [
|
package/locales/ar/common.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.93",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent 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",
|
|
@@ -5,17 +5,17 @@ import { getPgliteInstance } from './electron';
|
|
|
5
5
|
import { getDBInstance } from './web-server';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Lazy-load database instance
|
|
9
|
+
* Avoid initializing the database every time the module is imported
|
|
10
10
|
*/
|
|
11
11
|
let cachedDB: LobeChatDatabase | null = null;
|
|
12
12
|
|
|
13
13
|
export const getServerDB = async (): Promise<LobeChatDatabase> => {
|
|
14
|
-
//
|
|
14
|
+
// If there's already a cached instance, return it directly
|
|
15
15
|
if (cachedDB) return cachedDB;
|
|
16
16
|
|
|
17
17
|
try {
|
|
18
|
-
//
|
|
18
|
+
// Select the appropriate database instance based on the environment
|
|
19
19
|
cachedDB = isDesktop ? await getPgliteInstance() : getDBInstance();
|
|
20
20
|
return cachedDB;
|
|
21
21
|
} catch (error) {
|
|
@@ -101,7 +101,7 @@ export class FileModel {
|
|
|
101
101
|
|
|
102
102
|
delete = async (id: string, removeGlobalFile: boolean = true, trx?: Transaction) => {
|
|
103
103
|
const executeInTransaction = async (tx: Transaction) => {
|
|
104
|
-
// pglite
|
|
104
|
+
// In pglite environment, non-transactional operations cannot be used within a transaction as it will block
|
|
105
105
|
const file = await this.findById(id, tx);
|
|
106
106
|
if (!file) return;
|
|
107
107
|
|
|
@@ -151,26 +151,26 @@ export class FileModel {
|
|
|
151
151
|
if (ids.length === 0) return [];
|
|
152
152
|
|
|
153
153
|
return await this.db.transaction(async (trx) => {
|
|
154
|
-
// 1.
|
|
154
|
+
// 1. First get the file list to return the deleted files
|
|
155
155
|
const fileList = await trx.query.files.findMany({
|
|
156
156
|
where: and(inArray(files.id, ids), eq(files.userId, this.userId)),
|
|
157
157
|
});
|
|
158
158
|
|
|
159
159
|
if (fileList.length === 0) return [];
|
|
160
160
|
|
|
161
|
-
//
|
|
161
|
+
// Extract file hashes that need to be checked
|
|
162
162
|
const hashList = fileList.map((file) => file.fileHash!).filter(Boolean);
|
|
163
163
|
|
|
164
|
-
// 2.
|
|
164
|
+
// 2. Delete related chunks
|
|
165
165
|
await this.deleteFileChunks(trx as any, ids);
|
|
166
166
|
|
|
167
|
-
// 3.
|
|
167
|
+
// 3. Delete file records
|
|
168
168
|
await trx.delete(files).where(and(inArray(files.id, ids), eq(files.userId, this.userId)));
|
|
169
169
|
|
|
170
|
-
//
|
|
170
|
+
// If global files don't need to be deleted, return directly
|
|
171
171
|
if (!removeGlobalFile || hashList.length === 0) return fileList;
|
|
172
172
|
|
|
173
|
-
// 4.
|
|
173
|
+
// 4. Find hashes that are no longer referenced
|
|
174
174
|
const remainingFiles = await trx
|
|
175
175
|
.select({
|
|
176
176
|
fileHash: files.fileHash,
|
|
@@ -178,18 +178,18 @@ export class FileModel {
|
|
|
178
178
|
.from(files)
|
|
179
179
|
.where(inArray(files.fileHash, hashList));
|
|
180
180
|
|
|
181
|
-
//
|
|
181
|
+
// Put still-in-use hashes into a Set for quick lookup
|
|
182
182
|
const usedHashes = new Set(remainingFiles.map((file) => file.fileHash));
|
|
183
183
|
|
|
184
|
-
//
|
|
184
|
+
// Find hashes to delete (those no longer used by any file)
|
|
185
185
|
const hashesToDelete = hashList.filter((hash) => !usedHashes.has(hash));
|
|
186
186
|
|
|
187
187
|
if (hashesToDelete.length === 0) return fileList;
|
|
188
188
|
|
|
189
|
-
// 5.
|
|
189
|
+
// 5. Delete global files that are no longer referenced
|
|
190
190
|
await trx.delete(globalFiles).where(inArray(globalFiles.hashId, hashesToDelete));
|
|
191
191
|
|
|
192
|
-
//
|
|
192
|
+
// Return the list of deleted files
|
|
193
193
|
return fileList;
|
|
194
194
|
});
|
|
195
195
|
};
|
|
@@ -206,7 +206,7 @@ export class FileModel {
|
|
|
206
206
|
knowledgeBaseId,
|
|
207
207
|
showFilesInKnowledgeBase,
|
|
208
208
|
}: QueryFileListParams = {}) => {
|
|
209
|
-
// 1.
|
|
209
|
+
// 1. Build where clause
|
|
210
210
|
let whereClause = and(
|
|
211
211
|
q ? ilike(files.name, `%${q}%`) : undefined,
|
|
212
212
|
eq(files.userId, this.userId),
|
|
@@ -224,7 +224,7 @@ export class FileModel {
|
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
// 2. order
|
|
227
|
+
// 2. Build order clause
|
|
228
228
|
|
|
229
229
|
let orderByClause = desc(files.createdAt);
|
|
230
230
|
// create a map for sortable fields
|
|
@@ -241,7 +241,7 @@ export class FileModel {
|
|
|
241
241
|
orderByClause = sortFunction(sortableFields[sorter as SortableField]);
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
// 3.
|
|
244
|
+
// 3. Build base query
|
|
245
245
|
let query = this.db
|
|
246
246
|
.select({
|
|
247
247
|
chunkTaskId: files.chunkTaskId,
|
|
@@ -256,7 +256,7 @@ export class FileModel {
|
|
|
256
256
|
})
|
|
257
257
|
.from(files);
|
|
258
258
|
|
|
259
|
-
// 4.
|
|
259
|
+
// 4. Add knowledge base query if needed
|
|
260
260
|
if (knowledgeBaseId) {
|
|
261
261
|
// if knowledgeBaseId is provided, it means we are querying files in a knowledge-base
|
|
262
262
|
|
|
@@ -269,7 +269,7 @@ export class FileModel {
|
|
|
269
269
|
),
|
|
270
270
|
);
|
|
271
271
|
}
|
|
272
|
-
// 5.
|
|
272
|
+
// 5. If we don't show files in knowledge base, exclude them
|
|
273
273
|
else if (!showFilesInKnowledgeBase) {
|
|
274
274
|
whereClause = and(
|
|
275
275
|
whereClause,
|
|
@@ -279,7 +279,7 @@ export class FileModel {
|
|
|
279
279
|
);
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
-
//
|
|
282
|
+
// Otherwise, we are just filtering in the global files
|
|
283
283
|
return query.where(whereClause).orderBy(orderByClause);
|
|
284
284
|
};
|
|
285
285
|
|
|
@@ -347,11 +347,11 @@ export class FileModel {
|
|
|
347
347
|
),
|
|
348
348
|
});
|
|
349
349
|
|
|
350
|
-
//
|
|
350
|
+
// Abstract common method for deleting chunks
|
|
351
351
|
private deleteFileChunks = async (trx: PgTransaction<any>, fileIds: string[]) => {
|
|
352
352
|
if (fileIds.length === 0) return;
|
|
353
353
|
|
|
354
|
-
//
|
|
354
|
+
// Get all chunk IDs related to the files to be deleted (knowledge base protection logic removed)
|
|
355
355
|
const relatedChunks = await trx
|
|
356
356
|
.select({ chunkId: fileChunks.chunkId })
|
|
357
357
|
.from(fileChunks)
|
|
@@ -361,15 +361,15 @@ export class FileModel {
|
|
|
361
361
|
|
|
362
362
|
if (chunkIds.length === 0) return;
|
|
363
363
|
|
|
364
|
-
//
|
|
364
|
+
// Batch processing configuration
|
|
365
365
|
const BATCH_SIZE = 1000;
|
|
366
366
|
const MAX_CONCURRENT_BATCHES = 3;
|
|
367
367
|
|
|
368
|
-
//
|
|
368
|
+
// Process in batches concurrently
|
|
369
369
|
for (let i = 0; i < chunkIds.length; i += BATCH_SIZE * MAX_CONCURRENT_BATCHES) {
|
|
370
370
|
const batchPromises = [];
|
|
371
371
|
|
|
372
|
-
//
|
|
372
|
+
// Create multiple parallel batches
|
|
373
373
|
for (let j = 0; j < MAX_CONCURRENT_BATCHES; j++) {
|
|
374
374
|
const startIdx = i + j * BATCH_SIZE;
|
|
375
375
|
if (startIdx >= chunkIds.length) break;
|
|
@@ -377,29 +377,29 @@ export class FileModel {
|
|
|
377
377
|
const batchChunkIds = chunkIds.slice(startIdx, startIdx + BATCH_SIZE);
|
|
378
378
|
if (batchChunkIds.length === 0) continue;
|
|
379
379
|
|
|
380
|
-
//
|
|
380
|
+
// Process each batch in the correct deletion order, failures do not block the flow
|
|
381
381
|
const batchPromise = (async () => {
|
|
382
|
-
// 1.
|
|
382
|
+
// 1. Delete embeddings (top-level, has foreign key dependencies)
|
|
383
383
|
try {
|
|
384
384
|
await trx.delete(embeddings).where(inArray(embeddings.chunkId, batchChunkIds));
|
|
385
385
|
} catch (e) {
|
|
386
|
-
//
|
|
386
|
+
// Silent handling, does not block deletion process
|
|
387
387
|
console.warn('Failed to delete embeddings:', e);
|
|
388
388
|
}
|
|
389
389
|
|
|
390
|
-
// 2.
|
|
390
|
+
// 2. Delete documentChunks association (if exists)
|
|
391
391
|
try {
|
|
392
392
|
await trx.delete(documentChunks).where(inArray(documentChunks.chunkId, batchChunkIds));
|
|
393
393
|
} catch (e) {
|
|
394
|
-
//
|
|
394
|
+
// Silent handling, does not block deletion process
|
|
395
395
|
console.warn('Failed to delete documentChunks:', e);
|
|
396
396
|
}
|
|
397
397
|
|
|
398
|
-
// 3.
|
|
398
|
+
// 3. Delete chunks (core data)
|
|
399
399
|
try {
|
|
400
400
|
await trx.delete(chunks).where(inArray(chunks.id, batchChunkIds));
|
|
401
401
|
} catch (e) {
|
|
402
|
-
//
|
|
402
|
+
// Silent handling, does not block deletion process
|
|
403
403
|
console.warn('Failed to delete chunks:', e);
|
|
404
404
|
}
|
|
405
405
|
})();
|
|
@@ -407,15 +407,15 @@ export class FileModel {
|
|
|
407
407
|
batchPromises.push(batchPromise);
|
|
408
408
|
}
|
|
409
409
|
|
|
410
|
-
//
|
|
410
|
+
// Wait for all tasks in the current batch to complete
|
|
411
411
|
await Promise.all(batchPromises);
|
|
412
412
|
}
|
|
413
413
|
|
|
414
|
-
// 4.
|
|
414
|
+
// 4. Finally delete fileChunks association table records
|
|
415
415
|
try {
|
|
416
416
|
await trx.delete(fileChunks).where(inArray(fileChunks.fileId, fileIds));
|
|
417
417
|
} catch (e) {
|
|
418
|
-
//
|
|
418
|
+
// Silent handling, does not block deletion process
|
|
419
419
|
console.warn('Failed to delete fileChunks:', e);
|
|
420
420
|
}
|
|
421
421
|
|
|
@@ -155,7 +155,7 @@ export class MessageModel {
|
|
|
155
155
|
})),
|
|
156
156
|
);
|
|
157
157
|
|
|
158
|
-
//
|
|
158
|
+
// Get associated document content
|
|
159
159
|
const fileIds = relatedFileList.map((file) => file.id).filter(Boolean);
|
|
160
160
|
|
|
161
161
|
let documentsMap: Record<string, string> = {};
|
|
@@ -662,17 +662,17 @@ export class MessageModel {
|
|
|
662
662
|
|
|
663
663
|
deleteMessage = async (id: string) => {
|
|
664
664
|
return this.db.transaction(async (tx) => {
|
|
665
|
-
// 1.
|
|
665
|
+
// 1. Query the complete information of the message to be deleted
|
|
666
666
|
const message = await tx
|
|
667
667
|
.select()
|
|
668
668
|
.from(messages)
|
|
669
669
|
.where(and(eq(messages.id, id), eq(messages.userId, this.userId)))
|
|
670
670
|
.limit(1);
|
|
671
671
|
|
|
672
|
-
//
|
|
672
|
+
// If the message to be deleted is not found, return directly
|
|
673
673
|
if (message.length === 0) return;
|
|
674
674
|
|
|
675
|
-
// 2.
|
|
675
|
+
// 2. Check if the message contains tools
|
|
676
676
|
const toolCallIds = (message[0].tools as ChatToolPayload[])
|
|
677
677
|
?.map((tool) => tool.id)
|
|
678
678
|
.filter(Boolean);
|
|
@@ -680,7 +680,7 @@ export class MessageModel {
|
|
|
680
680
|
let relatedMessageIds: string[] = [];
|
|
681
681
|
|
|
682
682
|
if (toolCallIds?.length > 0) {
|
|
683
|
-
// 3.
|
|
683
|
+
// 3. If the message contains tools, query all associated message ids
|
|
684
684
|
const res = await tx
|
|
685
685
|
.select({ id: messagePlugins.id })
|
|
686
686
|
.from(messagePlugins)
|
|
@@ -689,10 +689,10 @@ export class MessageModel {
|
|
|
689
689
|
relatedMessageIds = res.map((row) => row.id);
|
|
690
690
|
}
|
|
691
691
|
|
|
692
|
-
// 4.
|
|
692
|
+
// 4. Merge the list of message ids to be deleted
|
|
693
693
|
const messageIdsToDelete = [id, ...relatedMessageIds];
|
|
694
694
|
|
|
695
|
-
// 5.
|
|
695
|
+
// 5. Delete all related messages
|
|
696
696
|
await tx.delete(messages).where(inArray(messages.id, messageIdsToDelete));
|
|
697
697
|
});
|
|
698
698
|
};
|
|
@@ -38,7 +38,7 @@ export type NewGlobalFile = typeof globalFiles.$inferInsert;
|
|
|
38
38
|
export type GlobalFileItem = typeof globalFiles.$inferSelect;
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
41
|
+
* Documents table - Stores file content or web search results
|
|
42
42
|
*/
|
|
43
43
|
// @ts-ignore
|
|
44
44
|
export const documents = pgTable(
|
|
@@ -48,7 +48,7 @@ export const documents = pgTable(
|
|
|
48
48
|
.$defaultFn(() => idGenerator('documents', 16))
|
|
49
49
|
.primaryKey(),
|
|
50
50
|
|
|
51
|
-
//
|
|
51
|
+
// Basic information
|
|
52
52
|
title: text('title'),
|
|
53
53
|
content: text('content'),
|
|
54
54
|
|
|
@@ -56,34 +56,34 @@ export const documents = pgTable(
|
|
|
56
56
|
fileType: varchar('file_type', { length: 255 }).notNull(),
|
|
57
57
|
filename: text('filename'),
|
|
58
58
|
|
|
59
|
-
//
|
|
59
|
+
// Statistics
|
|
60
60
|
totalCharCount: integer('total_char_count').notNull(),
|
|
61
61
|
totalLineCount: integer('total_line_count').notNull(),
|
|
62
62
|
|
|
63
|
-
//
|
|
63
|
+
// Metadata
|
|
64
64
|
metadata: jsonb('metadata').$type<Record<string, any>>(),
|
|
65
65
|
|
|
66
|
-
//
|
|
66
|
+
// Page/chunk data
|
|
67
67
|
pages: jsonb('pages').$type<LobeDocumentPage[]>(),
|
|
68
68
|
|
|
69
|
-
//
|
|
69
|
+
// Source type
|
|
70
70
|
sourceType: text('source_type', { enum: ['file', 'web', 'api'] }).notNull(),
|
|
71
|
-
source: text('source').notNull(), //
|
|
71
|
+
source: text('source').notNull(), // File path or web URL
|
|
72
72
|
|
|
73
|
-
//
|
|
73
|
+
// Associated file (optional)
|
|
74
74
|
// Forward reference to files table defined below
|
|
75
75
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
76
76
|
// @ts-expect-error - files is defined later in this file, forward reference is valid at runtime
|
|
77
77
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
78
78
|
fileId: text('file_id').references(() => files.id, { onDelete: 'set null' }),
|
|
79
79
|
|
|
80
|
-
//
|
|
80
|
+
// Parent document (for folder hierarchy structure)
|
|
81
81
|
// @ts-ignore
|
|
82
82
|
parentId: varchar('parent_id', { length: 255 }).references(() => documents.id, {
|
|
83
83
|
onDelete: 'set null',
|
|
84
84
|
}),
|
|
85
85
|
|
|
86
|
-
//
|
|
86
|
+
// User association
|
|
87
87
|
userId: text('user_id')
|
|
88
88
|
.references(() => users.id, { onDelete: 'cascade' })
|
|
89
89
|
.notNull(),
|
|
@@ -91,7 +91,7 @@ export const documents = pgTable(
|
|
|
91
91
|
|
|
92
92
|
editorData: jsonb('editor_data').$type<Record<string, any>>(),
|
|
93
93
|
|
|
94
|
-
//
|
|
94
|
+
// Timestamps
|
|
95
95
|
...timestamps,
|
|
96
96
|
},
|
|
97
97
|
(table) => [
|
|
@@ -133,7 +133,7 @@ export const files = pgTable(
|
|
|
133
133
|
url: text('url').notNull(),
|
|
134
134
|
source: text('source').$type<FileSource>(),
|
|
135
135
|
|
|
136
|
-
//
|
|
136
|
+
// Parent document (for folder hierarchy structure)
|
|
137
137
|
// @ts-ignore
|
|
138
138
|
parentId: varchar('parent_id', { length: 255 }).references(() => documents.id, {
|
|
139
139
|
onDelete: 'set null',
|
|
@@ -36,25 +36,25 @@ export const messageGroups = pgTable(
|
|
|
36
36
|
.$defaultFn(() => idGenerator('messageGroups'))
|
|
37
37
|
.notNull(),
|
|
38
38
|
|
|
39
|
-
//
|
|
39
|
+
// Association - only needs topic level
|
|
40
40
|
topicId: text('topic_id').references(() => topics.id, { onDelete: 'cascade' }),
|
|
41
41
|
userId: text('user_id')
|
|
42
42
|
.references(() => users.id, { onDelete: 'cascade' })
|
|
43
43
|
.notNull(),
|
|
44
44
|
|
|
45
|
-
//
|
|
45
|
+
// Support nested structure
|
|
46
46
|
// @ts-ignore
|
|
47
47
|
parentGroupId: varchar255('parent_group_id').references(() => messageGroups.id, {
|
|
48
48
|
onDelete: 'cascade',
|
|
49
49
|
}),
|
|
50
50
|
|
|
51
|
-
//
|
|
51
|
+
// Associated user message
|
|
52
52
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
53
53
|
parentMessageId: text('parent_message_id').references(() => messages.id, {
|
|
54
54
|
onDelete: 'cascade',
|
|
55
55
|
}),
|
|
56
56
|
|
|
57
|
-
//
|
|
57
|
+
// Metadata
|
|
58
58
|
title: varchar255('title'),
|
|
59
59
|
description: text('description'),
|
|
60
60
|
|
|
@@ -89,8 +89,8 @@ export type NewEmbeddingsItem = typeof embeddings.$inferInsert;
|
|
|
89
89
|
export type EmbeddingsSelectItem = typeof embeddings.$inferSelect;
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
|
-
*
|
|
93
|
-
*
|
|
92
|
+
* Document chunks table - Splits document content into chunks and associates them with the chunks table for vector retrieval
|
|
93
|
+
* Note: This table is optional, if the pages field is already being used to store document chunks, this table may not be needed
|
|
94
94
|
*/
|
|
95
95
|
export const documentChunks = pgTable(
|
|
96
96
|
'document_chunks',
|
|
@@ -12,9 +12,7 @@ export default async (props: DynamicLayoutProps) => {
|
|
|
12
12
|
// Conditionally load and render based on device type
|
|
13
13
|
// Using native dynamic import ensures complete code splitting
|
|
14
14
|
// Mobile and Desktop bundles will be completely separate
|
|
15
|
-
|
|
16
|
-
console.log('isMobile', isMobile);
|
|
17
|
-
console.log('locale', locale);
|
|
15
|
+
|
|
18
16
|
if (isMobile) {
|
|
19
17
|
return <MobileRouter locale={locale} />;
|
|
20
18
|
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { Skeleton } from 'antd';
|
|
2
|
-
import dynamic from 'next/dynamic';
|
|
3
|
-
|
|
4
|
-
import { enableClerk } from '@/const/auth';
|
|
5
|
-
import { metadataModule } from '@/server/metadata';
|
|
6
|
-
import { translation } from '@/server/translation';
|
|
7
|
-
import { DynamicLayoutProps } from '@/types/next';
|
|
8
|
-
import { RouteVariants } from '@/utils/server/routeVariants';
|
|
9
|
-
|
|
10
|
-
import Client from '../Client';
|
|
11
|
-
|
|
12
|
-
// 为了兼容 ClerkProfile, 需要使用 [[...slug]]
|
|
13
|
-
|
|
14
|
-
const ClerkProfile = dynamic(() => import('../../features/ClerkProfile'), {
|
|
15
|
-
loading: () => (
|
|
16
|
-
<div style={{ flex: 1 }}>
|
|
17
|
-
<Skeleton paragraph={{ rows: 8 }} title={false} />
|
|
18
|
-
</div>
|
|
19
|
-
),
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
export const generateMetadata = async (props: DynamicLayoutProps) => {
|
|
23
|
-
const locale = await RouteVariants.getLocale(props);
|
|
24
|
-
const { t } = await translation('auth', locale);
|
|
25
|
-
return metadataModule.generate({
|
|
26
|
-
description: t('header.desc'),
|
|
27
|
-
title: t('tab.profile'),
|
|
28
|
-
url: '/profile',
|
|
29
|
-
});
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const Page = async (props: DynamicLayoutProps) => {
|
|
33
|
-
const mobile = await RouteVariants.getIsMobile(props);
|
|
34
|
-
|
|
35
|
-
if (enableClerk) return <ClerkProfile mobile={mobile} />;
|
|
36
|
-
|
|
37
|
-
return <Client mobile={mobile} />;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export default Page;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from '@/components/404';
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import ServerLayout from '@/components/server/ServerLayout';
|
|
2
|
-
import { serverFeatureFlags } from '@/config/featureFlags';
|
|
3
|
-
import { metadataModule } from '@/server/metadata';
|
|
4
|
-
import { translation } from '@/server/translation';
|
|
5
|
-
import { DynamicLayoutProps } from '@/types/next';
|
|
6
|
-
import { RouteVariants } from '@/utils/server/routeVariants';
|
|
7
|
-
|
|
8
|
-
import SettingsContextProvider from './_layout/ContextProvider';
|
|
9
|
-
import Desktop from './_layout/Desktop';
|
|
10
|
-
import Mobile from './_layout/Mobile';
|
|
11
|
-
import { LayoutProps } from './_layout/type';
|
|
12
|
-
|
|
13
|
-
export const generateMetadata = async (props: DynamicLayoutProps) => {
|
|
14
|
-
const locale = await RouteVariants.getLocale(props);
|
|
15
|
-
const { t } = await translation('setting', locale);
|
|
16
|
-
return metadataModule.generate({
|
|
17
|
-
description: t('header.desc'),
|
|
18
|
-
title: t('header.title'),
|
|
19
|
-
url: '/settings',
|
|
20
|
-
});
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const SettingsLayout = ServerLayout<LayoutProps>({ Desktop, Mobile });
|
|
24
|
-
|
|
25
|
-
const SettingsPage = async (props: DynamicLayoutProps) => {
|
|
26
|
-
const { showOpenAIProxyUrl, showOpenAIApiKey } = serverFeatureFlags();
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<SettingsContextProvider
|
|
30
|
-
value={{
|
|
31
|
-
showOpenAIApiKey: showOpenAIApiKey,
|
|
32
|
-
showOpenAIProxyUrl: showOpenAIProxyUrl,
|
|
33
|
-
}}
|
|
34
|
-
>
|
|
35
|
-
{/* @ts-ignore */}
|
|
36
|
-
<SettingsLayout {...props} />
|
|
37
|
-
</SettingsContextProvider>
|
|
38
|
-
);
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export default SettingsPage;
|
|
File without changes
|