Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions backend/utils/ps/ps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ps

import (
"fmt"
"github.com/shirou/gopsutil/v3/host"
"github.com/shirou/gopsutil/v3/process"
"strconv"
"testing"
Expand Down Expand Up @@ -61,8 +62,17 @@ func TestPs(t *testing.T) {
if err == nil {
fmt.Println(cmdLine)
}
ss, err := pro.Terminal()
if err == nil {
fmt.Println(ss)
}

fmt.Println(fmt.Sprintf("Name: %s PId: %v ParentID: %v Username: %v status:%s startTime: %s numThreads: %v numConnections:%v cpuPercent:%v rss:%s MB IORead: %s IOWrite: %s",
name, pro.Pid, parentID, userName, status, startTime, numThreads, numConnections, cpuPercent, rss, ioRead, ioWrite))
}
users, err := host.Users()
if err == nil {
fmt.Println(users)
}

}
71 changes: 71 additions & 0 deletions backend/utils/websocket/process_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/shirou/gopsutil/v3/host"
"github.com/shirou/gopsutil/v3/net"
"github.com/shirou/gopsutil/v3/process"
"strings"
Expand All @@ -15,6 +16,7 @@ type WsInput struct {
Type string `json:"type"`
DownloadProgress
PsProcessConfig
SSHSessionConfig
}

