@lobehub/chat 1.74.2 → 1.74.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.
Files changed (85) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/changelog/v1.json +9 -0
  3. package/docs/developer/database-schema.dbml +54 -2
  4. package/locales/ar/common.json +51 -0
  5. package/locales/ar/models.json +69 -3
  6. package/locales/ar/providers.json +6 -0
  7. package/locales/bg-BG/common.json +51 -0
  8. package/locales/bg-BG/models.json +69 -3
  9. package/locales/bg-BG/providers.json +6 -0
  10. package/locales/de-DE/common.json +51 -0
  11. package/locales/de-DE/models.json +69 -3
  12. package/locales/de-DE/providers.json +6 -0
  13. package/locales/en-US/common.json +51 -0
  14. package/locales/en-US/models.json +69 -3
  15. package/locales/en-US/providers.json +6 -3
  16. package/locales/es-ES/common.json +51 -0
  17. package/locales/es-ES/models.json +69 -3
  18. package/locales/es-ES/providers.json +6 -0
  19. package/locales/fa-IR/common.json +51 -0
  20. package/locales/fa-IR/models.json +69 -3
  21. package/locales/fa-IR/providers.json +6 -0
  22. package/locales/fr-FR/common.json +51 -0
  23. package/locales/fr-FR/models.json +69 -3
  24. package/locales/fr-FR/providers.json +6 -0
  25. package/locales/it-IT/common.json +51 -0
  26. package/locales/it-IT/models.json +69 -3
  27. package/locales/it-IT/providers.json +6 -0
  28. package/locales/ja-JP/common.json +51 -0
  29. package/locales/ja-JP/models.json +78 -4
  30. package/locales/ja-JP/providers.json +6 -0
  31. package/locales/ko-KR/common.json +51 -0
  32. package/locales/ko-KR/models.json +69 -3
  33. package/locales/ko-KR/providers.json +6 -0
  34. package/locales/nl-NL/common.json +51 -0
  35. package/locales/nl-NL/models.json +69 -3
  36. package/locales/nl-NL/providers.json +6 -0
  37. package/locales/pl-PL/common.json +51 -0
  38. package/locales/pl-PL/models.json +69 -3
  39. package/locales/pl-PL/providers.json +6 -0
  40. package/locales/pt-BR/common.json +51 -0
  41. package/locales/pt-BR/models.json +69 -3
  42. package/locales/pt-BR/providers.json +6 -0
  43. package/locales/ru-RU/common.json +51 -0
  44. package/locales/ru-RU/models.json +69 -3
  45. package/locales/ru-RU/providers.json +6 -0
  46. package/locales/tr-TR/common.json +51 -0
  47. package/locales/tr-TR/models.json +69 -3
  48. package/locales/tr-TR/providers.json +6 -0
  49. package/locales/vi-VN/common.json +51 -0
  50. package/locales/vi-VN/models.json +69 -3
  51. package/locales/vi-VN/providers.json +6 -0
  52. package/locales/zh-CN/common.json +53 -2
  53. package/locales/zh-CN/models.json +79 -13
  54. package/locales/zh-CN/providers.json +6 -4
  55. package/locales/zh-TW/common.json +51 -0
  56. package/locales/zh-TW/models.json +81 -4
  57. package/locales/zh-TW/providers.json +6 -0
  58. package/package.json +1 -1
  59. package/packages/web-crawler/src/utils/__tests__/withTimeout.test.ts +0 -1
  60. package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/Checker.tsx +4 -0
  61. package/src/database/client/db.ts +102 -11
  62. package/src/database/client/migrations.json +38 -8
  63. package/src/database/migrations/0018_add_client_id_for_entities.sql +32 -0
  64. package/src/database/migrations/meta/0018_snapshot.json +4212 -0
  65. package/src/database/migrations/meta/_journal.json +7 -0
  66. package/src/database/models/drizzleMigration.ts +23 -0
  67. package/src/database/schemas/agent.ts +48 -31
  68. package/src/database/schemas/file.ts +32 -16
  69. package/src/database/schemas/message.ts +91 -54
  70. package/src/database/schemas/rag.ts +65 -32
  71. package/src/database/schemas/session.ts +6 -3
  72. package/src/database/schemas/topic.ts +31 -24
  73. package/src/features/InitClientDB/ErrorResult.tsx +53 -32
  74. package/src/features/InitClientDB/features/DatabaseRepair/Backup.tsx +77 -0
  75. package/src/features/InitClientDB/features/DatabaseRepair/Diagnosis.tsx +98 -0
  76. package/src/features/InitClientDB/features/DatabaseRepair/Repair.tsx +220 -0
  77. package/src/features/InitClientDB/features/DatabaseRepair/index.tsx +85 -0
  78. package/src/features/ModelSwitchPanel/index.tsx +13 -7
  79. package/src/locales/default/common.ts +53 -1
  80. package/src/store/global/actions/clientDb.ts +19 -3
  81. package/src/store/global/initialState.ts +6 -1
  82. package/src/store/global/selectors/clientDB.ts +43 -0
  83. package/src/store/global/selectors/index.ts +1 -0
  84. package/src/store/user/slices/settings/selectors/general.test.ts +90 -0
  85. package/src/types/clientDB.ts +13 -0
