mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-04-14 19:45:47 +00:00
252 lines
6 KiB
Go
252 lines
6 KiB
Go
|
|
package config
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"errors"
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
"strconv"
|
||
|
|
"strings"
|
||
|
|
)
|
||
|
|
|
||
|
|
const (
|
||
|
|
DatabaseDriverSQLite = "sqlite"
|
||
|
|
DatabaseDriverPostgres = "postgres"
|
||
|
|
|
||
|
|
DatabaseConfigSourceDefault = "default"
|
||
|
|
DatabaseConfigSourceFile = "file"
|
||
|
|
DatabaseConfigSourceEnv = "env"
|
||
|
|
|
||
|
|
DatabaseModeLocal = "local"
|
||
|
|
DatabaseModeExternal = "external"
|
||
|
|
)
|
||
|
|
|
||
|
|
type SQLiteDatabaseConfig struct {
|
||
|
|
Path string `json:"path"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type PostgresDatabaseConfig struct {
|
||
|
|
Mode string `json:"mode"`
|
||
|
|
Host string `json:"host"`
|
||
|
|
Port int `json:"port"`
|
||
|
|
DBName string `json:"dbName"`
|
||
|
|
User string `json:"user"`
|
||
|
|
Password string `json:"password,omitempty"`
|
||
|
|
SSLMode string `json:"sslMode"`
|
||
|
|
ManagedLocally bool `json:"managedLocally"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type DatabaseConfig struct {
|
||
|
|
Driver string `json:"driver"`
|
||
|
|
ConfigSource string `json:"configSource,omitempty"`
|
||
|
|
SQLite SQLiteDatabaseConfig `json:"sqlite"`
|
||
|
|
Postgres PostgresDatabaseConfig `json:"postgres"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func DefaultDatabaseConfig() *DatabaseConfig {
|
||
|
|
name := GetName()
|
||
|
|
if name == "" {
|
||
|
|
name = "x-ui"
|
||
|
|
}
|
||
|
|
return (&DatabaseConfig{
|
||
|
|
Driver: DatabaseDriverSQLite,
|
||
|
|
SQLite: SQLiteDatabaseConfig{
|
||
|
|
Path: GetDBPath(),
|
||
|
|
},
|
||
|
|
Postgres: PostgresDatabaseConfig{
|
||
|
|
Mode: DatabaseModeExternal,
|
||
|
|
Host: "127.0.0.1",
|
||
|
|
Port: 5432,
|
||
|
|
DBName: name,
|
||
|
|
User: name,
|
||
|
|
SSLMode: "disable",
|
||
|
|
ManagedLocally: false,
|
||
|
|
},
|
||
|
|
}).Normalize()
|
||
|
|
}
|
||
|
|
|
||
|
|
func (c *DatabaseConfig) Clone() *DatabaseConfig {
|
||
|
|
if c == nil {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
cloned := *c
|
||
|
|
return &cloned
|
||
|
|
}
|
||
|
|
|
||
|
|
func (c *DatabaseConfig) Normalize() *DatabaseConfig {
|
||
|
|
if c == nil {
|
||
|
|
return DefaultDatabaseConfig()
|
||
|
|
}
|
||
|
|
|
||
|
|
if c.Driver == "" {
|
||
|
|
c.Driver = DatabaseDriverSQLite
|
||
|
|
}
|
||
|
|
c.Driver = strings.ToLower(strings.TrimSpace(c.Driver))
|
||
|
|
if c.Driver != DatabaseDriverSQLite && c.Driver != DatabaseDriverPostgres {
|
||
|
|
c.Driver = DatabaseDriverSQLite
|
||
|
|
}
|
||
|
|
|
||
|
|
if c.SQLite.Path == "" {
|
||
|
|
c.SQLite.Path = GetDBPath()
|
||
|
|
}
|
||
|
|
|
||
|
|
c.Postgres.Mode = strings.ToLower(strings.TrimSpace(c.Postgres.Mode))
|
||
|
|
if c.Postgres.Mode != DatabaseModeLocal && c.Postgres.Mode != DatabaseModeExternal {
|
||
|
|
if c.Postgres.ManagedLocally {
|
||
|
|
c.Postgres.Mode = DatabaseModeLocal
|
||
|
|
} else {
|
||
|
|
c.Postgres.Mode = DatabaseModeExternal
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if c.Postgres.Host == "" {
|
||
|
|
c.Postgres.Host = "127.0.0.1"
|
||
|
|
}
|
||
|
|
if c.Postgres.Port <= 0 {
|
||
|
|
c.Postgres.Port = 5432
|
||
|
|
}
|
||
|
|
if c.Postgres.DBName == "" {
|
||
|
|
c.Postgres.DBName = GetName()
|
||
|
|
}
|
||
|
|
if c.Postgres.User == "" {
|
||
|
|
c.Postgres.User = GetName()
|
||
|
|
}
|
||
|
|
if c.Postgres.SSLMode == "" {
|
||
|
|
c.Postgres.SSLMode = "disable"
|
||
|
|
}
|
||
|
|
if c.Postgres.Mode == DatabaseModeLocal {
|
||
|
|
c.Postgres.ManagedLocally = true
|
||
|
|
}
|
||
|
|
|
||
|
|
return c
|
||
|
|
}
|
||
|
|
|
||
|
|
func (c *DatabaseConfig) UsesSQLite() bool {
|
||
|
|
return c != nil && c.Normalize().Driver == DatabaseDriverSQLite
|
||
|
|
}
|
||
|
|
|
||
|
|
func (c *DatabaseConfig) UsesPostgres() bool {
|
||
|
|
return c != nil && c.Normalize().Driver == DatabaseDriverPostgres
|
||
|
|
}
|
||
|
|
|
||
|
|
func HasDatabaseEnvOverride() bool {
|
||
|
|
driver := strings.TrimSpace(os.Getenv("XUI_DB_DRIVER"))
|
||
|
|
if driver != "" {
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
func loadDatabaseConfigFromEnv() (*DatabaseConfig, error) {
|
||
|
|
driver := strings.ToLower(strings.TrimSpace(os.Getenv("XUI_DB_DRIVER")))
|
||
|
|
if driver == "" {
|
||
|
|
return nil, errors.New("database env override is not configured")
|
||
|
|
}
|
||
|
|
|
||
|
|
cfg := DefaultDatabaseConfig()
|
||
|
|
cfg.ConfigSource = DatabaseConfigSourceEnv
|
||
|
|
cfg.Driver = driver
|
||
|
|
|
||
|
|
if path := strings.TrimSpace(os.Getenv("XUI_DB_PATH")); path != "" {
|
||
|
|
cfg.SQLite.Path = path
|
||
|
|
}
|
||
|
|
|
||
|
|
if mode := strings.TrimSpace(os.Getenv("XUI_DB_MODE")); mode != "" {
|
||
|
|
cfg.Postgres.Mode = mode
|
||
|
|
}
|
||
|
|
if host := strings.TrimSpace(os.Getenv("XUI_DB_HOST")); host != "" {
|
||
|
|
cfg.Postgres.Host = host
|
||
|
|
}
|
||
|
|
if portStr := strings.TrimSpace(os.Getenv("XUI_DB_PORT")); portStr != "" {
|
||
|
|
port, err := strconv.Atoi(portStr)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
cfg.Postgres.Port = port
|
||
|
|
}
|
||
|
|
if dbName := strings.TrimSpace(os.Getenv("XUI_DB_NAME")); dbName != "" {
|
||
|
|
cfg.Postgres.DBName = dbName
|
||
|
|
}
|
||
|
|
if user := strings.TrimSpace(os.Getenv("XUI_DB_USER")); user != "" {
|
||
|
|
cfg.Postgres.User = user
|
||
|
|
}
|
||
|
|
if password := os.Getenv("XUI_DB_PASSWORD"); password != "" {
|
||
|
|
cfg.Postgres.Password = password
|
||
|
|
}
|
||
|
|
if sslMode := strings.TrimSpace(os.Getenv("XUI_DB_SSLMODE")); sslMode != "" {
|
||
|
|
cfg.Postgres.SSLMode = sslMode
|
||
|
|
}
|
||
|
|
if managedLocally := strings.TrimSpace(os.Getenv("XUI_DB_MANAGED_LOCALLY")); managedLocally != "" {
|
||
|
|
value, err := strconv.ParseBool(managedLocally)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
cfg.Postgres.ManagedLocally = value
|
||
|
|
}
|
||
|
|
|
||
|
|
return cfg.Normalize(), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func GetDBConfigPath() string {
|
||
|
|
return filepath.Join(GetDBFolderPath(), "database.json")
|
||
|
|
}
|
||
|
|
|
||
|
|
func GetBackupFolderPath() string {
|
||
|
|
return filepath.Join(GetDBFolderPath(), "backups")
|
||
|
|
}
|
||
|
|
|
||
|
|
func GetPostgresManagerPath() string {
|
||
|
|
return filepath.Join(getBaseDir(), "postgres-manager.sh")
|
||
|
|
}
|
||
|
|
|
||
|
|
func LoadDatabaseConfig() (*DatabaseConfig, error) {
|
||
|
|
if HasDatabaseEnvOverride() {
|
||
|
|
return loadDatabaseConfigFromEnv()
|
||
|
|
}
|
||
|
|
|
||
|
|
configPath := GetDBConfigPath()
|
||
|
|
contents, err := os.ReadFile(configPath)
|
||
|
|
if err != nil {
|
||
|
|
if os.IsNotExist(err) {
|
||
|
|
cfg := DefaultDatabaseConfig()
|
||
|
|
cfg.ConfigSource = DatabaseConfigSourceDefault
|
||
|
|
return cfg, nil
|
||
|
|
}
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
cfg := DefaultDatabaseConfig()
|
||
|
|
if err := json.Unmarshal(contents, cfg); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
cfg.ConfigSource = DatabaseConfigSourceFile
|
||
|
|
return cfg.Normalize(), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func SaveDatabaseConfig(cfg *DatabaseConfig) error {
|
||
|
|
if HasDatabaseEnvOverride() {
|
||
|
|
return errors.New("database configuration is managed by environment variables")
|
||
|
|
}
|
||
|
|
if cfg == nil {
|
||
|
|
return errors.New("database configuration is nil")
|
||
|
|
}
|
||
|
|
|
||
|
|
normalized := cfg.Clone().Normalize()
|
||
|
|
normalized.ConfigSource = DatabaseConfigSourceFile
|
||
|
|
|
||
|
|
configPath := GetDBConfigPath()
|
||
|
|
if err := os.MkdirAll(filepath.Dir(configPath), 0o755); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
data, err := json.MarshalIndent(normalized, "", " ")
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
tempPath := configPath + ".tmp"
|
||
|
|
if err := os.WriteFile(tempPath, data, 0o600); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
return os.Rename(tempPath, configPath)
|
||
|
|
}
|