You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

297 lines
7.7 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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()
}