diff --git a/pages/settings/org/members/[org_id].tsx b/pages/settings/org/members/[org_id].tsx index 3fbc23f3..06086a91 100644 --- a/pages/settings/org/members/[org_id].tsx +++ b/pages/settings/org/members/[org_id].tsx @@ -48,11 +48,23 @@ const UserProfilePage = () => { setSecret(res.data) } + const onDelete = async (member) => { + await requestApi.delete(`/org/member/${org.id}/${member.id}`) + toast({ + description: "删除用户成功", + status: "success", + duration: 2000, + isClosable: true, + }) + getMembers() + } + const onEdit = (member) => { setCurrentMember(member) onOpen() } + const onChangeRole = e => { const member = cloneDeep(currentMember) member.role = e.currentTarget.value; @@ -91,7 +103,7 @@ const UserProfilePage = () => { {users && { - users.map(u => ) + users.map(u => ) } } diff --git a/pages/settings/orgs.tsx b/pages/settings/orgs.tsx index 0d815928..daad3901 100644 --- a/pages/settings/orgs.tsx +++ b/pages/settings/orgs.tsx @@ -14,15 +14,16 @@ import userCustomTheme from "theme/user-custom" import { useRouter } from "next/router" import Link from "next/link" import { ReserveUrls } from "src/data/reserve-urls" +import { Role } from "src/types/role" const UserOrgsPage = () => { - const [orgs, setOrgs]:[Org[],any] = useState([]) + const [orgs, setOrgs]: [Org[], any] = useState([]) const { isOpen, onOpen, onClose } = useDisclosure() - const { isOpen:isOpen1, onOpen:onOpen1, onClose:onClose1 } = useDisclosure() + const { isOpen: isOpen1, onOpen: onOpen1, onClose: onClose1 } = useDisclosure() const router = useRouter() const stackBorderColor = useColorModeValue(userCustomTheme.borderColor.light, userCustomTheme.borderColor.dark) - const [secret,setSecret] = useState('') + const [secret, setSecret] = useState('') useEffect(() => { getOrgs() @@ -34,7 +35,7 @@ const UserOrgsPage = () => { } - const createOrg = async (values:Org) => { + const createOrg = async (values: Org) => { await requestApi.post(`/org/create`, values) onClose() router.push(`/${values.username}`) @@ -44,15 +45,21 @@ const UserOrgsPage = () => { onOpen() } - const onJoinOrg = () => { - onOpen1() - } + const onJoinOrg = () => { + onOpen1() + } + + const joinOrg = async () => { + await requestApi.post(`/org/join`, { secret: secret }) + onClose1() + getOrgs() + } + + const leaveOrg = async orgID => { + await requestApi.post(`/org/leave/${orgID}`) + getOrgs() + } - const joinOrg = async () => { - await requestApi.post(`/org/join`,{secret: secret}) - onClose1() - getOrgs() - } return ( <> @@ -62,25 +69,28 @@ const UserOrgsPage = () => { 组织列表 - + } alignItems="left"> - { - orgs.map(o => - - - - {o.nickname} - {isAdmin(o.role) ? 'admin' : 'member'} + { + orgs.map(o => + + + + {o.nickname} + {isAdmin(o.role) ? 'admin' : 'member'} + + + + + {isAdmin(o.role) && } + {o.role !== Role.SUPER_ADMIN && } - - - {isAdmin(o.role) && } - ) - } + ) + } @@ -92,8 +102,8 @@ const UserOrgsPage = () => { { 新建组织 - {(props) => ( @@ -140,10 +150,10 @@ const UserOrgsPage = () => { { - Secret code - Provided to you by an org admin - setSecret(e.currentTarget.value)}/> - + Secret code + Provided to you by an org admin + setSecret(e.currentTarget.value)} /> + } diff --git a/server/internal/api/org.go b/server/internal/api/org.go index 0eb7a4e7..23c5a12f 100644 --- a/server/internal/api/org.go +++ b/server/internal/api/org.go @@ -246,3 +246,80 @@ func TransferOrg(c *gin.Context) { c.JSON(http.StatusOK, common.RespSuccess(nil)) } + +func DeleteOrgMember(c *gin.Context) { + orgID := c.Param("orgID") + memberID := c.Param("memberID") + + // 获取待删除用户的组织角色 + targetRole, err := org.GetMemberRole(orgID, memberID) + if err != nil { + c.JSON(http.StatusBadRequest, common.RespError(e.BadRequest)) + return + } + + // 获取当前用户的组织角色 + u := user.CurrentUser(c) + currentRole, err := org.GetMemberRole(orgID, u.ID) + if err != nil { + c.JSON(http.StatusBadRequest, common.RespError(e.BadRequest)) + return + } + + // 超级管理员不能被删除 + if targetRole == models.ROLE_SUPER_ADMIN { + c.JSON(http.StatusForbidden, common.RespError("无法删除超级管理员")) + return + } + // 不能删除自己 + if u.ID == memberID { + c.JSON(http.StatusForbidden, common.RespError("无法删除自己")) + return + } + + // 若目标用户角色是管理员,则当前用户角色需要是超级管理员 + if targetRole == models.ROLE_ADMIN { + if currentRole != models.ROLE_SUPER_ADMIN { + c.JSON(http.StatusForbidden, common.RespError("只有超级管理员才能删除管理员")) + return + } + } else { + if !currentRole.IsAdmin() { + c.JSON(http.StatusForbidden, common.RespError("只有管理员才能删除组织成员")) + return + } + } + + err0 := org.Delete(orgID, memberID) + if err0 != nil { + c.JSON(err0.Status, common.RespError(err0.Message)) + return + } + + c.JSON(http.StatusOK, common.RespSuccess(nil)) +} + +func LeaveOrg(c *gin.Context) { + orgID := c.Param("orgID") + + // 获取当前用户的组织角色 + u := user.CurrentUser(c) + currentRole, err := org.GetMemberRole(orgID, u.ID) + if err != nil { + c.JSON(http.StatusBadRequest, common.RespError(e.BadRequest)) + return + } + + if currentRole == models.ROLE_SUPER_ADMIN { + c.JSON(http.StatusForbidden, common.RespError("超级管理员必须先转移组织,才能离开")) + return + } + + err0 := org.Delete(orgID, u.ID) + if err0 != nil { + c.JSON(err0.Status, common.RespError(err0.Message)) + return + } + + c.JSON(http.StatusOK, common.RespSuccess(nil)) +} diff --git a/server/internal/cache/cache.go b/server/internal/cache/cache.go index 63e5b7f6..55e67ff2 100644 --- a/server/internal/cache/cache.go +++ b/server/internal/cache/cache.go @@ -13,7 +13,7 @@ import ( var logger = log.RootLogger.New("logger", "cache") func Init() { - time.Sleep(2 * time.Second) + time.Sleep(1 * time.Second) for { // load users rows, err := db.Conn.Query(`SELECT id,type,username,role,nickname,avatar,last_seen_at,created FROM user`) diff --git a/server/internal/org/org.go b/server/internal/org/org.go index 71561ceb..6e4e5935 100644 --- a/server/internal/org/org.go +++ b/server/internal/org/org.go @@ -196,3 +196,13 @@ func Transfer(orgID, currentOwner, newOwner string) *e.Error { return nil } + +func Delete(orgID, memberID string) *e.Error { + _, err := db.Conn.Exec("DELETE FROM org_member WHERE org_id=? and user_id=?", orgID, memberID) + if err != nil { + logger.Warn("delete org member error", "error", err) + return e.New(http.StatusInternalServerError, e.Internal) + } + + return nil +} diff --git a/server/internal/server.go b/server/internal/server.go index a0e589ab..a3eeee41 100644 --- a/server/internal/server.go +++ b/server/internal/server.go @@ -112,7 +112,9 @@ func (s *Server) Start() error { r.GET("/org/secret/:id", IsLogin(), api.GetOrgSecret) r.POST("/org/join", IsLogin(), api.JoinOrg) r.POST("/org/member/role", IsLogin(), api.UpdateOrgMember) - r.POST("/org/transfer", api.TransferOrg) + r.POST("/org/transfer", IsLogin(), api.TransferOrg) + r.DELETE("/org/member/:orgID/:memberID", IsLogin(), api.DeleteOrgMember) + r.POST("/org/leave/:orgID", IsLogin(), api.LeaveOrg) // admin apis r.POST("/admin/user", IsLogin(), api.AdminSubmitUser) r.GET("/admin/user/all", IsLogin(), api.AdminGetUsers) diff --git a/src/components/users/org-member.tsx b/src/components/users/org-member.tsx index b6872c25..c104fbb1 100644 --- a/src/components/users/org-member.tsx +++ b/src/components/users/org-member.tsx @@ -13,9 +13,10 @@ type Props = PropsOf & { user : User highlight?: string onEdit: any + onDelete: any } -export const OrgMember= ({user,highlight,onEdit}:Props) =>{ +export const OrgMember= ({user,highlight,onEdit,onDelete}:Props) =>{ const router = useRouter() return ( @@ -43,7 +44,8 @@ export const OrgMember= ({user,highlight,onEdit}:Props) =>{ followers - + +