From 9e63b0e2b36018b35d212bbf0a44ff64d28d5344 Mon Sep 17 00:00:00 2001
From: kaveh-ahangar <kavehahangar021@gmail.com>
Date: Thu, 4 May 2023 19:20:52 +0330
Subject: [PATCH 1/6] feat: add Makefile and improve building

---
 .gitignore                  |  1 +
 Makefile                    | 14 ++++++++++++++
 main.go => cmd/x-ui/main.go |  0
 3 files changed, 15 insertions(+)
 create mode 100644 Makefile
 rename main.go => cmd/x-ui/main.go (100%)

diff --git a/.gitignore b/.gitignore
index 7ba03558..0ec48eda 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ release/
 access.log
 error.log
 .cache
+/build/x-ui
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..ae396706
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,14 @@
+
+all: build
+
+init:
+	# create dirs
+	mkdir -p ./build
+
+clear:
+	# clear dirs
+	rm -rf ./build
+
+build: clear init
+	# build
+	go build -o ./build/x-ui ./cmd/x-ui/main.go
\ No newline at end of file
diff --git a/main.go b/cmd/x-ui/main.go
similarity index 100%
rename from main.go
rename to cmd/x-ui/main.go

From 3a023593254b058d15e02970e9a11681feadb531 Mon Sep 17 00:00:00 2001
From: kaveh-ahangar <kavehahangar021@gmail.com>
Date: Thu, 4 May 2023 19:46:45 +0330
Subject: [PATCH 2/6] feat: Adding Cobra to provide a more intuitive command
 line interface

---
 cmd/x-ui/main.go | 216 ++++++++++++++++++++++++++---------------------
 go.mod           |   3 +
 go.sum           |   8 ++
 3 files changed, 130 insertions(+), 97 deletions(-)

diff --git a/cmd/x-ui/main.go b/cmd/x-ui/main.go
index 54ff6bf3..73a9b168 100644
--- a/cmd/x-ui/main.go
+++ b/cmd/x-ui/main.go
@@ -1,8 +1,8 @@
 package main
 
 import (
-	"flag"
 	"fmt"
+	"github.com/spf13/cobra"
 	"log"
 	"os"
 	"os/signal"
@@ -70,7 +70,10 @@ func runWebServer() {
 				return
 			}
 		default:
-			server.Stop()
+			err := server.Stop()
+			if err != nil {
+				return
+			}
 			return
 		}
 	}
@@ -235,109 +238,128 @@ func removeSecret() {
 	}
 }
 