@@ -126,6 +126,13 @@
126
126
  "when": 1742269437903,
127
127
  "tag": "0017_add_user_id_to_tables",
128
128
  "breakpoints": true
129
+ },
130
+ {
131
+ "idx": 18,
132
+ "version": "7",
133
+ "when": 1742616026643,
134
+ "tag": "0018_add_client_id_for_entities",
135
+ "breakpoints": true
129
136
  }
130
137
  ],
131
138
  "version": "6"
@@ -0,0 +1,23 @@
1
+ import { LobeChatDatabase } from '@/database/type';
2
+ import { MigrationTableItem } from '@/types/clientDB';
3
+
4
+ export class DrizzleMigrationModel {
5
+ private db: LobeChatDatabase;
6
+
7
+ constructor(db: LobeChatDatabase) {
8
+ this.db = db;
9
+ }
10
+
11
+ getMigrationList = async () => {
12
+ const res = await this.db.execute(
13
+ 'SELECT * FROM "drizzle"."__drizzle_migrations" ORDER BY "created_at" DESC;',
14
+ );
15
+
16
+ return res.rows as unknown as MigrationTableItem[];
17
+ };
18
+ getLatestMigrationHash = async () => {
19
+ const res = await this.getMigrationList();
20
+
21
+ return res[0].hash;
22
+ };
23
+ }
@@ -1,5 +1,13 @@
1
1
  /* eslint-disable sort-keys-fix/sort-keys-fix */
2
- import { boolean, jsonb, pgTable, primaryKey, text, varchar } from 'drizzle-orm/pg-core';
2
+ import {
3
+ boolean,
4
+ jsonb,
5
+ pgTable,
6
+ primaryKey,
7
+ text,
8
+ uniqueIndex,
9
+ varchar,
10
+ } from 'drizzle-orm/pg-core';
3
11
  import { createInsertSchema } from 'drizzle-zod';
4
12
 
5
13
  import { idGenerator, randomSlug } from '@/database/utils/idGenerator';
@@ -13,36 +21,45 @@ import { users } from './user';
13
21
  // agent is a model that represents the assistant that is created by the user
14
22
  // agent can have its own knowledge base and files
15
23
 
