| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492 | 
							- // Package main is the entry point for the 3x-ui web panel application.
 
- // It initializes the database, web server, and handles command-line operations for managing the panel.
 
- package main
 
- import (
 
- 	"flag"
 
- 	"fmt"
 
- 	"log"
 
- 	"os"
 
- 	"os/signal"
 
- 	"syscall"
 
- 	_ "unsafe"
 
- 	"github.com/mhsanaei/3x-ui/v2/config"
 
- 	"github.com/mhsanaei/3x-ui/v2/database"
 
- 	"github.com/mhsanaei/3x-ui/v2/logger"
 
- 	"github.com/mhsanaei/3x-ui/v2/sub"
 
- 	"github.com/mhsanaei/3x-ui/v2/util/crypto"
 
- 	"github.com/mhsanaei/3x-ui/v2/web"
 
- 	"github.com/mhsanaei/3x-ui/v2/web/global"
 
- 	"github.com/mhsanaei/3x-ui/v2/web/service"
 
- 	"github.com/joho/godotenv"
 
- 	"github.com/op/go-logging"
 
- )
 
- // runWebServer initializes and starts the web server for the 3x-ui panel.
 
- func runWebServer() {
 
- 	log.Printf("Starting %v %v", config.GetName(), config.GetVersion())
 
- 	switch config.GetLogLevel() {
 
- 	case config.Debug:
 
- 		logger.InitLogger(logging.DEBUG)
 
- 	case config.Info:
 
- 		logger.InitLogger(logging.INFO)
 
- 	case config.Notice:
 
- 		logger.InitLogger(logging.NOTICE)
 
- 	case config.Warning:
 
- 		logger.InitLogger(logging.WARNING)
 
- 	case config.Error:
 
- 		logger.InitLogger(logging.ERROR)
 
- 	default:
 
- 		log.Fatalf("Unknown log level: %v", config.GetLogLevel())
 
- 	}
 
- 	godotenv.Load()
 
- 	err := database.InitDB(config.GetDBPath())
 
- 	if err != nil {
 
- 		log.Fatalf("Error initializing database: %v", err)
 
- 	}
 
- 	var server *web.Server
 
- 	server = web.NewServer()
 
- 	global.SetWebServer(server)
 
- 	err = server.Start()
 
- 	if err != nil {
 
- 		log.Fatalf("Error starting web server: %v", err)
 
- 		return
 
- 	}
 
- 	var subServer *sub.Server
 
- 	subServer = sub.NewServer()
 
- 	global.SetSubServer(subServer)
 
- 	err = subServer.Start()
 
- 	if err != nil {
 
- 		log.Fatalf("Error starting sub server: %v", err)
 
- 		return
 
- 	}
 
- 	sigCh := make(chan os.Signal, 1)
 
- 	// Trap shutdown signals
 
- 	signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGTERM)
 
- 	for {
 
- 		sig := <-sigCh
 
- 		switch sig {
 
- 		case syscall.SIGHUP:
 
- 			logger.Info("Received SIGHUP signal. Restarting servers...")
 
- 			err := server.Stop()
 
- 			if err != nil {
 
- 				logger.Debug("Error stopping web server:", err)
 
- 			}
 
- 			err = subServer.Stop()
 
- 			if err != nil {
 
- 				logger.Debug("Error stopping sub server:", err)
 
- 			}
 
- 			server = web.NewServer()
 
- 			global.SetWebServer(server)
 
- 			err = server.Start()
 
- 			if err != nil {
 
- 				log.Fatalf("Error restarting web server: %v", err)
 
- 				return
 
- 			}
 
- 			log.Println("Web server restarted successfully.")
 
- 			subServer = sub.NewServer()
 
- 			global.SetSubServer(subServer)
 
- 			err = subServer.Start()
 
- 			if err != nil {
 
- 				log.Fatalf("Error restarting sub server: %v", err)
 
- 				return
 
- 			}
 
- 			log.Println("Sub server restarted successfully.")
 
- 		default:
 
- 			server.Stop()
 
- 			subServer.Stop()
 
- 			log.Println("Shutting down servers.")
 
- 			return
 
- 		}
 
- 	}
 
- }
 
- // resetSetting resets all panel settings to their default values.
 
- func resetSetting() {
 
- 	err := database.InitDB(config.GetDBPath())
 
- 	if err != nil {
 
- 		fmt.Println("Failed to initialize database:", err)
 
- 		return
 
- 	}
 
- 	settingService := service.SettingService{}
 
- 	err = settingService.ResetSettings()
 
- 	if err != nil {
 
- 		fmt.Println("Failed to reset settings:", err)
 
- 	} else {
 
- 		fmt.Println("Settings successfully reset.")
 
- 	}
 
- }
 
