From fe4f385a2263d4b5ad2c0a0c60cfd65108b8c1e2 Mon Sep 17 00:00:00 2001 From: ze0s <43699394+zze0s@users.noreply.github.com> Date: Mon, 5 May 2025 21:15:24 +0200 Subject: [PATCH] feat(database): connect postgres via socket and read config from env _FILE secrets (#2061) * feat(database): connect postgres via socket * feat(config): read env var secrets from file * docs: explain env var secrets * refactor: generate postgres dsn --- Dockerfile | 4 +- README.md | 52 +++++++++- config.toml | 30 ++++++ docker-compose.yml | 15 ++- internal/config/config.go | 152 +++++++++++++++++------------ internal/database/database.go | 43 ++++++-- internal/database/postgres.go | 56 +++++++++++ internal/database/postgres_test.go | 67 +++++++++++++ internal/domain/config.go | 2 + 9 files changed, 345 insertions(+), 76 deletions(-) create mode 100644 internal/database/postgres_test.go diff --git a/Dockerfile b/Dockerfile index c904a76..c671be8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,8 @@ # build web FROM node:20.17.0-alpine3.20 AS web-builder -RUN corepack enable +# Update and enable Corepack +RUN npm install -g corepack@latest && \ + corepack enable WORKDIR /web diff --git a/README.md b/README.md index 8ca32fa..d789303 100644 --- a/README.md +++ b/README.md @@ -317,7 +317,7 @@ If you are not running a reverse proxy change `host` in the `config.toml` to `0. The following environment variables can be used: | Variable | Description | Default | -| -------------------------------------- | -------------------------------------------------------- | ---------------------------------------- | +|----------------------------------------|----------------------------------------------------------|------------------------------------------| | `AUTOBRR__HOST` | Listen address | `127.0.0.1` | | `AUTOBRR__PORT` | Listen port | `7474` | | `AUTOBRR__BASE_URL` | Base URL for reverse proxy | `/` | @@ -329,12 +329,16 @@ The following environment variables can be used: | `AUTOBRR__CUSTOM_DEFINITIONS` | Path to custom indexer definitions | - | | `AUTOBRR__CHECK_FOR_UPDATES` | Enable update checks | `true` | | `AUTOBRR__DATABASE_TYPE` | Database type (sqlite/postgres) | `sqlite` | +| `AUTOBRR__DATABASE_DSN` | Database connection string. Use this or individual vars | - | | `AUTOBRR__POSTGRES_HOST` | PostgreSQL host | - | | `AUTOBRR__POSTGRES_PORT` | PostgreSQL port | `5432` | | `AUTOBRR__POSTGRES_DATABASE` | PostgreSQL database name | - | +| `AUTOBRR__POSTGRES_DB` | PostgreSQL database name | - | | `AUTOBRR__POSTGRES_USER` | PostgreSQL username | - | | `AUTOBRR__POSTGRES_PASS` | PostgreSQL password | - | +| `AUTOBRR__POSTGRES_PASSWORD` | PostgreSQL password | - | | `AUTOBRR__POSTGRES_SSLMODE` | PostgreSQL SSL mode | `disable` | +| `AUTOBRR__POSTGRES_SOCKET` | PostgreSQL unix socket | - | | `AUTOBRR__POSTGRES_EXTRA_PARAMS` | Additional PostgreSQL parameters | - | | `AUTOBRR__OIDC_ENABLED` | Enable OpenID Connect authentication | `false` | | `AUTOBRR__OIDC_ISSUER` | OIDC issuer URL | - | @@ -347,6 +351,52 @@ The following environment variables can be used: | `AUTOBRR__METRICS_PORT` | Metrics listen port | `9074` | | `AUTOBRR__METRICS_BASIC_AUTH_USERS` | Metrics basic auth users | - | +#### Docker secrets + +All ENV vars have a `_FILE` option where it can read contents from a file, such as docker secrets. See the example below: + +```yaml +services: + autobrr: + image: ghcr.io/autobrr/autobrr:latest + container_name: autobrr + volumes: + - ./config:/config + ports: + - "7474:7474" + restart: unless-stopped + environment: + - AUTOBRR__DATABASE_TYPE=postgres + - AUTOBRR__POSTGRES_HOST=postgres + - AUTOBRR__POSTGRES_PORT=5432 + - AUTOBRR__POSTGRES_USER=autobrr + - AUTOBRR__POSTGRES_PASSWORD_FILE=/run/secrets/db_password + - AUTOBRR__POSTGRES_DB=autobrr + secrets: + - db_password + + postgres: + image: postgres:12.10 + container_name: postgres + volumes: + - postgres:/var/lib/postgresql/data + ports: + - "5432:5432" + environment: + - POSTGRES_USER=autobrr + - POSTGRES_PASSWORD_FILE=/run/secrets/db_password + - POSTGRES_DB=autobrr + secrets: + - db_password + +secrets: + db_password: + file: db_password.txt + +volumes: + postgres: +``` + ## Community Join our friendly and welcoming community on [Discord](https://discord.gg/WQ2eUycxyT)! Connect with fellow autobrr users, get advice, and share your experiences. Whether you're seeking help, wanting to contribute, or just looking to discuss your ideas, our community is a hub of discussion and support. We're all here to help each other out, so don't hesitate to jump in! diff --git a/config.toml b/config.toml index 83691f4..1fb0cab 100644 --- a/config.toml +++ b/config.toml @@ -69,6 +69,36 @@ checkForUpdates = true # sessionSecret = "secret-session-key" +# Database configuration +# +#databaseType = "postgres" +#databaseDSN = "postgresql://autobrr:postgres@localhost:5432/autobrr?sslmode=disable" +# +#databaseType = "sqlite" +#databaseDSN = "file:/config/data/autobrr.db" +# +# Use databaseDSN or the individual fields +# +#postgresHost = "localhost" +# +#postgresPort = 5432 +# +#postgresUser = "autobrr" +# +#postgresPass = "postgres" +# +#postgresDatabase = "autobrr" +# +#postgresSSLMode = "disable" +# +# Optional +# +#postgresSocket = "/run/socket.sock" +# +# Optional +# +#postgresExtraParams = "connect_timeout=10" + # OpenID Connect Configuration # # Enable OIDC authentication diff --git a/docker-compose.yml b/docker-compose.yml index 75d7ac1..81ded7a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,14 +1,21 @@ --- -version: "2.1" services: autobrr: - image: autobrr:dev +# image: ghcr.io/autobrr/autobrr:develop + build: + context: . + dockerfile: Dockerfile container_name: autobrr volumes: - ./config:/config ports: - "7474:7474" restart: unless-stopped +# environment: +# AUTOBRR__POSTGRES_PASSWORD_FILE: /run/secrets/db_password +# secrets: +# - db_password + postgres: image: postgres:12.10 container_name: postgres @@ -20,6 +27,7 @@ services: - POSTGRES_USER=autobrr - POSTGRES_PASSWORD=postgres - POSTGRES_DB=autobrr + test_postgres: image: postgres:12.10 container_name: autobrr_postgres_test @@ -32,6 +40,9 @@ services: - POSTGRES_PASSWORD=testdb - POSTGRES_DB=autobrr +#secrets: +# db_password: +# file: db_password.txt volumes: postgres: diff --git a/internal/config/config.go b/internal/config/config.go index c262f6e..78fcd6f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -24,6 +24,8 @@ import ( "github.com/spf13/viper" ) +var EnvVarPrefix = "AUTOBRR__" + var configTemplate = `# config.toml # Hostname / IP @@ -281,6 +283,7 @@ func (c *AppConfig) defaults() { CustomDefinitions: "", CheckForUpdates: true, DatabaseType: "sqlite", + DatabaseDSN: "", PostgresHost: "", PostgresPort: 0, PostgresDatabase: "", @@ -288,6 +291,7 @@ func (c *AppConfig) defaults() { PostgresPass: "", PostgresSSLMode: "disable", PostgresExtraParams: "", + PostgresSocket: "", ProfilingEnabled: false, ProfilingHost: "127.0.0.1", ProfilingPort: 6060, @@ -300,165 +304,187 @@ func (c *AppConfig) defaults() { } func (c *AppConfig) loadFromEnv() { - prefix := "AUTOBRR__" - - if v := os.Getenv(prefix + "HOST"); v != "" { + if v := GetEnvStr("HOST"); v != "" { c.Config.Host = v } - if v := os.Getenv(prefix + "PORT"); v != "" { - i, _ := strconv.ParseInt(v, 10, 32) - if i > 0 { - c.Config.Port = int(i) - } + if v := GetEnvInt("PORT"); v > 0 { + c.Config.Port = v } - if v := os.Getenv(prefix + "BASE_URL"); v != "" { + if v := GetEnvStr("BASE_URL"); v != "" { c.Config.BaseURL = v } - if v := os.Getenv(prefix + "BASE_URL_MODE_LEGACY"); v != "" { + if v := GetEnvStr("BASE_URL_MODE_LEGACY"); v != "" { c.Config.BaseURLModeLegacy = strings.EqualFold(strings.ToLower(v), "true") } - if v := os.Getenv(prefix + "LOG_LEVEL"); v != "" { + if v := GetEnvStr("LOG_LEVEL"); v != "" { c.Config.LogLevel = v } - if v := os.Getenv(prefix + "LOG_PATH"); v != "" { + if v := GetEnvStr("LOG_PATH"); v != "" { c.Config.LogPath = v } - if v := os.Getenv(prefix + "LOG_MAX_SIZE"); v != "" { - i, _ := strconv.ParseInt(v, 10, 32) - if i > 0 { - c.Config.LogMaxSize = int(i) - } + if v := GetEnvInt("LOG_MAX_SIZE"); v > 0 { + c.Config.LogMaxSize = v } - if v := os.Getenv(prefix + "LOG_MAX_BACKUPS"); v != "" { - i, _ := strconv.ParseInt(v, 10, 32) - if i > 0 { - c.Config.LogMaxBackups = int(i) - } + if v := GetEnvInt("LOG_MAX_BACKUPS"); v > 0 { + c.Config.LogMaxBackups = v } - if v := os.Getenv(prefix + "SESSION_SECRET"); v != "" { + if v := GetEnvStr("SESSION_SECRET"); v != "" { c.Config.SessionSecret = v } - if v := os.Getenv(prefix + "CUSTOM_DEFINITIONS"); v != "" { + if v := GetEnvStr("CUSTOM_DEFINITIONS"); v != "" { c.Config.CustomDefinitions = v } - if v := os.Getenv(prefix + "CHECK_FOR_UPDATES"); v != "" { + if v := GetEnvStr("CHECK_FOR_UPDATES"); v != "" { c.Config.CheckForUpdates = strings.EqualFold(strings.ToLower(v), "true") } - if v := os.Getenv(prefix + "DATABASE_TYPE"); v != "" { + if v := GetEnvStr("DATABASE_DSN"); v != "" { + c.Config.DatabaseDSN = v + } + + if v := GetEnvStr("DATABASE_TYPE"); v != "" { if validDatabaseType(v) { c.Config.DatabaseType = v } } - if v := os.Getenv(prefix + "DATABASE_MAX_BACKUPS"); v != "" { - i, _ := strconv.ParseInt(v, 10, 32) - if i > 0 { - c.Config.DatabaseMaxBackups = int(i) - } + if v := GetEnvInt("DATABASE_MAX_BACKUPS"); v > 0 { + c.Config.DatabaseMaxBackups = v } - if v := os.Getenv(prefix + "POSTGRES_HOST"); v != "" { + if v := GetEnvStr("POSTGRES_HOST"); v != "" { c.Config.PostgresHost = v } - if v := os.Getenv(prefix + "POSTGRES_PORT"); v != "" { - i, _ := strconv.ParseInt(v, 10, 32) - if i > 0 { - c.Config.PostgresPort = int(i) - } + if v := GetEnvInt("POSTGRES_PORT"); v > 0 { + c.Config.PostgresPort = v } - if v := os.Getenv(prefix + "POSTGRES_DATABASE"); v != "" { + if v := GetEnvStr("POSTGRES_DATABASE"); v != "" { c.Config.PostgresDatabase = v } - if v := os.Getenv(prefix + "POSTGRES_USER"); v != "" { + if v := GetEnvStr("POSTGRES_DB"); v != "" { + c.Config.PostgresDatabase = v + } + + if v := GetEnvStr("POSTGRES_USER"); v != "" { c.Config.PostgresUser = v } - if v := os.Getenv(prefix + "POSTGRES_PASS"); v != "" { + if v := GetEnvStr("POSTGRES_PASS"); v != "" { c.Config.PostgresPass = v } - if v := os.Getenv(prefix + "POSTGRES_SSLMODE"); v != "" { + if v := GetEnvStr("POSTGRES_PASSWORD"); v != "" { + c.Config.PostgresPass = v + } + + if v := GetEnvStr("POSTGRES_SSLMODE"); v != "" { c.Config.PostgresSSLMode = v } - if v := os.Getenv(prefix + "POSTGRES_EXTRA_PARAMS"); v != "" { + if v := GetEnvStr("POSTGRES_SOCKET"); v != "" { + c.Config.PostgresSocket = v + } + + if v := GetEnvStr("POSTGRES_EXTRA_PARAMS"); v != "" { c.Config.PostgresExtraParams = v } - if v := os.Getenv(prefix + "PROFILING_ENABLED"); v != "" { + if v := GetEnvStr("PROFILING_ENABLED"); v != "" { c.Config.ProfilingEnabled = strings.EqualFold(strings.ToLower(v), "true") } - if v := os.Getenv(prefix + "PROFILING_HOST"); v != "" { + if v := GetEnvStr("PROFILING_HOST"); v != "" { c.Config.ProfilingHost = v } - if v := os.Getenv(prefix + "PROFILING_PORT"); v != "" { - i, _ := strconv.ParseInt(v, 10, 32) - if i > 0 { - c.Config.ProfilingPort = int(i) - } + if v := GetEnvInt("PROFILING_PORT"); v > 0 { + c.Config.ProfilingPort = v } // OIDC Configuration - if v := os.Getenv(prefix + "OIDC_ENABLED"); v != "" { + if v := GetEnvStr("OIDC_ENABLED"); v != "" { c.Config.OIDCEnabled = strings.EqualFold(strings.ToLower(v), "true") } - if v := os.Getenv(prefix + "OIDC_ISSUER"); v != "" { + if v := GetEnvStr("OIDC_ISSUER"); v != "" { c.Config.OIDCIssuer = v } - if v := os.Getenv(prefix + "OIDC_CLIENT_ID"); v != "" { + if v := GetEnvStr("OIDC_CLIENT_ID"); v != "" { c.Config.OIDCClientID = v } - if v := os.Getenv(prefix + "OIDC_CLIENT_SECRET"); v != "" { + if v := GetEnvStr("OIDC_CLIENT_SECRET"); v != "" { c.Config.OIDCClientSecret = v } - if v := os.Getenv(prefix + "OIDC_REDIRECT_URL"); v != "" { + if v := GetEnvStr("OIDC_REDIRECT_URL"); v != "" { c.Config.OIDCRedirectURL = v } - if v := os.Getenv(prefix + "OIDC_DISABLE_BUILT_IN_LOGIN"); v != "" { + if v := GetEnvStr("OIDC_DISABLE_BUILT_IN_LOGIN"); v != "" { c.Config.OIDCDisableBuiltInLogin = strings.EqualFold(strings.ToLower(v), "true") } - if v := os.Getenv(prefix + "METRICS_ENABLED"); v != "" { + if v := GetEnvStr("METRICS_ENABLED"); v != "" { c.Config.MetricsEnabled = strings.EqualFold(strings.ToLower(v), "true") } - if v := os.Getenv(prefix + "METRICS_HOST"); v != "" { + if v := GetEnvStr("METRICS_HOST"); v != "" { c.Config.MetricsHost = v } - if v := os.Getenv(prefix + "METRICS_PORT"); v != "" { - i, _ := strconv.ParseInt(v, 10, 32) - if i > 0 { - c.Config.MetricsPort = int(i) - } + if v := GetEnvInt("METRICS_PORT"); v > 0 { + c.Config.MetricsPort = v } - if v := os.Getenv(prefix + "METRICS_BASIC_AUTH_USERS"); v != "" { + if v := GetEnvStr("METRICS_BASIC_AUTH_USERS"); v != "" { c.Config.MetricsBasicAuthUsers = v } } +func GetEnvStr(key string) string { + // first check if we have a variable with a _FILE ending + // commonly used for docker secrets and similar + if filePath := os.Getenv(EnvVarPrefix + key + "_FILE"); filePath != "" { + content, err := os.ReadFile(filePath) + if err != nil { + log.Fatalf("Could not read file: %s err: %q", filePath, err) + return "" + } + return strings.TrimSpace(string(content)) + } + + if v := os.Getenv(EnvVarPrefix + key); v != "" { + return v + } + + return "" +} + +func GetEnvInt(key string) int { + value := GetEnvStr(key) + + i, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return 0 + } + return int(i) +} + func validDatabaseType(v string) bool { valid := []string{"sqlite", "postgres"} for _, s := range valid { diff --git a/internal/database/database.go b/internal/database/database.go index 05cb802..264c9bf 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -6,8 +6,8 @@ package database import ( "context" "database/sql" - "fmt" "os" + "strings" "sync" "github.com/autobrr/autobrr/internal/domain" @@ -35,13 +35,33 @@ type DB struct { func NewDB(cfg *domain.Config, log logger.Logger) (*DB, error) { db := &DB{ - // set default placeholder for squirrel to support both sqlite and postgres + // set a default placeholder for squirrel to support both sqlite and postgres squirrel: sq.StatementBuilder.PlaceholderFormat(sq.Dollar), log: log.With().Str("module", "database").Str("type", cfg.DatabaseType).Logger(), cfg: cfg, } db.ctx, db.cancel = context.WithCancel(context.Background()) + // Check for directly configured DSN in config + if cfg.DatabaseDSN != "" { + if strings.HasPrefix(cfg.DatabaseDSN, "postgres://") || strings.HasPrefix(cfg.DatabaseDSN, "postgresql://") { + db.Driver = "postgres" + db.DSN = cfg.DatabaseDSN + return db, nil + } else if strings.HasPrefix(cfg.DatabaseDSN, "file:") || cfg.DatabaseDSN == ":memory:" || strings.HasSuffix(cfg.DatabaseDSN, ".db") { + db.Driver = "sqlite" + if strings.HasPrefix(cfg.DatabaseDSN, "file:") && strings.HasSuffix(cfg.DatabaseDSN, ".db") { + db.DSN = strings.TrimPrefix(cfg.DatabaseDSN, "file:") + } else { + db.DSN = cfg.DatabaseDSN + } + return db, nil + } + + return nil, errors.New("unsupported database DSN: %s", cfg.DatabaseDSN) + } + + // If no direct DSN is provided, build it from individual settings switch cfg.DatabaseType { case "sqlite": db.Driver = "sqlite" @@ -51,14 +71,19 @@ func NewDB(cfg *domain.Config, log logger.Logger) (*DB, error) { db.DSN = dataSourceName(cfg.ConfigPath, "autobrr.db") } case "postgres": - if cfg.PostgresHost == "" || cfg.PostgresPort == 0 || cfg.PostgresDatabase == "" { - return nil, errors.New("postgres: bad variables") - } - db.DSN = fmt.Sprintf("postgres://%v:%v@%v:%d/%v?sslmode=%v", cfg.PostgresUser, cfg.PostgresPass, cfg.PostgresHost, cfg.PostgresPort, cfg.PostgresDatabase, cfg.PostgresSSLMode) - if cfg.PostgresExtraParams != "" { - db.DSN = fmt.Sprintf("%s&%s", db.DSN, cfg.PostgresExtraParams) - } db.Driver = "postgres" + + // If no database-specific settings are provided, return an error + if cfg.PostgresDatabase == "" && cfg.DatabaseDSN == "" { + return nil, errors.New("postgres: database name is required") + } + + pgDsn, err := PostgresDSN(cfg.PostgresHost, cfg.PostgresPort, cfg.PostgresUser, cfg.PostgresPass, cfg.PostgresDatabase, cfg.PostgresSocket, cfg.PostgresSSLMode, cfg.PostgresExtraParams) + if err != nil { + return nil, errors.Wrap(err, "postgres: failed to build DSN") + } + db.DSN = pgDsn + default: return nil, errors.New("unsupported database: %v", cfg.DatabaseType) } diff --git a/internal/database/postgres.go b/internal/database/postgres.go index c5e8bba..b3c5067 100644 --- a/internal/database/postgres.go +++ b/internal/database/postgres.go @@ -5,6 +5,9 @@ package database import ( "database/sql" + "fmt" + "net" + "net/url" "github.com/autobrr/autobrr/pkg/errors" @@ -87,3 +90,56 @@ func (db *DB) migratePostgres() error { return tx.Commit() } + +// PostgresDSN build postgres dsn connect string +func PostgresDSN(host string, port int, user, pass, database, socket, sslMode, extraParams string) (string, error) { + // If no database is provided, return an error + if database == "" { + return "", errors.New("postgres: database name is required") + } + + pgDsn, err := url.Parse("postgres://") + if err != nil { + return "", errors.Wrap(err, "could not parse postgres DSN") + } + + pgDsn.Path = database + if user != "" { + pgDsn.User = url.UserPassword(user, pass) + } + queryParams := pgDsn.Query() + + // Build DSN based on the connection type (TCP vs. Unix socket) + if socket != "" { + // Unix socket connection via the host param + queryParams.Add("host", socket) + } else { + // TCP connection + if host == "" && port == 0 { + return "", errors.New("postgres: host and port are required for TCP connection") + } + if port > 0 { + pgDsn.Host = net.JoinHostPort(host, fmt.Sprintf("%d", port)) + } else { + pgDsn.Host = database + } + } + + // Add SSL mode if provided + if sslMode != "" { + queryParams.Add("sslmode", sslMode) + } + + pgDsn.RawQuery = queryParams.Encode() + + // Add any extra parameters + if extraParams != "" { + values, err := url.ParseQuery(extraParams) + if err != nil { + return "", errors.Wrap(err, "could not parse extra params") + } + pgDsn.RawQuery = fmt.Sprintf("%s&%s", pgDsn.RawQuery, values.Encode()) + } + + return pgDsn.String(), nil +} diff --git a/internal/database/postgres_test.go b/internal/database/postgres_test.go new file mode 100644 index 0000000..acb6e6c --- /dev/null +++ b/internal/database/postgres_test.go @@ -0,0 +1,67 @@ +package database + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPostgresDSN(t *testing.T) { + type args struct { + host string + port int + user string + pass string + database string + socket string + sslMode string + extraParams string + } + tests := []struct { + name string + args args + want string + }{ + { + name: "default", + args: args{ + host: "localhost", + port: 5432, + user: "postgres", + pass: "PASSWORD", + database: "postgres", + sslMode: "disable", + socket: "", + }, + want: "postgres://postgres:PASSWORD@localhost:5432/postgres?sslmode=disable", + }, + { + name: "default", + args: args{ + host: "localhost", + port: 5432, + user: "postgres", + pass: "PASSWORD", + database: "postgres", + sslMode: "disable", + extraParams: "connect_timeout=10", + socket: "", + }, + want: "postgres://postgres:PASSWORD@localhost:5432/postgres?sslmode=disable&connect_timeout=10", + }, + { + name: "default", + args: args{ + database: "postgres", + socket: "/path/to/socket", + }, + want: "postgres://postgres?host=%2Fpath%2Fto%2Fsocket", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := PostgresDSN(tt.args.host, tt.args.port, tt.args.user, tt.args.pass, tt.args.database, tt.args.socket, tt.args.sslMode, tt.args.extraParams) + assert.Equalf(t, tt.want, got, "PostgresDSN(%v, %v, %v, %v, %v, %v, %v, %v)", tt.args.host, tt.args.port, tt.args.user, tt.args.pass, tt.args.database, tt.args.socket, tt.args.sslMode, tt.args.extraParams) + }) + } +} diff --git a/internal/domain/config.go b/internal/domain/config.go index 6fc030c..badf032 100644 --- a/internal/domain/config.go +++ b/internal/domain/config.go @@ -18,6 +18,7 @@ type Config struct { CustomDefinitions string `toml:"customDefinitions"` CheckForUpdates bool `toml:"checkForUpdates"` DatabaseType string `toml:"databaseType"` + DatabaseDSN string `toml:"databaseDSN"` DatabaseMaxBackups int `toml:"databaseMaxBackups"` PostgresHost string `toml:"postgresHost"` PostgresPort int `toml:"postgresPort"` @@ -25,6 +26,7 @@ type Config struct { PostgresUser string `toml:"postgresUser"` PostgresPass string `toml:"postgresPass"` PostgresSSLMode string `toml:"postgresSSLMode"` + PostgresSocket string `toml:"postgresSocket"` PostgresExtraParams string `toml:"postgresExtraParams"` ProfilingEnabled bool `toml:"profilingEnabled"` ProfilingHost string `toml:"profilingHost"`