16
- export const agents = pgTable('agents', {
17
- id: text('id')
18
- .primaryKey()
19
- .$defaultFn(() => idGenerator('agents'))
20
- .notNull(),
21
- slug: varchar('slug', { length: 100 })
22
- .$defaultFn(() => randomSlug(4))
23
- .unique(),
24
- title: text('title'),
25
- description: text('description'),
26
- tags: jsonb('tags').$type<string[]>().default([]),
27
- avatar: text('avatar'),
28
- backgroundColor: text('background_color'),
29
-
30
- plugins: jsonb('plugins').$type<string[]>().default([]),
31
- userId: text('user_id')
32
- .references(() => users.id, { onDelete: 'cascade' })
33
- .notNull(),
34
-
35
- chatConfig: jsonb('chat_config').$type<LobeAgentChatConfig>(),
36
-
37
- fewShots: jsonb('few_shots'),
38
- model: text('model'),
39
- params: jsonb('params').default({}),
40
- provider: text('provider'),
41
- systemRole: text('system_role'),
42
- tts: jsonb('tts').$type<LobeAgentTTSConfig>(),
43
-
44
- ...timestamps,
45
- });
24
+ export const agents = pgTable(
25
+ 'agents',
26
+ {
27
+ id: text('id')
28
+ .primaryKey()
29
+ .$defaultFn(() => idGenerator('agents'))
30
+ .notNull(),
31
+ slug: varchar('slug', { length: 100 })
32
+ .$defaultFn(() => randomSlug(4))
33
+ .unique(),
34
+ title: text('title'),
35
+ description: text('description'),
36
+ tags: jsonb('tags').$type<string[]>().default([]),
37
+ avatar: text('avatar'),
38
+ backgroundColor: text('background_color'),
39
+
40
+ plugins: jsonb('plugins').$type<string[]>().default([]),
41
+
42
+ clientId: text('client_id'),
43
+
44
+ userId: text('user_id')
45
+ .references(() => users.id, { onDelete: 'cascade' })
46
+ .notNull(),
47
+
48
+ chatConfig: jsonb('chat_config').$type<LobeAgentChatConfig>(),
49
+
50
+ fewShots: jsonb('few_shots'),
51
+ model: text('model'),
52
+ params: jsonb('params').default({}),
53
+ provider: text('provider'),
54
+ systemRole: text('system_role'),
55
+ tts: jsonb('tts').$type<LobeAgentTTSConfig>(),
56
+
57
+ ...timestamps,
58
+ },
59
+ (t) => ({
60
+ clientIdUnique: uniqueIndex('client_id_user_id_unique').on(t.clientId, t.userId),
61
+ }),
62
+ );
46
63
 
47
64
  export const insertAgentSchema = createInsertSchema(agents);
48
65
 
@@ -7,6 +7,7 @@ import {
7
7
  pgTable,
8
8
  primaryKey,
9
9
  text,
10
+ uniqueIndex,
10
11
  uuid,
11
12
  varchar,
12
13
  } from 'drizzle-orm/pg-core';