- // showSetting displays the current panel settings if show is true.
 
- func showSetting(show bool) {
 
- 	if show {
 
- 		settingService := service.SettingService{}
 
- 		port, err := settingService.GetPort()
 
- 		if err != nil {
 
- 			fmt.Println("get current port failed, error info:", err)
 
- 		}
 
- 		webBasePath, err := settingService.GetBasePath()
 
- 		if err != nil {
 
- 			fmt.Println("get webBasePath failed, error info:", err)
 
- 		}
 
- 		certFile, err := settingService.GetCertFile()
 
- 		if err != nil {
 
- 			fmt.Println("get cert file failed, error info:", err)
 
- 		}
 
- 		keyFile, err := settingService.GetKeyFile()
 
- 		if err != nil {
 
- 			fmt.Println("get key file failed, error info:", err)
 
- 		}
 
- 		userService := service.UserService{}
 
- 		userModel, err := userService.GetFirstUser()
 
- 		if err != nil {
 
- 			fmt.Println("get current user info failed, error info:", err)
 
- 		}
 
- 		if userModel.Username == "" || userModel.Password == "" {
 
- 			fmt.Println("current username or password is empty")
 
- 		}
 
- 		fmt.Println("current panel settings as follows:")
 
- 		if certFile == "" || keyFile == "" {
 
- 			fmt.Println("Warning: Panel is not secure with SSL")
 
- 		} else {
 
- 			fmt.Println("Panel is secure with SSL")
 
- 		}
 
- 		hasDefaultCredential := func() bool {
 
- 			return userModel.Username == "admin" && crypto.CheckPasswordHash(userModel.Password, "admin")
 
- 		}()
 
- 		fmt.Println("hasDefaultCredential:", hasDefaultCredential)
 
- 		fmt.Println("port:", port)
 
- 		fmt.Println("webBasePath:", webBasePath)
 
- 	}
 
- }
 
- // updateTgbotEnableSts enables or disables the Telegram bot notifications based on the status parameter.
 
- func updateTgbotEnableSts(status bool) {
 
- 	settingService := service.SettingService{}
 
- 	currentTgSts, err := settingService.GetTgbotEnabled()
 
- 	if err != nil {
 
- 		fmt.Println(err)
 
- 		return
 
- 	}
 
- 	logger.Infof("current enabletgbot status[%v],need update to status[%v]", currentTgSts, status)
 
- 	if currentTgSts != status {
 
- 		err := settingService.SetTgbotEnabled(status)
 
- 		if err != nil {
 
- 			fmt.Println(err)
 
- 			return
 
- 		} else {
 
- 			logger.Infof("SetTgbotEnabled[%v] success", status)
 
- 		}
 
- 	}
 
- }
 
- // updateTgbotSetting updates Telegram bot settings including token, chat ID, and runtime schedule.
 
- func updateTgbotSetting(tgBotToken string, tgBotChatid string, tgBotRuntime string) {
 
- 	err := database.InitDB(config.GetDBPath())
 
- 	if err != nil {
 
- 		fmt.Println("Error initializing database:", err)
 
- 		return
 
- 	}
 
- 	settingService := service.SettingService{}
 
- 	if tgBotToken != "" {
 
- 		err := settingService.SetTgBotToken(tgBotToken)
 
- 		if err != nil {
 
- 			fmt.Printf("Error setting Telegram bot token: %v\n", err)
 
- 			return
 
- 		}
 
- 		logger.Info("Successfully updated Telegram bot token.")
 
- 	}
 
- 	if tgBotRuntime != "" {
 
- 		err := settingService.SetTgbotRuntime(tgBotRuntime)
 
- 		if err != nil {
 
- 			fmt.Printf("Error setting Telegram bot runtime: %v\n", err)
 
- 			return
 
- 		}
 
- 		logger.Infof("Successfully updated Telegram bot runtime to [%s].", tgBotRuntime)
 
- 	}
 
- 	if tgBotChatid != "" {
 
- 		err := settingService.SetTgBotChatId(tgBotChatid)
 
- 		if err != nil {
 
- 			fmt.Printf("Error setting Telegram bot chat ID: %v\n", err)
 
- 			return
 
- 		}
 
- 		logger.Info("Successfully updated Telegram bot chat ID.")
 
- 	}
 
- }
 
- // updateSetting updates various panel settings including port, credentials, base path, listen IP, and two-factor authentication.
 