+var vars struct {
+	showVersion          bool
+	dbPath               string
+	port                 int
+	username             string
+	password             string
+	tgbottoken           string
+	tgbotchatid          string
+	enableTelegramBot    bool
+	tgbotRuntime         string
+	resetSettings        bool
+	showSettings         bool
+	removeAllSecretsFlag bool
+}
+
 func main() {
-	if len(os.Args) < 2 {
-		runWebServer()
-		return
+	var rootCmd = &cobra.Command{
+		Use: "x-ui",
 	}
 
-	var showVersion bool
-	flag.BoolVar(&showVersion, "v", false, "show version")
-
-	runCmd := flag.NewFlagSet("run", flag.ExitOnError)
-
-	v2uiCmd := flag.NewFlagSet("v2-ui", flag.ExitOnError)
-	var dbPath string
-	v2uiCmd.StringVar(&dbPath, "db", fmt.Sprintf("%s/v2-ui.db", config.GetDBFolderPath()), "set v2-ui db file path")
-
-	settingCmd := flag.NewFlagSet("setting", flag.ExitOnError)
-	var port int
-	var username string
-	var password string
-	var tgbottoken string
-	var tgbotchatid string
-	var enabletgbot bool
-	var tgbotRuntime string
-	var reset bool
-	var show bool
-	var remove_secret bool
-	settingCmd.BoolVar(&reset, "reset", false, "reset all settings")
-	settingCmd.BoolVar(&show, "show", false, "show current settings")
-	settingCmd.IntVar(&port, "port", 0, "set panel port")
-	settingCmd.StringVar(&username, "username", "", "set login username")
-	settingCmd.StringVar(&password, "password", "", "set login password")
-	settingCmd.StringVar(&tgbottoken, "tgbottoken", "", "set telegram bot token")
-	settingCmd.StringVar(&tgbotRuntime, "tgbotRuntime", "", "set telegram bot cron time")
-	settingCmd.StringVar(&tgbotchatid, "tgbotchatid", "", "set telegram bot chat id")
-	settingCmd.BoolVar(&enabletgbot, "enabletgbot", false, "enable telegram bot notify")
-
-	oldUsage := flag.Usage
-	flag.Usage = func() {
-		oldUsage()
-		fmt.Println()
-		fmt.Println("Commands:")
-		fmt.Println("    run            run web panel")
-		fmt.Println("    v2-ui          migrate form v2-ui")
-		fmt.Println("    migrate        migrate form other/old x-ui")
-		fmt.Println("    setting        set settings")
+	var runCmd = &cobra.Command{
+		Use:   "run",
+		Short: "Run the web server",
+		Run: func(cmd *cobra.Command, args []string) {
+			runWebServer()
+		},
 	}
 
-	flag.Parse()
-	if showVersion {
-		fmt.Println(config.GetVersion())
-		return
+	var migrateCmd = &cobra.Command{
+		Use:   "migrate",
+		Short: "Migrate from other/old x-ui",
+		Run: func(cmd *cobra.Command, args []string) {
+			migrateDb()
+		},
 	}
 
-	switch os.Args[1] {
-	case "run":
-		err := runCmd.Parse(os.Args[2:])
-		if err != nil {
-			fmt.Println(err)
-			return
-		}
-		runWebServer()
-	case "migrate":
-		migrateDb()
-	case "v2-ui":
-		err := v2uiCmd.Parse(os.Args[2:])
-		if err != nil {
-			fmt.Println(err)
-			return
-		}
-		err = v2ui.MigrateFromV2UI(dbPath)
-		if err != nil {
-			fmt.Println("migrate from v2-ui failed:", err)
-		}
-	case "setting":
-		err := settingCmd.Parse(os.Args[2:])
-		if err != nil {
-			fmt.Println(err)
-			return
-		}
-		if reset {
+	var v2uiCmd = &cobra.Command{
+		Use:   "v2-ui",
+		Short: "Migrate from v2-ui",
+		Run: func(cmd *cobra.Command, args []string) {
+			dbPath, _ := cmd.Flags().GetString("db")
+			err := v2ui.MigrateFromV2UI(dbPath)
+			if err != nil {
+				fmt.Println("migrate from v2-ui failed:", err)
+			}
+		},
+	}
+
+	v2uiCmd.Flags().String("db", fmt.Sprintf("%s/v2-ui.db", config.GetDBFolderPath()), "set v2-ui db file path")
+
+	var settingCmd = &cobra.Command{
+		Use:   "setting",
+		Short: "Set settings",
+	}
+
+	var resetCmd = &cobra.Command{
+		Use:   "reset",
+		Short: "Reset all settings",
+		Run: func(cmd *cobra.Command, args []string) {
 			resetSetting()
-		} else {
+		},
+	}
+
+	var showCmd = &cobra.Command{
+		Use:   "show",
+		Short: "Show current settings",
+		Run: func(cmd *cobra.Command, args []string) {
+			showSetting(true)
+		},
+	}
+
+	var updateCmd = &cobra.Command{
+		Use:   "update",
+		Short: "Update settings",
+		Run: func(cmd *cobra.Command, args []string) {
+			port, _ := cmd.Flags().GetInt("port")
+			username, _ := cmd.Flags().GetString("username")
+			password, _ := cmd.Flags().GetString("password")
 			updateSetting(port, username, password)
-		}
-		if show {
-			showSetting(show)
-		}
-		if (tgbottoken != "") || (tgbotchatid != "") || (tgbotRuntime != "") {
-			updateTgbotSetting(tgbottoken, tgbotchatid, tgbotRuntime)
-		}
-		if remove_secret {
-			removeSecret()
-		}
-		if enabletgbot {
-			updateTgbotEnableSts(enabletgbot)
-		}
-	default:
-		fmt.Println("except 'run' or 'v2-ui' or 'setting' subcommands")
-		fmt.Println()
-		runCmd.Usage()
-		fmt.Println()
-		v2uiCmd.Usage()
-		fmt.Println()
-		settingCmd.Usage()
+		},
+	}
+
+	updateCmd.Flags().Int("port", 0, "set panel port")
+	updateCmd.Flags().String("username", "", "set login username")
+	updateCmd.Flags().String("password", "", "set login password")
+
+	var tgbotCmd = &cobra.Command{
+		Use:   "tgbot",
+		Short: "Update telegram bot settings",
+		Run: func(cmd *cobra.Command, args []string) {
+			tgbottoken, _ := cmd.Flags().GetString("tgbottoken")
+			tgbotchatid, _ := cmd.Flags().GetString("tgbotchatid")
+			tgbotRuntime, _ := cmd.Flags().GetString("tgbotRuntime")
+			enabletgbot, _ := cmd.Flags().GetBool("enabletgbot")
+			remove_secret, _ := cmd.Flags().GetBool("remove_secret")
+
+			if tgbottoken != "" || tgbotchatid != "" || tgbotRuntime != "" {
+				updateTgbotSetting(tgbottoken, tgbotchatid, tgbotRuntime)
+			}
+
+			if remove_secret {
+				removeSecret()
+			}
+
+			if enabletgbot {
+				updateTgbotEnableSts(enabletgbot)
+			}
+		},
+	}
+
+	tgbotCmd.Flags().String("tgbottoken", "", "set telegram bot token")
+	tgbotCmd.Flags().String("tgbotchatid", "", "set telegram bot chat id")
+	tgbotCmd.Flags().String("tgbotRuntime", "", "set telegram bot cron time")
+	tgbotCmd.Flags().Bool("enabletgbot", false, "enable telegram bot notify")
+	tgbotCmd.Flags().Bool("remove_secret", false, "remove secret")
+
+	settingCmd.AddCommand(resetCmd, showCmd, updateCmd, tgbotCmd)
+
+	rootCmd.AddCommand(runCmd, migrateCmd, v2uiCmd, settingCmd)
+
+	if err := rootCmd.Execute(); err != nil {
+		fmt.Println(err)
+		os.Exit(1)
 	}
 }
diff --git a/go.mod b/go.mod
index 56dbfdd1..63cddb27 100644
--- a/go.mod
+++ b/go.mod
@@ -14,6 +14,7 @@ require (
 	github.com/pelletier/go-toml/v2 v2.0.7
 	github.com/robfig/cron/v3 v3.0.1
 	github.com/shirou/gopsutil/v3 v3.23.4
+	github.com/spf13/cobra v1.7.0
 	github.com/xtls/xray-core v1.8.1
 	go.uber.org/atomic v1.10.0
 	golang.org/x/text v0.9.0
@@ -35,6 +36,7 @@ require (
 	github.com/gorilla/context v1.1.1 // indirect
 	github.com/gorilla/securecookie v1.1.1 // indirect
 	github.com/gorilla/sessions v1.2.1 // indirect
+	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/jinzhu/inflection v1.0.0 // indirect
 	github.com/jinzhu/now v1.1.5 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
@@ -48,6 +50,7 @@ require (
 	github.com/pires/go-proxyproto v0.7.0 // indirect
 	github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
 	github.com/shoenig/go-m1cpu v0.1.5 // indirect
+	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/tklauser/go-sysconf v0.3.11 // indirect
 	github.com/tklauser/numcpus v0.6.0 // indirect
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
diff --git a/go.sum b/go.sum
index e3af703f..d4fc7bc0 100644
--- a/go.sum
+++ b/go.sum
@@ -14,6 +14,7 @@ github.com/bytedance/sonic v1.8.8/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZX
 github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
 github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
 github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
+github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -72,6 +73,8 @@ github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/z
 github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
 github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
 github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
 github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
 github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
@@ -130,6 +133,7 @@ github.com/refraction-networking/utls v1.3.2 h1:o+AkWB57mkcoW36ET7uJ002CpBWHu0KP
 github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
 github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
 github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/sagernet/sing v0.2.3 h1:V50MvZ4c3Iij2lYFWPlzL1PyipwSzjGeN9x+Ox89vpk=
 github.com/sagernet/sing-shadowsocks v0.2.1 h1:FvdLQOqpvxHBJUcUe4fvgiYP2XLLwH5i1DtXQviVEPw=
 github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
@@ -140,6 +144,10 @@ github.com/shoenig/go-m1cpu v0.1.5 h1:LF57Z/Fpb/WdGLjt2HZilNnmZOxg/q2bSKTQhgbrLr
 github.com/shoenig/go-m1cpu v0.1.5/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
 github.com/shoenig/test v0.6.3 h1:GVXWJFk9PiOjN0KoJ7VrJGH6uLPnqxR7/fe3HUPfE0c=
 github.com/shoenig/test v0.6.3/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
+github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
+github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=

From 83ffa25d6f86395f08217a4c9442f227489eed73 Mon Sep 17 00:00:00 2001
From: kaveh-ahangar <kavehahangar021@gmail.com>
Date: Fri, 5 May 2023 12:40:20 +0330
Subject: [PATCH 3/6] feat: Rollback files (main.go , Makefile)

---
 Makefile                    | 14 --------------
 cmd/x-ui/main.go => main.go | 15 ---------------
 2 files changed, 29 deletions(-)
 delete mode 100644 Makefile
 rename cmd/x-ui/main.go => main.go (95%)

diff --git a/Makefile b/Makefile
deleted file mode 100644
index ae396706..00000000
--- a/Makefile
+++ /dev/null
@@ -1,14 +0,0 @@
-
-all: build
-
-init:
-	# create dirs
-	mkdir -p ./build
-
-clear:
-	# clear dirs
-	rm -rf ./build
-
-build: clear init
-	# build
-	go build -o ./build/x-ui ./cmd/x-ui/main.go
\ No newline at end of file
diff --git a/cmd/x-ui/main.go b/main.go
similarity index 95%
rename from cmd/x-ui/main.go
rename to main.go
index 73a9b168..8cb2af22 100644
--- a/cmd/x-ui/main.go
+++ b/main.go
@@ -238,21 +238,6 @@ func removeSecret() {
 	}
 }
 
-var vars struct {
-	showVersion          bool
-	dbPath               string
-	port                 int
-	username             string
-	password             string
-	tgbottoken           string
-	tgbotchatid          string
-	enableTelegramBot    bool
-	tgbotRuntime         string
-	resetSettings        bool
-	showSettings         bool
-	removeAllSecretsFlag bool
-}
-
 func main() {
 	var rootCmd = &cobra.Command{
 		Use: "x-ui",

From 7d09b4e840375e806f2d6fc22b6bb7e46ed39380 Mon Sep 17 00:00:00 2001
From: kaveh-ahangar <kavehahangar021@gmail.com>
Date: Fri, 5 May 2023 12:41:21 +0330
Subject: [PATCH 4/6] feat: Rollback files (main.go , Makefile)

---
 .gitignore | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/.gitignore b/.gitignore
index 0ec48eda..a5684025 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,5 +11,4 @@ main
 release/
 access.log
 error.log
-.cache
-/build/x-ui
+.cache
\ No newline at end of file

From cd483c191a20e2616161db4d51ce95d8083bf5f6 Mon Sep 17 00:00:00 2001
From: kaveh-ahangar <kavehahangar021@gmail.com>
Date: Fri, 5 May 2023 12:42:05 +0330
Subject: [PATCH 5/6] feat: Rollback files (.gitignore)

---
 .gitignore | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/.gitignore b/.gitignore
index a5684025..92e17d61 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,5 +10,4 @@ x-ui-*.tar.gz
 main
 release/
 access.log
-error.log
-.cache
\ No newline at end of file
+error.log
\ No newline at end of file

From 1680bb36c3e9c2f89c72947e1735337fe5fd1e8b Mon Sep 17 00:00:00 2001
From: kaveh-ahangar <kavehahangar021@gmail.com>
Date: Fri, 5 May 2023 12:43:09 +0330
Subject: [PATCH 6/6] feat: Rollback files (.gitignore)

---
 .gitignore | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore
index 92e17d61..a5684025 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,4 +10,5 @@ x-ui-*.tar.gz
 main
 release/
 access.log
-error.log
\ No newline at end of file
+error.log
+.cache
\ No newline at end of file