@@ -52,6 +53,7 @@ export const files = pgTable(
52
53
  size: integer('size').notNull(),
53
54
  url: text('url').notNull(),
54
55
 
56
+ clientId: text('client_id'),
55
57
  metadata: jsonb('metadata'),
56
58
  chunkTaskId: uuid('chunk_task_id').references(() => asyncTasks.id, { onDelete: 'set null' }),
57
59
  embeddingTaskId: uuid('embedding_task_id').references(() => asyncTasks.id, {
@@ -63,33 +65,47 @@ export const files = pgTable(
63
65
  (table) => {
64
66
  return {
65
67
  fileHashIdx: index('file_hash_idx').on(table.fileHash),
68
+ clientIdUnique: uniqueIndex('files_client_id_user_id_unique').on(
69
+ table.clientId,
70
+ table.userId,
71
+ ),
66
72
  };
67
73
  },
68
74
  );
69
75
  export type NewFile = typeof files.$inferInsert;
70
76
  export type FileItem = typeof files.$inferSelect;
71
77
 
72
- export const knowledgeBases = pgTable('knowledge_bases', {
73
- id: text('id')
74
- .$defaultFn(() => idGenerator('knowledgeBases'))
75
- .primaryKey(),
78
+ export const knowledgeBases = pgTable(
79
+ 'knowledge_bases',
80
+ {
81
+ id: text('id')
82
+ .$defaultFn(() => idGenerator('knowledgeBases'))
83
+ .primaryKey(),
76
84
 
77
- name: text('name').notNull(),
78
- description: text('description'),
79
- avatar: text('avatar'),
85
+ name: text('name').notNull(),
86
+ description: text('description'),
87
+ avatar: text('avatar'),
80
88
 
81
- // different types of knowledge bases need to be distinguished
82
- type: text('type'),
83
- userId: text('user_id')
84
- .references(() => users.id, { onDelete: 'cascade' })
85
- .notNull(),
89
+ // different types of knowledge bases need to be distinguished
90
+ type: text('type'),
91
+ userId: text('user_id')
92
+ .references(() => users.id, { onDelete: 'cascade' })
93
+ .notNull(),
94
+ clientId: text('client_id'),
86
95
 
87
- isPublic: boolean('is_public').default(false),
96
+ isPublic: boolean('is_public').default(false),
88
97
 
89
- settings: jsonb('settings'),
98
+ settings: jsonb('settings'),
90
99
 
91
- ...timestamps,
92
- });
100
+ ...timestamps,
101
+ },
102
+ (t) => ({
103
+ clientIdUnique: uniqueIndex('knowledge_bases_client_id_user_id_unique').on(
104
+ t.clientId,
105
+ t.userId,
106
+ ),
107
+ }),
108
+ );
93
109
 
94
110
  export const insertKnowledgeBasesSchema = createInsertSchema(knowledgeBases);
95
111
 
@@ -80,52 +80,79 @@ export const messages = pgTable(
80
80
  );
81
81
 
82
82
  // if the message container a plugin
83
- export const messagePlugins = pgTable('message_plugins', {
84
- id: text('id')
85
- .references(() => messages.id, { onDelete: 'cascade' })
86
- .primaryKey(),
87
-
88
- toolCallId: text('tool_call_id'),
89
- type: text('type', {
90
- enum: ['default', 'markdown', 'standalone', 'builtin'],
91
- }).default('default'),
92
-
93
- apiName: text('api_name'),
94
- arguments: text('arguments'),
95
- identifier: text('identifier'),
96
- state: jsonb('state'),
97
- error: jsonb('error'),
98
- userId: text('user_id')
99
- .references(() => users.id, { onDelete: 'cascade' })
100
- .notNull(),
101
- });
83
+ export const messagePlugins = pgTable(
84
+ 'message_plugins',
85
+ {
86
+ id: text('id')
87
+ .references(() => messages.id, { onDelete: 'cascade' })
88
+ .primaryKey(),
89
+
90
+ toolCallId: text('tool_call_id'),
91
+ type: text('type', {
92
+ enum: ['default', 'markdown', 'standalone', 'builtin'],
93
+ }).default('default'),
94
+
95
+ apiName: text('api_name'),
96
+ arguments: text('arguments'),
97
+ identifier: text('identifier'),
98
+ state: jsonb('state'),
99
+ error: jsonb('error'),
100
+ clientId: text('client_id'),
101
+ userId: text('user_id')
102
+ .references(() => users.id, { onDelete: 'cascade' })
103
+ .notNull(),
104
+ },
105
+ (t) => ({
106
+ clientIdUnique: uniqueIndex('message_plugins_client_id_user_id_unique').on(
107
+ t.clientId,
108
+ t.userId,
109
+ ),
110
+ }),
111
+ );
102
112
 
103
113
  export type MessagePluginItem = typeof messagePlugins.$inferSelect;
104
114
  export const updateMessagePluginSchema = createSelectSchema(messagePlugins);
105
115
 
106
- export const messageTTS = pgTable('message_tts', {
107
- id: text('id')
108
- .references(() => messages.id, { onDelete: 'cascade' })
109
- .primaryKey(),
110
- contentMd5: text('content_md5'),
111
- fileId: text('file_id').references(() => files.id, { onDelete: 'cascade' }),
112
- voice: text('voice'),
113
- userId: text('user_id')
114
- .references(() => users.id, { onDelete: 'cascade' })
115
- .notNull(),
116
- });
117
-
118
- export const messageTranslates = pgTable('message_translates', {
119
- id: text('id')
120
- .references(() => messages.id, { onDelete: 'cascade' })
121
- .primaryKey(),
122
- content: text('content'),
123
- from: text('from'),
124
- to: text('to'),
125
- userId: text('user_id')
126
- .references(() => users.id, { onDelete: 'cascade' })
127
- .notNull(),
128
- });
116
+ export const messageTTS = pgTable(
117
+ 'message_tts',
118
+ {
119
+ id: text('id')
120
+ .references(() => messages.id, { onDelete: 'cascade' })
121
+ .primaryKey(),
122
+ contentMd5: text('content_md5'),
123
+ fileId: text('file_id').references(() => files.id, { onDelete: 'cascade' }),
124
+ voice: text('voice'),
125
+ clientId: text('client_id'),
126
+ userId: text('user_id')
127
+ .references(() => users.id, { onDelete: 'cascade' })
128
+ .notNull(),
129
+ },
130
+ (t) => ({
131
+ clientIdUnique: uniqueIndex('message_tts_client_id_user_id_unique').on(t.clientId, t.userId),
132
+ }),
133
+ );
134
+
135
+ export const messageTranslates = pgTable(
136
+ 'message_translates',
137
+ {
138
+ id: text('id')
139
+ .references(() => messages.id, { onDelete: 'cascade' })
140
+ .primaryKey(),
141
+ content: text('content'),
142
+ from: text('from'),
143
+ to: text('to'),
144
+ clientId: text('client_id'),
145
+ userId: text('user_id')
146
+ .references(() => users.id, { onDelete: 'cascade' })
147
+ .notNull(),
148
+ },
149
+ (t) => ({
150
+ clientIdUnique: uniqueIndex('message_translates_client_id_user_id_unique').on(
151
+ t.clientId,
152
+ t.userId,
153
+ ),
154
+ }),
155
+ );
129
156
 
130
157
  // if the message contains a file
131
158
  // save the file id and message id
@@ -147,18 +174,28 @@ export const messagesFiles = pgTable(
147
174
  }),
148
175
  );
149
176
 
150
- export const messageQueries = pgTable('message_queries', {
151
- id: uuid('id').defaultRandom().primaryKey(),
152
- messageId: text('message_id')
153
- .references(() => messages.id, { onDelete: 'cascade' })
154
- .notNull(),
155
- rewriteQuery: text('rewrite_query'),
156
- userQuery: text('user_query'),
157
- userId: text('user_id')
158
- .references(() => users.id, { onDelete: 'cascade' })
159
- .notNull(),
160
- embeddingsId: uuid('embeddings_id').references(() => embeddings.id, { onDelete: 'set null' }),
161
- });
177
+ export const messageQueries = pgTable(
178
+ 'message_queries',
179
+ {
180
+ id: uuid('id').defaultRandom().primaryKey(),
181
+ messageId: text('message_id')
182
+ .references(() => messages.id, { onDelete: 'cascade' })
183
+ .notNull(),
184
+ rewriteQuery: text('rewrite_query'),
185
+ userQuery: text('user_query'),
186
+ clientId: text('client_id'),
187
+ userId: text('user_id')
188
+ .references(() => users.id, { onDelete: 'cascade' })
189
+ .notNull(),
190
+ embeddingsId: uuid('embeddings_id').references(() => embeddings.id, { onDelete: 'set null' }),
191
+ },
192
+ (t) => ({
193
+ clientIdUnique: uniqueIndex('message_queries_client_id_user_id_unique').on(
194
+ t.clientId,
195
+ t.userId,
196
+ ),
197
+ }),
198
+ );
162
199
 
163
200
  export type NewMessageQuery = typeof messageQueries.$inferInsert;
164
201
 
@@ -1,51 +1,84 @@
1
1
  /* eslint-disable sort-keys-fix/sort-keys-fix */
2
- import { integer, jsonb, pgTable, text, uuid, varchar, vector } from 'drizzle-orm/pg-core';
2
+ import {
3
+ integer,
4
+ jsonb,
5
+ pgTable,
6
+ text,
7
+ uniqueIndex,
8
+ uuid,
9
+ varchar,
10
+ vector,
11
+ } from 'drizzle-orm/pg-core';
3
12
 
4
13
  import { timestamps } from './_helpers';
5
14
  import { files } from './file';
6
15
  import { users } from './user';
7
16
 
8
- export const chunks = pgTable('chunks', {
9
- id: uuid('id').defaultRandom().primaryKey(),
10
- text: text('text'),
11
- abstract: text('abstract'),
12
- metadata: jsonb('metadata'),
13
- index: integer('index'),
14
- type: varchar('type'),
17
+ export const chunks = pgTable(
18
+ 'chunks',
19
+ {
20
+ id: uuid('id').defaultRandom().primaryKey(),
21
+ text: text('text'),
22
+ abstract: text('abstract'),
23
+ metadata: jsonb('metadata'),
24
+ index: integer('index'),
25
+ type: varchar('type'),
15
26
 
16
- userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
27
+ clientId: text('client_id'),
28
+ userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
17
29
 
18
- ...timestamps,
19
- });
30
+ ...timestamps,
31
+ },
32
+ (t) => ({
33
+ clientIdUnique: uniqueIndex('chunks_client_id_user_id_unique').on(t.clientId, t.userId),
34
+ }),
35
+ );
20
36
 
21
37
  export type NewChunkItem = typeof chunks.$inferInsert & { fileId?: string };
22
38
 
23
- export const unstructuredChunks = pgTable('unstructured_chunks', {
24
- id: uuid('id').defaultRandom().primaryKey(),
25
- text: text('text'),
26
- metadata: jsonb('metadata'),
27
- index: integer('index'),
28
- type: varchar('type'),
39
+ export const unstructuredChunks = pgTable(
40
+ 'unstructured_chunks',
41
+ {
42
+ id: uuid('id').defaultRandom().primaryKey(),
43
+ text: text('text'),
44
+ metadata: jsonb('metadata'),
45
+ index: integer('index'),
46
+ type: varchar('type'),
29
47
 
30
- ...timestamps,
48
+ ...timestamps,
31
49
 
32
- parentId: varchar('parent_id'),
33
- compositeId: uuid('composite_id').references(() => chunks.id, { onDelete: 'cascade' }),
34
- userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
35
- fileId: varchar('file_id').references(() => files.id, { onDelete: 'cascade' }),
36
- });
50
+ parentId: varchar('parent_id'),
51
+ compositeId: uuid('composite_id').references(() => chunks.id, { onDelete: 'cascade' }),
52
+ clientId: text('client_id'),
53
+ userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
54
+ fileId: varchar('file_id').references(() => files.id, { onDelete: 'cascade' }),
55
+ },
56
+ (t) => ({
57
+ clientIdUnique: uniqueIndex('unstructured_chunks_client_id_user_id_unique').on(
58
+ t.clientId,
59
+ t.userId,
60
+ ),
61
+ }),
62
+ );
37
63
 
38
64
  export type NewUnstructuredChunkItem = typeof unstructuredChunks.$inferInsert;
39
65
 
40
- export const embeddings = pgTable('embeddings', {
41
- id: uuid('id').defaultRandom().primaryKey(),
42
- chunkId: uuid('chunk_id')
43
- .references(() => chunks.id, { onDelete: 'cascade' })
44
- .unique(),
45
- embeddings: vector('embeddings', { dimensions: 1024 }),
46
- model: text('model'),
47
- userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
48
- });
66
+ export const embeddings = pgTable(
67
+ 'embeddings',
68
+ {
69
+ id: uuid('id').defaultRandom().primaryKey(),
70
+ chunkId: uuid('chunk_id')
71
+ .references(() => chunks.id, { onDelete: 'cascade' })
72
+ .unique(),
73
+ embeddings: vector('embeddings', { dimensions: 1024 }),
74
+ model: text('model'),
75
+ clientId: text('client_id'),
76
+ userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
77
+ },
78
+ (t) => ({
79
+ clientIdUnique: uniqueIndex('embeddings_client_id_user_id_unique').on(t.clientId, t.userId),
80
+ }),
81
+ );
49
82
 
50
83
  export type NewEmbeddingsItem = typeof embeddings.$inferInsert;
51
84
  export type EmbeddingsSelectItem = typeof embeddings.$inferSelect;
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable sort-keys-fix/sort-keys-fix */
2
- import { boolean, integer, pgTable, text, unique, uniqueIndex, varchar } from 'drizzle-orm/pg-core';
2
+ import { boolean, integer, pgTable, text, uniqueIndex, varchar } from 'drizzle-orm/pg-core';
3
3
  import { createInsertSchema } from 'drizzle-zod';
4
4
 
5
5
  import { idGenerator, randomSlug } from '@/database/utils/idGenerator';
@@ -26,7 +26,10 @@ export const sessionGroups = pgTable(
26
26
  ...timestamps,
27
27
  },
28
28
  (table) => ({
29
- clientIdUnique: unique('session_group_client_id_user_unique').on(table.clientId, table.userId),
29
+ clientIdUnique: uniqueIndex('session_groups_client_id_user_id_unique').on(
30
+ table.clientId,
31
+ table.userId,
32
+ ),
30
33
  }),
31
34
  );
