Files
trihuy-russian/main.go
T

556 lines
15 KiB
Go
Raw Normal View History

2025-09-20 09:35:50 +02:00
// 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.
2023-02-09 22:48:06 +03:30
package main
import (
"flag"
2023-02-09 22:48:06 +03:30
"fmt"
"log"
"os"
"os/signal"
"syscall"
_ "unsafe"
2024-03-11 01:01:24 +03:30
2026-05-10 02:13:42 +02:00
"github.com/mhsanaei/3x-ui/v3/config"
"github.com/mhsanaei/3x-ui/v3/database"
"github.com/mhsanaei/3x-ui/v3/logger"
"github.com/mhsanaei/3x-ui/v3/sub"
"github.com/mhsanaei/3x-ui/v3/util/crypto"
"github.com/mhsanaei/3x-ui/v3/util/sys"
"github.com/mhsanaei/3x-ui/v3/web"
"github.com/mhsanaei/3x-ui/v3/web/global"
"github.com/mhsanaei/3x-ui/v3/web/service"
2023-02-09 22:48:06 +03:30
2025-05-17 14:03:22 +03:30
"github.com/joho/godotenv"
2023-02-09 22:48:06 +03:30
"github.com/op/go-logging"
)
2025-09-20 09:35:50 +02:00
// runWebServer initializes and starts the web server for the 3x-ui panel.
2023-02-09 22:48:06 +03:30
func runWebServer() {
2024-07-08 23:08:00 +02:00
log.Printf("Starting %v %v", config.GetName(), config.GetVersion())
2023-02-09 22:48:06 +03:30
switch config.GetLogLevel() {
case config.Debug:
logger.InitLogger(logging.DEBUG)
case config.Info:
logger.InitLogger(logging.INFO)
2023-06-16 18:25:33 +03:30
case config.Notice:
logger.InitLogger(logging.NOTICE)
case config.Warning:
2023-02-09 22:48:06 +03:30
logger.InitLogger(logging.WARNING)
case config.Error:
logger.InitLogger(logging.ERROR)
default:
2024-07-08 23:08:00 +02:00
log.Fatalf("Unknown log level: %v", config.GetLogLevel())
2023-02-09 22:48:06 +03:30
}
2025-05-17 14:03:22 +03:30
godotenv.Load()
2023-02-09 22:48:06 +03:30
err := database.InitDB(config.GetDBPath())
if err != nil {
2024-07-08 23:08:00 +02:00
log.Fatalf("Error initializing database: %v", err)
2023-02-09 22:48:06 +03:30
}
var server *web.Server
server = web.NewServer()
global.SetWebServer(server)
err = server.Start()
if err != nil {
2024-07-08 23:08:00 +02:00
log.Fatalf("Error starting web server: %v", err)
2023-02-09 22:48:06 +03:30
return
}
2023-05-22 21:51:52 +03:30
var subServer *sub.Server
sub.SetDistFS(web.EmbeddedDist())
service.RegisterSubLinkProvider(sub.NewLinkProvider())
2023-05-22 21:51:52 +03:30
subServer = sub.NewServer()
global.SetSubServer(subServer)
err = subServer.Start()
if err != nil {
2024-07-08 23:08:00 +02:00
log.Fatalf("Error starting sub server: %v", err)
2023-05-22 21:51:52 +03:30
return
}
2023-02-09 22:48:06 +03:30
sigCh := make(chan os.Signal, 1)
2023-04-20 15:49:24 +03:30
// Trap shutdown signals
2026-02-20 02:07:46 +01:00
signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGTERM, sys.SIGUSR1)
2023-02-09 22:48:06 +03:30
for {
sig := <-sigCh
switch sig {
case syscall.SIGHUP:
2024-07-08 23:08:00 +02:00
logger.Info("Received SIGHUP signal. Restarting servers...")
2026-05-12 11:36:05 +02:00
err := server.StopPanelOnly()
2023-02-09 22:48:06 +03:30
if err != nil {
2024-08-08 17:40:26 +02:00
logger.Debug("Error stopping web server:", err)
2023-02-09 22:48:06 +03:30
}
2023-05-22 21:51:52 +03:30
err = subServer.Stop()
if err != nil {
2024-08-08 17:40:26 +02:00
logger.Debug("Error stopping sub server:", err)
2023-05-22 21:51:52 +03:30
}
2023-02-09 22:48:06 +03:30
server = web.NewServer()
global.SetWebServer(server)
2026-05-12 11:36:05 +02:00
err = server.StartPanelOnly()
2023-02-09 22:48:06 +03:30
if err != nil {
2024-07-08 23:08:00 +02:00
log.Fatalf("Error restarting web server: %v", err)
2023-02-09 22:48:06 +03:30
return
}
2024-07-08 23:08:00 +02:00
log.Println("Web server restarted successfully.")
2023-05-22 21:51:52 +03:30
sub.SetDistFS(web.EmbeddedDist())
2023-05-22 21:51:52 +03:30
subServer = sub.NewServer()
global.SetSubServer(subServer)
err = subServer.Start()
if err != nil {
2024-07-08 23:08:00 +02:00
log.Fatalf("Error restarting sub server: %v", err)
2023-05-22 21:51:52 +03:30
return
}
2024-07-08 23:08:00 +02:00
log.Println("Sub server restarted successfully.")
2026-02-20 02:07:46 +01:00
case sys.SIGUSR1:
2026-02-20 00:03:16 +01:00
logger.Info("Received USR1 signal, restarting xray-core...")
err := server.RestartXray()
if err != nil {
logger.Error("Failed to restart xray-core:", err)
}
2024-07-08 23:08:00 +02:00
2023-02-09 22:48:06 +03:30
default:
// --- FIX FOR TELEGRAM BOT CONFLICT (409) on full shutdown ---
service.StopBot()
// ------------------------------------------------------------
server.Stop()
2023-05-22 21:51:52 +03:30
subServer.Stop()
2024-07-08 23:08:00 +02:00
log.Println("Shutting down servers.")
2023-02-09 22:48:06 +03:30
return
}
}
}
2025-09-20 09:35:50 +02:00
// resetSetting resets all panel settings to their default values.
2026-04-27 15:31:32 +02:00
func resetSetting() error {
2023-02-09 22:48:06 +03:30
err := database.InitDB(config.GetDBPath())
if err != nil {
2024-07-08 23:08:00 +02:00
fmt.Println("Failed to initialize database:", err)
2026-04-27 15:31:32 +02:00
return err
2023-02-09 22:48:06 +03:30
}
settingService := service.SettingService{}
err = settingService.ResetSettings()
if err != nil {
2024-07-08 23:08:00 +02:00
fmt.Println("Failed to reset settings:", err)
2026-04-27 15:31:32 +02:00
return err
2023-02-09 22:48:06 +03:30
} else {
2024-07-08 23:08:00 +02:00
fmt.Println("Settings successfully reset.")
2023-02-09 22:48:06 +03:30
}
2026-04-27 15:31:32 +02:00
return nil
2023-02-09 22:48:06 +03:30
}
2025-09-20 09:35:50 +02:00
// showSetting displays the current panel settings if show is true.
2023-02-09 22:48:06 +03:30
func showSetting(show bool) {
if show {
settingService := service.SettingService{}
port, err := settingService.GetPort()
if err != nil {
2024-05-24 11:08:16 +02:00
fmt.Println("get current port failed, error info:", err)
2023-02-09 22:48:06 +03:30
}
2024-05-24 11:08:16 +02:00
webBasePath, err := settingService.GetBasePath()
if err != nil {
fmt.Println("get webBasePath failed, error info:", err)
}
2024-10-30 20:26:37 +01:00
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)
}
2023-02-09 22:48:06 +03:30
userService := service.UserService{}
userModel, err := userService.GetFirstUser()
if err != nil {
2024-05-24 11:08:16 +02:00
fmt.Println("get current user info failed, error info:", err)
2023-02-09 22:48:06 +03:30
}
2024-05-24 11:08:16 +02:00
if userModel.Username == "" || userModel.Password == "" {
2023-02-09 22:48:06 +03:30
fmt.Println("current username or password is empty")
}
2024-05-24 11:08:16 +02:00
2023-04-14 17:22:49 +03:30
fmt.Println("current panel settings as follows:")
2024-10-30 20:26:37 +01:00
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)
2023-02-09 22:48:06 +03:30
fmt.Println("port:", port)
2024-10-30 20:26:37 +01:00
fmt.Println("webBasePath:", webBasePath)
2023-02-09 22:48:06 +03:30
}
}
2025-09-20 09:35:50 +02:00
// updateTgbotEnableSts enables or disables the Telegram bot notifications based on the status parameter.
2023-02-09 22:48:06 +03:30
func updateTgbotEnableSts(status bool) {
settingService := service.SettingService{}
2024-07-07 11:55:59 +02:00
currentTgSts, err := settingService.GetTgbotEnabled()
2023-02-09 22:48:06 +03:30
if err != nil {
fmt.Println(err)
return
}
logger.Infof("current enabletgbot status[%v],need update to status[%v]", currentTgSts, status)
if currentTgSts != status {
2024-07-07 11:55:59 +02:00
err := settingService.SetTgbotEnabled(status)
2023-02-09 22:48:06 +03:30
if err != nil {
fmt.Println(err)
return
} else {
2024-07-07 11:55:59 +02:00
logger.Infof("SetTgbotEnabled[%v] success", status)
2023-02-09 22:48:06 +03:30
}
}
}
2025-09-20 09:35:50 +02:00
// updateTgbotSetting updates Telegram bot settings including token, chat ID, and runtime schedule.
2023-03-17 19:37:49 +03:30
func updateTgbotSetting(tgBotToken string, tgBotChatid string, tgBotRuntime string) {
2023-02-09 22:48:06 +03:30
err := database.InitDB(config.GetDBPath())
if err != nil {
2024-07-08 23:08:00 +02:00
fmt.Println("Error initializing database:", err)
2023-02-09 22:48:06 +03:30
return
}
settingService := service.SettingService{}
if tgBotToken != "" {
err := settingService.SetTgBotToken(tgBotToken)
if err != nil {
2024-07-08 23:08:00 +02:00
fmt.Printf("Error setting Telegram bot token: %v\n", err)
2023-02-09 22:48:06 +03:30
return
}
2024-07-08 23:08:00 +02:00
logger.Info("Successfully updated Telegram bot token.")
2023-02-09 22:48:06 +03:30
}
if tgBotRuntime != "" {
err := settingService.SetTgbotRuntime(tgBotRuntime)
if err != nil {
2024-07-08 23:08:00 +02:00
fmt.Printf("Error setting Telegram bot runtime: %v\n", err)
2023-02-09 22:48:06 +03:30
return
}
2024-07-08 23:08:00 +02:00
logger.Infof("Successfully updated Telegram bot runtime to [%s].", tgBotRuntime)
2023-02-09 22:48:06 +03:30
}
2023-03-17 19:37:49 +03:30
if tgBotChatid != "" {
2023-02-09 22:48:06 +03:30
err := settingService.SetTgBotChatId(tgBotChatid)
if err != nil {
2024-07-08 23:08:00 +02:00
fmt.Printf("Error setting Telegram bot chat ID: %v\n", err)
2023-02-09 22:48:06 +03:30
return
}
2024-07-08 23:08:00 +02:00
logger.Info("Successfully updated Telegram bot chat ID.")
2023-02-09 22:48:06 +03:30
}
}
2025-09-20 09:35:50 +02:00
// updateSetting updates various panel settings including port, credentials, base path, listen IP, and two-factor authentication.
2026-04-27 15:31:32 +02:00
func updateSetting(port int, username string, password string, webBasePath string, listenIP string, resetTwoFactor bool) error {
2023-02-09 22:48:06 +03:30
err := database.InitDB(config.GetDBPath())
if err != nil {
2024-07-08 23:08:00 +02:00
fmt.Println("Database initialization failed:", err)
2026-04-27 15:31:32 +02:00
return err
2023-02-09 22:48:06 +03:30
}
settingService := service.SettingService{}
2024-07-08 23:08:00 +02:00
userService := service.UserService{}
2023-02-09 22:48:06 +03:30
if port > 0 {
err := settingService.SetPort(port)
if err != nil {
2024-07-08 23:08:00 +02:00
fmt.Println("Failed to set port:", err)
2023-02-09 22:48:06 +03:30
} else {
2024-07-08 23:08:00 +02:00
fmt.Printf("Port set successfully: %v\n", port)
2023-02-09 22:48:06 +03:30
}
}
2024-05-24 11:08:16 +02:00
2023-02-09 22:48:06 +03:30
if username != "" || password != "" {
err := userService.UpdateFirstUser(username, password)
if err != nil {
2024-07-08 23:08:00 +02:00
fmt.Println("Failed to update username and password:", err)
2023-02-09 22:48:06 +03:30
} else {
2024-07-08 23:08:00 +02:00
fmt.Println("Username and password updated successfully")
2023-02-09 22:48:06 +03:30
}
}
if webBasePath != "" {
err := settingService.SetBasePath(webBasePath)
if err != nil {
2024-07-08 23:08:00 +02:00
fmt.Println("Failed to set base URI path:", err)
} else {
2024-07-08 23:08:00 +02:00
fmt.Println("Base URI path set successfully")
}
}
2024-10-30 16:35:36 +01:00
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")
}
}
2024-10-30 16:35:36 +01:00
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)
}
}
2026-04-27 15:31:32 +02:00
return nil
2023-02-09 22:48:06 +03:30
}
2025-09-20 09:35:50 +02:00
// updateCert updates the SSL certificate files for the panel.
2024-05-25 13:05:27 +03:30
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")
}
err = settingService.SetSubCertFile(publicKey)
if err != nil {
fmt.Println("set certificate for subscription public key failed:", err)
} else {
fmt.Println("set certificate for subscription public key success")
}
err = settingService.SetSubKeyFile(privateKey)
if err != nil {
fmt.Println("set certificate for subscription private key failed:", err)
} else {
fmt.Println("set certificate for subscription private key success")
}
2024-05-25 13:05:27 +03:30
} else {
fmt.Println("both public and private key should be entered.")
}
}
2025-09-20 09:35:50 +02:00
// GetCertificate displays the current SSL certificate settings if getCert is true.
2024-10-30 20:26:37 +01:00
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)
}
}
2025-09-20 09:35:50 +02:00
// GetListenIP displays the current panel listen IP address if getListen is true.
2024-10-30 20:26:37 +01:00
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)
}
}
func GetApiToken(getApiToken bool) {
if !getApiToken {
return
}
apiTokenService := service.ApiTokenService{}
tokens, err := apiTokenService.List()
if err != nil {
fmt.Println("get apiToken failed, error info:", err)
return
}
if len(tokens) > 0 {
fmt.Println("apiToken:", tokens[0].Token)
return
}
created, err := apiTokenService.Create("install")
if err != nil {
fmt.Println("create apiToken failed, error info:", err)
return
}
fmt.Println("apiToken:", created.Token)
}
2025-09-20 09:35:50 +02:00
// migrateDb performs database migration operations for the 3x-ui panel.
2023-04-24 14:13:25 +03:30
func migrateDb() {
inboundService := service.InboundService{}
err := database.InitDB(config.GetDBPath())
if err != nil {
log.Fatal(err)
}
fmt.Println("Start migrating database...")
inboundService.MigrateDB()
2023-04-24 14:13:25 +03:30
fmt.Println("Migration done!")
}
2025-09-20 09:35:50 +02:00
// 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.
2023-02-09 22:48:06 +03:30
func main() {
if len(os.Args) < 2 {
runWebServer()
return
2023-02-09 22:48:06 +03:30
}
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
2024-05-24 11:08:16 +02:00
var webBasePath string
2024-10-30 16:35:36 +01:00
var listenIP string
2024-10-30 20:26:37 +01:00
var getListen bool
2024-05-25 13:05:27 +03:30
var webCertFile string
var webKeyFile string
var tgbottoken string
var tgbotchatid string
var enabletgbot bool
var tgbotRuntime string
var reset bool
var show bool
2024-10-30 20:26:37 +01:00
var getCert bool
var getApiToken bool
var resetTwoFactor bool
2024-07-08 23:08:00 +02:00
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")
2024-10-30 20:26:37 +01:00
settingCmd.StringVar(&listenIP, "listenIP", "", "set panel listenIP IP")
settingCmd.BoolVar(&resetTwoFactor, "resetTwoFactor", false, "Reset two-factor authentication settings")
2024-10-30 20:26:37 +01:00
settingCmd.BoolVar(&getListen, "getListen", false, "Display current panel listenIP IP")
settingCmd.BoolVar(&getCert, "getCert", false, "Display current certificate settings")
settingCmd.BoolVar(&getApiToken, "getApiToken", false, "Display current API token")
2024-07-08 23:08:00 +02:00
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 {
2026-04-27 15:31:32 +02:00
if err = resetSetting(); err != nil {
return
}
} else {
2026-04-27 15:31:32 +02:00
if err = updateSetting(port, username, password, webBasePath, listenIP, resetTwoFactor); err != nil {
return
}
}
if show {
showSetting(show)
}
2024-10-30 20:26:37 +01:00
if getListen {
GetListenIP(getListen)
}
if getCert {
GetCertificate(getCert)
}
if getApiToken {
GetApiToken(getApiToken)
}
if (tgbottoken != "") || (tgbotchatid != "") || (tgbotRuntime != "") {
updateTgbotSetting(tgbottoken, tgbotchatid, tgbotRuntime)
}
if enabletgbot {
updateTgbotEnableSts(enabletgbot)
}
2024-05-25 13:05:27 +03:30
case "cert":
err := settingCmd.Parse(os.Args[2:])
if err != nil {
fmt.Println(err)
return
}
if reset {
updateCert("", "")
} else {
updateCert(webCertFile, webKeyFile)
}
default:
2024-03-11 11:46:54 +03:30
fmt.Println("Invalid subcommands")
fmt.Println()
runCmd.Usage()
fmt.Println()
settingCmd.Usage()
2023-02-09 22:48:06 +03:30
}
}