- func updateSetting(port int, username string, password string, webBasePath string, listenIP string, resetTwoFactor bool) {
 
- 	err := database.InitDB(config.GetDBPath())
 
- 	if err != nil {
 
- 		fmt.Println("Database initialization failed:", err)
 
- 		return
 
- 	}
 
- 	settingService := service.SettingService{}
 
- 	userService := service.UserService{}
 
- 	if port > 0 {
 
- 		err := settingService.SetPort(port)
 
- 		if err != nil {
 
- 			fmt.Println("Failed to set port:", err)
 
- 		} else {
 
- 			fmt.Printf("Port set successfully: %v\n", port)
 
- 		}
 
- 	}
 
- 	if username != "" || password != "" {
 
- 		err := userService.UpdateFirstUser(username, password)
 
- 		if err != nil {
 
- 			fmt.Println("Failed to update username and password:", err)
 
- 		} else {
 
- 			fmt.Println("Username and password updated successfully")
 
- 		}
 
- 	}
 
- 	if webBasePath != "" {
 
- 		err := settingService.SetBasePath(webBasePath)
 
- 		if err != nil {
 
- 			fmt.Println("Failed to set base URI path:", err)
 
- 		} else {
 
- 			fmt.Println("Base URI path set successfully")
 
- 		}
 
- 	}
 
- 	if resetTwoFactor {
 
- 		err := settingService.SetTwoFactorEnable(false)
 
- 		if err != nil {
 
- 			fmt.Println("Failed to reset two-factor authentication:", err)
 
- 		} else {
 
- 			settingService.SetTwoFactorToken("")
 
- 			fmt.Println("Two-factor authentication reset successfully")
 
- 		}
 
- 	}
 
- 	if listenIP != "" {
 
- 		err := settingService.SetListen(listenIP)
 
- 		if err != nil {
 
- 			fmt.Println("Failed to set listen IP:", err)
 
- 		} else {
 
- 			fmt.Printf("listen %v set successfully", listenIP)
 
- 		}
 
- 	}
 
- }
 
- // updateCert updates the SSL certificate files for the panel.
 
- func updateCert(publicKey string, privateKey string) {
 
- 	err := database.InitDB(config.GetDBPath())
 
- 	if err != nil {
 
- 		fmt.Println(err)
 
- 		return
 
- 	}
 
- 	if (privateKey != "" && publicKey != "") || (privateKey == "" && publicKey == "") {
 
- 		settingService := service.SettingService{}
 
- 		err = settingService.SetCertFile(publicKey)
 
- 		if err != nil {
 
- 			fmt.Println("set certificate public key failed:", err)
 
- 		} else {
 
- 			fmt.Println("set certificate public key success")
 
- 		}
 
- 		err = settingService.SetKeyFile(privateKey)
 
- 		if err != nil {
 
- 			fmt.Println("set certificate private key failed:", err)
 
- 		} else {
 
- 			fmt.Println("set certificate private key success")
 
- 		}
 
- 	} else {
 
- 		fmt.Println("both public and private key should be entered.")
 
- 	}
 
- }
 
- // GetCertificate displays the current SSL certificate settings if getCert is true.
 
- func GetCertificate(getCert bool) {
 
- 	if getCert {
 
- 		settingService := service.SettingService{}
 
- 		certFile, err := settingService.GetCertFile()
 
- 		if err != nil {
 
- 			fmt.Println("get cert file failed, error info:", err)
 
- 		}
 
- 		keyFile, err := settingService.GetKeyFile()
 
- 		if err != nil {
 
- 			fmt.Println("get key file failed, error info:", err)
 
- 		}
 
- 		fmt.Println("cert:", certFile)
 
- 		fmt.Println("key:", keyFile)
 
- 	}
 
- }
 
- // GetListenIP displays the current panel listen IP address if getListen is true.
 
- func GetListenIP(getListen bool) {
 
- 	if getListen {
 
- 		settingService := service.SettingService{}
 
- 		ListenIP, err := settingService.GetListen()
 
- 		if err != nil {
 
- 			log.Printf("Failed to retrieve listen IP: %v", err)
 
- 			return
 
- 		}
 
- 		fmt.Println("listenIP:", ListenIP)
 
- 	}
 
- }
 
- // migrateDb performs database migration operations for the 3x-ui panel.
 
- func migrateDb() {
 
- 	inboundService := service.InboundService{}
 
- 	err := database.InitDB(config.GetDBPath())
 
- 	if err != nil {
 
- 		log.Fatal(err)
 
- 	}
 
- 	fmt.Println("Start migrating database...")
 
- 	inboundService.MigrateDB()
 
- 	fmt.Println("Migration done!")
 
- }
 
- // main is the entry point of the 3x-ui application.
 
- // It parses command-line arguments to run the web server, migrate database, or update settings.
 
