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

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 CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 2.0.0-next.144](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.143...v2.0.0-next.144)
6
+
7
+ <sup>Released on **2025-12-02**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: User email unique migration error.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: User email unique migration error, closes [#10548](https://github.com/lobehub/lobe-chat/issues/10548) ([ca2a1a2](https://github.com/lobehub/lobe-chat/commit/ca2a1a2))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
5
30
  ## [Version 2.0.0-next.143](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.142...v2.0.0-next.143)
6
31
 
7
32
  <sup>Released on **2025-12-02**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,13 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "fixes": [
5
+ "User email unique migration error."
6
+ ]
7
+ },
8
+ "date": "2025-12-02",
9
+ "version": "2.0.0-next.144"
10
+ },
2
11
  {
3
12
  "children": {
4
13
  "features": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/lobehub",
3
- "version": "2.0.0-next.143",
3
+ "version": "2.0.0-next.144",
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",
@@ -35,6 +35,8 @@ CREATE INDEX IF NOT EXISTS "verification_identifier_idx" ON "verifications" USIN
35
35
  DO $$
36
36
  BEGIN
37
37
  IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_email_unique') THEN
38
+ -- Normalize empty emails so the unique constraint can be created safely
39
+ UPDATE "users" SET "email" = NULL WHERE "email" = '';
38
40
  ALTER TABLE "users" ADD CONSTRAINT "users_email_unique" UNIQUE ("email");
39
41
  END IF;
40
42
  END $$;
@@ -884,7 +884,7 @@
884
884
  "\nCREATE INDEX IF NOT EXISTS \"account_userId_idx\" ON \"accounts\" USING btree (\"user_id\");\n",
885
885
  "\nCREATE INDEX IF NOT EXISTS \"auth_session_userId_idx\" ON \"auth_sessions\" USING btree (\"user_id\");\n",
886
886
  "\nCREATE INDEX IF NOT EXISTS \"verification_identifier_idx\" ON \"verifications\" USING btree (\"identifier\");\n",
887
- "\nDO $$\nBEGIN\n IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_email_unique') THEN\n ALTER TABLE \"users\" ADD CONSTRAINT \"users_email_unique\" UNIQUE (\"email\");\n END IF;\nEND $$;\n",
887
+ "\nDO $$\nBEGIN\n IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_email_unique') THEN\n UPDATE \"users\" SET \"email\" = NULL WHERE \"email\" = '';\n ALTER TABLE \"users\" ADD CONSTRAINT \"users_email_unique\" UNIQUE (\"email\");\n END IF;\nEND $$;\n",
888
888
  "\nDO $$\nBEGIN\n IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_phone_number_unique') THEN\n ALTER TABLE \"users\" ADD CONSTRAINT \"users_phone_number_unique\" UNIQUE (\"phone_number\");\n END IF;\nEND $$;\n"
889
889
  ],
890
890
  "bps": true,
@@ -149,9 +149,11 @@ export class UserModel {
149
149
  };
150
150
 
151
151
  updateUser = async (value: Partial<UserItem>) => {
152
+ const nextValue = UserModel.normalizeUniqueUserFields(value);
153
+
152
154
  return this.db
153
155
  .update(users)
154
- .set({ ...value, updatedAt: new Date() })
156
+ .set({ ...nextValue, updatedAt: new Date() })
155
157
  .where(eq(users.id, this.userId));
156
158
  };
157
159
 
@@ -193,6 +195,26 @@ export class UserModel {
193
195
  .where(eq(users.id, this.userId));
194
196
  };
195
197
 
198
+ /**
199
+ * Normalize unique user fields so empty strings become null, keeping unique constraints safe.
200
+ */
201
+ private static normalizeUniqueUserFields = <
202
+ T extends { email?: string | null; phone?: string | null },
203
+ >(
204
+ value: T,
205
+ ) => {
206
+ const normalizedEmail =
207
+ typeof value.email === 'string' && value.email.trim() === '' ? null : value.email;
208
+ const normalizedPhone =
209
+ typeof value.phone === 'string' && value.phone.trim() === '' ? null : value.phone;
210
+
211
+ return {
212
+ ...value,
213
+ ...(value.email !== undefined ? { email: normalizedEmail } : {}),
214
+ ...(value.phone !== undefined ? { phone: normalizedPhone } : {}),
215
+ };
216
+ };
217
+
196
218
  // Static method
197
219
  static makeSureUserExist = async (db: LobeChatDatabase, userId: string) => {
198
220
  await db.insert(users).values({ id: userId }).onConflictDoNothing();
@@ -205,10 +227,8 @@ export class UserModel {
205
227
  if (!!user) return { duplicate: true };
206
228
  }
207
229
 
208
- const [user] = await db
209
- .insert(users)
210
- .values({ ...params })
211
- .returning();
230
+ const normalizedParams = this.normalizeUniqueUserFields(params);
231
+ const [user] = await db.insert(users).values(normalizedParams).returning();
212
232
 
213
233
  return { duplicate: false, user };
214
234
  };
@@ -24,7 +24,33 @@ DATABASE_DRIVER=node
24
24
  if you have any other question, please open issue here: https://github.com/lobehub/lobe-chat/issues
25
25
  `;
26
26
 
27
+ const DUPLICATE_EMAIL_HINT = `------------------------------------------------------------------------------------------
28
+ ⚠️ Database migration failed due to duplicate email addresses in the users table.
29
+
30
+ The database schema requires each email to be unique, but multiple users currently share the same email value.
31
+
32
+ Recommended solutions (choose one and rerun the migration):
33
+
34
+ 1) Update duplicate emails to make them unique: change the conflicting email addresses to another unique email address or just change them email to NULL
35
+ 2) Remove duplicate user records (dangerously, only if safe to delete)
36
+
37
+ ⚠️ IMPORTANT: Always backup your database before making any changes!
38
+
39
+ To find duplicate emails, run this query:
40
+
41
+ \`\`\`sql
42
+ SELECT email, COUNT(*) as count
43
+ FROM users
44
+ WHERE email IS NOT NULL
45
+ GROUP BY email
46
+ HAVING COUNT(*) > 1;
47
+ \`\`\`
48
+
49
+ If you need further assistance, please open an issue: https://github.com/lobehub/lobe-chat/issues
50
+ `;
51
+
27
52
  module.exports = {
28
53
  DB_FAIL_INIT_HINT,
54
+ DUPLICATE_EMAIL_HINT,
29
55
  PGVECTOR_HINT,
30
56
  };
