From 1fd334190206940f2eb8305442938011e81ebdc4 Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Fri, 16 Jan 2026 14:57:41 +0800 Subject: [PATCH 1/3] feat: Add endpoint to retrieve listening processes and update related services --- agent/app/api/v2/process.go | 15 +++ agent/app/service/firewall.go | 11 +-- agent/app/service/process.go | 61 +++++++++++- agent/router/ro_process.go | 1 + frontend/src/api/interface/process.ts | 8 ++ frontend/src/api/modules/process.ts | 4 + .../src/views/host/firewall/port/index.vue | 92 ++++++++++++++----- 7 files changed, 155 insertions(+), 37 deletions(-) diff --git a/agent/app/api/v2/process.go b/agent/app/api/v2/process.go index d7190b717b7a..7b9fbf944497 100644 --- a/agent/app/api/v2/process.go +++ b/agent/app/api/v2/process.go @@ -57,3 +57,18 @@ func (b *BaseApi) GetProcessInfoByPID(c *gin.Context) { } helper.SuccessWithData(c, data) } + +// @Tags Process +// @Summary Get Listening Process +// @Success 200 +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /process/listening [post] +func (b *BaseApi) GetListeningProcess(c *gin.Context) { + procs, err := processService.GetListeningProcess() + if err != nil { + helper.BadRequest(c, err) + return + } + helper.SuccessWithData(c, procs) +} diff --git a/agent/app/service/firewall.go b/agent/app/service/firewall.go index 0e55b82800d8..f54c3eef2bc7 100644 --- a/agent/app/service/firewall.go +++ b/agent/app/service/firewall.go @@ -720,16 +720,9 @@ func checkPortUsed(ports, proto string, apps []portOfApp) string { for _, app := range apps { if app.HttpPort == ports || app.HttpsPort == ports { - return fmt.Sprintf("(%s)", app.AppName) + return app.AppName } } - port, err := strconv.Atoi(ports) - if err != nil { - global.LOG.Errorf(" convert string %v to int failed, err: %v", port, err) - return "" - } - if common.ScanPortWithProto(port, proto) { - return "inUsed" - } + return "" } diff --git a/agent/app/service/process.go b/agent/app/service/process.go index 3bcefe9f1291..0eda7c1c0a8c 100644 --- a/agent/app/service/process.go +++ b/agent/app/service/process.go @@ -2,15 +2,18 @@ package service import ( "bufio" + "context" "fmt" - "github.com/1Panel-dev/1Panel/agent/app/dto/request" - "github.com/1Panel-dev/1Panel/agent/utils/common" - "github.com/1Panel-dev/1Panel/agent/utils/websocket" - "github.com/shirou/gopsutil/v4/process" "os" "strconv" "strings" "time" + + "github.com/1Panel-dev/1Panel/agent/app/dto/request" + "github.com/1Panel-dev/1Panel/agent/utils/common" + "github.com/1Panel-dev/1Panel/agent/utils/websocket" + "github.com/shirou/gopsutil/v4/net" + "github.com/shirou/gopsutil/v4/process" ) type ProcessService struct{} @@ -18,6 +21,7 @@ type ProcessService struct{} type IProcessService interface { StopProcess(req request.ProcessReq) error GetProcessInfoByPID(pid int32) (*websocket.PsProcessData, error) + GetListeningProcess() ([]ListeningProcess, error) } func NewIProcessService() IProcessService { @@ -35,6 +39,55 @@ func (ps *ProcessService) StopProcess(req request.ProcessReq) error { return nil } +type ListeningProcess struct { + PID int32 + Port map[uint32]struct{} + Protocol uint32 + Name string +} + +func (ps *ProcessService) GetListeningProcess() ([]ListeningProcess, error) { + conn, err := net.ConnectionsMaxWithContext(context.Background(), "inet", 32768) + if err != nil { + return nil, err + } + procCache := make(map[int32]ListeningProcess, 64) + + for _, conn := range conn { + if conn.Pid == 0 { + continue + } + + if conn.Status == "LISTEN" { + if _, exists := procCache[conn.Pid]; !exists { + proc, err := process.NewProcess(conn.Pid) + if err != nil { + continue + } + procData := ListeningProcess{ + PID: conn.Pid, + } + procData.Name, _ = proc.Name() + procData.Port = make(map[uint32]struct{}) + procData.Port[conn.Laddr.Port] = struct{}{} + procData.Protocol = conn.Type + procCache[conn.Pid] = procData + } else { + p := procCache[conn.Pid] + p.Port[conn.Laddr.Port] = struct{}{} + procCache[conn.Pid] = p + } + } + } + + procs := make([]ListeningProcess, 0, len(procCache)) + for _, proc := range procCache { + procs = append(procs, proc) + } + + return procs, nil +} + func (ps *ProcessService) GetProcessInfoByPID(pid int32) (*websocket.PsProcessData, error) { p, err := process.NewProcess(pid) if err != nil { diff --git a/agent/router/ro_process.go b/agent/router/ro_process.go index 32e15b91dd7e..685de151f2ce 100644 --- a/agent/router/ro_process.go +++ b/agent/router/ro_process.go @@ -14,6 +14,7 @@ func (f *ProcessRouter) InitRouter(Router *gin.RouterGroup) { { processRouter.GET("/ws", baseApi.ProcessWs) processRouter.POST("/stop", baseApi.StopProcess) + processRouter.POST("/listening", baseApi.GetListeningProcess) processRouter.GET("/:pid", baseApi.GetProcessInfoByPID) } } diff --git a/frontend/src/api/interface/process.ts b/frontend/src/api/interface/process.ts index a20093ea83b0..ddf47fb3b93c 100644 --- a/frontend/src/api/interface/process.ts +++ b/frontend/src/api/interface/process.ts @@ -55,4 +55,12 @@ export namespace Process { path: string; fd: number; } + + export interface ListeningProcess { + PID: number; + Port: { [key: string]: {} }; + Protocol: number; + Name: string; + CmdLine?: string; + } } diff --git a/frontend/src/api/modules/process.ts b/frontend/src/api/modules/process.ts index 28545abec5b6..16ac1fbba1f3 100644 --- a/frontend/src/api/modules/process.ts +++ b/frontend/src/api/modules/process.ts @@ -8,3 +8,7 @@ export const stopProcess = (req: Process.StopReq) => { export const getProcessByID = (pid: number) => { return http.get(`/process/${pid}`); }; + +export const getListeningProcess = () => { + return http.post(`/process/listening`); +}; diff --git a/frontend/src/views/host/firewall/port/index.vue b/frontend/src/views/host/firewall/port/index.vue index 5ae29f50487c..8b77404b4590 100644 --- a/frontend/src/views/host/firewall/port/index.vue +++ b/frontend/src/views/host/firewall/port/index.vue @@ -88,28 +88,20 @@ @@ -163,6 +155,7 @@ + @@ -171,12 +164,16 @@ import FireRouter from '@/views/host/firewall/index.vue'; import OperateDialog from '@/views/host/firewall/port/operate/index.vue'; import ImportDialog from '@/views/host/firewall/port/import/index.vue'; import FireStatus from '@/views/host/firewall/status/index.vue'; +import ProcessDetail from '@/views/host/process/process/detail/index.vue'; import { onMounted, reactive, ref } from 'vue'; import { batchOperateRule, searchFireRule, updateFirewallDescription, updatePortRule } from '@/api/modules/host'; +import { getListeningProcess } from '@/api/modules/process'; import { Host } from '@/api/interface/host'; +import { Process } from '@/api/interface/process'; import i18n from '@/lang'; import { MsgSuccess } from '@/utils/message'; import { ElMessageBox } from 'element-plus'; +import { Expand } from '@element-plus/icons-vue'; import { routerToName } from '@/utils/router'; import { downloadWithContent, getCurrentDateFormatted } from '@/utils/util'; @@ -195,6 +192,9 @@ const fireStatusRef = ref(); const opRef = ref(); const dialogImportRef = ref(); +const processDetailRef = ref(); + +const listeningProcesses = ref([]); const data = ref(); const paginationConfig = reactive({ @@ -204,6 +204,46 @@ const paginationConfig = reactive({ total: 0, }); +const extractPortsFromObject = (portObj: { [key: string]: {} }): number[] => { + return Object.keys(portObj) + .map((portStr) => parseInt(portStr)) + .filter((port) => !isNaN(port)); +}; + +const isSinglePort = (portStr: string): boolean => { + return portStr.indexOf('-') === -1 && portStr.indexOf(':') === -1 && portStr.indexOf(',') === -1; +}; + +const loadListeningProcesses = async () => { + try { + const res = await getListeningProcess(); + listeningProcesses.value = res.data || []; + + for (const item of data.value) { + if (!item.usedStatus && isSinglePort(item.port)) { + const portNum = parseInt(item.port.trim()); + if (!isNaN(portNum)) { + const protocolNum = + item.protocol.toLowerCase() === 'tcp' ? 1 : item.protocol.toLowerCase() === 'udp' ? 2 : 0; + + for (const proc of listeningProcesses.value) { + if (proc.Protocol === protocolNum) { + const procPorts = extractPortsFromObject(proc.Port); + if (procPorts.includes(portNum)) { + item.usedStatus = proc.Name; + item.processInfo = proc; + break; + } + } + } + } + } + } + } catch (error) { + console.error('Failed to load listening processes:', error); + } +}; + const search = async () => { if (!isActive.value) { loading.value = false; @@ -221,12 +261,12 @@ const search = async () => { }; loading.value = true; await searchFireRule(params) - .then((res) => { + .then(async (res) => { loading.value = false; data.value = res.data.items || []; - for (const item of data.value) { - item.usedPorts = item.usedStatus ? item.usedStatus.split(',') : []; - } + + await loadListeningProcesses(); + paginationConfig.total = res.data.total; }) .catch(() => { @@ -381,6 +421,10 @@ const onExport = () => { }); }; +const showProcessDetail = (pid: number) => { + processDetailRef.value?.acceptParams(pid); +}; + const buttons = [ { label: i18n.global.t('commons.button.edit'), From a5484dad5f2e97b99fcf4ffe4d1d0f0ef1f908d7 Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Fri, 16 Jan 2026 15:12:25 +0800 Subject: [PATCH 2/3] fix: Update listening process retrieval logic to include socket type checks --- agent/app/service/process.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/agent/app/service/process.go b/agent/app/service/process.go index 0eda7c1c0a8c..bb19ae1fd1b8 100644 --- a/agent/app/service/process.go +++ b/agent/app/service/process.go @@ -7,6 +7,7 @@ import ( "os" "strconv" "strings" + "syscall" "time" "github.com/1Panel-dev/1Panel/agent/app/dto/request" @@ -58,7 +59,7 @@ func (ps *ProcessService) GetListeningProcess() ([]ListeningProcess, error) { continue } - if conn.Status == "LISTEN" { + if (conn.Status == "LISTEN" && conn.Type == syscall.SOCK_STREAM) || (conn.Type == syscall.SOCK_DGRAM && conn.Raddr.Port == 0) { if _, exists := procCache[conn.Pid]; !exists { proc, err := process.NewProcess(conn.Pid) if err != nil { From 2297961187a01a9aa5294cb5110fd73dad930884 Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Fri, 16 Jan 2026 15:22:51 +0800 Subject: [PATCH 3/3] refactor: accept context for improved request handling --- agent/app/api/v2/process.go | 2 +- agent/app/service/process.go | 6 +++--- frontend/src/api/interface/process.ts | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/agent/app/api/v2/process.go b/agent/app/api/v2/process.go index 7b9fbf944497..b84fb45819bd 100644 --- a/agent/app/api/v2/process.go +++ b/agent/app/api/v2/process.go @@ -65,7 +65,7 @@ func (b *BaseApi) GetProcessInfoByPID(c *gin.Context) { // @Security Timestamp // @Router /process/listening [post] func (b *BaseApi) GetListeningProcess(c *gin.Context) { - procs, err := processService.GetListeningProcess() + procs, err := processService.GetListeningProcess(c) if err != nil { helper.BadRequest(c, err) return diff --git a/agent/app/service/process.go b/agent/app/service/process.go index bb19ae1fd1b8..7b2686708866 100644 --- a/agent/app/service/process.go +++ b/agent/app/service/process.go @@ -22,7 +22,7 @@ type ProcessService struct{} type IProcessService interface { StopProcess(req request.ProcessReq) error GetProcessInfoByPID(pid int32) (*websocket.PsProcessData, error) - GetListeningProcess() ([]ListeningProcess, error) + GetListeningProcess(c context.Context) ([]ListeningProcess, error) } func NewIProcessService() IProcessService { @@ -47,8 +47,8 @@ type ListeningProcess struct { Name string } -func (ps *ProcessService) GetListeningProcess() ([]ListeningProcess, error) { - conn, err := net.ConnectionsMaxWithContext(context.Background(), "inet", 32768) +func (ps *ProcessService) GetListeningProcess(c context.Context) ([]ListeningProcess, error) { + conn, err := net.ConnectionsMaxWithContext(c, "inet", 32768) if err != nil { return nil, err } diff --git a/frontend/src/api/interface/process.ts b/frontend/src/api/interface/process.ts index ddf47fb3b93c..cc1a22c40eb5 100644 --- a/frontend/src/api/interface/process.ts +++ b/frontend/src/api/interface/process.ts @@ -61,6 +61,5 @@ export namespace Process { Port: { [key: string]: {} }; Protocol: number; Name: string; - CmdLine?: string; } }