@lobehub/lobehub 2.0.0-next.127 → 2.0.0-next.129

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 (61) hide show
  1. package/.env.example +23 -3
  2. package/.env.example.development +5 -0
  3. package/CHANGELOG.md +50 -0
  4. package/README.md +6 -6
  5. package/README.zh-CN.md +6 -6
  6. package/changelog/v1.json +18 -0
  7. package/docker-compose/local/docker-compose.yml +24 -1
  8. package/docker-compose/local/logto/docker-compose.yml +25 -2
  9. package/docker-compose.development.yml +6 -0
  10. package/docs/development/database-schema.dbml +8 -6
  11. package/locales/ar/auth.json +114 -1
  12. package/locales/bg-BG/auth.json +114 -1
  13. package/locales/de-DE/auth.json +114 -1
  14. package/locales/en-US/auth.json +42 -22
  15. package/locales/es-ES/auth.json +114 -1
  16. package/locales/fa-IR/auth.json +114 -1
  17. package/locales/fr-FR/auth.json +114 -1
  18. package/locales/it-IT/auth.json +114 -1
  19. package/locales/ja-JP/auth.json +114 -1
  20. package/locales/ko-KR/auth.json +114 -1
  21. package/locales/nl-NL/auth.json +114 -1
  22. package/locales/pl-PL/auth.json +114 -1
  23. package/locales/pt-BR/auth.json +114 -1
  24. package/locales/ru-RU/auth.json +114 -1
  25. package/locales/tr-TR/auth.json +114 -1
  26. package/locales/vi-VN/auth.json +114 -1
  27. package/locales/zh-CN/auth.json +36 -29
  28. package/locales/zh-TW/auth.json +114 -1
  29. package/package.json +4 -1
  30. package/packages/database/migrations/0050_thread_and_user_id.sql +18 -0
  31. package/packages/database/migrations/meta/0050_snapshot.json +8792 -0
  32. package/packages/database/migrations/meta/_journal.json +7 -0
  33. package/packages/database/src/client/db.ts +21 -21
  34. package/packages/database/src/core/migrations.json +51 -10
  35. package/packages/database/src/repositories/dataImporter/deprecated/index.ts +5 -5
  36. package/packages/database/src/repositories/dataImporter/index.ts +59 -59
  37. package/packages/database/src/repositories/knowledge/index.test.ts +17 -5
  38. package/packages/database/src/repositories/knowledge/index.ts +6 -6
  39. package/packages/database/src/schemas/generation.ts +16 -16
  40. package/packages/database/src/schemas/nextauth.ts +3 -3
  41. package/packages/database/src/schemas/oidc.ts +36 -36
  42. package/packages/database/src/schemas/topic.ts +8 -3
  43. package/packages/model-runtime/src/providers/newapi/index.ts +61 -18
  44. package/packages/model-runtime/src/runtimeMap.ts +1 -0
  45. package/packages/types/src/topic/thread.ts +3 -3
  46. package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/UpdateProviderInfo/SettingModal.tsx +10 -6
  47. package/src/envs/redis.ts +106 -0
  48. package/src/libs/redis/index.ts +5 -0
  49. package/src/libs/redis/manager.test.ts +107 -0
  50. package/src/libs/redis/manager.ts +56 -0
  51. package/src/libs/redis/redis.test.ts +158 -0
  52. package/src/libs/redis/redis.ts +117 -0
  53. package/src/libs/redis/types.ts +71 -0
  54. package/src/libs/redis/upstash.test.ts +154 -0
  55. package/src/libs/redis/upstash.ts +109 -0
  56. package/src/libs/redis/utils.test.ts +46 -0
  57. package/src/libs/redis/utils.ts +53 -0
  58. package/src/store/chat/slices/thread/action.ts +1 -1
  59. package/src/store/chat/slices/thread/initialState.ts +1 -1
  60. package/src/store/chat/slices/thread/selectors/util.ts +1 -1
  61. package/.github/workflows/check-console-log.yml +0 -117
@@ -67,7 +67,7 @@ const IMPORT_TABLE_CONFIG: TableImportConfig[] = [
67
67
  },
68
68
  {
69
69
  conflictStrategy: 'skip',
70
- preserveId: true, // 需要保留原始ID
70
+ preserveId: true, // Need to preserve original ID
71
71
  relations: [
72
72
  {
73
73
  field: 'providerId',
@@ -89,7 +89,7 @@ const IMPORT_TABLE_CONFIG: TableImportConfig[] = [
89
89
  uniqueConstraints: ['slug'],
90
90
  },
91
91
  {
92
- // slug字段进行特殊处理
92
+ // Special processing for slug field
93
93
  fieldProcessors: {
94
94
  slug: (value) => `${value}-${uuid().slice(0, 8)}`,
95
95
  },
@@ -113,7 +113,7 @@ const IMPORT_TABLE_CONFIG: TableImportConfig[] = [
113
113
  },
114
114
  {
115
115
  conflictStrategy: 'skip',
116
- isCompositeKey: true, // 使用复合主键 [agentId, sessionId]
116
+ isCompositeKey: true, // Uses composite primary key [agentId, sessionId]
117
117
  relations: [
118
118
  {
119
119
  field: 'agentId',
@@ -172,7 +172,7 @@ const IMPORT_TABLE_CONFIG: TableImportConfig[] = [
172
172
  },
173
173
  {
174
174
  conflictStrategy: 'skip',
175
- preserveId: true, // 使用消息ID作为主键
175
+ preserveId: true, // Uses message ID as primary key
176
176
  relations: [
177
177
  {
178
178
  field: 'id',
@@ -182,7 +182,7 @@ const IMPORT_TABLE_CONFIG: TableImportConfig[] = [
182
182
  table: 'messagePlugins',
183
183
  },
184
184
  {
185
- isCompositeKey: true, // 使用复合主键 [messageId, chunkId]
185
+ isCompositeKey: true, // Uses composite primary key [messageId, chunkId]
186
186
  relations: [
187
187
  {
188
188
  field: 'messageId',
@@ -196,7 +196,7 @@ const IMPORT_TABLE_CONFIG: TableImportConfig[] = [
196
196
  table: 'messageChunks',
197
197
  },
198
198
  {
199
- isCompositeKey: true, // 使用复合主键 [id, queryId, chunkId]
199
+ isCompositeKey: true, // Uses composite primary key [id, queryId, chunkId]
200
200
  relations: [
201
201
  {
202
202
  field: 'id',
@@ -228,7 +228,7 @@ const IMPORT_TABLE_CONFIG: TableImportConfig[] = [
228
228
  // },
229
229
  {
230
230
  conflictStrategy: 'skip',
231
- preserveId: true, // 使用消息ID作为主键
231
+ preserveId: true, // Uses message ID as primary key
232
232
  relations: [
233
233
  {
234
234
  field: 'id',
@@ -239,7 +239,7 @@ const IMPORT_TABLE_CONFIG: TableImportConfig[] = [
239
239
  },
240
240
  // {
241
241
  // conflictStrategy: 'skip',
242
- // preserveId: true, // 使用消息ID作为主键
242
+ // preserveId: true, // Uses message ID as primary key
243
243
  // relations: [
244
244
  // {
245
245
  // field: 'id',
@@ -273,7 +273,7 @@ export class DataImporterRepos {
273
273
  };
274
274
 
275
275
  /**
276
- * 导入PostgreSQL数据
276
+ * Import PostgreSQL data
277
277
  */
278
278
  async importPgData(
279
279
  dbData: ImportPgDataStructure,
@@ -282,13 +282,13 @@ export class DataImporterRepos {
282
282
  const results: Record<string, ImportResult> = {};
283
283
  const { data } = dbData;
284
284
 
285
- // 初始化ID映射表和冲突记录
285
+ // Initialize ID mapping table and conflict records
286
286
  this.idMaps = {};
287
287
  this.conflictRecords = {};
288
288
 
289
289
  try {
290
290
  await this.db.transaction(async (trx) => {
291
- // 按配置顺序导入表
291
+ // Import tables in configuration order
292
292
  for (const config of IMPORT_TABLE_CONFIG) {
293
293
  const { table: tableName } = config;
294
294
 
@@ -299,7 +299,7 @@ export class DataImporterRepos {
299
299
  continue;
300
300
  }
301
301
 
302
- // 使用统一的导入方法
302
+ // Use unified import method
303
303
  const result = await this.importTableData(trx, config, tableData, conflictStrategy);
304
304
  console.log(`imported table: ${tableName}, records: ${tableData.length}`);
305
305
 
@@ -325,11 +325,11 @@ export class DataImporterRepos {
325
325
  }
326
326
 
327
327
  /**
328
- * 从错误中提取详细信息
328
+ * Extract detailed information from error
329
329
  */
330
330
  private extractErrorDetails(error: any) {
331
331
  if (error.code === '23505') {
332
- // PostgreSQL 唯一约束错误码
332
+ // PostgreSQL unique constraint error code
333
333
  const match = error.detail?.match(/Key \((.+?)\)=\((.+?)\) already exists/);
334
334
  if (match) {
335
335
  return {
@@ -344,7 +344,7 @@ export class DataImporterRepos {
344
344
  }
345
345
 
346
346
  /**
347
- * 统一的表数据导入函数 - 处理所有类型的表
347
+ * Unified table data import function - Handles all types of tables
348
348
  */
349
349
  private async importTableData(
350
350
  trx: any,
@@ -368,13 +368,13 @@ export class DataImporterRepos {
368
368
  const table = EXPORT_TABLES[tableName];
369
369
  const result: ImportResult = { added: 0, errors: 0, skips: 0, updated: 0 };
370
370
 
371
- // 初始化该表的ID映射
371
+ // Initialize ID mapping for this table
372
372
  if (!this.idMaps[tableName]) {
373
373
  this.idMaps[tableName] = {};
374
374
  }
375
375
 
376
376
  try {
377
- // 1. 查找已存在的记录(基于clientIduserId
377
+ // 1. Find existing records (based on clientId and userId)
378
378
  let existingRecords: any[] = [];
379
379
 
380
380
  if ('clientId' in table && 'userId' in table) {
@@ -387,7 +387,7 @@ export class DataImporterRepos {
387
387
  }
388
388
  }
389
389
 
390
- // 如果需要保留原始ID,还需要检查ID是否已存在
390
+ // If need to preserve original ID, also check if ID already exists
391
391
  if (preserveId && !isCompositeKey) {
392
392
  const ids = tableData.map((item) => item.id).filter(Boolean);
393
393
  if (ids.length > 0) {
@@ -395,7 +395,7 @@ export class DataImporterRepos {
395
395
  where: inArray(table.id, ids),
396
396
  });
397
397
 
398
- // 合并到已存在记录集合中
398
+ // Merge into existing records set
399
399
  existingRecords = [
400
400
  ...existingRecords,
401
401
  ...idExistingRecords.filter(
@@ -407,28 +407,28 @@ export class DataImporterRepos {
407
407
 
408
408
  result.skips = existingRecords.length;
409
409
 
410
- // 2. 为已存在的记录建立ID映射
410
+ // 2. Establish ID mapping for existing records
411
411
  for (const record of existingRecords) {
412
- // 只有非复合主键表才需要ID映射
412
+ // Only non-composite key tables need ID mapping
413
413
  if (!isCompositeKey) {
414
414
  this.idMaps[tableName][record.id] = record.id;
415
415
  if (record.clientId) {
416
416
  this.idMaps[tableName][record.clientId] = record.id;
417
417
  }
418
418
 
419
- // 记录中可能使用的任何其他ID标识符
419
+ // Any other ID identifiers that may be used in records
420
420
  const originalRecord = tableData.find(
421
421
  (item) => item.id === record.id || item.clientId === record.clientId,
422
422
  );
423
423
 
424
424
  if (originalRecord) {
425
- // 确保原始记录ID也映射到数据库记录ID
425
+ // Ensure original record ID also maps to database record ID
426
426
  this.idMaps[tableName][originalRecord.id] = record.id;
427
427
  }
428
428
  }
429
429
  }
430
430
 
431
- // 3. 筛选出需要插入的记录
431
+ // 3. Filter out records that need to be inserted
432
432
  const recordsToInsert = tableData.filter(
433
433
  (item) =>
434
434
  !existingRecords.some(
@@ -442,22 +442,22 @@ export class DataImporterRepos {
442
442
  return result;
443
443
  }
444
444
 
445
- // 4. 准备导入数据
445
+ // 4. Prepare import data
446
446
  const preparedData = recordsToInsert.map((item) => {
447
447
  const originalId = item.id;
448
448
 
449
- // 处理日期字段
449
+ // Process date fields
450
450
  const dateFields: any = {};
451
451
  if (item.createdAt) dateFields.createdAt = new Date(item.createdAt);
452
452
  if (item.updatedAt) dateFields.updatedAt = new Date(item.updatedAt);
453
453
  if (item.accessedAt) dateFields.accessedAt = new Date(item.accessedAt);
454
454
 
455
- // 创建新记录对象
455
+ // Create new record object
456
456
  let newRecord: any = {};
457
457
 
458
- // 根据是否复合主键和是否保留ID决定如何处理
458
+ // Decide how to process based on whether it's composite key and whether to preserve ID
459
459
  if (isCompositeKey) {
460
- // 对于复合主键表,不包含id字段
460
+ // For composite key tables, don't include id field
461
461
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
462
462
  const { id: _, ...rest } = item;
463
463
  newRecord = {
@@ -467,7 +467,7 @@ export class DataImporterRepos {
467
467
  userId: this.userId,
468
468
  };
469
469
  } else {
470
- // 非复合主键表处理
470
+ // Non-composite key table processing
471
471
  newRecord = {
472
472
  ...(preserveId ? item : { ...item, id: undefined }),
473
473
  ...dateFields,
@@ -476,19 +476,19 @@ export class DataImporterRepos {
476
476
  };
477
477
  }
478
478
 
479
- // 应用字段处理器
479
+ // Apply field processors
480
480
  for (const field in fieldProcessors) {
481
481
  if (newRecord[field] !== undefined) {
482
482
  newRecord[field] = fieldProcessors[field](newRecord[field]);
483
483
  }
484
484
  }
485
485
 
486
- // 特殊表处理
486
+ // Special table processing
487
487
  if (tableName === 'userSettings') {
488
488
  newRecord.id = this.userId;
489
489
  }
490
490
 
491
- // 处理关系字段(外键引用)
491
+ // Process relation fields (foreign key references)
492
492
  for (const relation of relations) {
493
493
  const { field, sourceTable } = relation;
494
494
 
@@ -498,7 +498,7 @@ export class DataImporterRepos {
498
498
  if (mappedId) {
499
499
  newRecord[field] = mappedId;
500
500
  } else {
501
- // 找不到映射,设为null
501
+ // Cannot find mapping, set to null
502
502
  console.warn(
503
503
  `Could not find mapped ID for ${field}=${newRecord[field]} in table ${sourceTable}`,
504
504
  );
@@ -507,7 +507,7 @@ export class DataImporterRepos {
507
507
  }
508
508
  }
509
509
 
510
- // 简化处理自引用字段 - 直接设为null
510
+ // Simplified processing of self-reference fields - directly set to null
511
511
  for (const selfRef of selfReferences) {
512
512
  const { field } = selfRef;
513
513
  if (newRecord[field] !== undefined) {
@@ -518,15 +518,15 @@ export class DataImporterRepos {
518
518
  return { newRecord, originalId };
519
519
  });
520
520
 
521
- // 5. 检查唯一约束并应用冲突策略
521
+ // 5. Check unique constraints and apply conflict strategy
522
522
  for (const record of preparedData) {
523
523
  if (isCompositeKey && uniqueConstraints.length > 0) {
524
- // 对于复合主键表,将所有唯一约束字段作为一个组合条件
524
+ // For composite key tables, treat all unique constraint fields as a combined condition
525
525
  const whereConditions = uniqueConstraints
526
526
  .filter((field) => record.newRecord[field] !== undefined)
527
527
  .map((field) => eq(table[field], record.newRecord[field]));
528
528
 
529
- // 添加userId条件(如果表有userId字段)
529
+ // Add userId condition (if table has userId field)
530
530
  if ('userId' in table) {
531
531
  whereConditions.push(eq(table.userId, this.userId));
532
532
  }
@@ -537,7 +537,7 @@ export class DataImporterRepos {
537
537
  });
538
538
 
539
539
  if (exists) {
540
- // 记录冲突
540
+ // Record conflict
541
541
  if (!this.conflictRecords[tableName]) this.conflictRecords[tableName] = [];
542
542
  this.conflictRecords[tableName].push({
543
543
  field: uniqueConstraints.join(','),
@@ -546,13 +546,13 @@ export class DataImporterRepos {
546
546
  .join(','),
547
547
  });
548
548
 
549
- // 应用冲突策略
549
+ // Apply conflict strategy
550
550
  switch (conflictStrategy) {
551
551
  case 'skip': {
552
552
  record.newRecord._skip = true;
553
553
  result.skips++;
554
554
 
555
- // 关键改进:即使跳过,也建立ID映射关系
555
+ // Key improvement: establish ID mapping even if skipped
556
556
  if (!isCompositeKey) {
557
557
  this.idMaps[tableName][record.originalId] = exists.id;
558
558
  if (record.newRecord.clientId) {
@@ -562,11 +562,11 @@ export class DataImporterRepos {
562
562
  break;
563
563
  }
564
564
  case 'override': {
565
- // 不需要额外操作,插入时会覆盖
565
+ // No additional operation needed, will be overridden on insert
566
566
  break;
567
567
  }
568
568
  case 'merge': {
569
- // 合并数据
569
+ // Merge data
570
570
  await trx
571
571
  .update(table)
572
572
  .set(record.newRecord)
@@ -582,30 +582,30 @@ export class DataImporterRepos {
582
582
  }
583
583
  }
584
584
  } else {
585
- // 处理唯一约束
585
+ // Process unique constraints
586
586
  for (const field of uniqueConstraints) {
587
587
  if (!record.newRecord[field]) continue;
588
588
 
589
- // 检查字段值是否已存在
589
+ // Check if field value already exists
590
590
  const exists = await trx.query[tableName].findFirst({
591
591
  where: eq(table[field], record.newRecord[field]),
592
592
  });
593
593
 
594
594
  if (exists) {
595
- // 记录冲突
595
+ // Record conflict
596
596
  if (!this.conflictRecords[tableName]) this.conflictRecords[tableName] = [];
597
597
  this.conflictRecords[tableName].push({
598
598
  field,
599
599
  value: record.newRecord[field],
600
600
  });
601
601
 
602
- // 应用冲突策略
602
+ // Apply conflict strategy
603
603
  switch (conflictStrategy) {
604
604
  case 'skip': {
605
605
  record.newRecord._skip = true;
606
606
  result.skips++;
607
607
 
608
- // 关键改进:即使跳过,也建立ID映射关系
608
+ // Key improvement: establish ID mapping even if skipped
609
609
  if (!isCompositeKey) {
610
610
  this.idMaps[tableName][record.originalId] = exists.id;
611
611
  if (record.newRecord.clientId) {
@@ -615,7 +615,7 @@ export class DataImporterRepos {
615
615
  break;
616
616
  }
617
617
  case 'override': {
618
- // 应用字段处理器
618
+ // Apply field processor
619
619
  if (field in fieldProcessors) {
620
620
  record.newRecord[field] = fieldProcessors[field](record.newRecord[field]);
621
621
  }
@@ -623,7 +623,7 @@ export class DataImporterRepos {
623
623
  }
624
624
 
625
625
  case 'merge': {
626
- // 合并数据
626
+ // Merge data
627
627
  await trx
628
628
  .update(table)
629
629
  .set(record.newRecord)
@@ -641,13 +641,13 @@ export class DataImporterRepos {
641
641
  }
642
642
  }
643
643
 
644
- // 过滤掉标记为跳过的记录
644
+ // Filter out records marked to be skipped
645
645
  const filteredData = preparedData.filter((record) => !record.newRecord._skip);
646
646
 
647
- // 清除临时标记
647
+ // Clear temporary markers
648
648
  filteredData.forEach((record) => delete record.newRecord._skip);
649
649
 
650
- // 6. 批量插入数据
650
+ // 6. Batch insert data
651
651
  const BATCH_SIZE = 100;
652
652
 
653
653
  for (let i = 0; i < filteredData.length; i += BATCH_SIZE) {
@@ -657,12 +657,12 @@ export class DataImporterRepos {
657
657
  const originalIds = batch.map((item) => item.originalId);
658
658
 
659
659
  try {
660
- // 插入并返回结果
660
+ // Insert and return result
661
661
  const insertQuery = trx.insert(table).values(itemsToInsert);
662
662
 
663
663
  let insertResult;
664
664
 
665
- // 只对非复合主键表需要返回ID
665
+ // Only non-composite key tables need to return ID
666
666
  if (!isCompositeKey) {
667
667
  const res = await insertQuery.returning();
668
668
  insertResult = res.map((item: any) => ({
@@ -671,18 +671,18 @@ export class DataImporterRepos {
671
671
  }));
672
672
  } else {
673
673
  await insertQuery;
674
- insertResult = itemsToInsert.map(() => ({})); // 创建空结果以维持计数
674
+ insertResult = itemsToInsert.map(() => ({})); // Create empty result to maintain count
675
675
  }
676
676
 
677
677
  result.added += insertResult.length;
678
678
 
679
- // 建立ID映射关系 (只对非复合主键表)
679
+ // Establish ID mapping relationship (only for non-composite key tables)
680
680
  if (!isCompositeKey) {
681
681
  for (const [j, newRecord] of insertResult.entries()) {
682
682
  const originalId = originalIds[j];
683
683
  this.idMaps[tableName][originalId] = newRecord.id;
684
684
 
685
- // 同时确保clientId也能映射到正确的ID
685
+ // Also ensure clientId can map to the correct ID
686
686
  const originalRecord = tableData.find((item) => item.id === originalId);
687
687
  if (originalRecord && originalRecord.clientId) {
688
688
  this.idMaps[tableName][originalRecord.clientId] = newRecord.id;
@@ -692,7 +692,7 @@ export class DataImporterRepos {
692
692
  } catch (error) {
693
693
  console.error(`Error batch inserting ${tableName}:`, error);
694
694
 
695
- // 处理错误并记录
695
+ // Handle error and record
696
696
  if ((error as any).code === '23505') {
697
697
  const match = (error as any).detail?.match(/Key \((.+?)\)=\((.+?)\) already exists/);
698
698
  if (match) {
@@ -146,17 +146,29 @@ describe('KnowledgeRepo', () => {
146
146
  expect(regularFile?.sourceType).toBe('file');
147
147
  });
148
148
 
149
- it('should show all documents in All category (no filtering)', async () => {
149
+ it('should exclude sourceType=file documents from All category', async () => {
150
150
  const results = await knowledgeRepo.query({ category: FilesTabs.All });
151
151
 
152
- // All category should include everything
153
- expect(results.length).toBeGreaterThanOrEqual(6); // 2 files + 4 documents
152
+ // All category should include files from files table + documents with sourceType != 'file'
153
+ // 2 files + 2 documents (api-pdf and web-doc)
154
+ // Excluded: uploaded-pdf and editor-doc both have sourceType='file'
155
+ expect(results.length).toBe(4);
154
156
 
157
+ // Should NOT include documents with sourceType='file' (globally excluded now)
155
158
  const editorDoc = results.find((item) => item.name === 'editor-doc.md');
156
159
  const uploadedPdf = results.find((item) => item.name === 'uploaded-pdf.pdf');
160
+ expect(editorDoc).toBeUndefined();
161
+ expect(uploadedPdf).toBeUndefined();
157
162
 
158
- expect(editorDoc).toBeDefined();
159
- expect(uploadedPdf).toBeDefined();
163
+ // Should include documents with sourceType != 'file'
164
+ const apiPdf = results.find((item) => item.name === 'api-pdf.pdf');
165
+ const webDoc = results.find((item) => item.name === 'web-doc.txt');
166
+ expect(apiPdf).toBeDefined();
167
+ expect(webDoc).toBeDefined();
168
+
169
+ // Should include files from files table
170
+ const regularFile = results.find((item) => item.name === 'regular-pdf-file.pdf');
171
+ expect(regularFile).toBeDefined();
160
172
  });
161
173
 
162
174
  it('should apply both filters together in Documents category', async () => {
@@ -281,7 +281,10 @@ export class KnowledgeRepo {
281
281
  q,
282
282
  knowledgeBaseId,
283
283
  }: QueryFileListParams = {}): ReturnType<typeof sql> {
284
- let whereConditions: any[] = [sql`${documents.userId} = ${this.userId}`];
284
+ let whereConditions: any[] = [
285
+ sql`${documents.userId} = ${this.userId}`,
286
+ sql`${documents.sourceType} != ${'file'}`,
287
+ ];
285
288
 
286
289
  // Search filter
287
290
  if (q) {
@@ -300,12 +303,9 @@ export class KnowledgeRepo {
300
303
  );
301
304
  whereConditions.push(sql`(${sql.join(orConditions, sql` OR `)})`);
302
305
 
303
- // Exclude custom/document and source_type='file' from Documents category
306
+ // Exclude custom/document from Documents category
304
307
  if (category === FilesTabs.Documents) {
305
- whereConditions.push(
306
- sql`${documents.fileType} != ${'custom/document'}`,
307
- sql`${documents.sourceType} != ${'file'}`,
308
- );
308
+ whereConditions.push(sql`${documents.fileType} != ${'custom/document'}`);
309
309
  }
310
310
  } else if (fileTypePrefix) {
311
311
  whereConditions.push(sql`${documents.fileType} ILIKE ${`${fileTypePrefix}%`}`);
@@ -10,7 +10,7 @@ import { files } from './file';
10
10
  import { users } from './user';
11
11
 
12
12
  /**
13
- * 生成主题表 - 用于组织和管理 AI 生成内容的主题
13
+ * Generation topics table - Used to organize and manage AI-generated content topics
14
14
  */
15
15
  export const generationTopics = pgTable('generation_topics', {
16
16
  id: text('id')
@@ -22,10 +22,10 @@ export const generationTopics = pgTable('generation_topics', {
22
22
  .references(() => users.id, { onDelete: 'cascade' })
23
23
  .notNull(),
24
24
 
25
- /** 简要描述主题内容,由 LLM 生成 */
25
+ /** Brief description of topic content, generated by LLM */
26
26
  title: text('title'),
27
27
 
28
- /** 主题封面图片 URL */
28
+ /** Topic cover image URL */
29
29
  coverUrl: text('cover_url'),
30
30
 
31
31
  ...timestamps,
@@ -37,7 +37,7 @@ export type NewGenerationTopic = typeof generationTopics.$inferInsert;
37
37
  export type GenerationTopicItem = typeof generationTopics.$inferSelect;
38
38
 
39
39
  /**
40
- * 生成批次表 - 存储一次生成请求的配置信息
40
+ * Generation batches table - Stores configuration information for a single generation request
41
41
  */
42
42
  export const generationBatches = pgTable('generation_batches', {
43
43
  id: text('id')
@@ -53,25 +53,25 @@ export const generationBatches = pgTable('generation_batches', {
53
53
  .notNull()
54
54
  .references(() => generationTopics.id, { onDelete: 'cascade' }),
55
55
 
56
- /** 服务商名称 */
56
+ /** Provider name */
57
57
  provider: text('provider').notNull(),
58
58
 
59
- /** 模型名称 */
59
+ /** Model name */
60
60
  model: text('model').notNull(),
61
61
 
62
- /** 生成提示词 */
62
+ /** Generation prompt */
63
63
  prompt: text('prompt').notNull(),
64
64
 
65
- /** 图片宽度 */
65
+ /** Image width */
66
66
  width: integer('width'),
67
67
 
68
- /** 图片高度 */
68
+ /** Image height */
69
69
  height: integer('height'),
70
70
 
71
- /** 图片比例 */
71
+ /** Image aspect ratio */
72
72
  ratio: varchar('ratio', { length: 64 }),
73
73
 
74
- /** 存储生成批次的配置,存放不需要建立索引的公共配置 */
74
+ /** Stores generation batch configuration for common settings that don't need indexing */
75
75
  config: jsonb('config'),
76
76
 
77
77
  ...timestamps,
@@ -86,7 +86,7 @@ export type GenerationBatchWithGenerations = GenerationBatchItem & {
86
86
  };
87
87
 
88
88
  /**
89
- * 存储单个 AI 生成信息
89
+ * Stores individual AI generation information
90
90
  */
91
91
  export const generations = pgTable('generations', {
92
92
  id: text('id')
@@ -102,18 +102,18 @@ export const generations = pgTable('generations', {
102
102
  .notNull()
103
103
  .references(() => generationBatches.id, { onDelete: 'cascade' }),
104
104
 
105
- /** 关联的异步任务 ID */
105
+ /** Associated async task ID */
106
106
  asyncTaskId: uuid('async_task_id').references(() => asyncTasks.id, {
107
107
  onDelete: 'set null',
108
108
  }),
109
109
 
110
- /** 关联的生成文件 ID,删除文件时连带删除生成记录 */
110
+ /** Associated generated file ID, deletes generation record when file is deleted */
111
111
  fileId: text('file_id').references(() => files.id, { onDelete: 'cascade' }),
112
112
 
113
- /** 生成种子值 */
113
+ /** Generation seed value */
114
114
  seed: integer('seed'),
115
115
 
116
- /** 生成的资源信息,包含存储在 s3 上的 key, 图片实际宽高,缩略图 key */
116
+ /** Generated asset information, including S3 storage key, actual image dimensions, thumbnail key, etc. */
117
117
  asset: jsonb('asset').$type<ImageGenerationAsset>(),
118
118
 
119
119
  ...timestamps,
@@ -20,7 +20,7 @@ export const nextauthAccounts = pgTable(
20
20
  session_state: text('session_state'),
21
21
  token_type: text('token_type'),
22
22
  type: text('type').$type<AdapterAccount>().notNull(),
23
- userId: text('userId')
23
+ userId: text('user_id')
24
24
  .notNull()
25
25
  .references(() => users.id, { onDelete: 'cascade' }),
26
26
  },
@@ -40,7 +40,7 @@ export const nextauthAccounts = pgTable(
40
40
  export const nextauthSessions = pgTable(`nextauth_sessions`, {
41
41
  expires: timestamp('expires', { mode: 'date' }).notNull(),
42
42
  sessionToken: text('sessionToken').primaryKey(),
43
- userId: text('userId')
43
+ userId: text('user_id')
44
44
  .notNull()
45
45
  .references(() => users.id, { onDelete: 'cascade' }),
46
46
  });
@@ -77,7 +77,7 @@ export const nextauthAuthenticators = pgTable(
77
77
  credentialPublicKey: text('credentialPublicKey').notNull(),
78
78
  providerAccountId: text('providerAccountId').notNull(),
79
79
  transports: text('transports'),
80
- userId: text('userId')
80
+ userId: text('user_id')
81
81
  .notNull()
82
82
  .references(() => users.id, { onDelete: 'cascade' }),
83
83
  },