support admin disable of accounts

This commit is contained in:
Roland Osborne 2022-06-07 10:54:33 -07:00
parent 172d28f0be
commit 8ff747f90f
8 changed files with 164 additions and 33 deletions

View File

@ -212,10 +212,8 @@ paths:
delete:
tags:
- admin
description: Remove account from node. Access granted to admin username and password.
description: Remove account from node. Access granted to admin token
operationId: remove-node-account
security:
- basicAuth: []
parameters:
- name: accountId
in: path
@ -238,6 +236,37 @@ paths:
description: account not found
'500':
description: internal server error
put:
tags:
- admin
description: Disable account. Access granted to admin token
operationId: set-node-account
parameters:
- name: accountId
in: path
description: id of account to delete
required: true
schema:
type: string
- name: token
in: query
description: token for admin access
required: true
schema:
type: string
responses:
'200':
description: successful operation
content:
application/json:
schema:
type: boolean
'401':
description: invalid authentication
'404':
description: account not found
'500':
description: internal server error
/admin/accounts/import:
post:

View File

@ -0,0 +1,38 @@
package databag
import (
"strconv"
"net/http"
"github.com/gorilla/mux"
"databag/internal/store"
)
func SetNodeAccountStatus(w http.ResponseWriter, r *http.Request) {
// get referenced account id
params := mux.Vars(r)
accountId, res := strconv.ParseUint(params["accountId"], 10, 32)
if res != nil {
ErrResponse(w, http.StatusBadRequest, res)
return
}
if code, err := ParamAdminToken(r); err != nil {
ErrResponse(w, code, err)
return
}
var flag bool
if err := ParseRequest(r, w, &flag); err != nil {
ErrResponse(w, http.StatusBadRequest, err)
return
}
if err := store.DB.Model(store.Account{}).Where("id = ?", accountId).Update("disabled", flag).Error; err != nil {
ErrResponse(w, http.StatusInternalServerError, err)
return
}
WriteResponse(w, nil)
}

View File

