diff --git a/server/locales/en-US/translation.json b/server/locales/en-US/translation.json index d1fbf10e..9fcb7a35 100644 --- a/server/locales/en-US/translation.json +++ b/server/locales/en-US/translation.json @@ -1,4 +1,5 @@ { + "k127fc33c": "User banned", "k158d2868": "No delete permission", "k16605863": "Token content is incorrect", "k17f8532": "No message found", diff --git a/server/locales/zh-CN/translation.json b/server/locales/zh-CN/translation.json index 42f07a60..721efaeb 100644 --- a/server/locales/zh-CN/translation.json +++ b/server/locales/zh-CN/translation.json @@ -1,4 +1,5 @@ { + "k127fc33c": "用户被封禁", "k158d2868": "没有删除权限", "k16605863": "Token 内容不正确", "k17f8532": "没有找到消息", diff --git a/server/mixins/socketio.mixin.ts b/server/mixins/socketio.mixin.ts index 96f8a6ee..e1cd56c4 100644 --- a/server/mixins/socketio.mixin.ts +++ b/server/mixins/socketio.mixin.ts @@ -397,6 +397,27 @@ export const TcSocketIOService = ( }, }, + /** + * 踢出用户 + */ + tickUser: { + visibility: 'public', + params: { + userId: 'string', + }, + async handler(this: TcService, ctx: TcContext<{ userId: string }>) { + const userId = ctx.params.userId; + const io: SocketServer = this.io; + const remoteSockets = await io + .in(buildUserRoomId(userId)) + .fetchSockets(); + + remoteSockets.forEach((remoteSocket) => { + remoteSocket.disconnect(true); + }); + }, + }, + /** * 服务端通知 */ diff --git a/server/models/user/user.ts b/server/models/user/user.ts index 7e90147f..334e3a3f 100644 --- a/server/models/user/user.ts +++ b/server/models/user/user.ts @@ -106,6 +106,14 @@ export class User extends TimeStamps implements Base { }) emailVerified: boolean; + /** + * 是否被封禁 + */ + @prop({ + default: false, + }) + banned: boolean; + /** * 用户的额外信息 */ diff --git a/server/services/core/user/user.service.ts b/server/services/core/user/user.service.ts index d8652749..e5296958 100644 --- a/server/services/core/user/user.service.ts +++ b/server/services/core/user/user.service.ts @@ -2,6 +2,7 @@ import { TcCacheCleaner } from '../../../mixins/cache.cleaner.mixin'; import jwt from 'jsonwebtoken'; import bcrypt from 'bcryptjs'; import type { + User, UserDocument, UserLoginRes, UserModel, @@ -19,6 +20,7 @@ import { EntityError, db, call, + NoPermissionError, } from 'tailchat-server-sdk'; import { generateRandomNumStr, @@ -282,10 +284,15 @@ class UserService extends TcService { } const res = await this.comparePassword(password, user.password); - if (!res) + if (!res) { throw new EntityError(t('密码错误'), 422, '', [ { field: 'password', message: t('密码错误') }, ]); + } + + if (user.banned === true) { + throw new NoPermissionError(t('用户被封禁'), 403); + } // Transform user entity (remove password and all protected fields) const doc = await this.transformDocuments(ctx, {}, user); @@ -630,7 +637,12 @@ class UserService extends TcService { throw new EntityError(t('Token 内容不正确')); } const doc = await this.getById(decoded._id); - const user = await this.transformDocuments(ctx, {}, doc); + const user: User = await this.transformDocuments(ctx, {}, doc); + + if (user.banned === true) { + throw new NoPermissionError(t('用户被封禁')); + } + const json = await this.transformEntity(user, true, ctx.meta.token); return json; }