mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-20 18:11:00 +00:00
feat(docker): support XUI_PORT runtime override (#5240)
* feat(docker): support XUI_PORT runtime override Allow deployments to select the panel listener port without mutating the persisted webPort setting. Invalid values fall back to the database-backed port and are covered by parser boundary tests. * docs: describe XUI_PORT deployment usage Add commented local and Compose examples, explain runtime precedence, and call out matching Docker bridge port mappings.
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -63,6 +64,23 @@ func IsSkipHSTS() bool {
|
||||
return os.Getenv("XUI_SKIP_HSTS") == "true"
|
||||
}
|
||||
|
||||
func GetPortOverride() (port int, configured bool, err error) {
|
||||
value, ok := os.LookupEnv("XUI_PORT")
|
||||
if !ok || strings.TrimSpace(value) == "" {
|
||||
return 0, false, nil
|
||||
}
|
||||
|
||||
port, err = strconv.Atoi(strings.TrimSpace(value))
|
||||
if err != nil {
|
||||
return 0, true, fmt.Errorf("parse XUI_PORT: %w", err)
|
||||
}
|
||||
if port < 1 || port > 65535 {
|
||||
return 0, true, fmt.Errorf("XUI_PORT must be between 1 and 65535")
|
||||
}
|
||||
|
||||
return port, true, nil
|
||||
}
|
||||
|
||||
// GetBinFolderPath returns the path to the binary folder, defaulting to "bin" if not set via XUI_BIN_FOLDER.
|
||||
func GetBinFolderPath() string {
|
||||
binFolderPath := os.Getenv("XUI_BIN_FOLDER")
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetPortOverride(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value string
|
||||
set bool
|
||||
wantPort int
|
||||
configured bool
|
||||
wantErr bool
|
||||
}{
|
||||
{name: "unset"},
|
||||
{name: "empty", value: "", set: true},
|
||||
{name: "whitespace", value: " ", set: true},
|
||||
{name: "minimum", value: "1", set: true, wantPort: 1, configured: true},
|
||||
{name: "default panel port", value: "2053", set: true, wantPort: 2053, configured: true},
|
||||
{name: "surrounding whitespace", value: " 8080 ", set: true, wantPort: 8080, configured: true},
|
||||
{name: "maximum", value: "65535", set: true, wantPort: 65535, configured: true},
|
||||
{name: "zero", value: "0", set: true, configured: true, wantErr: true},
|
||||
{name: "above maximum", value: "65536", set: true, configured: true, wantErr: true},
|
||||
{name: "negative", value: "-1", set: true, configured: true, wantErr: true},
|
||||
{name: "non-numeric", value: "abc", set: true, configured: true, wantErr: true},
|
||||
{name: "decimal", value: "8080.0", set: true, configured: true, wantErr: true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.set {
|
||||
t.Setenv("XUI_PORT", tt.value)
|
||||
} else {
|
||||
original, existed := os.LookupEnv("XUI_PORT")
|
||||
if err := os.Unsetenv("XUI_PORT"); err != nil {
|
||||
t.Fatalf("unset XUI_PORT: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
if existed {
|
||||
_ = os.Setenv("XUI_PORT", original)
|
||||
} else {
|
||||
_ = os.Unsetenv("XUI_PORT")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
port, configured, err := GetPortOverride()
|
||||
if port != tt.wantPort {
|
||||
t.Errorf("port = %d, want %d", port, tt.wantPort)
|
||||
}
|
||||
if configured != tt.configured {
|
||||
t.Errorf("configured = %t, want %t", configured, tt.configured)
|
||||
}
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("error = %v, wantErr %t", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -407,6 +407,14 @@ func (s *Server) start(restartXray bool, startTgBot bool) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if envPort, configured, envErr := config.GetPortOverride(); configured {
|
||||
if envErr != nil {
|
||||
logger.Warning("Ignoring invalid XUI_PORT; using configured web port:", port, envErr)
|
||||
} else {
|
||||
port = envPort
|
||||
logger.Info("Using XUI_PORT override for web panel port:", port)
|
||||
}
|
||||
}
|
||||
listenAddr := net.JoinHostPort(listen, strconv.Itoa(port))
|
||||
listener, err := net.Listen("tcp", listenAddr)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user