package main import ( "fmt" "github.com/gogf/gf/v2/errors/gerror" "github.com/towgo/towgo/lib/jsonrpc" "github.com/towgo/towgo/lib/system" "github.com/towgo/towgo/lib/www" "go.uber.org/zap" "tgk-touch/internal/core" g "tgk-touch/internal/global" "tgk-touch/internal/initialize" licenseterminal "tgk-touch/internal/module/license_terminal" "tgk-touch/internal/module/maincontrollerClient" "log" "os" "os/exec" "os/user" "path/filepath" "strings" "time" "github.com/towgo/towgo/errors/tcode" "github.com/towgo/towgo/towgo" ) var ( productNumber string = "LampServer" ) func main() { log.SetFlags(log.Lshortfile | log.LstdFlags) defer func() { if exception := recover(); exception != nil { if v, ok := exception.(error); ok && gerror.HasStack(v) { zap.S().Errorf("err %+v \n", v) } else { zap.S().Errorf("recover exception %+v\n", gerror.NewCodef(tcode.CodeInternalPanic, "%+v", exception)) } } }() appInit() start() } func appInit() { initialize.Init() initLicense() } func initLicense() { licenseterminal.SetProductNumber(productNumber) licenseterminal.AutoFristActive() licenseterminal.RegExpirationCall(productNumber, func() { log.Println("产品:" + productNumber + "许可证到期") list := []string{ "/account/login", "/license/activecode/get", "/license/activecode/online/request", "/license/getActiveCredential", "/license/active", "/license/list", "/license/accesscode/get", } jsonrpc.MethodLockAll(list...) }) licenseterminal.RegReNewCall(productNumber, func() { log.Println("产品:" + productNumber + "许可证续存") jsonrpc.MethodUnlockAll() }) } func start() { var err error var serialPortAddress string = g.Config().Tty.SerialPortAddress if serialPortAddress == "" { panic(gerror.Wrap(err, "串口地址未配置 tty.serialPortAddress")) } var baudRateint int = g.Config().Tty.BaudRate if baudRateint == 0 { zap.S().Info("波特率未配置 tty.baudRate , 使用 默认 9600") baudRateint = 9600 } towgo.SetFunc("/getMessageInterval", getMessageInterval) err = maincontrollerClient.UseSerialPort(serialPortAddress, baudRateint) if err != nil { panic(gerror.Wrap(err, "串口启动失败")) } webServer := www.WebServer{} webServer.Wwwroot = system.GetPathOfProgram() + "wwwroot" webServer.Index = []string{"index.html"} g.HttpServerMux().HandleFunc("/", webServer.WebServerHandller, ) g.HttpServerMux().HandleFunc("/jsonrpc/websocket", towgo.DefaultWebSocketServer.WebsocketServiceHandller.ServeHTTP, ) g.HttpServerMux().HandleFunc("/jsonrpc", towgo.HttpHandller) towgo.DefaultExec = func(rpcConn towgo.JsonRpcConnection) { if exception := recover(); exception != nil { var msg string if v, ok := exception.(error); ok && gerror.HasStack(v) { g.Log().Error("towgo jsonrpc exception \n", v) msg = v.Error() } else { g.Log().Error("towgo jsonrpc recover exception \n", gerror.NewCodef(tcode.CodeInternalPanic, "%+v", exception)) msg = v.Error() } rpcConn.WriteError(500, rpcConn.GetRpcRequest().Method+":"+msg) } } install() go runClient(g.Config().Server.Port) core.RunServer() } func getMessageInterval(rpcConn towgo.JsonRpcConnection) { rpcConn.WriteResult(g.Config().MessageInterval) } func install() { // 1. 检查并安装Firefox if !isFirefoxInstalled() { fmt.Println("Firefox未安装,正在安装...") if err := installFirefox(); err != nil { fmt.Printf("安装Firefox失败: %v\n", err) os.Exit(1) } fmt.Println("Firefox安装成功") } else { fmt.Println("Firefox已安装") } // 2. 设置当前应用开机自启动 appPath, err := os.Executable() if err != nil { fmt.Printf("读取应用路径失败: %v\n", err) os.Exit(1) } if !isAutoStartEnabled(appPath) { fmt.Println("正在设置开机自启动...") if err := enableAutoStart(appPath); err != nil { fmt.Printf("设置开机自启动失败: %v\n", err) os.Exit(1) } fmt.Println("开机自启动设置成功") } else { fmt.Println("已设置开机自启动") } } func runClient(port int) { // 配置显示器和Xauthority路径 display := ":0" currentUser, err := user.Current() if err != nil { fmt.Printf("读取当前用户失败: %v\n", err) os.Exit(1) } xauthPath := filepath.Join(currentUser.HomeDir, ".Xauthority") for { // 设置环境变量 os.Setenv("DISPLAY", display) os.Setenv("XAUTHORITY", xauthPath) // 关键:这组参数 = 完全关闭密码管理器 + 禁止保存/填充/弹窗 args := []string{ /*"--kiosk", "--noerrdialogs", "--no-first-run", "--new-instance", // 👇 下面这 3 个就是禁密码弹窗的核心 "--disable-features=PasswordManager", "--pref=signon.rememberSignons=false", "--pref=signon.autofillForms=false",*/ "http://127.0.0.1:" + fmt.Sprintf("%d", port), } // 启动Firefox cmd := exec.Command("firefox", args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { g.Log().Error(err.Error()) time.Sleep(5 * time.Second) // 等待后重试 continue } } } // 检查Firefox是否安装 func isFirefoxInstalled() bool { cmd := exec.Command("which", "firefox") return cmd.Run() == nil } // 安装Firefox func installFirefox() error { var p struct { DebPkgPath string `json:"DebPkgPath"` DebPkgName string `json:"DebPkgName"` InstallShellPath string `json:"InstallShellPath"` } p.DebPkgPath = g.Config().Firefox.DebPkgPath p.DebPkgName = g.Config().Firefox.DebPkgName p.InstallShellPath = g.Config().Firefox.InstallShellPath command := exec.Command("sudo", "sh", "-c", fmt.Sprintf(` echo "=== 当前工作目录 ===" pwd echo "" echo "=== 卸载旧版 Firefox ===" dpkg -P firefox || echo "警告:卸载旧版 Firefox 失败(可能未安装)" echo "" echo "=== 清理残留文件 ===" rm -rf /opt/firefox* 2>/dev/null echo "" echo "=== 开始安装 Firefox ===" echo "1. 复制安装包到 /opt 目录..." cp -v %s%s/%s /opt/firefox-deb.tar.gz || { echo "错误:复制安装包失败"; exit 1; } echo "" echo "2. 解压安装包..." cd /opt && tar -xzvf firefox-deb.tar.gz || { echo "错误:解压失败"; exit 1; } echo "" echo "3. 安装依赖和主程序..." cd /opt/firefox-deb && sudo dpkg -i *.deb || { echo "错误:安装 deb 包失败"; } echo "" echo "4. 验证安装..." firefox --version || { echo "错误:Firefox 未正确安装"; exit 1; } echo "" echo "=== Firefox 安装完成 ===" `, system.GetPathOfProgram(), p.DebPkgPath, p.DebPkgName)) // 将命令的 stdout/stderr 直接绑定到当前终端 command.Stdout = os.Stdout command.Stderr = os.Stderr return command.Run() } func isAutoStartEnabled(appPath string) bool { desktopFile := fmt.Sprintf("/etc/systemd/system/%s.service", strings.TrimSuffix(filepath.Base(appPath), filepath.Ext(appPath))) _, err := os.Stat(desktopFile) return !os.IsNotExist(err) } func enableAutoStart(appPath string) error { appName := strings.TrimSuffix(filepath.Base(appPath), filepath.Ext(appPath)) serviceFile := fmt.Sprintf("/etc/systemd/system/%s.service", appName) // 创建systemd服务文件内容 content := fmt.Sprintf(`[Unit] Description=%s Background Service After=network.target [Service] Type=simple ExecStart=%s Restart=on-failure RestartSec=10 User=root WorkingDirectory=%s [Install] WantedBy=multi-user.target `, appName, appPath, filepath.Dir(appPath)) // 写入服务文件 cmd := exec.Command("sudo", "bash", "-c", fmt.Sprintf("echo '%s' > %s", content, serviceFile)) if err := cmd.Run(); err != nil { return err } // 重新加载systemd配置 cmd = exec.Command("sudo", "systemctl", "daemon-reload") if err := cmd.Run(); err != nil { return err } // 启用服务 cmd = exec.Command("sudo", "systemctl", "enable", appName+".service") return cmd.Run() }