Initial commit
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,226 @@
|
||||
// Windows系统托盘管理
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package tray
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
|
||||
"github.com/getlantern/systray"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
//go:embed favicon.ico
|
||||
var trayIcon []byte
|
||||
|
||||
// TrayManager 托盘管理器
|
||||
type TrayManager struct {
|
||||
logger *zap.Logger
|
||||
webPort int
|
||||
onExit func()
|
||||
mQuit *systray.MenuItem
|
||||
mOpenWeb *systray.MenuItem
|
||||
mStatus *systray.MenuItem
|
||||
mRestart *systray.MenuItem
|
||||
}
|
||||
|
||||
// NewTrayManager 创建托盘管理器
|
||||
func NewTrayManager(logger *zap.Logger, webPort int, onExit func()) *TrayManager {
|
||||
return &TrayManager{
|
||||
logger: logger,
|
||||
webPort: webPort,
|
||||
onExit: onExit,
|
||||
}
|
||||
}
|
||||
|
||||
// Run 运行托盘(阻塞)
|
||||
func (t *TrayManager) Run() {
|
||||
systray.Run(t.onReady, t.onExit)
|
||||
}
|
||||
|
||||
// onReady 托盘初始化
|
||||
func (t *TrayManager) onReady() {
|
||||
// 设置托盘图标和提示
|
||||
systray.SetIcon(trayIcon)
|
||||
systray.SetTooltip("MeshRay - 智能组网工具")
|
||||
|
||||
// 添加菜单项
|
||||
t.mStatus = systray.AddMenuItem("🟢 状态:运行中", "MeshRay运行状态")
|
||||
t.mStatus.Disable()
|
||||
|
||||
systray.AddSeparator()
|
||||
|
||||
t.mOpenWeb = systray.AddMenuItem("🌐 打开管理界面", "打开Web管理界面")
|
||||
_ = systray.AddMenuItem("ℹ️ 关于", "关于MeshRay")
|
||||
|
||||
systray.AddSeparator()
|
||||
|
||||
_ = systray.AddMenuItem("❓ 帮助", "查看帮助")
|
||||
_ = systray.AddMenuItem("🔄 检查更新", "检查新版本")
|
||||
|
||||
systray.AddSeparator()
|
||||
|
||||
t.mRestart = systray.AddMenuItem("🔁 重启服务", "重启MeshRay服务")
|
||||
t.mQuit = systray.AddMenuItem("❌ 退出", "退出MeshRay")
|
||||
|
||||
// 处理菜单点击
|
||||
go t.handleMenuEvents()
|
||||
}
|
||||
|
||||
// handleMenuEvents 处理菜单事件
|
||||
func (t *TrayManager) handleMenuEvents() {
|
||||
for {
|
||||
select {
|
||||
case <-t.mOpenWeb.ClickedCh:
|
||||
t.openWebInterface()
|
||||
|
||||
case <-t.mRestart.ClickedCh:
|
||||
t.restartService()
|
||||
|
||||
case <-t.mQuit.ClickedCh:
|
||||
t.quit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// openWebInterface 打开Web管理界面
|
||||
func (t *TrayManager) openWebInterface() {
|
||||
url := fmt.Sprintf("http://localhost:%d", t.webPort)
|
||||
t.logger.Info("打开Web管理界面", zap.String("url", url))
|
||||
|
||||
var cmd *exec.Cmd
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
cmd = exec.Command("cmd", "/c", "start", url)
|
||||
case "darwin":
|
||||
cmd = exec.Command("open", url)
|
||||
default:
|
||||
cmd = exec.Command("xdg-open", url)
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.logger.Error("打开浏览器失败", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
// restartService 重启服务
|
||||
func (t *TrayManager) restartService() {
|
||||
t.logger.Info("重启服务...")
|
||||
// ✅ P3 阶段 - 简单重启:先退出,然后由外部监控进程重启
|
||||
// 完整实现需要 Windows 服务控制或守护进程
|
||||
t.quit()
|
||||
}
|
||||
|
||||
// quit 退出程序
|
||||
func (t *TrayManager) quit() {
|
||||
t.logger.Info("退出MeshRay...")
|
||||
systray.Quit()
|
||||
if t.onExit != nil {
|
||||
t.onExit()
|
||||
}
|
||||
}
|
||||
|
||||
// updateStatus 更新状态
|
||||
func (t *TrayManager) updateStatus() {
|
||||
// ✅ P3 阶段 - 暂不实现动态状态更新
|
||||
// 未来可以检查服务运行状态,更新托盘菜单(如:运行中/已停止)
|
||||
}
|
||||
|
||||
// MeshRayIcon 托盘图标(ICO格式,16x16)
|
||||
// 简单的 "MR" 文字图标
|
||||
var MeshRayIcon = []byte{
|
||||
// ICO 头部
|
||||
0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x10, 0x10,
|
||||
0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x68, 0x04,
|
||||
0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x28, 0x00,
|
||||
0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00,
|
||||
0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 图标数据 - 蓝色背景上的白色 "MR" 文字
|
||||
// 第1行
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
// 第2行
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
// 第3行
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
// 第4行
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
// 第5行
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
// 第6行
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
// 第7行
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
// 第8行
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
// 第9行
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
// 第10行
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
// 第11行
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
// 第12行
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
// 第13行
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
// 第14行
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
// 第15行
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
// 第16行
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff, 0x1b, 0x6b, 0xa2, 0xff,
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package tray
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// TrayManager 托盘管理器(非Windows平台实现)
|
||||
type TrayManager struct {
|
||||
logger *zap.Logger
|
||||
webPort int
|
||||
onExit func()
|
||||
}
|
||||
|
||||
// NewTrayManager 创建托盘管理器
|
||||
func NewTrayManager(logger *zap.Logger, webPort int, onExit func()) *TrayManager {
|
||||
return &TrayManager{
|
||||
logger: logger,
|
||||
webPort: webPort,
|
||||
onExit: onExit,
|
||||
}
|
||||
}
|
||||
|
||||
// Run 运行托盘(非Windows平台显示提示信息并阻塞)
|
||||
func (t *TrayManager) Run() {
|
||||
t.logger.Info("非Windows系统,跳过托盘初始化")
|
||||
|
||||
// 显示启动信息
|
||||
fmt.Println("")
|
||||
fmt.Println("========================================")
|
||||
fmt.Println("🌐 MeshRay 服务已启动")
|
||||
fmt.Println("========================================")
|
||||
fmt.Printf("📍 Web 管理界面: http://localhost:%d\n", t.webPort)
|
||||
fmt.Println("💡 提示: 使用 Ctrl+C 停止服务")
|
||||
fmt.Println("========================================")
|
||||
fmt.Println("")
|
||||
|
||||
// 等待退出信号
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
<-sigChan
|
||||
|
||||
t.logger.Info("收到退出信号,正在关闭...")
|
||||
if t.onExit != nil {
|
||||
t.onExit()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user