From 8ff747f90f6b5ea12340f5d363eb49fdb46e67e3 Mon Sep 17 00:00:00 2001 From: Roland Osborne Date: Tue, 7 Jun 2022 10:54:33 -0700 Subject: [PATCH] support admin disable of accounts --- doc/api.oa3 | 35 ++++++++++++++-- .../internal/api_setNodeAccountStatus.go | 38 ++++++++++++++++++ net/server/internal/routers.go | 7 ++++ .../Dashboard/AccountItem/AccountItem.jsx | 25 +++++++++--- .../AccountItem/AccountItem.styled.js | 40 ++++++++++++------- .../AccountItem/useAccountItem.hook.js | 21 +++++++++- net/web/src/Admin/Dashboard/Dashboard.jsx | 24 +++++++---- net/web/src/api/setAccountStatus.js | 7 ++++ 8 files changed, 164 insertions(+), 33 deletions(-) create mode 100644 net/server/internal/api_setNodeAccountStatus.go create mode 100644 net/web/src/api/setAccountStatus.js diff --git a/doc/api.oa3 b/doc/api.oa3 index ec508721..44747dcb 100644 --- a/doc/api.oa3 +++ b/doc/api.oa3 @@ -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: diff --git a/net/server/internal/api_setNodeAccountStatus.go b/net/server/internal/api_setNodeAccountStatus.go new file mode 100644 index 00000000..b20cf2cd --- /dev/null +++ b/net/server/internal/api_setNodeAccountStatus.go @@ -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) +} + diff --git a/net/server/internal/routers.go b/net/server/internal/routers.go index 44ef51b4..af3235a4 100644 --- a/net/server/internal/routers.go +++ b/net/server/internal/routers.go @@ -193,6 +193,13 @@ var routes = Routes{ GetNodeAccountImage, }, + Route{ + "SetNodeAccountStatus", + strings.ToUpper("Put"), + "/admin/accounts/{accountId}/status", + SetNodeAccountStatus, + }, + Route{ "GetNodeAccounts", strings.ToUpper("Get"), diff --git a/net/web/src/Admin/Dashboard/AccountItem/AccountItem.jsx b/net/web/src/Admin/Dashboard/AccountItem/AccountItem.jsx index 0bb30bad..3bb9e271 100644 --- a/net/web/src/Admin/Dashboard/AccountItem/AccountItem.jsx +++ b/net/web/src/Admin/Dashboard/AccountItem/AccountItem.jsx @@ -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 }> + return ( + + } + onClick={() => actions.setStatus(false)}> + + ) } - return }> + return ( + + } + onClick={() => actions.setStatus(true)}> + + ) } return ( @@ -19,14 +30,18 @@ export function AccountItem({ token, item }) {
-
+
{ state.handle }
{ state.guid }
- }> + + }> + - }> + + }> +
); diff --git a/net/web/src/Admin/Dashboard/AccountItem/AccountItem.styled.js b/net/web/src/Admin/Dashboard/AccountItem/AccountItem.styled.js index 9e62a1a1..da814787 100644 --- a/net/web/src/Admin/Dashboard/AccountItem/AccountItem.styled.js +++ b/net/web/src/Admin/Dashboard/AccountItem/AccountItem.styled.js @@ -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; } `; diff --git a/net/web/src/Admin/Dashboard/AccountItem/useAccountItem.hook.js b/net/web/src/Admin/Dashboard/AccountItem/useAccountItem.hook.js index 451c0e0d..cc453933 100644 --- a/net/web/src/Admin/Dashboard/AccountItem/useAccountItem.hook.js +++ b/net/web/src/Admin/Dashboard/AccountItem/useAccountItem.hook.js @@ -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 }; diff --git a/net/web/src/Admin/Dashboard/Dashboard.jsx b/net/web/src/Admin/Dashboard/Dashboard.jsx index 9e5cac05..5a2bc670 100644 --- a/net/web/src/Admin/Dashboard/Dashboard.jsx +++ b/net/web/src/Admin/Dashboard/Dashboard.jsx @@ -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 }) {
Accounts
- } - onClick={() => actions.getAccounts()}> + + } + onClick={() => actions.getAccounts()}> +
- } - onClick={() => actions.setShowSettings(true)}> + + } + onClick={() => actions.setShowSettings(true)}> +
- } - onClick={() => logout()}> + + } + onClick={() => logout()}> +
- }> + + }> +
diff --git a/net/web/src/api/setAccountStatus.js b/net/web/src/api/setAccountStatus.js new file mode 100644 index 00000000..a76459fe --- /dev/null +++ b/net/web/src/api/setAccountStatus.js @@ -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); +} +