mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-04-14 11:35:50 +00:00
- Add SQLite/PostgreSQL switching via panel UI and env variables - Introduce portable .xui-backup format for cross-backend backups - Add connection pooling and PrepareStmt cache for PostgreSQL - Fix raw SQL double-quote bug breaking queries on PostgreSQL - Fix GORM record-not-found log spam on every Xray config poll - Add database section to Settings with full EN/RU i18n
719 lines
20 KiB
Go
719 lines
20 KiB
Go
// 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 (
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/signal"
|
|
"path/filepath"
|
|
"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/util/sys"
|
|
"github.com/mhsanaei/3x-ui/v2/web"
|
|
"github.com/mhsanaei/3x-ui/v2/web/entity"
|
|
"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()
|
|
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, sys.SIGUSR1)
|
|
for {
|
|
sig := <-sigCh
|
|
|
|
switch sig {
|
|
case syscall.SIGHUP:
|
|
logger.Info("Received SIGHUP signal. Restarting servers...")
|
|
|
|
// --- FIX FOR TELEGRAM BOT CONFLICT (409): Stop bot before restart ---
|
|
service.StopBot()
|
|
// --
|
|
|
|
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.")
|
|
case sys.SIGUSR1:
|
|
logger.Info("Received USR1 signal, restarting xray-core...")
|
|
err := server.RestartXray()
|
|
if err != nil {
|
|
logger.Error("Failed to restart xray-core:", err)
|
|
}
|
|
|
|
default:
|
|
// --- FIX FOR TELEGRAM BOT CONFLICT (409) on full shutdown ---
|
|
service.StopBot()
|
|
// ------------------------------------------------------------
|
|
|
|
server.Stop()
|
|
subServer.Stop()
|
|
log.Println("Shutting down servers.")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// resetSetting resets all panel settings to their default values.
|
|
func resetSetting() {
|
|
err := database.InitDB()
|
|
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)
|
|
}
|
|
subPort, err := settingService.GetSubPort()
|
|
if err != nil {
|
|
fmt.Println("get current sub port failed, error info:", err)
|
|
}
|
|
|
|
webBasePath, err := settingService.GetBasePath()
|
|
if err != nil {
|
|
fmt.Println("get webBasePath failed, error info:", err)
|
|
}
|
|
listen, err := settingService.GetListen()
|
|
if err != nil {
|
|
fmt.Println("get current listen failed, error info:", err)
|
|
}
|
|
subListen, err := settingService.GetSubListen()
|
|
if err != nil {
|
|
fmt.Println("get current sub listen 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("listen:", listen)
|
|
fmt.Println("subPort:", subPort)
|
|
fmt.Println("subListen:", subListen)
|
|
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()
|
|
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 ports, credentials, base path, listen IPs, and two-factor authentication.
|
|
func updateSetting(port int, subPort int, username string, password string, webBasePath string, listenIP string, subListenIP string, resetTwoFactor bool) {
|
|
err := database.InitDB()
|
|
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\n", listenIP)
|
|
}
|
|
}
|
|
|
|
if subPort > 0 {
|
|
err := settingService.SetSubPort(subPort)
|
|
if err != nil {
|
|
fmt.Println("Failed to set sub port:", err)
|
|
} else {
|
|
fmt.Printf("Sub port set successfully: %v\n", subPort)
|
|
}
|
|
}
|
|
|
|
if subListenIP != "" {
|
|
err := settingService.SetSubListen(subListenIP)
|
|
if err != nil {
|
|
fmt.Println("Failed to set sub listen IP:", err)
|
|
} else {
|
|
fmt.Printf("sub listen %v set successfully\n", subListenIP)
|
|
}
|
|
}
|
|
}
|
|
|
|
// updateCert updates the SSL certificate files for the panel.
|
|
func updateCert(publicKey string, privateKey string) {
|
|
err := database.InitDB()
|
|
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")
|
|
}
|
|
} 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()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Println("Start migrating database...")
|
|
inboundService.MigrateDB()
|
|
fmt.Println("Migration done!")
|
|
}
|
|
|
|
func defaultDatabaseSetting() *entity.DatabaseSetting {
|
|
databaseService := &service.DatabaseService{}
|
|
setting, err := databaseService.GetSetting()
|
|
if err == nil && setting != nil {
|
|
return setting
|
|
}
|
|
return entity.DatabaseSettingFromConfig(config.DefaultDatabaseConfig())
|
|
}
|
|
|
|
func addDatabaseFlags(fs *flag.FlagSet, setting *entity.DatabaseSetting) {
|
|
fs.StringVar(&setting.Driver, "driver", setting.Driver, "Database driver: sqlite or postgres")
|
|
fs.StringVar(&setting.SQLitePath, "sqlite-path", setting.SQLitePath, "SQLite database path")
|
|
fs.StringVar(&setting.PostgresMode, "postgres-mode", setting.PostgresMode, "PostgreSQL mode: local or external")
|
|
fs.StringVar(&setting.PostgresHost, "postgres-host", setting.PostgresHost, "PostgreSQL host")
|
|
fs.IntVar(&setting.PostgresPort, "postgres-port", setting.PostgresPort, "PostgreSQL port")
|
|
fs.StringVar(&setting.PostgresDBName, "postgres-db", setting.PostgresDBName, "PostgreSQL database name")
|
|
fs.StringVar(&setting.PostgresUser, "postgres-user", setting.PostgresUser, "PostgreSQL user")
|
|
fs.StringVar(&setting.PostgresPassword, "postgres-password", "", "PostgreSQL password")
|
|
fs.StringVar(&setting.PostgresSSLMode, "postgres-sslmode", setting.PostgresSSLMode, "PostgreSQL sslmode")
|
|
fs.BoolVar(&setting.ManagedLocally, "postgres-local", setting.ManagedLocally, "Treat PostgreSQL as locally managed")
|
|
}
|
|
|
|
func writeExportFile(outputPath string, defaultName string, data []byte) (string, error) {
|
|
targetPath := outputPath
|
|
if targetPath == "" {
|
|
targetPath = defaultName
|
|
} else if info, err := os.Stat(targetPath); err == nil && info.IsDir() {
|
|
targetPath = filepath.Join(targetPath, defaultName)
|
|
}
|
|
|
|
if err := os.WriteFile(targetPath, data, 0o600); err != nil {
|
|
return "", err
|
|
}
|
|
return targetPath, nil
|
|
}
|
|
|
|
func handleDatabaseCommand(args []string) {
|
|
if len(args) == 0 {
|
|
fmt.Println("Usage:")
|
|
fmt.Println(" x-ui database show")
|
|
fmt.Println(" x-ui database test [database flags]")
|
|
fmt.Println(" x-ui database switch [database flags]")
|
|
fmt.Println(" x-ui database export -type portable|sqlite [-out path]")
|
|
fmt.Println(" x-ui database import -file backup.xui-backup")
|
|
fmt.Println(" x-ui database install-postgres")
|
|
return
|
|
}
|
|
|
|
databaseService := service.DatabaseService{}
|
|
|
|
switch args[0] {
|
|
case "show":
|
|
setting, err := databaseService.GetSetting()
|
|
if err != nil {
|
|
fmt.Println("Failed to load database settings:", err)
|
|
return
|
|
}
|
|
contents, err := json.MarshalIndent(setting, "", " ")
|
|
if err != nil {
|
|
fmt.Println("Failed to serialize database settings:", err)
|
|
return
|
|
}
|
|
fmt.Println(string(contents))
|
|
case "test":
|
|
setting := defaultDatabaseSetting()
|
|
testCmd := flag.NewFlagSet("database test", flag.ExitOnError)
|
|
addDatabaseFlags(testCmd, setting)
|
|
_ = testCmd.Parse(args[1:])
|
|
if err := databaseService.TestSetting(setting); err != nil {
|
|
fmt.Println("Database connection test failed:", err)
|
|
return
|
|
}
|
|
fmt.Println("Database connection test succeeded.")
|
|
case "switch":
|
|
setting := defaultDatabaseSetting()
|
|
switchCmd := flag.NewFlagSet("database switch", flag.ExitOnError)
|
|
addDatabaseFlags(switchCmd, setting)
|
|
_ = switchCmd.Parse(args[1:])
|
|
if err := databaseService.SwitchDatabase(setting); err != nil {
|
|
fmt.Println("Database switch failed:", err)
|
|
return
|
|
}
|
|
fmt.Println("Database configuration updated. Restart the panel service to apply changes.")
|
|
case "export":
|
|
exportCmd := flag.NewFlagSet("database export", flag.ExitOnError)
|
|
exportType := exportCmd.String("type", "portable", "Export type: portable or sqlite")
|
|
outputPath := exportCmd.String("out", "", "Output path or directory")
|
|
_ = exportCmd.Parse(args[1:])
|
|
|
|
if err := database.InitDB(); err != nil {
|
|
fmt.Println("Failed to initialize database:", err)
|
|
return
|
|
}
|
|
|
|
var (
|
|
data []byte
|
|
filename string
|
|
err error
|
|
)
|
|
switch *exportType {
|
|
case "sqlite":
|
|
data, filename, err = databaseService.ExportNativeSQLite()
|
|
default:
|
|
data, filename, err = databaseService.ExportPortableBackup()
|
|
}
|
|
if err != nil {
|
|
fmt.Println("Export failed:", err)
|
|
return
|
|
}
|
|
|
|
targetPath, err := writeExportFile(*outputPath, filename, data)
|
|
if err != nil {
|
|
fmt.Println("Failed to write backup:", err)
|
|
return
|
|
}
|
|
fmt.Println("Backup exported to:", targetPath)
|
|
case "import":
|
|
importCmd := flag.NewFlagSet("database import", flag.ExitOnError)
|
|
backupFile := importCmd.String("file", "", "Path to .xui-backup or legacy SQLite .db file")
|
|
_ = importCmd.Parse(args[1:])
|
|
if *backupFile == "" {
|
|
fmt.Println("Import requires -file")
|
|
return
|
|
}
|
|
|
|
if err := database.InitDB(); err != nil {
|
|
fmt.Println("Failed to initialize database:", err)
|
|
return
|
|
}
|
|
|
|
file, err := os.Open(*backupFile)
|
|
if err != nil {
|
|
fmt.Println("Failed to open backup file:", err)
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
backupType, err := databaseService.ImportBackup(file)
|
|
if err != nil {
|
|
fmt.Println("Import failed:", err)
|
|
return
|
|
}
|
|
fmt.Println("Import completed using", backupType, "backup. Restart the panel service to apply changes.")
|
|
case "install-postgres":
|
|
output, err := databaseService.InstallLocalPostgres()
|
|
if err != nil {
|
|
fmt.Println("PostgreSQL installation failed:", err)
|
|
return
|
|
}
|
|
fmt.Print(output)
|
|
default:
|
|
fmt.Println("Unknown database subcommand:", args[0])
|
|
}
|
|
}
|
|
|
|
// 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 subPort int
|
|
var username string
|
|
var password string
|
|
var webBasePath string
|
|
var listenIP string
|
|
var subListenIP 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.IntVar(&subPort, "subPort", 0, "Set subscription 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.StringVar(&subListenIP, "subListenIP", "", "set subscription 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")
|
|
fmt.Println(" database manage database backend")
|
|
}
|
|
|
|
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, subPort, username, password, webBasePath, listenIP, subListenIP, 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)
|
|
}
|
|
case "database":
|
|
handleDatabaseCommand(os.Args[2:])
|
|
default:
|
|
fmt.Println("Invalid subcommands")
|
|
fmt.Println()
|
|
runCmd.Usage()
|
|
fmt.Println()
|
|
settingCmd.Usage()
|
|
}
|
|
}
|