mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-13 19:20:04 +00:00
🐛 fix: user email unique migration error (#10548)
This commit is contained in:
@@ -35,6 +35,8 @@ CREATE INDEX IF NOT EXISTS "verification_identifier_idx" ON "verifications" USIN
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_email_unique') THEN
|
||||
-- Normalize empty emails so the unique constraint can be created safely
|
||||
UPDATE "users" SET "email" = NULL WHERE "email" = '';
|
||||
ALTER TABLE "users" ADD CONSTRAINT "users_email_unique" UNIQUE ("email");
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
@@ -884,7 +884,7 @@
|
||||
"\nCREATE INDEX IF NOT EXISTS \"account_userId_idx\" ON \"accounts\" USING btree (\"user_id\");\n",
|
||||
"\nCREATE INDEX IF NOT EXISTS \"auth_session_userId_idx\" ON \"auth_sessions\" USING btree (\"user_id\");\n",
|
||||
"\nCREATE INDEX IF NOT EXISTS \"verification_identifier_idx\" ON \"verifications\" USING btree (\"identifier\");\n",
|
||||
"\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",
|
||||
"\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",
|
||||
"\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"
|
||||
],
|
||||
"bps": true,
|
||||
|
||||
@@ -149,9 +149,11 @@ export class UserModel {
|
||||
};
|
||||
|
||||
updateUser = async (value: Partial<UserItem>) => {
|
||||
const nextValue = UserModel.normalizeUniqueUserFields(value);
|
||||
|
||||
return this.db
|
||||
.update(users)
|
||||
.set({ ...value, updatedAt: new Date() })
|
||||
.set({ ...nextValue, updatedAt: new Date() })
|
||||
.where(eq(users.id, this.userId));
|
||||
};
|
||||
|
||||
@@ -193,6 +195,26 @@ export class UserModel {
|
||||
.where(eq(users.id, this.userId));
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalize unique user fields so empty strings become null, keeping unique constraints safe.
|
||||
*/
|
||||
private static normalizeUniqueUserFields = <
|
||||
T extends { email?: string | null; phone?: string | null },
|
||||
>(
|
||||
value: T,
|
||||
) => {
|
||||
const normalizedEmail =
|
||||
typeof value.email === 'string' && value.email.trim() === '' ? null : value.email;
|
||||
const normalizedPhone =
|
||||
typeof value.phone === 'string' && value.phone.trim() === '' ? null : value.phone;
|
||||
|
||||
return {
|
||||
...value,
|
||||
...(value.email !== undefined ? { email: normalizedEmail } : {}),
|
||||
...(value.phone !== undefined ? { phone: normalizedPhone } : {}),
|
||||
};
|
||||
};
|
||||
|
||||
// Static method
|
||||
static makeSureUserExist = async (db: LobeChatDatabase, userId: string) => {
|
||||
await db.insert(users).values({ id: userId }).onConflictDoNothing();
|
||||
@@ -205,10 +227,8 @@ export class UserModel {
|
||||
if (!!user) return { duplicate: true };
|
||||
}
|
||||
|
||||
const [user] = await db
|
||||
.insert(users)
|
||||
.values({ ...params })
|
||||
.returning();
|
||||
const normalizedParams = this.normalizeUniqueUserFields(params);
|
||||
const [user] = await db.insert(users).values(normalizedParams).returning();
|
||||
|
||||
return { duplicate: false, user };
|
||||
};
|
||||
|
||||
@@ -24,7 +24,33 @@ DATABASE_DRIVER=node
|
||||
if you have any other question, please open issue here: https://github.com/lobehub/lobe-chat/issues
|
||||
`;
|
||||
|
||||
const DUPLICATE_EMAIL_HINT = `------------------------------------------------------------------------------------------
|
||||
⚠️ Database migration failed due to duplicate email addresses in the users table.
|
||||
|
||||
The database schema requires each email to be unique, but multiple users currently share the same email value.
|
||||
|
||||
Recommended solutions (choose one and rerun the migration):
|
||||
|
||||
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
|
||||
2) Remove duplicate user records (dangerously, only if safe to delete)
|
||||
|
||||
⚠️ IMPORTANT: Always backup your database before making any changes!
|
||||
|
||||
To find duplicate emails, run this query:
|
||||
|
||||
\`\`\`sql
|
||||
SELECT email, COUNT(*) as count
|
||||
FROM users
|
||||
WHERE email IS NOT NULL
|
||||
GROUP BY email
|
||||
HAVING COUNT(*) > 1;
|
||||
\`\`\`
|
||||
|
||||
If you need further assistance, please open an issue: https://github.com/lobehub/lobe-chat/issues
|
||||
`;
|
||||
|
||||
module.exports = {
|
||||
DB_FAIL_INIT_HINT,
|
||||
DUPLICATE_EMAIL_HINT,
|
||||
PGVECTOR_HINT,
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ import { migrate as nodeMigrate } from 'drizzle-orm/node-postgres/migrator';
|
||||
import { join } from 'node:path';
|
||||
|
||||
// @ts-ignore tsgo handle esm import cjs and compatibility issues
|
||||
import { DB_FAIL_INIT_HINT, PGVECTOR_HINT } from './errorHint';
|
||||
import { DB_FAIL_INIT_HINT, DUPLICATE_EMAIL_HINT, PGVECTOR_HINT } from './errorHint';
|
||||
|
||||
// Read the `.env` file if it exists, or a file specified by the
|
||||
// dotenv_config_path parameter that's passed to Node.js
|
||||
@@ -39,8 +39,12 @@ if (!isDesktop && connectionString) {
|
||||
|
||||
const errMsg = err.message as string;
|
||||
|
||||
const constraint = (err as { constraint?: string })?.constraint;
|
||||
|
||||
if (errMsg.includes('extension "vector" is not available')) {
|
||||
console.info(PGVECTOR_HINT);
|
||||
} else if (constraint === 'users_email_unique' || errMsg.includes('users_email_unique')) {
|
||||
console.info(DUPLICATE_EMAIL_HINT);
|
||||
} else if (errMsg.includes(`Cannot read properties of undefined (reading 'migrate')`)) {
|
||||
console.info(DB_FAIL_INIT_HINT);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user