@@ -4,7 +4,7 @@ import { migrate as nodeMigrate } from 'drizzle-orm/node-postgres/migrator';
4
4
  import { join } from 'node:path';
5
5
 
6
6
  // @ts-ignore tsgo handle esm import cjs and compatibility issues
7
- import { DB_FAIL_INIT_HINT, PGVECTOR_HINT } from './errorHint';
7
+ import { DB_FAIL_INIT_HINT, DUPLICATE_EMAIL_HINT, PGVECTOR_HINT } from './errorHint';
8
8
 
9
9
  // Read the `.env` file if it exists, or a file specified by the
10
10
  // dotenv_config_path parameter that's passed to Node.js
@@ -39,8 +39,12 @@ if (!isDesktop && connectionString) {
39
39
 
40
40
  const errMsg = err.message as string;
41
41
 
42
+ const constraint = (err as { constraint?: string })?.constraint;
43
+
42
44
  if (errMsg.includes('extension "vector" is not available')) {
43
45
  console.info(PGVECTOR_HINT);
46
+ } else if (constraint === 'users_email_unique' || errMsg.includes('users_email_unique')) {
47
+ console.info(DUPLICATE_EMAIL_HINT);
44
48
  } else if (errMsg.includes(`Cannot read properties of undefined (reading 'migrate')`)) {
45
49
  console.info(DB_FAIL_INIT_HINT);
46
50
  }