pull/52/head
sunface 4 years ago
parent fc935a6ac7
commit aa205c0086

@ -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 &&
<VStack alignItems="left" width="100%">
{
users.map(u => <OrgMember user={u} key={u.id} onEdit={onEdit}/>)
users.map(u => <OrgMember user={u} key={u.id} onEdit={onEdit} onDelete={onDelete}/>)
}
</VStack>}
</Card>

@ -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 (
<>
<PageContainer>
@ -62,25 +69,28 @@ const UserOrgsPage = () => {
<Flex justifyContent="space-between" alignItems="center">
<Heading size="sm"></Heading>
<HStack>
<Button size="sm" onClick={onJoinOrg} _focus={null}></Button>
<Button size="sm" onClick={onJoinOrg} _focus={null}></Button>
<Button colorScheme="teal" size="sm" onClick={onCreateOrg} _focus={null}></Button>
</HStack>
</Flex>
<VStack mt="3" divider={<StackDivider borderColor={stackBorderColor} />} alignItems="left">
{
orgs.map(o => <Flex key={o.id} justifyContent="space-between" alignItems="center" p="2">
<Link href={`/${o.username}`}>
<HStack cursor="pointer">
<Image src={o.avatar} height="30px"/>
<Heading size="sm" fontSize="1rem">{o.nickname}</Heading>
<Text layerStyle="textSecondary">{isAdmin(o.role) ? 'admin' : 'member'}</Text>
</HStack>
</Link>
{
orgs.map(o => <Flex key={o.id} justifyContent="space-between" alignItems="center" p="2">
<Link href={`/${o.username}`}>
<HStack cursor="pointer">
<Image src={o.avatar} height="30px" />
<Heading size="sm" fontSize="1rem">{o.nickname}</Heading>
<Text layerStyle="textSecondary">{isAdmin(o.role) ? 'admin' : 'member'}</Text>
</HStack>
</Link>
{isAdmin(o.role) && <Button variant="outline" size="md" onClick={() => router.push(`${ReserveUrls.Settings}/org/profile/${o.id}`)}>Manage</Button>}
</Flex>)
}
<HStack>
{isAdmin(o.role) && <Button variant="outline" size="md" onClick={() => router.push(`${ReserveUrls.Settings}/org/profile/${o.id}`)}>Manage</Button>}
{o.role !== Role.SUPER_ADMIN && <Button colorScheme="red" variant="outline" size="md" onClick={() => leaveOrg(o.id)}>Leave</Button>}
</HStack>
</Flex>)
}
</VStack>
</Card>
@ -92,8 +102,8 @@ const UserOrgsPage = () => {
{<ModalContent>
<ModalHeader></ModalHeader>
<ModalBody mb="2">
<Formik
initialValues={{username: '',nickname:''} as Org}
<Formik
initialValues={{ username: '', nickname: '' } as Org}
onSubmit={createOrg}
>
{(props) => (
@ -140,10 +150,10 @@ const UserOrgsPage = () => {
<ModalOverlay />
{<ModalContent p="3">
<ModalBody mb="2">
<Text>Secret code</Text>
<Text fontSize=".9rem" layerStyle="textSecondary">Provided to you by an org admin</Text>
<Input _focus={null} mt="3" placeholder="..." value={secret} onChange={e => setSecret(e.currentTarget.value)}/>
<Button mt="6" colorScheme="teal" onClick={joinOrg}>Join Organization</Button>
<Text>Secret code</Text>
<Text fontSize=".9rem" layerStyle="textSecondary">Provided to you by an org admin</Text>
<Input _focus={null} mt="3" placeholder="..." value={secret} onChange={e => setSecret(e.currentTarget.value)} />
<Button mt="6" colorScheme="teal" onClick={joinOrg}>Join Organization</Button>
</ModalBody>
</ModalContent>}
</Modal>

@ -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))
}

@ -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`)

@ -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
}

@ -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)

@ -13,9 +13,10 @@ type Props = PropsOf<typeof chakra.div> & {
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 (
<Flex alignItems="center" justifyContent="space-between">
@ -43,7 +44,8 @@ export const OrgMember= ({user,highlight,onEdit}:Props) =>{
</HStack>
<HStack>
<Text fontWeight="600" fontSize=".95rem"><Count count={user.follows??0}/> followers</Text>
<Button variant="outline" size="sm" onClick={() => onEdit(user)} _focus={null}>Edit</Button>
<Button variant="ghost" size="sm" onClick={() => onEdit(user)} _focus={null}>Edit</Button>
<Button variant="outline" colorScheme="red" size="sm" onClick={() => onDelete(user)} _focus={null}>Delete</Button>
</HStack>
</Flex>

Loading…
Cancel
Save