| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- // Package config provides configuration management utilities for the 3x-ui panel,
- // including version information, logging levels, database paths, and environment variable handling.
- package config
- import (
- _ "embed"
- "fmt"
- "io"
- "os"
- "path/filepath"
- "runtime"
- "strconv"
- "strings"
- "testing"
- )
- //go:embed version
- var version string
- //go:embed name
- var name string
- // buildCommit and buildDate are injected at build time via `-ldflags -X` for
- // CI per-commit (dev channel) builds; see .github/workflows/release.yml. They
- // stay empty for a plain `go build` and for stable tagged releases, which is how
- // IsDevBuild tells a rolling dev build apart from a stable/local one.
- var (
- buildCommit string
- buildDate string
- )
- // LogLevel represents the logging level for the application.
- type LogLevel string
- // Logging level constants
- const (
- Debug LogLevel = "debug"
- Info LogLevel = "info"
- Notice LogLevel = "notice"
- Warning LogLevel = "warning"
- Error LogLevel = "error"
- )
- // GetVersion returns the version string of the 3x-ui application.
- func GetVersion() string {
- return strings.TrimSpace(version)
- }
- // GetName returns the name of the 3x-ui application.
- func GetName() string {
- return strings.TrimSpace(name)
- }
- // GetBuildCommit returns the short git commit this binary was built from, or an
- // empty string for a plain/local build or a stable tagged release.
- func GetBuildCommit() string {
- return strings.TrimSpace(buildCommit)
- }
- // GetBuildDate returns the UTC build timestamp injected at build time, or empty.
- func GetBuildDate() string {
- return strings.TrimSpace(buildDate)
- }
- // IsDevBuild reports whether this binary is a CI per-commit (dev channel) build,
- // detected by the injected commit. Stable releases and local builds return false.
- func IsDevBuild() bool {
- return GetBuildCommit() != ""
- }
- // GetReportedVersion returns the version a panel advertises to a managing master
- // node: the plain version for stable builds, or "dev+<short commit>" for dev
- // builds. The dev form mirrors the master's getPanelUpdateInfo latestVersion so
- // a node on the current dev commit compares as up to date instead of always
- // showing "update available".
- func GetReportedVersion() string {
- if !IsDevBuild() {
- return GetVersion()
- }
- commit := GetBuildCommit()
- if len(commit) > 8 {
- commit = commit[:8]
- }
- return "dev+" + commit
- }
- // GetLogLevel returns the current logging level based on environment variables or defaults to Info.
- func GetLogLevel() LogLevel {
- if IsDebug() {
- return Debug
- }
- logLevel := os.Getenv("XUI_LOG_LEVEL")
- if logLevel == "" {
- return Info
- }
- return LogLevel(logLevel)
- }
- // IsDebug returns true if debug mode is enabled via the XUI_DEBUG environment variable.
- func IsDebug() bool {
- return os.Getenv("XUI_DEBUG") == "true"
- }
- // IsSkipHSTS returns true if skipping HSTS mode is enabled via the XUI_SKIP_HSTS environment variable.
- 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")
- if binFolderPath == "" {
- binFolderPath = "bin"
- }
- return binFolderPath
- }
- func getBaseDir() string {
- exePath, err := os.Executable()
- if err != nil {
- return "."
- }
- exeDir := filepath.Dir(exePath)
- exeDirLower := strings.ToLower(filepath.ToSlash(exeDir))
- if strings.Contains(exeDirLower, "/appdata/local/temp/") || strings.Contains(exeDirLower, "/go-build") {
- wd, err := os.Getwd()
- if err != nil {
- return "."
- }
- return wd
- }
- return exeDir
- }
- // GetDBFolderPath returns the path to the database folder based on environment variables or platform defaults.
- func GetDBFolderPath() string {
- dbFolderPath := os.Getenv("XUI_DB_FOLDER")
- if dbFolderPath != "" {
- return dbFolderPath
- }
- if runtime.GOOS == "windows" {
- return getBaseDir()
- }
- return "/etc/x-ui"
- }
- // GetDBPath returns the full path to the database file.
- func GetDBPath() string {
- return fmt.Sprintf("%s/%s.db", GetDBFolderPath(), GetName())
- }
- // GetDBKind returns the configured database backend: "sqlite" (default) or "postgres".
- func GetDBKind() string {
- v := strings.ToLower(strings.TrimSpace(os.Getenv("XUI_DB_TYPE")))
- switch v {
- case "postgres", "postgresql", "pg":
- return "postgres"
- default:
- return "sqlite"
- }
- }
- // GetDBDSN returns the PostgreSQL DSN from XUI_DB_DSN. Empty for sqlite.
- func GetDBDSN() string {
- return strings.TrimSpace(os.Getenv("XUI_DB_DSN"))
- }
- // GetEnvFilePaths returns the candidate service environment file paths (the file
- // systemd loads via EnvironmentFile) across the supported distro families.
- func GetEnvFilePaths() []string {
- if runtime.GOOS == "windows" {
- return nil
- }
- return []string{
- "/etc/default/x-ui",
- "/etc/conf.d/x-ui",
- "/etc/sysconfig/x-ui",
- }
- }
- // GetLogFolder returns the path to the log folder based on environment variables or platform defaults.
- func GetLogFolder() string {
- logFolderPath := os.Getenv("XUI_LOG_FOLDER")
- if logFolderPath != "" {
- return logFolderPath
- }
- // Under `go test` the Windows default below is CWD-relative ("./log"), which
- // scatters a log/ directory through the source tree (one per tested package).
- // Redirect test runs to a shared temp folder so the source tree stays clean.
- if testing.Testing() {
- return filepath.Join(os.TempDir(), "3x-ui-test-log")
- }
- if runtime.GOOS == "windows" {
- return filepath.Join(".", "log")
- }
- return "/var/log/x-ui"
- }
- func copyFile(src, dst string) error {
- in, err := os.Open(src)
- if err != nil {
- return err
- }
- defer in.Close()
- out, err := os.Create(dst)
- if err != nil {
- return err
- }
- defer out.Close()
- _, err = io.Copy(out, in)
- if err != nil {
- return err
- }
- return out.Sync()
- }
- func init() {
- if runtime.GOOS != "windows" {
- return
- }
- if os.Getenv("XUI_DB_FOLDER") != "" {
- return
- }
- oldDBFolder := "/etc/x-ui"
- oldDBPath := fmt.Sprintf("%s/%s.db", oldDBFolder, GetName())
- newDBFolder := GetDBFolderPath()
- newDBPath := fmt.Sprintf("%s/%s.db", newDBFolder, GetName())
- _, err := os.Stat(newDBPath)
- if err == nil {
- return // new exists
- }
- _, err = os.Stat(oldDBPath)
- if os.IsNotExist(err) {
- return // old does not exist
- }
- _ = copyFile(oldDBPath, newDBPath) // ignore error
- }
|