32
35
 
@@ -65,7 +68,7 @@ export const sessions = pgTable(
65
68
  (t) => ({
66
69
  slugUserIdUnique: uniqueIndex('slug_user_id_unique').on(t.slug, t.userId),
67
70
 
68
- clientIdUnique: unique('sessions_client_id_user_id_unique').on(t.clientId, t.userId),
71
+ clientIdUnique: uniqueIndex('sessions_client_id_user_id_unique').on(t.clientId, t.userId),
69
72
  }),
70
73
  );
71
74
 
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable sort-keys-fix/sort-keys-fix */
2
- import { boolean, jsonb, pgTable, text, unique } from 'drizzle-orm/pg-core';
2
+ import { boolean, jsonb, pgTable, text, uniqueIndex } from 'drizzle-orm/pg-core';
3
3
  import { createInsertSchema } from 'drizzle-zod';
4
4
 
5
5
  import { idGenerator } from '@/database/utils/idGenerator';
@@ -27,7 +27,7 @@ export const topics = pgTable(
27
27
  ...timestamps,
28
28
  },
29
29
  (t) => ({
30
- clientIdUnique: unique('topic_client_id_user_id_unique').on(t.clientId, t.userId),
30
+ clientIdUnique: uniqueIndex('topics_client_id_user_id_unique').on(t.clientId, t.userId),
31
31
  }),
