2.6-Preview最后一批代码上线
This commit is contained in:
+57
-27
@@ -1,4 +1,4 @@
|
||||
// process/manager.go
|
||||
// internal/process/manager.go
|
||||
// frpc-console 进程管理模块
|
||||
// 2.6-preview: 端口检测 + 单实例锁定 + 状态自述
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -41,7 +42,6 @@ const (
|
||||
// 数据结构
|
||||
// ================================================================
|
||||
|
||||
// PortStatus 端口检测结果
|
||||
type PortStatus struct {
|
||||
Port int `json:"port"`
|
||||
Occupied bool `json:"occupied"`
|
||||
@@ -50,17 +50,15 @@ type PortStatus struct {
|
||||
ProcessCmd string `json:"process_cmd,omitempty"`
|
||||
}
|
||||
|
||||
// ProcessStatus frpc 进程状态
|
||||
type ProcessStatus struct {
|
||||
State string `json:"state"` // "running" | "stopped" | "unknown" | "conflict"
|
||||
PID int `json:"pid"` // 进程 PID (如果运行中)
|
||||
Port int `json:"port"` // 监听的端口
|
||||
State string `json:"state"`
|
||||
PID int `json:"pid"`
|
||||
Port int `json:"port"`
|
||||
Uptime string `json:"uptime,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// FRPCStatus 来自 frpc admin API 的状态响应
|
||||
type FRPCStatus struct {
|
||||
Version string `json:"version"`
|
||||
RunID string `json:"run_id"`
|
||||
@@ -91,7 +89,6 @@ var (
|
||||
globalMu sync.Mutex
|
||||
)
|
||||
|
||||
// NewManager 创建进程管理器
|
||||
func NewManager(dataDir, configPath, frpcBinPath string) *ProcessManager {
|
||||
return &ProcessManager{
|
||||
dataDir: dataDir,
|
||||
@@ -101,48 +98,62 @@ func NewManager(dataDir, configPath, frpcBinPath string) *ProcessManager {
|
||||
}
|
||||
}
|
||||
|
||||
// SetGlobalManager 设置全局管理器
|
||||
func SetGlobalManager(pm *ProcessManager) {
|
||||
globalMu.Lock()
|
||||
defer globalMu.Unlock()
|
||||
globalManager = pm
|
||||
}
|
||||
|
||||
// GetGlobalManager 获取全局管理器
|
||||
func GetGlobalManager() *ProcessManager {
|
||||
globalMu.Lock()
|
||||
defer globalMu.Unlock()
|
||||
return globalManager
|
||||
}
|
||||
|
||||
// AdminPort 获取 admin_port
|
||||
func (pm *ProcessManager) AdminPort() int {
|
||||
return pm.adminPort
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// 配置读取
|
||||
// 配置读取 (带调试日志)
|
||||
// ================================================================
|
||||
|
||||
// LoadConfig 从 frpc.toml 读取 admin_port 配置
|
||||
// 兼容 frp 0.52.0 前后的配置格式
|
||||
func (pm *ProcessManager) LoadConfig() error {
|
||||
content, err := os.ReadFile(pm.configPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取配置文件失败: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] LoadConfig 读取到文件,长度: %d 字节", len(content))
|
||||
|
||||
preview := string(content)
|
||||
if len(preview) > 600 {
|
||||
preview = preview[:600] + "\n... (截断)"
|
||||
}
|
||||
log.Printf("[DEBUG] 文件内容预览:\n%s", preview)
|
||||
|
||||
if port := extractIntValue(string(content), "admin_port"); port > 0 {
|
||||
pm.adminPort = port
|
||||
log.Printf("[DEBUG] ✅ 从 admin_port 解析到端口: %d", port)
|
||||
return nil
|
||||
}
|
||||
log.Printf("[DEBUG] ❌ admin_port 未找到")
|
||||
|
||||
if port := extractIntValueFromSection(string(content), "webServer", "port"); port > 0 {
|
||||
pm.adminPort = port
|
||||
log.Printf("[DEBUG] ✅ 从 webServer.port 解析到端口: %d", port)
|
||||
return nil
|
||||
}
|
||||
log.Printf("[DEBUG] ❌ webServer.port 未找到")
|
||||
|
||||
return fmt.Errorf("未找到 admin_port 或 webServer.port 配置")
|
||||
if port := extractPortFromAddrSection(string(content), "webServer", "addr"); port > 0 {
|
||||
pm.adminPort = port
|
||||
log.Printf("[DEBUG] ✅ 从 webServer.addr 解析到端口: %d", port)
|
||||
return nil
|
||||
}
|
||||
log.Printf("[DEBUG] ❌ webServer.addr 未找到或解析失败")
|
||||
|
||||
return fmt.Errorf("未找到 admin_port 或 webServer.port/addr 配置")
|
||||
}
|
||||
|
||||
func extractIntValue(content, key string) int {
|
||||
@@ -169,7 +180,8 @@ func extractIntValueFromSection(content, section, key string) int {
|
||||
for _, line := range lines {
|
||||
trimmed := strings.TrimSpace(line)
|
||||
if strings.HasPrefix(trimmed, "[") && strings.HasSuffix(trimmed, "]") {
|
||||
inSection = strings.Trim(trimmed, "[]") == section
|
||||
sectionName := strings.TrimSpace(strings.Trim(trimmed, "[]"))
|
||||
inSection = strings.EqualFold(sectionName, section)
|
||||
continue
|
||||
}
|
||||
if inSection && strings.HasPrefix(trimmed, key) {
|
||||
@@ -186,14 +198,40 @@ func extractIntValueFromSection(content, section, key string) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func extractPortFromAddrSection(content, section, key string) int {
|
||||
lines := strings.Split(content, "\n")
|
||||
inSection := false
|
||||
for _, line := range lines {
|
||||
trimmed := strings.TrimSpace(line)
|
||||
if strings.HasPrefix(trimmed, "[") && strings.HasSuffix(trimmed, "]") {
|
||||
sectionName := strings.TrimSpace(strings.Trim(trimmed, "[]"))
|
||||
inSection = strings.EqualFold(sectionName, section)
|
||||
continue
|
||||
}
|
||||
if inSection && strings.HasPrefix(trimmed, key) {
|
||||
parts := strings.SplitN(trimmed, "=", 2)
|
||||
if len(parts) == 2 {
|
||||
val := strings.TrimSpace(parts[1])
|
||||
val = strings.Trim(val, `"`)
|
||||
if idx := strings.LastIndex(val, ":"); idx != -1 {
|
||||
portStr := val[idx+1:]
|
||||
if port, err := strconv.Atoi(portStr); err == nil && port > 0 {
|
||||
return port
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// 端口检测
|
||||
// ================================================================
|
||||
|
||||
// CheckPort 检测端口是否被占用
|
||||
func (pm *ProcessManager) CheckPort() (bool, error) {
|
||||
if pm.adminPort <= 0 {
|
||||
return false, fmt.Errorf("admin_port 未配置")
|
||||
return false, fmt.Errorf("admin_port 未配置 (当前值: %d)", pm.adminPort)
|
||||
}
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", pm.adminPort), PortCheckTimeout)
|
||||
if err != nil {
|
||||
@@ -203,7 +241,6 @@ func (pm *ProcessManager) CheckPort() (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// GetPortStatus 获取端口完整状态 (占用 + PID + 进程类型)
|
||||
func (pm *ProcessManager) GetPortStatus() (*PortStatus, error) {
|
||||
status := &PortStatus{Port: pm.adminPort, Occupied: false, PID: 0, IsFRPC: false}
|
||||
occupied, err := pm.CheckPort()
|
||||
@@ -389,11 +426,10 @@ func (pm *ProcessManager) deletePIDFile() error {
|
||||
// 状态查询
|
||||
// ================================================================
|
||||
|
||||
// Status 获取 frpc 进程实时状态
|
||||
func (pm *ProcessManager) Status() (*ProcessStatus, error) {
|
||||
status := &ProcessStatus{State: "unknown", PID: 0, Port: pm.adminPort}
|
||||
if pm.adminPort <= 0 {
|
||||
status.Error = "admin_port 未配置"
|
||||
status.Error = fmt.Sprintf("admin_port 未配置 (当前值: %d)", pm.adminPort)
|
||||
return status, nil
|
||||
}
|
||||
portStatus, err := pm.GetPortStatus()
|
||||
@@ -460,7 +496,6 @@ func (pm *ProcessManager) getFRPCStatus(pid int) *FRPCStatus {
|
||||
// 操作执行 (Start / Stop / Restart)
|
||||
// ================================================================
|
||||
|
||||
// Start 启动 frpc (幂等)
|
||||
func (pm *ProcessManager) Start(ctx context.Context) error {
|
||||
if err := pm.Lock(); err != nil {
|
||||
return fmt.Errorf("获取锁失败: %w", err)
|
||||
@@ -483,10 +518,7 @@ func (pm *ProcessManager) startLocked(ctx context.Context) error {
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, pm.frpcBinPath, "-c", pm.configPath)
|
||||
|
||||
// 设置进程属性 (平台相关)
|
||||
setProcessAttributes(cmd)
|
||||
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
@@ -508,7 +540,6 @@ func (pm *ProcessManager) startLocked(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop 停止 frpc (幂等)
|
||||
func (pm *ProcessManager) Stop(ctx context.Context) error {
|
||||
if err := pm.Lock(); err != nil {
|
||||
return fmt.Errorf("获取锁失败: %w", err)
|
||||
@@ -565,7 +596,6 @@ func (pm *ProcessManager) stopLocked(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Restart 重启 frpc (原子操作)
|
||||
func (pm *ProcessManager) Restart(ctx context.Context) error {
|
||||
if err := pm.Lock(); err != nil {
|
||||
return fmt.Errorf("获取锁失败: %w", err)
|
||||
|
||||
Reference in New Issue
Block a user