- Added 'remove' subcommand for Xray proxy - Enhanced VMESS installation with V2rayNG config generation - Updated Xray installation process with improved error handling - Modified vmess template to separate Clash and V2rayNG configurations - Fixed command existence check in PackageOperator
311 lines
7.0 KiB
Go
311 lines
7.0 KiB
Go
package cmd
|
||
|
||
import (
|
||
"agent-wdd/cmd/xray"
|
||
"agent-wdd/config"
|
||
"agent-wdd/log"
|
||
"agent-wdd/op"
|
||
"agent-wdd/utils"
|
||
"bytes"
|
||
"encoding/base64"
|
||
"encoding/json"
|
||
"fmt"
|
||
"os"
|
||
"strings"
|
||
"text/template"
|
||
|
||
"github.com/spf13/cobra"
|
||
)
|
||
|
||
// 添加proxy子命令
|
||
func addProxySubcommands(cmd *cobra.Command) {
|
||
xrayCmd := &cobra.Command{
|
||
Use: "xray",
|
||
Short: "Xray相关操作",
|
||
}
|
||
xrayCmd.AddCommand(
|
||
&cobra.Command{
|
||
Use: "install",
|
||
Short: "安装Xray",
|
||
Run: func(cmd *cobra.Command, args []string) {
|
||
log.Info("Installing Xray...")
|
||
|
||
installXray()
|
||
},
|
||
},
|
||
&cobra.Command{
|
||
Use: "remove",
|
||
Short: "移除Xray",
|
||
Run: func(cmd *cobra.Command, args []string) {
|
||
log.Info("Removing Xray...")
|
||
|
||
removeXray()
|
||
},
|
||
},
|
||
// 其他xray子命令...
|
||
)
|
||
|
||
vmessCmd := &cobra.Command{
|
||
Use: "vmess [port]",
|
||
Short: "VMESS代理安装",
|
||
Run: func(cmd *cobra.Command, args []string) {
|
||
log.Info("Setting up VMESS proxy...")
|
||
|
||
var port string
|
||
|
||
if len(args) == 0 {
|
||
log.Info("no port provided, using default port 443")
|
||
port = "443"
|
||
} else {
|
||
port = args[0]
|
||
}
|
||
|
||
// 执行info all命令 获得归一化的名字 高耦合方案 不推荐
|
||
// for _, command := range cmd.Parent().Parent().Commands() {
|
||
// if command.Name() == "info" {
|
||
// command.Run(command, []string{"all"})
|
||
// }
|
||
// }
|
||
|
||
// 2. 调用 info all 命令
|
||
if err := executeInfoAll(cmd); err != nil {
|
||
fmt.Printf("Error executing info all: %v\n", err)
|
||
}
|
||
|
||
installVmess(port)
|
||
},
|
||
}
|
||
|
||
vlessCmd := &cobra.Command{
|
||
Use: "vless [port]",
|
||
Short: "VLESS代理安装",
|
||
Run: func(cmd *cobra.Command, args []string) {
|
||
log.Info("Setting up VLESS proxy...")
|
||
|
||
// var port string
|
||
|
||
// if len(args) == 0 {
|
||
// log.Info("no port provided, using default port 443")
|
||
// port = "443"
|
||
// }
|
||
},
|
||
}
|
||
cmd.AddCommand(
|
||
xrayCmd,
|
||
vmessCmd,
|
||
vlessCmd,
|
||
// 其他proxy子命令...
|
||
)
|
||
}
|
||
|
||
// 移除Xray
|
||
func removeXray() {
|
||
|
||
}
|
||
|
||
func executeInfoAll(cmd *cobra.Command) any {
|
||
// 获取根命令
|
||
rootCmd := cmd.Root()
|
||
|
||
// 创建临时上下文
|
||
infoCmd, _, err := rootCmd.Find([]string{"info", "all"})
|
||
if err != nil {
|
||
return fmt.Errorf("command not found: %w", err)
|
||
}
|
||
|
||
// 克隆命令避免污染原始配置
|
||
clonedCmd := cloneCommand(infoCmd)
|
||
|
||
// 重置命令状态
|
||
clonedCmd.SetArgs([]string{})
|
||
clonedCmd.SilenceErrors = true
|
||
clonedCmd.SilenceUsage = true
|
||
|
||
// 执行命令
|
||
return clonedCmd.Execute()
|
||
}
|
||
|
||
// 深度克隆命令的工具函数
|
||
func cloneCommand(cmd *cobra.Command) *cobra.Command {
|
||
cloned := &cobra.Command{
|
||
Use: cmd.Use,
|
||
Run: cmd.Run,
|
||
PreRun: cmd.PreRun,
|
||
PostRun: cmd.PostRun,
|
||
}
|
||
cloned.Flags().AddFlagSet(cmd.Flags())
|
||
return cloned
|
||
}
|
||
|
||
// VmessConfig 定义模板数据模型
|
||
type VmessConfig struct {
|
||
PORT string
|
||
UUID string
|
||
}
|
||
|
||
type VmessClientConfig struct {
|
||
ServerNodeName string
|
||
ServerNodeAddress string
|
||
VmessConfig
|
||
}
|
||
|
||
// 安装VMESS代理 最简单快速的模式
|
||
func installVmess(port string) (bool, []string) {
|
||
|
||
// 检查是否安装了xray
|
||
if !op.CommandExistsByPath("xray") {
|
||
log.Error("Xray is not installed, please install xray first")
|
||
return false, []string{"Xray is not installed, please install xray first"}
|
||
}
|
||
|
||
// 构建配置
|
||
// 创建新模板并解析
|
||
tmpl, err := template.New("vmessConfig").Parse(xray.VmessServerTemplate)
|
||
if err != nil {
|
||
return false, []string{err.Error()}
|
||
}
|
||
|
||
// 准备模板数据
|
||
|
||
// 执行 xray uuid 拿到返回值
|
||
ok, resultLog := op.SingleLineCommandExecutor([]string{"xray", "uuid"})
|
||
if !ok {
|
||
return false, resultLog
|
||
}
|
||
|
||
if len(resultLog) == 0 {
|
||
return false, []string{
|
||
"xray uuid generate error ! cant not get the uuid",
|
||
}
|
||
}
|
||
uuid := resultLog[0]
|
||
|
||
vmessConfig := VmessConfig{
|
||
PORT: port,
|
||
UUID: uuid,
|
||
}
|
||
|
||
// 执行模板渲染
|
||
var result bytes.Buffer
|
||
if err := tmpl.Execute(&result, vmessConfig); err != nil {
|
||
return false, []string{
|
||
err.Error(),
|
||
}
|
||
}
|
||
|
||
// 将result写入到 /usr/local/etc/xray/config.json 中
|
||
// 直接访问底层字节切片避免拷贝
|
||
data := result.Bytes()
|
||
|
||
// 快速验证JSON格式
|
||
if !json.Valid(data) {
|
||
return false, []string{fmt.Sprintf("Invalid JSON format: %s", data)}
|
||
}
|
||
|
||
// 使用适当权限创建文件并写入(O_WRONLY|O_CREATE|O_TRUNC, 0644)
|
||
filename := "/usr/local/etc/xray/config.json"
|
||
if err := os.WriteFile(filename, data, 0644); err != nil {
|
||
return false, []string{err.Error()}
|
||
}
|
||
|
||
fmt.Println("xray config file is written to ", filename)
|
||
// 移除encode自动添加的换行,重新缩进格式化
|
||
var prettyJSON bytes.Buffer
|
||
if err := json.Indent(&prettyJSON, bytes.TrimSpace(data), "", " "); err != nil {
|
||
fmt.Printf("JSON indent error: %v\n", err)
|
||
}
|
||
|
||
fmt.Println(prettyJSON.String())
|
||
fmt.Println()
|
||
|
||
// 重启Xray
|
||
op.SystemdRestart("xray")
|
||
ok, resultLog = op.SystemIsRunning("xray")
|
||
if !ok {
|
||
log.Error("Xray service is not running => %s Maybe the config file is not valid", resultLog)
|
||
return false, resultLog
|
||
}
|
||
|
||
result.Reset() // 清空全局buffer以备后续使用
|
||
|
||
// 输出 client的配置
|
||
configCache := config.ConfigCache
|
||
|
||
// 获取服务器节点名称
|
||
serverNodeName := configCache.Agent.OS.Hostname
|
||
|
||
vmessClientConfig := VmessClientConfig{
|
||
ServerNodeName: serverNodeName,
|
||
ServerNodeAddress: configCache.Agent.Network.Public.IPv4,
|
||
VmessConfig: vmessConfig,
|
||
}
|
||
|
||
// 执行模板渲染
|
||
clientTmpl, err := template.New("vmessClientConfig").Parse(xray.VmessClientTemplate)
|
||
if err != nil {
|
||
return false, []string{err.Error()}
|
||
}
|
||
if err := clientTmpl.Execute(&result, vmessClientConfig); err != nil {
|
||
return false, []string{
|
||
err.Error(),
|
||
}
|
||
}
|
||
|
||
lines := strings.Split(string(result.Bytes()), "\n")
|
||
|
||
// for index, line := range lines {
|
||
// fmt.Println(index, line)
|
||
// }
|
||
|
||
// 转换为字节切片并进行Base64编码
|
||
v2rayNGConfig := "vmess://" + base64.StdEncoding.EncodeToString([]byte(lines[2]))
|
||
|
||
// 将result 打印的终端
|
||
fmt.Println("Clash client config is below: ")
|
||
fmt.Println()
|
||
fmt.Println(lines[1])
|
||
fmt.Println()
|
||
fmt.Println("V2rayNG config is below: ")
|
||
fmt.Println()
|
||
fmt.Println(v2rayNGConfig)
|
||
fmt.Println()
|
||
|
||
return true, []string{"Xray Configuration file written successfully"}
|
||
}
|
||
|
||
// 安装Xray 最新版本 主机可以联网才可以
|
||
func installXray() {
|
||
|
||
installScriptUrl := "https://gitea.107421.xyz/zeaslity/Xray-install/raw/branch/main/install-release.sh"
|
||
// 下载安装脚本
|
||
ok, err := utils.DownloadFile(installScriptUrl, "/tmp/install-release.sh")
|
||
if !ok {
|
||
log.Error("Download Xray install script failed ! from %s error is %s", installScriptUrl, err)
|
||
return
|
||
}
|
||
|
||
// 添加执行权限
|
||
os.Chmod("/tmp/install-release.sh", 0777)
|
||
|
||
// 执行安装脚本
|
||
ok, resultLog := op.HardCodeCommandExecutor("/bin/bash /tmp/install-release.sh -u root install")
|
||
if !ok {
|
||
log.Error("Install Xray failed ! error is %s", resultLog)
|
||
return
|
||
}
|
||
|
||
op.SystemdUp("xray")
|
||
op.SystemdEnable("xray")
|
||
|
||
// 默认服务已经启动! 检查
|
||
ok, resultLog = op.SystemIsRunning("xray")
|
||
if !ok {
|
||
log.Error("Xray service is not running => %s", resultLog)
|
||
return
|
||
}
|
||
|
||
// 安装成功
|
||
log.Info("Xray installed successfully")
|
||
|
||
}
|