diff --git a/client/web/src/components/modals/SettingsView/Account.tsx b/client/web/src/components/modals/SettingsView/Account.tsx
index 63a38af3..126f9708 100644
--- a/client/web/src/components/modals/SettingsView/Account.tsx
+++ b/client/web/src/components/modals/SettingsView/Account.tsx
@@ -24,11 +24,11 @@ import {
} from 'tailchat-shared';
import { EmailVerify } from '../EmailVerify';
import { ModifyPassword } from '../ModifyPassword';
+import { isBuiltinEmail } from '@/utils/user-helper';
export const SettingsAccount: React.FC = React.memo(() => {
const userInfo = useUserInfo();
const dispatch = useAppDispatch();
- const navigate = useNavigate();
const userExtra = userInfo?.extra ?? {};
const [, handleUserAvatarChange] = useAsyncRequest(
@@ -113,7 +113,11 @@ export const SettingsAccount: React.FC = React.memo(() => {
content={
{userInfo.email}
- {userInfo.emailVerified ? (
+ {isBuiltinEmail(userInfo.email) ? (
+
+ {t('内置邮箱')}
+
+ ) : userInfo.emailVerified ? (
{t('已认证')}
diff --git a/client/web/src/utils/user-helper.ts b/client/web/src/utils/user-helper.ts
index 7895ad50..139ff797 100644
--- a/client/web/src/utils/user-helper.ts
+++ b/client/web/src/utils/user-helper.ts
@@ -39,3 +39,28 @@ export async function tryAutoLogin(): Promise {
return userLoginInfo;
}
+
+/**
+ * 是否为内置邮箱
+ *
+ * 内置邮箱则为非用户输入,通过其他途径进来但是没有邮箱属于自动补全的邮箱类型
+ * 比如临时用户,比如iam
+ */
+export function isBuiltinEmail(email: string): boolean {
+ if (typeof email !== 'string') {
+ // 一般来说内置邮箱都是表示不可用状态,因此如果不是合法输入直接视为true
+ return true;
+ }
+
+ const domain = email.split('@')[1];
+
+ if (!domain) {
+ return true;
+ }
+
+ if (domain.endsWith('.msgbyte.com')) {
+ return true;
+ }
+
+ return false;
+}
diff --git a/server/plugins/com.msgbyte.iam/services/iam.service.ts b/server/plugins/com.msgbyte.iam/services/iam.service.ts
index ceb534f0..d05b9453 100644
--- a/server/plugins/com.msgbyte.iam/services/iam.service.ts
+++ b/server/plugins/com.msgbyte.iam/services/iam.service.ts
@@ -84,9 +84,14 @@ class IAMService extends TcService {
});
}
+ const username = providerUserInfo.username;
+ const email =
+ providerUserInfo.email ||
+ `${username}.${strategyName}@iam.msgbyte.com`;
+
// 不存在记录,查找是否已经注册过,如果已经注册过需要绑定,如果没有注册过则创建账号并绑定用户关系
const userInfo = await ctx.call('user.findUserByEmail', {
- email: providerUserInfo.email,
+ email,
});
if (!!userInfo) {
// 用户已存在,需要登录后才能确定绑定关系
@@ -114,7 +119,7 @@ class IAMService extends TcService {
const newUserInfo: UserStructWithToken = await ctx.call(
'user.register',
{
- email: providerUserInfo.email,
+ email,
nickname: providerUserInfo.nickname,
password: String(new db.Types.ObjectId()), // random password
avatar,
diff --git a/server/plugins/com.msgbyte.iam/strategies/github.ts b/server/plugins/com.msgbyte.iam/strategies/github.ts
index 9fbc80a5..96cf3a30 100644
--- a/server/plugins/com.msgbyte.iam/strategies/github.ts
+++ b/server/plugins/com.msgbyte.iam/strategies/github.ts
@@ -46,13 +46,20 @@ export const GithubStrategy: StrategyType = {
Authorization: `token ${accessToken}`,
},
})
- .json<{ id: number; name: string; email: string; avatar_url: string }>();
+ .json<{
+ id: number;
+ login: string;
+ name: string;
+ email: string;
+ avatar_url: string;
+ }>();
console.log(`[github oauth] user info:`, result);
return {
id: String(result.id),
nickname: result.name,
+ username: result.login,
email: result.email,
avatar: result.avatar_url,
};
diff --git a/server/plugins/com.msgbyte.iam/strategies/types.ts b/server/plugins/com.msgbyte.iam/strategies/types.ts
index 687823f7..66d09b0f 100644
--- a/server/plugins/com.msgbyte.iam/strategies/types.ts
+++ b/server/plugins/com.msgbyte.iam/strategies/types.ts
@@ -7,6 +7,7 @@ export interface StrategyType {
getUserInfo: (code: string) => Promise<{
id: string;
nickname: string;
+ username: string;
email: string;
avatar: string;
}>;
diff --git a/server/plugins/com.msgbyte.iam/web/plugins/com.msgbyte.iam/src/IAMAction.tsx b/server/plugins/com.msgbyte.iam/web/plugins/com.msgbyte.iam/src/IAMAction.tsx
index ff53b6e0..41aec224 100644
--- a/server/plugins/com.msgbyte.iam/web/plugins/com.msgbyte.iam/src/IAMAction.tsx
+++ b/server/plugins/com.msgbyte.iam/web/plugins/com.msgbyte.iam/src/IAMAction.tsx
@@ -30,6 +30,8 @@ export const IAMAction: React.FC = React.memo(() => {
if (payload.type === 'existed') {
showToasts(Translate.accountExistedTip, 'warning');
+ } else if (payload.type === 'infoDeviant') {
+ showToasts(Translate.infoDeviantTip, 'error');
} else if (payload.type === 'token') {
const token = payload.token;
setUserJWT(token)
diff --git a/server/plugins/com.msgbyte.iam/web/plugins/com.msgbyte.iam/src/translate.ts b/server/plugins/com.msgbyte.iam/web/plugins/com.msgbyte.iam/src/translate.ts
index 6a708363..56617b3b 100644
--- a/server/plugins/com.msgbyte.iam/web/plugins/com.msgbyte.iam/src/translate.ts
+++ b/server/plugins/com.msgbyte.iam/web/plugins/com.msgbyte.iam/src/translate.ts
@@ -15,6 +15,12 @@ export const Translate = {
'zh-CN': '账号已存在,请使用账号密码登录',
'en-US': 'Account Existed, please log in with account password',
}),
+ infoDeviantTip: localTrans({
+ // 'zh-CN': '账号已存在,你应该在登录后绑定账号',
+ // 'en-US': 'Account Existed, You should bind provider account after login',
+ 'zh-CN': '账号信息异常,请使用账号密码登录',
+ 'en-US': 'Account Info Deviant, please log in with account password',
+ }),
notSupportMobile: localTrans({
'zh-CN': '第三方登录功能暂不支持移动端使用',
'en-US': 'The third-party login function does not support mobile use',
diff --git a/server/services/core/user/user.service.ts b/server/services/core/user/user.service.ts
index 62be7bb8..e9b5b16d 100644
--- a/server/services/core/user/user.service.ts
+++ b/server/services/core/user/user.service.ts
@@ -200,6 +200,12 @@ class UserService extends TcService {
email: 'string',
},
});
+ this.registerAction('findUserByUsername', this.findUserByUsername, {
+ visibility: 'public',
+ params: {
+ username: 'string',
+ },
+ });
this.registerAction('updateUserField', this.updateUserField, {
params: {
fieldName: 'string',
@@ -537,7 +543,7 @@ class UserService extends TcService {
const password = await this.hashPassword(generateRandomStr());
const doc = await this.adapter.insert({
- email: `${generateRandomStr()}.temporary@msgbyte.com`,
+ email: `${generateRandomStr()}@temporary.msgbyte.com`,
password,
nickname,
discriminator,
@@ -861,6 +867,29 @@ class UserService extends TcService {
return user;
}
+ /**
+ * 通过用户邮箱查找用户
+ */
+ async findUserByUsername(
+ ctx: TcContext<{
+ username: string;
+ }>
+ ): Promise {
+ const username = ctx.params.username;
+
+ const doc = await this.adapter.model.findOne({
+ username,
+ });
+
+ if (!doc) {
+ return null;
+ }
+
+ const user = await this.transformDocuments(ctx, {}, doc);
+
+ return user;
+ }
+
/**
* 修改用户字段
*/
@@ -1073,7 +1102,7 @@ class UserService extends TcService {
}
/**
- * 根据用户邮件获取开放平台机器人id
+ * 根据用户邮箱获取开放平台机器人id
*/
findOpenapiBotId(ctx: TcContext<{ email: string }>): string {
return this.parseOpenapiBotEmail(ctx.params.email);
@@ -1216,18 +1245,23 @@ class UserService extends TcService {
}
private buildPluginBotEmail(botId: string) {
- return `${botId}@tailchat-plugin.com`;
+ return `${botId}@plugin.msgbyte.com`;
}
private buildOpenapiBotEmail(botId: string) {
- return `${botId}@tailchat-openapi.com`;
+ return `${botId}@openapi.msgbyte.com`;
}
private parseOpenapiBotEmail(email: string): string | null {
if (email.endsWith('@tailchat-openapi.com')) {
+ // 旧的实现,兼容代码
return email.replace('@tailchat-openapi.com', '');
}
+ if (email.endsWith('@openapi.msgbyte.com')) {
+ return email.replace('@openapi.msgbyte.com', '');
+ }
+
return null;
}