- func main() {
 
- 	if len(os.Args) < 2 {
 
- 		runWebServer()
 
- 		return
 
- 	}
 
- 	var showVersion bool
 
- 	flag.BoolVar(&showVersion, "v", false, "show version")
 
- 	runCmd := flag.NewFlagSet("run", flag.ExitOnError)
 
- 	settingCmd := flag.NewFlagSet("setting", flag.ExitOnError)
 
- 	var port int
 
- 	var username string
 
- 	var password string
 
- 	var webBasePath string
 
- 	var listenIP string
 
- 	var getListen bool
 
- 	var webCertFile string
 
- 	var webKeyFile string
 
- 	var tgbottoken string
 
- 	var tgbotchatid string
 
- 	var enabletgbot bool
 
- 	var tgbotRuntime string
 
- 	var reset bool
 
- 	var show bool
 
- 	var getCert bool
 
- 	var resetTwoFactor bool
 
- 	settingCmd.BoolVar(&reset, "reset", false, "Reset all settings")
 
- 	settingCmd.BoolVar(&show, "show", false, "Display current settings")
 
- 	settingCmd.IntVar(&port, "port", 0, "Set panel port number")
 
- 	settingCmd.StringVar(&username, "username", "", "Set login username")
 
- 	settingCmd.StringVar(&password, "password", "", "Set login password")
 
- 	settingCmd.StringVar(&webBasePath, "webBasePath", "", "Set base path for Panel")
 
- 	settingCmd.StringVar(&listenIP, "listenIP", "", "set panel listenIP IP")
 
- 	settingCmd.BoolVar(&resetTwoFactor, "resetTwoFactor", false, "Reset two-factor authentication settings")
 
- 	settingCmd.BoolVar(&getListen, "getListen", false, "Display current panel listenIP IP")
 
- 	settingCmd.BoolVar(&getCert, "getCert", false, "Display current certificate settings")
 
- 	settingCmd.StringVar(&webCertFile, "webCert", "", "Set path to public key file for panel")
 
- 	settingCmd.StringVar(&webKeyFile, "webCertKey", "", "Set path to private key file for panel")
 
- 	settingCmd.StringVar(&tgbottoken, "tgbottoken", "", "Set token for Telegram bot")
 
- 	settingCmd.StringVar(&tgbotRuntime, "tgbotRuntime", "", "Set cron time for Telegram bot notifications")
 
- 	settingCmd.StringVar(&tgbotchatid, "tgbotchatid", "", "Set chat ID for Telegram bot notifications")
 
- 	settingCmd.BoolVar(&enabletgbot, "enabletgbot", false, "Enable notifications via Telegram bot")
 
- 	oldUsage := flag.Usage
 
- 	flag.Usage = func() {
 
- 		oldUsage()
 
- 		fmt.Println()
 
- 		fmt.Println("Commands:")
 
- 		fmt.Println("    run            run web panel")
 
- 		fmt.Println("    migrate        migrate form other/old x-ui")
 
- 		fmt.Println("    setting        set settings")
 
- 	}
 
- 	flag.Parse()
 
- 	if showVersion {
 
- 		fmt.Println(config.GetVersion())
 
- 		return
 
- 	}
 
- 	switch os.Args[1] {
 
- 	case "run":
 
- 		err := runCmd.Parse(os.Args[2:])
 
- 		if err != nil {
 
- 			fmt.Println(err)
 
- 			return
 
- 		}
 
- 		runWebServer()
 
- 	case "migrate":
 
- 		migrateDb()
 
- 	case "setting":
 
- 		err := settingCmd.Parse(os.Args[2:])
 
- 		if err != nil {
 
- 			fmt.Println(err)
 
- 			return
 
- 		}
 
- 		if reset {
 
- 			resetSetting()
 
- 		} else {
 
- 			updateSetting(port, username, password, webBasePath, listenIP, resetTwoFactor)
 
- 		}
 
- 		if show {
 
- 			showSetting(show)
 
- 		}
 
- 		if getListen {
 
- 			GetListenIP(getListen)
 
- 		}
 
- 		if getCert {
 
- 			GetCertificate(getCert)
 
- 		}
 
- 		if (tgbottoken != "") || (tgbotchatid != "") || (tgbotRuntime != "") {
 
- 			updateTgbotSetting(tgbottoken, tgbotchatid, tgbotRuntime)
 
- 		}
 
- 		if enabletgbot {
 
- 			updateTgbotEnableSts(enabletgbot)
 
- 		}
 
- 	case "cert":
 
- 		err := settingCmd.Parse(os.Args[2:])
 
- 		if err != nil {
 
- 			fmt.Println(err)
 
- 			return
 
- 		}
 
- 		if reset {
 
- 			updateCert("", "")
 
- 		} else {
 
- 			updateCert(webCertFile, webKeyFile)
 
- 		}
 
- 	default:
 
- 		fmt.Println("Invalid subcommands")
 
- 		fmt.Println()
 
- 		runCmd.Usage()
 
- 		fmt.Println()
 
- 		settingCmd.Usage()
 
- 	}
 
- }
 
 
  |