@ -193,6 +193,13 @@ var routes = Routes{
GetNodeAccountImage,
},
Route{
"SetNodeAccountStatus",
strings.ToUpper("Put"),
"/admin/accounts/{accountId}/status",
SetNodeAccountStatus,
},
Route{
"GetNodeAccounts",
strings.ToUpper("Get"),

View File

@ -2,6 +2,7 @@ import { Avatar } from 'avatar/Avatar';
import { AccountItemWrapper, DeleteButton, EnableButton, DisableButton, ResetButton } from './AccountItem.styled';
import { useAccountItem } from './useAccountItem.hook';
import { UserDeleteOutlined, UnlockOutlined, CloseCircleOutlined, CheckCircleOutlined } from '@ant-design/icons';
import { Tooltip } from 'antd';
export function AccountItem({ token, item }) {
@ -9,9 +10,19 @@ export function AccountItem({ token, item }) {
const Enable = () => {
if (state.disabled) {
return <EnableButton type="text" size="large" icon={<CloseCircleOutlined />}></EnableButton>
return (
<Tooltip placement="topLeft" title="Enable Account">
<EnableButton type="text" size="large" icon={<CheckCircleOutlined />}
onClick={() => actions.setStatus(false)}></EnableButton>
</Tooltip>
)
}
return <DisableButton type="text" size="large" icon={<CheckCircleOutlined />}></DisableButton>
return (
<Tooltip placement="topLeft" title="Disable Account">
<DisableButton type="text" size="large" icon={<CloseCircleOutlined />}
onClick={() => actions.setStatus(true)}></DisableButton>
</Tooltip>
)
}
return (
@ -19,14 +30,18 @@ export function AccountItem({ token, item }) {
<div class="avatar">
<Avatar imageUrl={state.imageUrl} />
</div>
<div class="id">
<div class={state.activeClass}>
<div class="handle">{ state.handle }</div>
<div class="guid">{ state.guid }</div>
</div>
<div class="control">
<ResetButton type="text" size="large" icon={<UnlockOutlined />}></ResetButton>
<Tooltip placement="topLeft" title="Reset Password">
<ResetButton type="text" size="large" icon={<UnlockOutlined />}></ResetButton>
</Tooltip>
<Enable />
<DeleteButton type="text" size="large" icon={<UserDeleteOutlined />}></DeleteButton>
<Tooltip placement="topLeft" title="Delete Account">
<DeleteButton type="text" size="large" icon={<UserDeleteOutlined />}></DeleteButton>
</Tooltip>
</div>
</AccountItemWrapper>
);

View File

@ -11,6 +11,7 @@ export const AccountItemWrapper = styled.div`
padding-top: 2px;
padding-bottom: 2px;
border-bottom: 1px solid #eeeeee;
align-items: center;
&:hover {
background-color: #eeeeee;
@ -23,29 +24,38 @@ export const AccountItemWrapper = styled.div`
justify-content: center;
}
.id {
.inactive {
padding-left: 16px;
padding-right: 8px;
display: flex;
flex-direction: column;
flex-grow: 1;
color: #cccccc;
}
.handle {
font-size: 0.8em;
font-weight: bold;
}
.active {
padding-left: 16px;
padding-right: 8px;
display: flex;
flex-direction: column;
flex-grow: 1;
}
.guid {
font-size: 0.8em;
font-weight: bold;
}
.handle {
font-size: 0.8em;
font-weight: bold;
}
.control {
flex-grow: 1;
display: flex;
justify-content: flex-end;
align-items: center;
}
.guid {
font-size: 0.8em;
font-weight: bold;
}
.control {
flex-grow: 1;
display: flex;
justify-content: flex-end;
align-items: center;
}
`;

View File

@ -1,9 +1,11 @@
import { useContext, useState, useEffect } from 'react';
import { getAccountImageUrl } from 'api/getAccountImageUrl';
import { setAccountStatus } from 'api/setAccountStatus';
export function useAccountItem(token, item) {
const [state, setState] = useState({
statusBusy: false,
});
const updateState = (value) => {
@ -12,16 +14,31 @@ export function useAccountItem(token, item) {
useEffect(() => {
updateState({
disabled: false,
disabled: item?.disabled,
activeClass: item?.disabled ? 'inactive' : 'active',
accountId: item?.accountId,
name: item?.name,
guid: item?.guid,
handle: item?.handle,
imageUrl: item?.imageSet ? getAccountImageUrl(token, item?.accountId) : null,
});
}, []);
}, [token, item]);
const actions = {
setStatus: async (disabled) => {
if (!state.statusBusy) {
updateState({ statusBusy: true });
try {
await setAccountStatus(token, item.accountId, disabled);
updateState({ disabled, activeClass: disabled ? 'inactive' : 'active' });
}
catch(err) {
console.log(err);
window.alert(err);
}
updateState({ statusBusy: false });
}
},
};
return { state, actions };

View File

@ -1,5 +1,5 @@
import { DashboardWrapper, SettingsButton, AddButton, SettingsLayout } from './Dashboard.styled';
import { Button, Modal, Input, InputNumber, Space, List } from 'antd';
import { Tooltip, Button, Modal, Input, InputNumber, Space, List } from 'antd';
import { SettingOutlined, UserAddOutlined, LogoutOutlined, ReloadOutlined } from '@ant-design/icons';
import { useDashboard } from './useDashboard.hook';
import { AccountItem } from './AccountItem/AccountItem';
@ -15,19 +15,27 @@ export function Dashboard({ token, config, logout }) {
<div class="header">
<div class="label">Accounts</div>
<div class="settings">
<SettingsButton type="text" size="small" icon={<ReloadOutlined />}
onClick={() => actions.getAccounts()}></SettingsButton>
<Tooltip placement="topRight" title="Reload Accounts">
<SettingsButton type="text" size="small" icon={<ReloadOutlined />}
onClick={() => actions.getAccounts()}></SettingsButton>
</Tooltip>
</div>
<div class="settings">
<SettingsButton type="text" size="small" icon={<SettingOutlined />}
onClick={() => actions.setShowSettings(true)}></SettingsButton>
<Tooltip placement="topRight" title="Configure Server">
<SettingsButton type="text" size="small" icon={<SettingOutlined />}
onClick={() => actions.setShowSettings(true)}></SettingsButton>
</Tooltip>
</div>
<div class="settings">
<SettingsButton type="text" size="small" icon={<LogoutOutlined />}
onClick={() => logout()}></SettingsButton>
<Tooltip placement="topRight" title="Logout">
<SettingsButton type="text" size="small" icon={<LogoutOutlined />}
onClick={() => logout()}></SettingsButton>
</Tooltip>
</div>
<div class="add">
<AddButton type="text" size="large" icon={<UserAddOutlined />}></AddButton>
<Tooltip placement="topRight" title="Add Account">
<AddButton type="text" size="large" icon={<UserAddOutlined />}></AddButton>
</Tooltip>
</div>
</div>

View File

@ -0,0 +1,7 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
export async function setAccountStatus(token, accountId, disabled) {
let res = await fetchWithTimeout(`/admin/accounts/${accountId}/status?token=${token}`, { method: 'PUT', body: JSON.stringify(disabled) })
checkResponse(res);
}