From 1bd62e8feb3a0cbfd513c8d39473baa5313553bb Mon Sep 17 00:00:00 2001 From: charlieJ107 <42380833+charlieJ107@users.noreply.github.com> Date: Mon, 23 Jun 2025 10:12:20 +0100 Subject: [PATCH] [Feature](database): Add Support for SSL Connections and Database URL Configuration (#2540) * feat(database): add support for SSL connections and database URL configuration * feat(config): update Redis configuration to use TLS in configurre name instead of SSL * fix(database): remove default values for DatabaseURL and SSLMode in DatabaseConfig * chore(.gitignore): add cloudreve built binary to ignore list --- .gitignore | 4 +- application/dependency/dependency.go | 6 +-- inventory/client.go | 79 ++++++++++++++++------------ pkg/cache/redis.go | 18 ++++--- pkg/conf/conf.go | 5 +- pkg/conf/types.go | 35 +++++++----- 6 files changed, 85 insertions(+), 62 deletions(-) diff --git a/.gitignore b/.gitignore index 3b2e45c..d8a123e 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,6 @@ conf/conf.ini dist/ data/ -tmp/ \ No newline at end of file +tmp/ +.devcontainer/ +cloudreve diff --git a/application/dependency/dependency.go b/application/dependency/dependency.go index 300fb8a..70d8514 100644 --- a/application/dependency/dependency.go +++ b/application/dependency/dependency.go @@ -329,11 +329,7 @@ func (d *dependency) KV() cache.Driver { d.kv = cache.NewRedisStore( d.Logger(), 10, - config.Network, - config.Server, - config.User, - config.Password, - config.DB, + config, ) } else { d.kv = cache.NewMemoStore(util.DataPath(cache.DefaultCacheFile), d.Logger()) diff --git a/inventory/client.go b/inventory/client.go index 6e3d293..5242053 100644 --- a/inventory/client.go +++ b/inventory/client.go @@ -58,45 +58,56 @@ func NewRawEntClient(l logging.Logger, config conf.ConfigProvider) (*ent.Client, client *sql.Driver ) - switch confDBType { - case conf.SQLiteDB: - dbFile := util.RelativePath(dbConfig.DBFile) - l.Info("Connect to SQLite database %q.", dbFile) - client, err = sql.Open("sqlite3", util.RelativePath(dbConfig.DBFile)) - case conf.PostgresDB: - l.Info("Connect to Postgres database %q.", dbConfig.Host) - client, err = sql.Open("postgres", fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=disable", - dbConfig.Host, - dbConfig.User, - dbConfig.Password, - dbConfig.Name, - dbConfig.Port)) - case conf.MySqlDB, conf.MsSqlDB: - l.Info("Connect to MySQL/SQLServer database %q.", dbConfig.Host) - var host string - if dbConfig.UnixSocket { - host = fmt.Sprintf("unix(%s)", - dbConfig.Host) - } else { - host = fmt.Sprintf("(%s:%d)", + // Check if the database type is supported. + if confDBType != conf.SQLiteDB && confDBType != conf.MySqlDB && confDBType != conf.PostgresDB { + return nil, fmt.Errorf("unsupported database type: %s", confDBType) + } + // If Database connection string provided, use it directly. + if dbConfig.DatabaseURL != "" { + l.Info("Connect to database with connection string %q.", dbConfig.DatabaseURL) + client, err = sql.Open(string(confDBType), dbConfig.DatabaseURL) + } else { + + switch confDBType { + case conf.SQLiteDB: + dbFile := util.RelativePath(dbConfig.DBFile) + l.Info("Connect to SQLite database %q.", dbFile) + client, err = sql.Open("sqlite3", util.RelativePath(dbConfig.DBFile)) + case conf.PostgresDB: + l.Info("Connect to Postgres database %q.", dbConfig.Host) + client, err = sql.Open("postgres", fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=allow", dbConfig.Host, - dbConfig.Port) + dbConfig.User, + dbConfig.Password, + dbConfig.Name, + dbConfig.Port)) + case conf.MySqlDB, conf.MsSqlDB: + l.Info("Connect to MySQL/SQLServer database %q.", dbConfig.Host) + var host string + if dbConfig.UnixSocket { + host = fmt.Sprintf("unix(%s)", + dbConfig.Host) + } else { + host = fmt.Sprintf("(%s:%d)", + dbConfig.Host, + dbConfig.Port) + } + + client, err = sql.Open(string(confDBType), fmt.Sprintf("%s:%s@%s/%s?charset=%s&parseTime=True&loc=Local", + dbConfig.User, + dbConfig.Password, + host, + dbConfig.Name, + dbConfig.Charset)) + default: + return nil, fmt.Errorf("unsupported database type %q", confDBType) } - client, err = sql.Open(string(confDBType), fmt.Sprintf("%s:%s@%s/%s?charset=%s&parseTime=True&loc=Local", - dbConfig.User, - dbConfig.Password, - host, - dbConfig.Name, - dbConfig.Charset)) - default: - return nil, fmt.Errorf("unsupported database type %q", confDBType) - } + if err != nil { + return nil, fmt.Errorf("failed to open database: %w", err) + } - if err != nil { - return nil, fmt.Errorf("failed to open database: %w", err) } - // Set connection pool db := client.DB() db.SetMaxIdleConns(50) diff --git a/pkg/cache/redis.go b/pkg/cache/redis.go index 0cdee1a..d564768 100644 --- a/pkg/cache/redis.go +++ b/pkg/cache/redis.go @@ -3,10 +3,12 @@ package cache import ( "bytes" "encoding/gob" - "github.com/cloudreve/Cloudreve/v4/pkg/logging" "strconv" "time" + "github.com/cloudreve/Cloudreve/v4/pkg/conf" + "github.com/cloudreve/Cloudreve/v4/pkg/logging" + "github.com/gomodule/redigo/redis" ) @@ -44,7 +46,7 @@ func deserializer(value []byte) (any, error) { } // NewRedisStore 创建新的redis存储 -func NewRedisStore(l logging.Logger, size int, network, address, user, password, database string) *RedisStore { +func NewRedisStore(l logging.Logger, size int, redisConfig *conf.Redis) *RedisStore { return &RedisStore{ pool: &redis.Pool{ MaxIdle: size, @@ -54,17 +56,19 @@ func NewRedisStore(l logging.Logger, size int, network, address, user, password, return err }, Dial: func() (redis.Conn, error) { - db, err := strconv.Atoi(database) + db, err := strconv.Atoi(redisConfig.DB) if err != nil { return nil, err } c, err := redis.Dial( - network, - address, + redisConfig.Network, + redisConfig.Server, redis.DialDatabase(db), - redis.DialPassword(password), - redis.DialUsername(user), + redis.DialPassword(redisConfig.Password), + redis.DialUsername(redisConfig.User), + redis.DialUseTLS(redisConfig.UseTLS), + redis.DialTLSSkipVerify(redisConfig.TLSSkipVerify), ) if err != nil { l.Panic("Failed to create Redis connection: %s", err) diff --git a/pkg/conf/conf.go b/pkg/conf/conf.go index 78e770b..372f68e 100644 --- a/pkg/conf/conf.go +++ b/pkg/conf/conf.go @@ -2,12 +2,13 @@ package conf import ( "fmt" + "os" + "strings" + "github.com/cloudreve/Cloudreve/v4/pkg/logging" "github.com/cloudreve/Cloudreve/v4/pkg/util" "github.com/go-ini/ini" "github.com/go-playground/validator/v10" - "os" - "strings" ) const ( diff --git a/pkg/conf/types.go b/pkg/conf/types.go index 32e2e4f..285278f 100644 --- a/pkg/conf/types.go +++ b/pkg/conf/types.go @@ -24,6 +24,10 @@ type Database struct { Port int Charset string UnixSocket bool + // 允许直接使用DATABASE_URL来配置数据库连接 + DatabaseURL string + // SSLMode 允许使用SSL连接数据库, 用户可以在sslmode string中添加证书等配置 + SSLMode string } type SysMode string @@ -65,11 +69,13 @@ type Slave struct { // Redis 配置 type Redis struct { - Network string - Server string - User string - Password string - DB string + Network string + Server string + User string + Password string + DB string + UseTLS bool + TLSSkipVerify bool } // 跨域配置 @@ -85,18 +91,21 @@ type Cors struct { // RedisConfig Redis服务器配置 var RedisConfig = &Redis{ - Network: "tcp", - Server: "", - Password: "", - DB: "0", + Network: "tcp", + Server: "", + Password: "", + DB: "0", + UseTLS: false, + TLSSkipVerify: true, } // DatabaseConfig 数据库配置 var DatabaseConfig = &Database{ - Charset: "utf8mb4", - DBFile: util.DataPath("cloudreve.db"), - Port: 3306, - UnixSocket: false, + Charset: "utf8mb4", + DBFile: util.DataPath("cloudreve.db"), + Port: 3306, + UnixSocket: false, + DatabaseURL: "", } // SystemConfig 系统公用配置