diff --git a/doc/api.oa3 b/doc/api.oa3 index bbce1057..ebb0ba21 100644 --- a/doc/api.oa3 +++ b/doc/api.oa3 @@ -913,30 +913,12 @@ paths: required: true schema: type: string - - name: appName + - name: all in: query - description: name of connecting app + description: whether all app tokens should be cleared required: false schema: - type: string - - name: appVersion - in: query - description: version of connecting app - required: false - schema: - type: string - - name: platform - in: query - description: device platform - required: false - schema: - type: string - - name: deviceToken - in: query - description: deviceToken for push notification - required: false - schema: - type: string + type: boolean responses: '200': description: ok diff --git a/net/server/internal/api_removeAgentToken.go b/net/server/internal/api_removeAgentToken.go index 93bff182..cf4c7898 100644 --- a/net/server/internal/api_removeAgentToken.go +++ b/net/server/internal/api_removeAgentToken.go @@ -10,6 +10,9 @@ import ( //RemoveAgentToken func RemoveAgentToken(w http.ResponseWriter, r *http.Request) { + // logout of all devices + logoutMode := r.FormValue("all") == "true" + // parse authentication token target, access, err := ParseToken(r.FormValue("agent")) if err != nil { @@ -17,30 +20,54 @@ func RemoveAgentToken(w http.ResponseWriter, r *http.Request) { return } - // load session - var session store.Session - if err = store.DB.Where("account_id = ? AND token = ?", target, access).Find(&session).Error; err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - ErrResponse(w, http.StatusNotFound, err); - } else { + if logoutMode { + var sessions []store.Session + if err = store.DB.Where("account_id = ?", target, access).Find(&sessions).Error; err != nil { ErrResponse(w, http.StatusInternalServerError, err); + return; } - return; - } - // delete session - err = store.DB.Transaction(func(tx *gorm.DB) error { - if res := tx.Where("session_id = ?", session.ID).Delete(&store.PushEvent{}).Error; res != nil { - return res + // delete all sessions + err = store.DB.Transaction(func(tx *gorm.DB) error { + for _, session := range sessions { + if res := tx.Where("session_id = ?", session.ID).Delete(&store.PushEvent{}).Error; res != nil { + return res + } + if res := tx.Where("id = ?", session.ID).Delete(&store.Session{}).Error; res != nil { + return res + } + } + return nil + }) + if err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return } - if res := tx.Where("id = ?", session.ID).Delete(&store.Session{}).Error; res != nil { - return res + } else { + var session store.Session + if err = store.DB.Where("account_id = ? AND token = ?", target, access).Find(&session).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + ErrResponse(w, http.StatusNotFound, err); + } else { + ErrResponse(w, http.StatusInternalServerError, err); + } + return; + } + + // delete session + err = store.DB.Transaction(func(tx *gorm.DB) error { + if res := tx.Where("session_id = ?", session.ID).Delete(&store.PushEvent{}).Error; res != nil { + return res + } + if res := tx.Where("id = ?", session.ID).Delete(&store.Session{}).Error; res != nil { + return res + } + return nil + }) + if err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return } - return nil - }) - if err != nil { - ErrResponse(w, http.StatusInternalServerError, err) - return } WriteResponse(w, nil) diff --git a/net/web/src/api/clearLogin.js b/net/web/src/api/clearLogin.js index 8dcf4bde..cc2887ff 100644 --- a/net/web/src/api/clearLogin.js +++ b/net/web/src/api/clearLogin.js @@ -1,7 +1,10 @@ import { checkResponse, fetchWithTimeout } from './fetchUtil'; -export async function clearLogin(token) { - let logout = await fetchWithTimeout(`/account/apps?agent=${token}`, { method: 'DELETE' }) +export async function clearLogin(token, all) { +console.log("LOGOUT: ", token, all); + + const param = all ? '&all=true' : '' + const logout = await fetchWithTimeout(`/account/apps?agent=${token}${param}`, { method: 'DELETE' }) checkResponse(logout) } diff --git a/net/web/src/context/useAppContext.hook.js b/net/web/src/context/useAppContext.hook.js index eec9b889..7b5bbfee 100644 --- a/net/web/src/context/useAppContext.hook.js +++ b/net/web/src/context/useAppContext.hook.js @@ -71,8 +71,8 @@ export function useAppContext(websocket) { } const actions = { - logout: async () => { - await appLogout(); + logout: async (all) => { + await appLogout(all); }, access: async (token) => { await appAccess(token) @@ -140,10 +140,10 @@ export function useAppContext(websocket) { return access.created; } - const appLogout = async () => { + const appLogout = async (all) => { clearSession(); try { - await clearLogin(appToken.current); + await clearLogin(appToken.current, all); } catch (err) { console.log(err); diff --git a/net/web/src/session/identity/Identity.jsx b/net/web/src/session/identity/Identity.jsx index f8d53d7d..a41a00d8 100644 --- a/net/web/src/session/identity/Identity.jsx +++ b/net/web/src/session/identity/Identity.jsx @@ -1,6 +1,7 @@ -import { Modal, Dropdown, Menu, Tooltip } from 'antd'; +import { useRef } from 'react'; +import { Modal, Switch, Dropdown, Menu, Tooltip } from 'antd'; import { Logo } from 'logo/Logo'; -import { IdentityWrapper, ErrorNotice, InfoNotice } from './Identity.styled'; +import { IdentityWrapper, LogoutContent, ErrorNotice, InfoNotice } from './Identity.styled'; import { useIdentity } from './useIdentity.hook'; import { LogoutOutlined, InfoCircleOutlined, ExclamationCircleOutlined, DownOutlined } from '@ant-design/icons'; @@ -8,14 +9,19 @@ export function Identity({ openAccount, openCards, cardUpdated }) { const [modal, modalContext] = Modal.useModal(); const { state, actions } = useIdentity(); + const all = useRef(false); const logout = () => { modal.confirm({ title: 'Are you sure you want to logout?', icon: , + content: e.stopPropagation()}> + Logout of All Devices + {all.current = e}} size="small" /> + , bodyStyle: { padding: 16 }, onOk() { - actions.logout(); + actions.logout(all.current); }, onCancel() {}, }); diff --git a/net/web/src/session/identity/Identity.styled.js b/net/web/src/session/identity/Identity.styled.js index 7514fa2e..0c122d4b 100644 --- a/net/web/src/session/identity/Identity.styled.js +++ b/net/web/src/session/identity/Identity.styled.js @@ -60,6 +60,18 @@ export const IdentityWrapper = styled.div` } `; +export const LogoutContent = styled.div` + display: flex; + align-items: center; + justify-content: center; + padding: 8px; + + .logoutMode { + padding-right: 8px; + color: ${Colors.text}; + } +` + export const ErrorNotice = styled.div` color: ${Colors.alert}; ` diff --git a/net/web/src/session/identity/useIdentity.hook.js b/net/web/src/session/identity/useIdentity.hook.js index 637e6ece..911e4c95 100644 --- a/net/web/src/session/identity/useIdentity.hook.js +++ b/net/web/src/session/identity/useIdentity.hook.js @@ -33,7 +33,9 @@ export function useIdentity() { }, [app.state]); const actions = { - logout: app.actions.logout, + logout: (all) => { + app.actions.logout(all); + }, }; return { state, actions };