mirror of https://github.com/msgbyte/tailchat
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
421 lines
8.2 KiB
TypeScript
421 lines
8.2 KiB
TypeScript
import { request } from '../api/request';
|
|
import { buildCachedRequest } from '../cache/utils';
|
|
import { sharedEvent } from '../event';
|
|
import { SYSTEM_USERID } from '../utils/consts';
|
|
import {
|
|
createAutoMergedRequest,
|
|
createAutoSplitRequest,
|
|
} from '../utils/request';
|
|
import _pick from 'lodash/pick';
|
|
import _uniq from 'lodash/uniq';
|
|
import _flatten from 'lodash/flatten';
|
|
import _zipObject from 'lodash/zipObject';
|
|
import { t } from '../i18n';
|
|
|
|
export interface UserBaseInfo {
|
|
_id: string;
|
|
email: string;
|
|
nickname: string;
|
|
discriminator: string;
|
|
avatar: string | null;
|
|
temporary: boolean;
|
|
emailVerified: boolean;
|
|
banned: boolean;
|
|
extra?: Record<string, unknown>;
|
|
}
|
|
|
|
export interface UserLoginInfo extends UserBaseInfo {
|
|
token: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface UserSettings {
|
|
/**
|
|
* 消息列表虚拟化
|
|
*/
|
|
messageListVirtualization?: boolean;
|
|
|
|
/**
|
|
* 消息通知免打扰(静音)
|
|
*/
|
|
messageNotificationMuteList?: string[];
|
|
|
|
/**
|
|
* 群组排序, 内容为群组id
|
|
*/
|
|
groupOrderList?: string[];
|
|
|
|
/**
|
|
* 其他的设置项
|
|
*/
|
|
[key: string]: any;
|
|
}
|
|
|
|
export function pickUserBaseInfo(userInfo: UserLoginInfo): UserBaseInfo {
|
|
return _pick(userInfo, [
|
|
'_id',
|
|
'email',
|
|
'nickname',
|
|
'discriminator',
|
|
'avatar',
|
|
'temporary',
|
|
'emailVerified',
|
|
'banned',
|
|
]);
|
|
}
|
|
|
|
// 内置用户信息
|
|
const builtinUserInfo: Record<string, () => UserBaseInfo> = {
|
|
[SYSTEM_USERID]: () => ({
|
|
_id: SYSTEM_USERID,
|
|
email: 'admin@msgbyte.com',
|
|
nickname: t('系统'),
|
|
discriminator: '0000',
|
|
avatar: null,
|
|
temporary: false,
|
|
emailVerified: false,
|
|
banned: false,
|
|
}),
|
|
};
|
|
|
|
/**
|
|
* 用户私信列表
|
|
*/
|
|
export interface UserDMList {
|
|
userId: string;
|
|
converseIds: string[];
|
|
}
|
|
|
|
/**
|
|
* 邮箱登录
|
|
* @param email 邮箱
|
|
* @param password 密码
|
|
*/
|
|
export async function loginWithEmail(
|
|
email: string,
|
|
password: string
|
|
): Promise<UserLoginInfo> {
|
|
const { data } = await request.post('/api/user/login', {
|
|
email,
|
|
password,
|
|
});
|
|
|
|
sharedEvent.emit('loginSuccess', pickUserBaseInfo(data));
|
|
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* 使用 Token 登录
|
|
* @param token JWT令牌
|
|
*/
|
|
export async function loginWithToken(token: string): Promise<UserLoginInfo> {
|
|
const { data } = await request.post('/api/user/resolveToken', {
|
|
token,
|
|
});
|
|
|
|
sharedEvent.emit('loginSuccess', pickUserBaseInfo(data));
|
|
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* 发送邮箱校验码
|
|
* @param email 邮箱
|
|
*/
|
|
export async function verifyEmail(email: string): Promise<UserLoginInfo> {
|
|
const { data } = await request.post('/api/user/verifyEmail', {
|
|
email,
|
|
});
|
|
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* 检查邮箱校验码并更新用户字段
|
|
* @param email 邮箱
|
|
*/
|
|
export async function verifyEmailWithOTP(
|
|
emailOTP: string
|
|
): Promise<UserLoginInfo> {
|
|
const { data } = await request.post('/api/user/verifyEmailWithOTP', {
|
|
emailOTP,
|
|
});
|
|
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* 邮箱注册账号
|
|
* @param email 邮箱
|
|
* @param password 密码
|
|
*/
|
|
export async function registerWithEmail({
|
|
email,
|
|
password,
|
|
nickname,
|
|
emailOTP,
|
|
}: {
|
|
email: string;
|
|
password: string;
|
|
nickname?: string;
|
|
emailOTP?: string;
|
|
}): Promise<UserLoginInfo> {
|
|
const { data } = await request.post('/api/user/register', {
|
|
email,
|
|
nickname,
|
|
password,
|
|
emailOTP,
|
|
});
|
|
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* 修改密码
|
|
*/
|
|
export async function modifyUserPassword(
|
|
oldPassword: string,
|
|
newPassword: string
|
|
): Promise<void> {
|
|
await request.post('/api/user/modifyPassword', {
|
|
oldPassword,
|
|
newPassword,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 忘记密码
|
|
* @param email 邮箱
|
|
*/
|
|
export async function forgetPassword(email: string) {
|
|
await request.post('/api/user/forgetPassword', {
|
|
email,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 忘记密码
|
|
* @param email 邮箱
|
|
*/
|
|
export async function resetPassword(
|
|
email: string,
|
|
password: string,
|
|
otp: string
|
|
) {
|
|
await request.post('/api/user/resetPassword', {
|
|
email,
|
|
password,
|
|
otp,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 创建访客账号
|
|
* @param nickname 访客昵称
|
|
*/
|
|
export async function createTemporaryUser(
|
|
nickname: string
|
|
): Promise<UserLoginInfo> {
|
|
const { data } = await request.post('/api/user/createTemporaryUser', {
|
|
nickname,
|
|
});
|
|
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* 认领访客账号
|
|
*/
|
|
export async function claimTemporaryUser(
|
|
userId: string,
|
|
email: string,
|
|
password: string,
|
|
emailOTP?: string
|
|
): Promise<UserLoginInfo> {
|
|
const { data } = await request.post('/api/user/claimTemporaryUser', {
|
|
userId,
|
|
email,
|
|
password,
|
|
emailOTP,
|
|
});
|
|
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* 使用唯一标识名搜索用户
|
|
* @param uniqueName 唯一标识用户昵称: 用户昵称#0000
|
|
*/
|
|
export async function searchUserWithUniqueName(
|
|
uniqueName: string
|
|
): Promise<UserBaseInfo> {
|
|
const { data } = await request.post('/api/user/searchUserWithUniqueName', {
|
|
uniqueName,
|
|
});
|
|
|
|
return data;
|
|
}
|
|
|
|
const _fetchUserInfo = createAutoMergedRequest<string, UserBaseInfo>(
|
|
createAutoSplitRequest(
|
|
async (userIds) => {
|
|
// 这里用post是为了防止一次性获取的userId过多超过url限制
|
|
const { data } = await request.post('/api/user/getUserInfoList', {
|
|
userIds,
|
|
});
|
|
|
|
return data;
|
|
},
|
|
'serial',
|
|
1000
|
|
)
|
|
);
|
|
/**
|
|
* 获取用户基本信息
|
|
* @param userId 用户ID
|
|
*/
|
|
export async function fetchUserInfo(userId: string): Promise<UserBaseInfo> {
|
|
if (
|
|
builtinUserInfo[userId] &&
|
|
typeof builtinUserInfo[userId] === 'function'
|
|
) {
|
|
return builtinUserInfo[userId]();
|
|
}
|
|
|
|
const userInfo = await _fetchUserInfo(userId);
|
|
|
|
return userInfo;
|
|
}
|
|
|
|
const _fetchUserOnlineStatus = createAutoMergedRequest<string[], boolean[]>(
|
|
createAutoSplitRequest(
|
|
async (userIdsList) => {
|
|
const uniqList = _uniq(_flatten(userIdsList));
|
|
// 这里用post是为了防止一次性获取的userId过多超过url限制
|
|
const { data } = await request.post('/api/gateway/checkUserOnline', {
|
|
userIds: uniqList,
|
|
});
|
|
|
|
const map = _zipObject<boolean>(uniqList, data);
|
|
|
|
// 将请求结果根据传输来源重新分组
|
|
return userIdsList.map((userIds) =>
|
|
userIds.map((userId) => map[userId] ?? false)
|
|
);
|
|
},
|
|
'serial',
|
|
1000
|
|
)
|
|
);
|
|
|
|
/**
|
|
* 获取用户在线状态
|
|
*/
|
|
export async function getUserOnlineStatus(
|
|
userIds: string[]
|
|
): Promise<boolean[]> {
|
|
return _fetchUserOnlineStatus(userIds);
|
|
}
|
|
|
|
/**
|
|
* 将会话添加到用户私信列表
|
|
* 如果已添加则后端忽略
|
|
*/
|
|
export async function appendUserDMConverse(
|
|
converseId: string
|
|
): Promise<UserDMList> {
|
|
const { data } = await request.post<UserDMList>(
|
|
'/api/user/dmlist/addConverse',
|
|
{
|
|
converseId,
|
|
}
|
|
);
|
|
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* 移除会话列表
|
|
*/
|
|
export async function removeUserDMConverse(
|
|
converseId: string
|
|
): Promise<UserDMList> {
|
|
const { data } = await request.post<UserDMList>(
|
|
'/api/user/dmlist/removeConverse',
|
|
{
|
|
converseId,
|
|
}
|
|
);
|
|
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* 修改用户属性
|
|
* @param fieldName 要修改的属性名
|
|
* @param fieldValue 要修改的属性的值
|
|
*/
|
|
type AllowedModifyField = 'nickname' | 'avatar';
|
|
export async function modifyUserField(
|
|
fieldName: AllowedModifyField,
|
|
fieldValue: unknown
|
|
): Promise<UserBaseInfo> {
|
|
const { data } = await request.post('/api/user/updateUserField', {
|
|
fieldName,
|
|
fieldValue,
|
|
});
|
|
|
|
return data;
|
|
}
|
|
|
|
export async function modifyUserExtra(
|
|
fieldName: string,
|
|
fieldValue: unknown
|
|
): Promise<UserBaseInfo> {
|
|
const { data } = await request.post('/api/user/updateUserExtra', {
|
|
fieldName,
|
|
fieldValue,
|
|
});
|
|
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* 获取用户设置
|
|
*/
|
|
export async function getUserSettings(): Promise<UserSettings> {
|
|
const { data } = await request.get('/api/user/getUserSettings');
|
|
|
|
sharedEvent.emit('userSettingsUpdate', data);
|
|
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* 设置用户设置
|
|
*/
|
|
export async function setUserSettings(
|
|
settings: UserSettings
|
|
): Promise<UserSettings> {
|
|
const { data } = await request.post('/api/user/setUserSettings', {
|
|
settings,
|
|
});
|
|
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* 检查Token是否可用
|
|
*/
|
|
export const checkTokenValid = buildCachedRequest(
|
|
'tokenValid',
|
|
async (token: string): Promise<boolean> => {
|
|
const { data } = await request.post<boolean>('/api/user/checkTokenValid', {
|
|
token,
|
|
});
|
|
|
|
return data;
|
|
}
|
|
);
|