type DownloadProgress struct {
Expand All @@ -27,6 +29,11 @@ type PsProcessConfig struct {
Username string `json:"username"`
}

type SSHSessionConfig struct {
LoginUser string `json:"loginUser"`
LoginIP string `json:"loginIP"`
}

type PsProcessData struct {
PID int32 `json:"PID"`
Name string `json:"name"`
Expand Down Expand Up @@ -64,6 +71,15 @@ type processConnect struct {
Status string `json:"status"`
Laddr net.Addr `json:"localaddr"`
Raddr net.Addr `json:"remoteaddr"`
PID string `json:"PID"`
}

type sshSession struct {
Username string `json:"username"`
PID int32 `json:"PID"`
Terminal string `json:"terminal"`
Host string `json:"host"`
LoginTime string `json:"loginTime"`
}

func ProcessData(c *Client, inputMsg []byte) {
Expand All @@ -86,6 +102,12 @@ func ProcessData(c *Client, inputMsg []byte) {
return
}
c.Msg <- res
case "ssh":
res, err := getSSHSessions(wsInput.SSHSessionConfig)
if err != nil {
return
}
c.Msg <- res
}

}
Expand Down Expand Up @@ -220,3 +242,52 @@ func getProcessData(processConfig PsProcessConfig) (res []byte, err error) {
res, err = json.Marshal(result)
return
}

func getSSHSessions(config SSHSessionConfig) (res []byte, err error) {
var (
result []sshSession
users []host.UserStat
processes []*process.Process
)
processes, err = process.Processes()
if err != nil {
return
}
users, err = host.Users()
if err != nil {
return
}
for _, proc := range processes {
name, _ := proc.Name()
if name != "sshd" || proc.Pid == 0 {
continue
}
connections, _ := proc.Connections()
for _, conn := range connections {
for _, user := range users {
if user.Host == "" {
continue
}
if conn.Raddr.IP == user.Host {
if config.LoginUser != "" && !strings.Contains(user.User, config.LoginUser) {
continue
}
if config.LoginIP != "" && !strings.Contains(user.Host, config.LoginIP) {
continue
}
session := sshSession{
Username: user.User,
Host: user.Host,
Terminal: user.Terminal,
PID: proc.Pid,
}
t := time.Unix(int64(user.Started), 0)
session.LoginTime = t.Format("2006-1-2 15:04:05")
result = append(result, session)
}
}
}
}
res, err = json.Marshal(result)
return
}
7 changes: 6 additions & 1 deletion frontend/src/lang/modules/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -912,7 +912,12 @@ const message = {
publickey: 'Key',
belong: 'Belong',
local: 'Local',
remote: 'Remote',
config: 'Configuration',
session: 'Session',
loginTime: 'Login Time',
loginIP: 'Login IP',
disconnect: 'Disconnect',
stopSSHWarn: 'Whether to disconnect this SSH connection',
},
setting: {
all: 'All',
Expand Down
10 changes: 8 additions & 2 deletions frontend/src/lang/modules/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ const message = {
website: '网站',
project: '项目',
config: '配置',
ssh: 'SSH 配置',
ssh: 'SSH 管理',
firewall: '防火墙',
ssl: '证书',
database: '数据库',
Expand Down Expand Up @@ -873,13 +873,19 @@ const message = {
keyAuthHelper: '是否启用密钥认证,默认启用。',
useDNS: '反向解析',
dnsHelper: '控制 SSH 服务器是否启用 DNS 解析功能,从而验证连接方的身份。',
loginLogs: 'SSH 登录日志',
loginLogs: '登录日志',
loginMode: '登录方式',
authenticating: '密钥',
publickey: '密钥',
belong: '归属地',
local: '内网',
remote: '外网',
config: '配置',
session: '会话',
loginTime: '登录时间',
loginIP: '登录IP',
disconnect: '断开',
stopSSHWarn: '是否断开此SSH连接',
},
setting: {
all: '全部',
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/routers/modules/host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ const hostRouter = {
requiresAuth: false,
},
},
{
path: '/hosts/ssh/session',
name: 'SSHSession',
component: () => import('@/views/host/ssh/session/index.vue'),
hidden: true,
meta: {
activeMenu: '/hosts/ssh/ssh',
requiresAuth: false,
},
},
],
};

Expand Down
16 changes: 9 additions & 7 deletions frontend/src/views/host/process/process/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,20 @@
ref="tableRef"
v-loading="data.length === 0"
>
<el-table-column :label="'PID'" fix prop="PID" max-width="60px" sortable>
<template #default="{ row }">
<el-link @click="openDetail(row)">{{ row.PID }}</el-link>
</template>
</el-table-column>
<el-table-column :label="'PID'" fix prop="PID" max-width="60px" sortable></el-table-column>
<el-table-column
:label="$t('commons.table.name')"
fix
prop="name"
min-width="120px"
></el-table-column>
<el-table-column :label="$t('process.ppid')" fix prop="PPID" sortable></el-table-column>
<el-table-column
:label="$t('process.ppid')"
min-width="120px"
fix
prop="PPID"
sortable
></el-table-column>
<el-table-column :label="$t('process.numThreads')" fix prop="numThreads"></el-table-column>
<el-table-column :label="$t('commons.table.user')" fix prop="username"></el-table-column>
<el-table-column
Expand Down Expand Up @@ -116,7 +118,7 @@
:label="$t('process.startTime')"
fix
prop="startTime"
min-width="120px"
min-width="140px"
></el-table-column>
<fu-table-operations :ellipsis="10" :buttons="buttons" :label="$t('commons.table.operate')" fix />
</ComplexTable>
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/views/host/ssh/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ import RouterButton from '@/components/router-button/index.vue';

const buttons = [
{
label: i18n.global.t('menu.ssh'),
label: i18n.global.t('menu.config'),
path: '/hosts/ssh/ssh',
},
{
label: i18n.global.t('ssh.loginLogs'),
path: '/hosts/ssh/log',
},
{
label: i18n.global.t('ssh.session'),
path: '/hosts/ssh/session',
},
];
</script>
143 changes: 143 additions & 0 deletions frontend/src/views/host/ssh/session/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
<template>
<div>
<FireRouter />
<LayoutContent :title="$t('ssh.session')">
<template #toolbar>
<div style="width: 100%">
<el-row :gutter="20">
<el-col :span="8"></el-col>
<el-col :span="8"></el-col>
<el-col :span="8">
<div class="search-button">
<el-input
v-model.trim="sshSearch.loginUser"
clearable
@clear="search()"
suffix-icon="Search"
@keyup.enter="search()"
@change="search()"
:placeholder="$t('commons.table.user')"
></el-input>
</div>
</el-col>
</el-row>
</div>
</template>
<template #main>
<ComplexTable :data="data" ref="tableRef" v-loading="loading">
<el-table-column :label="$t('commons.table.user')" fix prop="PID"></el-table-column>
<el-table-column :label="$t('commons.table.user')" fix prop="username"></el-table-column>
<el-table-column :label="'PTS'" fix prop="terminal"></el-table-column>
<el-table-column :label="$t('ssh.loginIP')" fix prop="host"></el-table-column>
<el-table-column
:label="$t('ssh.loginTime')"
fix
prop="loginTime"
min-width="120px"
></el-table-column>
<fu-table-operations :ellipsis="10" :buttons="buttons" :label="$t('commons.table.operate')" fix />
</ComplexTable>
</template>
</LayoutContent>
</div>
</template>

<script setup lang="ts">
import FireRouter from '@/views/host/ssh/index.vue';
import { ref, onMounted, onUnmounted, reactive } from 'vue';
import i18n from '@/lang';
import { StopProcess } from '@/api/modules/process';
import { MsgError, MsgSuccess } from '@/utils/message';

const sshSearch = reactive({
type: 'ssh',
loginUser: '',
});

const buttons = [
{
label: i18n.global.t('ssh.disconnect'),
click: function (row: any) {
stopProcess(row.PID);
},
},
];

let processSocket = ref(null) as unknown as WebSocket;
const data = ref([]);
const loading = ref(false);
const tableRef = ref();

const isWsOpen = () => {
const readyState = processSocket && processSocket.readyState;
return readyState === 1;
};
const closeSocket = () => {
if (isWsOpen()) {
processSocket && processSocket.close();
}
};

const onOpenProcess = () => {};
const onMessage = (message: any) => {
let result: any[] = JSON.parse(message.data);
data.value = result;
loading.value = false;
};

const onerror = () => {};
const onClose = () => {
closeSocket();
};

const initProcess = () => {
let href = window.location.href;
let protocol = href.split('//')[0] === 'http:' ? 'ws' : 'wss';
let ipLocal = href.split('//')[1].split('/')[0];
processSocket = new WebSocket(`${protocol}://${ipLocal}/api/v1/process/ws`);
processSocket.onopen = onOpenProcess;
processSocket.onmessage = onMessage;
processSocket.onerror = onerror;
processSocket.onclose = onClose;
search();
sendMsg();
};

const sendMsg = () => {
loading.value = true;
setInterval(() => {
search();
}, 3000);
};

const search = () => {
if (isWsOpen()) {
processSocket.send(JSON.stringify(sshSearch));
}
};

const stopProcess = async (PID: number) => {
ElMessageBox.confirm(i18n.global.t('ssh.stopSSHWarn'), i18n.global.t('ssh.disconnect'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
})
.then(async () => {
try {
await StopProcess({ PID: PID });
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
} catch (error) {
MsgError(error);
}
})
.catch(() => {});
};

onMounted(() => {
initProcess();
});

onUnmounted(() => {
closeSocket();
});
</script>
Loading