32
32
  );
33
33
 
@@ -35,28 +35,35 @@ export type NewTopic = typeof topics.$inferInsert;
35
35
  export type TopicItem = typeof topics.$inferSelect;
36
36
 
37
37
  // @ts-ignore
38
- export const threads = pgTable('threads', {
39
- id: text('id')
40
- .$defaultFn(() => idGenerator('threads', 16))
41
- .primaryKey(),
42
-
43
- title: text('title'),
44
- type: text('type', { enum: ['continuation', 'standalone'] }).notNull(),
45
- status: text('status', { enum: ['active', 'deprecated', 'archived'] }).default('active'),
46
- topicId: text('topic_id')
47
- .references(() => topics.id, { onDelete: 'cascade' })
48
- .notNull(),
49
- sourceMessageId: text('source_message_id').notNull(),
50
- // @ts-ignore
51
- parentThreadId: text('parent_thread_id').references(() => threads.id, { onDelete: 'set null' }),
52
-
53
- userId: text('user_id')
54
- .references(() => users.id, { onDelete: 'cascade' })
55
- .notNull(),
56
-
57
- lastActiveAt: timestamptz('last_active_at').defaultNow(),
58
- ...timestamps,
59
- });
38
+ export const threads = pgTable(
39
+ 'threads',
40
+ {
41
+ id: text('id')
42
+ .$defaultFn(() => idGenerator('threads', 16))
43
+ .primaryKey(),
44
+
45
+ title: text('title'),
46
+ type: text('type', { enum: ['continuation', 'standalone'] }).notNull(),
47
+ status: text('status', { enum: ['active', 'deprecated', 'archived'] }).default('active'),
48
+ topicId: text('topic_id')
49
+ .references(() => topics.id, { onDelete: 'cascade' })
50
+ .notNull(),
51
+ sourceMessageId: text('source_message_id').notNull(),
52
+ // @ts-ignore
53
+ parentThreadId: text('parent_thread_id').references(() => threads.id, { onDelete: 'set null' }),
54
+ clientId: text('client_id'),
55
+
56
+ userId: text('user_id')
57
+ .references(() => users.id, { onDelete: 'cascade' })
58
+ .notNull(),
59
+
60
+ lastActiveAt: timestamptz('last_active_at').defaultNow(),
61
+ ...timestamps,
62
+ },
63
+ (t) => ({
64
+ clientIdUnique: uniqueIndex('threads_client_id_user_id_unique').on(t.clientId, t.userId),
65
+ }),
66
+ );
60
67
 
61
68
  export type NewThread = typeof threads.$inferInsert;
62
69
  export type ThreadItem = typeof threads.$inferSelect;