mirror of https://github.com/bjdgyc/anylink.git
Merge pull request #298 from lanrenwo/add_online_search
新增在线用户的搜索和一键下线功能
This commit is contained in:
commit
098f321343
|
@ -12,17 +12,19 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
UserAuthFail = 0 // 认证失败
|
UserAuthFail = 0 // 认证失败
|
||||||
UserAuthSuccess = 1 // 认证成功
|
UserAuthSuccess = 1 // 认证成功
|
||||||
UserConnected = 2 // 连线成功
|
UserConnected = 2 // 连线成功
|
||||||
UserLogout = 3 // 用户登出
|
UserLogout = 3 // 用户登出
|
||||||
UserLogoutLose = 0 // 用户掉线
|
UserLogoutLose = 0 // 用户掉线
|
||||||
UserLogoutBanner = 1 // 用户banner弹窗取消
|
UserLogoutBanner = 1 // 用户banner弹窗取消
|
||||||
UserLogoutClient = 2 // 用户主动登出
|
UserLogoutClient = 2 // 用户主动登出
|
||||||
UserLogoutTimeout = 3 // 用户超时登出
|
UserLogoutTimeout = 3 // 用户超时登出
|
||||||
UserLogoutAdmin = 4 // 账号被管理员踢下线
|
UserLogoutAdmin = 4 // 账号被管理员踢下线
|
||||||
UserLogoutExpire = 5 // 账号过期被踢下线
|
UserLogoutExpire = 5 // 账号过期被踢下线
|
||||||
UserIdleTimeout = 6 // 用户空闲链接超时
|
UserIdleTimeout = 6 // 用户空闲链接超时
|
||||||
|
UserLogoutOneAdmin = 7 // 账号被管理员一键下线
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserActLogProcess struct {
|
type UserActLogProcess struct {
|
||||||
|
@ -57,13 +59,14 @@ var (
|
||||||
3: "AnyLink",
|
3: "AnyLink",
|
||||||
},
|
},
|
||||||
InfoOps: []string{ // 信息
|
InfoOps: []string{ // 信息
|
||||||
UserLogoutLose: "用户掉线",
|
UserLogoutLose: "用户掉线",
|
||||||
UserLogoutBanner: "用户取消弹窗/客户端发起的logout",
|
UserLogoutBanner: "用户取消弹窗/客户端发起的logout",
|
||||||
UserLogoutClient: "用户/客户端主动断开",
|
UserLogoutClient: "用户/客户端主动断开",
|
||||||
UserLogoutTimeout: "Session过期被踢下线",
|
UserLogoutTimeout: "Session过期被踢下线",
|
||||||
UserLogoutAdmin: "账号被管理员踢下线",
|
UserLogoutAdmin: "账号被管理员踢下线",
|
||||||
UserLogoutExpire: "账号过期被踢下线",
|
UserLogoutExpire: "账号过期被踢下线",
|
||||||
UserIdleTimeout: "用户空闲链接超时",
|
UserIdleTimeout: "用户空闲链接超时",
|
||||||
|
UserLogoutOneAdmin: "账号被管理员一键下线",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -126,6 +129,9 @@ func (ua *UserActLogProcess) GetStatusOpsWithTag() interface{} {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ua *UserActLogProcess) GetInfoOpsById(id uint8) string {
|
func (ua *UserActLogProcess) GetInfoOpsById(id uint8) string {
|
||||||
|
if int(id) >= len(ua.InfoOps) {
|
||||||
|
return "未知的信息类型"
|
||||||
|
}
|
||||||
return ua.InfoOps[id]
|
return ua.InfoOps[id]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"net"
|
"net"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bjdgyc/anylink/pkg/utils"
|
"github.com/bjdgyc/anylink/pkg/utils"
|
||||||
|
@ -42,33 +43,74 @@ func (o Onlines) Swap(i, j int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func OnlineSess() []Online {
|
func OnlineSess() []Online {
|
||||||
|
return GetOnlineSess("", "", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: GetOnlineSess
|
||||||
|
* @param search_cate 分类:用户名、登录组、MAC地址、IP地址、远端地址
|
||||||
|
* @param search_text 关键字,模糊搜索
|
||||||
|
* @param show_sleeper 是否显示休眠用户
|
||||||
|
* @return []Online
|
||||||
|
*/
|
||||||
|
func GetOnlineSess(search_cate string, search_text string, show_sleeper bool) []Online {
|
||||||
var datas Onlines
|
var datas Onlines
|
||||||
|
if strings.TrimSpace(search_text) == "" {
|
||||||
|
search_cate = ""
|
||||||
|
}
|
||||||
sessMux.Lock()
|
sessMux.Lock()
|
||||||
|
defer sessMux.Unlock()
|
||||||
for _, v := range sessions {
|
for _, v := range sessions {
|
||||||
v.mux.Lock()
|
v.mux.Lock()
|
||||||
if v.IsActive {
|
cSess := v.CSess
|
||||||
|
if cSess == nil {
|
||||||
|
cSess = &ConnSession{}
|
||||||
|
}
|
||||||
|
// 选择需要比较的字符串
|
||||||
|
var compareText string
|
||||||
|
switch search_cate {
|
||||||
|
case "username":
|
||||||
|
compareText = v.Username
|
||||||
|
case "group":
|
||||||
|
compareText = v.Group
|
||||||
|
case "mac_addr":
|
||||||
|
compareText = v.MacAddr
|
||||||
|
case "ip":
|
||||||
|
if cSess != nil {
|
||||||
|
compareText = cSess.IpAddr.String()
|
||||||
|
}
|
||||||
|
case "remote_addr":
|
||||||
|
if cSess != nil {
|
||||||
|
compareText = cSess.RemoteAddr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if search_cate != "" && !strings.Contains(compareText, search_text) {
|
||||||
|
v.mux.Unlock()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if show_sleeper || v.IsActive {
|
||||||
val := Online{
|
val := Online{
|
||||||
Token: v.Token,
|
Token: v.Token,
|
||||||
Ip: v.CSess.IpAddr,
|
Ip: cSess.IpAddr,
|
||||||
Username: v.Username,
|
Username: v.Username,
|
||||||
Group: v.Group,
|
Group: v.Group,
|
||||||
MacAddr: v.MacAddr,
|
MacAddr: v.MacAddr,
|
||||||
UniqueMac: v.UniqueMac,
|
UniqueMac: v.UniqueMac,
|
||||||
RemoteAddr: v.CSess.RemoteAddr,
|
RemoteAddr: cSess.RemoteAddr,
|
||||||
TunName: v.CSess.IfName,
|
TunName: cSess.IfName,
|
||||||
Mtu: v.CSess.Mtu,
|
Mtu: cSess.Mtu,
|
||||||
Client: v.CSess.Client,
|
Client: cSess.Client,
|
||||||
BandwidthUp: utils.HumanByte(v.CSess.BandwidthUpPeriod.Load()) + "/s",
|
BandwidthUp: utils.HumanByte(cSess.BandwidthUpPeriod.Load()) + "/s",
|
||||||
BandwidthDown: utils.HumanByte(v.CSess.BandwidthDownPeriod.Load()) + "/s",
|
BandwidthDown: utils.HumanByte(cSess.BandwidthDownPeriod.Load()) + "/s",
|
||||||
BandwidthUpAll: utils.HumanByte(v.CSess.BandwidthUpAll.Load()),
|
BandwidthUpAll: utils.HumanByte(cSess.BandwidthUpAll.Load()),
|
||||||
BandwidthDownAll: utils.HumanByte(v.CSess.BandwidthDownAll.Load()),
|
BandwidthDownAll: utils.HumanByte(cSess.BandwidthDownAll.Load()),
|
||||||
LastLogin: v.LastLogin,
|
LastLogin: v.LastLogin,
|
||||||
}
|
}
|
||||||
datas = append(datas, val)
|
datas = append(datas, val)
|
||||||
}
|
}
|
||||||
v.mux.Unlock()
|
v.mux.Unlock()
|
||||||
}
|
}
|
||||||
sessMux.Unlock()
|
|
||||||
sort.Sort(&datas)
|
sort.Sort(&datas)
|
||||||
return datas
|
return datas
|
||||||
}
|
}
|
||||||
|
|
|
@ -464,7 +464,7 @@ func CloseSess(token string, code ...uint8) {
|
||||||
sess.CSess.Close()
|
sess.CSess.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
AddUserActLogBySess(sess)
|
AddUserActLogBySess(sess, code...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CloseCSess(token string) {
|
func CloseCSess(token string) {
|
||||||
|
@ -501,7 +501,7 @@ func AddUserActLog(cs *ConnSession) {
|
||||||
dbdata.UserActLogIns.Add(ua, cs.UserAgent)
|
dbdata.UserActLogIns.Add(ua, cs.UserAgent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddUserActLogBySess(sess *Session) {
|
func AddUserActLogBySess(sess *Session, code ...uint8) {
|
||||||
ua := dbdata.UserActLog{
|
ua := dbdata.UserActLog{
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
GroupName: sess.Group,
|
GroupName: sess.Group,
|
||||||
|
@ -512,5 +512,8 @@ func AddUserActLogBySess(sess *Session) {
|
||||||
Status: dbdata.UserLogout,
|
Status: dbdata.UserLogout,
|
||||||
}
|
}
|
||||||
ua.Info = dbdata.UserActLogIns.GetInfoOpsById(dbdata.UserLogoutBanner)
|
ua.Info = dbdata.UserActLogIns.GetInfoOpsById(dbdata.UserLogoutBanner)
|
||||||
|
if len(code) > 0 {
|
||||||
|
ua.Info = dbdata.UserActLogIns.GetInfoOpsById(code[0])
|
||||||
|
}
|
||||||
dbdata.UserActLogIns.Add(ua, sess.UserAgent)
|
dbdata.UserActLogIns.Add(ua, sess.UserAgent)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,59 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-card>
|
<el-card>
|
||||||
|
<el-form :inline="true">
|
||||||
|
<el-form-item>
|
||||||
|
<el-select
|
||||||
|
v-model="searchCate"
|
||||||
|
style="width: 86px;"
|
||||||
|
@change="handleSearch">
|
||||||
|
<el-option
|
||||||
|
label="用户名"
|
||||||
|
value="username">
|
||||||
|
</el-option>
|
||||||
|
<el-option
|
||||||
|
label="登录组"
|
||||||
|
value="group">
|
||||||
|
</el-option>
|
||||||
|
<el-option
|
||||||
|
label="MAC地址"
|
||||||
|
value="mac_addr">
|
||||||
|
</el-option>
|
||||||
|
<el-option
|
||||||
|
label="IP地址"
|
||||||
|
value="ip">
|
||||||
|
</el-option>
|
||||||
|
<el-option
|
||||||
|
label="远端地址"
|
||||||
|
value="remote_addr">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-input
|
||||||
|
v-model="searchText"
|
||||||
|
placeholder="请输入搜索内容"
|
||||||
|
@input="handleSearch">
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
显示休眠用户:
|
||||||
|
<el-switch
|
||||||
|
v-model="showSleeper"
|
||||||
|
@change="handleSearch">
|
||||||
|
</el-switch>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
class="extra-small-button"
|
||||||
|
type="danger"
|
||||||
|
size="mini"
|
||||||
|
:loading="loadingOneOffline"
|
||||||
|
@click="handleOneOffline">
|
||||||
|
一键下线
|
||||||
|
</el-button>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
<el-table
|
<el-table
|
||||||
ref="multipleTable"
|
ref="multipleTable"
|
||||||
:data="tableData"
|
:data="tableData"
|
||||||
|
@ -20,14 +73,14 @@
|
||||||
|
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="group"
|
prop="group"
|
||||||
label="登陆组">
|
label="登录组">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="mac_addr"
|
prop="mac_addr"
|
||||||
label="MAC地址">
|
label="MAC地址">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="unique_mac"
|
prop="unique_mac"
|
||||||
label="唯一MAC">
|
label="唯一MAC">
|
||||||
|
@ -67,7 +120,6 @@
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="status"
|
|
||||||
label="实时 上行/下行"
|
label="实时 上行/下行"
|
||||||
width="220">
|
width="220">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
|
@ -77,7 +129,6 @@
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="status"
|
|
||||||
label="总量 上行/下行"
|
label="总量 上行/下行"
|
||||||
width="200">
|
width="200">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
|
@ -88,7 +139,7 @@
|
||||||
|
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="last_login"
|
prop="last_login"
|
||||||
label="登陆时间"
|
label="登录时间"
|
||||||
:formatter="tableDateFormat">
|
:formatter="tableDateFormat">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
|
@ -99,6 +150,7 @@
|
||||||
<el-button
|
<el-button
|
||||||
size="mini"
|
size="mini"
|
||||||
type="primary"
|
type="primary"
|
||||||
|
v-if="scope.row.remote_addr !== ''"
|
||||||
@click="handleReline(scope.row)">重连
|
@click="handleReline(scope.row)">重连
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
|
@ -123,6 +175,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { MessageBox } from 'element-ui';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Online",
|
name: "Online",
|
||||||
|
@ -147,6 +200,10 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
tableData: [],
|
tableData: [],
|
||||||
|
searchCate: 'username',
|
||||||
|
searchText: '',
|
||||||
|
showSleeper: false,
|
||||||
|
loadingOneOffline: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -185,8 +242,43 @@ export default {
|
||||||
handleEdit(a, row) {
|
handleEdit(a, row) {
|
||||||
console.log(a, row)
|
console.log(a, row)
|
||||||
},
|
},
|
||||||
|
handleOneOffline() {
|
||||||
|
if (this.tableData === null || this.tableData.length === 0) {
|
||||||
|
this.$message.error('错误:当前在线用户表为空,无法执行一键下线操作!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MessageBox.confirm('当前搜索条件下的所有用户将会“下线”,你确定执行吗?', '危险', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'danger'
|
||||||
|
}).then(() => {
|
||||||
|
try {
|
||||||
|
this.loadingOneOffline = true;
|
||||||
|
this.getData();
|
||||||
|
this.$message.success('操作成功');
|
||||||
|
this.loadingOneOffline = false;
|
||||||
|
// 清空当前表格
|
||||||
|
this.tableData = [];
|
||||||
|
} catch (error) {
|
||||||
|
this.loadingOneOffline = false;
|
||||||
|
this.$message.error('操作失败');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleSearch() {
|
||||||
|
this.getData();
|
||||||
|
},
|
||||||
getData() {
|
getData() {
|
||||||
axios.get('/user/online').then(resp => {
|
axios.get('/user/online',
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
search_cate: this.searchCate,
|
||||||
|
search_text: this.searchText,
|
||||||
|
show_sleeper: this.showSleeper,
|
||||||
|
one_offline: this.loadingOneOffline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).then(resp => {
|
||||||
var data = resp.data.data
|
var data = resp.data.data
|
||||||
console.log(data);
|
console.log(data);
|
||||||
this.tableData = data.datas;
|
this.tableData = data.datas;
|
||||||
|
@ -201,5 +293,23 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
/deep/ .el-form .el-form-item__label,
|
||||||
|
/deep/ .el-form .el-form-item__content,
|
||||||
|
/deep/ .el-form .el-input,
|
||||||
|
/deep/ .el-form .el-select,
|
||||||
|
/deep/ .el-form .el-button,
|
||||||
|
/deep/ .el-form .el-select-dropdown__item {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
.el-select-dropdown .el-select-dropdown__item {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
/deep/ .el-input__inner{
|
||||||
|
height: 30px;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
.extra-small-button {
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue