diff --git a/server/admin/lockmanager.go b/server/admin/lockmanager.go index c7c99a3f..968ae648 100644 --- a/server/admin/lockmanager.go +++ b/server/admin/lockmanager.go @@ -97,7 +97,25 @@ func UnlockUser(w http.ResponseWriter, r *http.Request) { lm.mu.Lock() defer lm.mu.Unlock() - lm.Unlock(lockinfo.State) + // 根据用户名和IP查找锁定状态 + var state *LockState + switch { + case lockinfo.IP == "" && lockinfo.Username != "": + state = lm.userLocks[lockinfo.Username] // 全局用户锁定 + case lockinfo.Username != "" && lockinfo.IP != "": + if userIPMap, exists := lm.ipUserLocks[lockinfo.Username]; exists { + state = userIPMap[lockinfo.IP] // 单用户 IP 锁定 + } + default: + state = lm.ipLocks[lockinfo.IP] // 全局 IP 锁定 + } + + if state == nil || !state.Locked { + RespError(w, RespInternalErr, fmt.Errorf("锁定状态未找到或已解锁")) + return + } + + lm.Unlock(state) base.Info("解锁成功:", lockinfo.Description, lockinfo.Username, lockinfo.IP) RespSucess(w, "解锁成功!") @@ -110,9 +128,9 @@ func (lm *LockManager) GetLocksInfo() []LockInfo { defer lm.mu.Unlock() for ip, state := range lm.ipLocks { - if state.Locked { + if base.Cfg.MaxGlobalIPBanCount > 0 && state.Locked { info := LockInfo{ - Description: "全局 IP 锁定", + Description: "全局IP锁定", Username: "", IP: ip, State: &LockState{ @@ -127,7 +145,7 @@ func (lm *LockManager) GetLocksInfo() []LockInfo { } for username, state := range lm.userLocks { - if state.Locked { + if base.Cfg.MaxGlobalUserBanCount > 0 && state.Locked { info := LockInfo{ Description: "全局用户锁定", Username: username, @@ -145,9 +163,9 @@ func (lm *LockManager) GetLocksInfo() []LockInfo { for username, ipStates := range lm.ipUserLocks { for ip, state := range ipStates { - if state.Locked { + if base.Cfg.MaxBanCount > 0 && state.Locked { info := LockInfo{ - Description: "单用户 IP 锁定", + Description: "单用户IP锁定", Username: username, IP: ip, State: &LockState{ @@ -205,7 +223,7 @@ func (lm *LockManager) IsWhitelisted(ip string) bool { } func (lm *LockManager) StartCleanupTicker() { - lm.cleanupTicker = time.NewTicker(5 * time.Minute) + lm.cleanupTicker = time.NewTicker(1 * time.Minute) go func() { for range lm.cleanupTicker.C { lm.CleanupExpiredLocks() @@ -220,20 +238,23 @@ func (lm *LockManager) CleanupExpiredLocks() { defer lm.mu.Unlock() for ip, state := range lm.ipLocks { - if now.Sub(state.LastAttempt) > time.Duration(base.Cfg.GlobalLockStateExpirationTime)*time.Second { + if !lm.CheckLockState(state, now, base.Cfg.GlobalIPLockTime) || + now.Sub(state.LastAttempt) > time.Duration(base.Cfg.GlobalLockStateExpirationTime)*time.Second { delete(lm.ipLocks, ip) } } for user, state := range lm.userLocks { - if now.Sub(state.LastAttempt) > time.Duration(base.Cfg.GlobalLockStateExpirationTime)*time.Second { + if !lm.CheckLockState(state, now, base.Cfg.GlobalUserLockTime) || + now.Sub(state.LastAttempt) > time.Duration(base.Cfg.GlobalLockStateExpirationTime)*time.Second { delete(lm.userLocks, user) } } for user, ipMap := range lm.ipUserLocks { for ip, state := range ipMap { - if now.Sub(state.LastAttempt) > time.Duration(base.Cfg.GlobalLockStateExpirationTime)*time.Second { + if !lm.CheckLockState(state, now, base.Cfg.LockTime) || + now.Sub(state.LastAttempt) > time.Duration(base.Cfg.GlobalLockStateExpirationTime)*time.Second { delete(ipMap, ip) if len(ipMap) == 0 { delete(lm.ipUserLocks, user) diff --git a/server/admin/server.go b/server/admin/server.go index 7329f879..dae1a660 100644 --- a/server/admin/server.go +++ b/server/admin/server.go @@ -88,6 +88,7 @@ func StartAdmin() { r.HandleFunc("/statsinfo/list", StatsInfoList) r.HandleFunc("/locksinfo/list", GetLocksInfo) + r.HandleFunc("/locksinfo/unlok", UnlockUser) // pprof if base.Cfg.Pprof { diff --git a/web/src/layout/LayoutAside.vue b/web/src/layout/LayoutAside.vue index cfbbc592..6bb927ea 100644 --- a/web/src/layout/LayoutAside.vue +++ b/web/src/layout/LayoutAside.vue @@ -7,17 +7,9 @@ - + 首页 @@ -44,6 +36,7 @@ 用户列表 用户策略 在线用户 + 锁定管理 IP映射 diff --git a/web/src/pages/user/LockManager.vue b/web/src/pages/user/LockManager.vue new file mode 100644 index 00000000..c4986a83 --- /dev/null +++ b/web/src/pages/user/LockManager.vue @@ -0,0 +1,109 @@ + + + + + \ No newline at end of file diff --git a/web/src/plugins/router.js b/web/src/plugins/router.js index 07b42e53..1daff4d8 100644 --- a/web/src/plugins/router.js +++ b/web/src/plugins/router.js @@ -1,35 +1,36 @@ import Vue from "vue"; import VueRouter from "vue-router"; -import {getToken} from "./token"; +import { getToken } from "./token"; Vue.use(VueRouter) const routes = [ - {path: '/login', component: () => import('@/pages/Login')}, + { path: '/login', component: () => import('@/pages/Login') }, { path: '/admin', component: () => import('@/layout/Layout'), redirect: '/admin/home', children: [ - {path: 'home', component: () => import('@/pages/Home')}, + { path: 'home', component: () => import('@/pages/Home') }, - {path: 'set/system', component: () => import('@/pages/set/System')}, - {path: 'set/soft', component: () => import('@/pages/set/Soft')}, - {path: 'set/other', component: () => import('@/pages/set/Other')}, - {path: 'set/audit', component: () => import('@/pages/set/Audit')}, + { path: 'set/system', component: () => import('@/pages/set/System') }, + { path: 'set/soft', component: () => import('@/pages/set/Soft') }, + { path: 'set/other', component: () => import('@/pages/set/Other') }, + { path: 'set/audit', component: () => import('@/pages/set/Audit') }, - {path: 'user/list', component: () => import('@/pages/user/List')}, - {path: 'user/policy', component: () => import('@/pages/user/Policy')}, - {path: 'user/online', component: () => import('@/pages/user/Online')}, - {path: 'user/ip_map', component: () => import('@/pages/user/IpMap')}, + { path: 'user/list', component: () => import('@/pages/user/List') }, + { path: 'user/policy', component: () => import('@/pages/user/Policy') }, + { path: 'user/online', component: () => import('@/pages/user/Online') }, + { path: 'user/ip_map', component: () => import('@/pages/user/IpMap') }, + { path: 'user/lockmanager', component: () => import('@/pages/user/LockManager') }, - {path: 'group/list', component: () => import('@/pages/group/List')}, + { path: 'group/list', component: () => import('@/pages/group/List') }, ], }, - {path: '*', redirect: '/admin/home'}, + { path: '*', redirect: '/admin/home' }, ] // 3. 创建 router 实例,然后传 `routes` 配置 @@ -64,7 +65,7 @@ router.beforeEach((to, from, next) => { } if (to.path === "/login") { - next({path: '/admin/home'}); + next({ path: '/admin/home' }); return; }