feat: add support for using TLS to connect to Redis

This commit is contained in:
iBenzene
2026-01-31 00:53:39 +08:00
parent c3d1891ccd
commit f2e206700c
15 changed files with 91 additions and 22 deletions

View File

@@ -415,6 +415,8 @@ type RedisConfig struct {
PoolSize int `mapstructure:"pool_size"`
// MinIdleConns: 最小空闲连接数,保持热连接减少冷启动延迟
MinIdleConns int `mapstructure:"min_idle_conns"`
// EnableTLS: 是否启用 TLS/SSL 连接
EnableTLS bool `mapstructure:"enable_tls"`
}
func (r *RedisConfig) Address() string {
@@ -762,6 +764,7 @@ func setDefaults() {
viper.SetDefault("redis.write_timeout_seconds", 3)
viper.SetDefault("redis.pool_size", 128)
viper.SetDefault("redis.min_idle_conns", 10)
viper.SetDefault("redis.enable_tls", false)
// Ops (vNext)
viper.SetDefault("ops.enabled", true)

View File

@@ -1,6 +1,7 @@
package repository
import (
"crypto/tls"
"time"
"github.com/Wei-Shaw/sub2api/internal/config"
@@ -26,7 +27,7 @@ func InitRedis(cfg *config.Config) *redis.Client {
// buildRedisOptions 构建 Redis 连接选项
// 从配置文件读取连接池和超时参数,支持生产环境调优
func buildRedisOptions(cfg *config.Config) *redis.Options {
return &redis.Options{
opts := &redis.Options{
Addr: cfg.Redis.Address(),
Password: cfg.Redis.Password,
DB: cfg.Redis.DB,
@@ -36,4 +37,13 @@ func buildRedisOptions(cfg *config.Config) *redis.Options {
PoolSize: cfg.Redis.PoolSize, // 连接池大小
MinIdleConns: cfg.Redis.MinIdleConns, // 最小空闲连接
}
if cfg.Redis.EnableTLS {
opts.TLSConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
ServerName: cfg.Redis.Host,
}
}
return opts
}

View File

@@ -32,4 +32,16 @@ func TestBuildRedisOptions(t *testing.T) {
require.Equal(t, 4*time.Second, opts.WriteTimeout)
require.Equal(t, 100, opts.PoolSize)
require.Equal(t, 10, opts.MinIdleConns)
require.Nil(t, opts.TLSConfig)
// Test case with TLS enabled
cfgTLS := &config.Config{
Redis: config.RedisConfig{
Host: "localhost",
EnableTLS: true,
},
}
optsTLS := buildRedisOptions(cfgTLS)
require.NotNil(t, optsTLS.TLSConfig)
require.Equal(t, "localhost", optsTLS.TLSConfig.ServerName)
}

View File

@@ -149,6 +149,8 @@ func RunCLI() error {
fmt.Println(" Invalid Redis DB. Must be between 0 and 15.")
}
cfg.Redis.EnableTLS = promptConfirm(reader, "Enable Redis TLS?")
fmt.Println()
fmt.Print("Testing Redis connection... ")
if err := TestRedisConnection(&cfg.Redis); err != nil {
@@ -205,6 +207,7 @@ func RunCLI() error {
fmt.Println("── Configuration Summary ──")
fmt.Printf("Database: %s@%s:%d/%s\n", cfg.Database.User, cfg.Database.Host, cfg.Database.Port, cfg.Database.DBName)
fmt.Printf("Redis: %s:%d\n", cfg.Redis.Host, cfg.Redis.Port)
fmt.Printf("Redis TLS: %s\n", map[bool]string{true: "enabled", false: "disabled"}[cfg.Redis.EnableTLS])
fmt.Printf("Admin: %s\n", cfg.Admin.Email)
fmt.Printf("Server: :%d\n", cfg.Server.Port)
fmt.Println()

View File

@@ -176,10 +176,11 @@ func testDatabase(c *gin.Context) {
// TestRedisRequest represents Redis test request
type TestRedisRequest struct {
Host string `json:"host" binding:"required"`
Port int `json:"port" binding:"required"`
Password string `json:"password"`
DB int `json:"db"`
Host string `json:"host" binding:"required"`
Port int `json:"port" binding:"required"`
Password string `json:"password"`
DB int `json:"db"`
EnableTLS bool `json:"enable_tls"`
}
// testRedis tests Redis connection
@@ -205,10 +206,11 @@ func testRedis(c *gin.Context) {
}
cfg := &RedisConfig{
Host: req.Host,
Port: req.Port,
Password: req.Password,
DB: req.DB,
Host: req.Host,
Port: req.Port,
Password: req.Password,
DB: req.DB,
EnableTLS: req.EnableTLS,
}
if err := TestRedisConnection(cfg); err != nil {

View File

@@ -3,6 +3,7 @@ package setup
import (
"context"
"crypto/rand"
"crypto/tls"
"database/sql"
"encoding/hex"
"fmt"
@@ -79,10 +80,11 @@ type DatabaseConfig struct {
}
type RedisConfig struct {
Host string `json:"host" yaml:"host"`
Port int `json:"port" yaml:"port"`
Password string `json:"password" yaml:"password"`
DB int `json:"db" yaml:"db"`
Host string `json:"host" yaml:"host"`
Port int `json:"port" yaml:"port"`
Password string `json:"password" yaml:"password"`
DB int `json:"db" yaml:"db"`
EnableTLS bool `json:"enable_tls" yaml:"enable_tls"`
}
type AdminConfig struct {
@@ -199,11 +201,20 @@ func TestDatabaseConnection(cfg *DatabaseConfig) error {
// TestRedisConnection tests the Redis connection
func TestRedisConnection(cfg *RedisConfig) error {
rdb := redis.NewClient(&redis.Options{
opts := &redis.Options{
Addr: fmt.Sprintf("%s:%d", cfg.Host, cfg.Port),
Password: cfg.Password,
DB: cfg.DB,
})
}
if cfg.EnableTLS {
opts.TLSConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
ServerName: cfg.Host,
}
}
rdb := redis.NewClient(opts)
defer func() {
if err := rdb.Close(); err != nil {
log.Printf("failed to close redis client: %v", err)
@@ -485,10 +496,11 @@ func AutoSetupFromEnv() error {
SSLMode: getEnvOrDefault("DATABASE_SSLMODE", "disable"),
},
Redis: RedisConfig{
Host: getEnvOrDefault("REDIS_HOST", "localhost"),
Port: getEnvIntOrDefault("REDIS_PORT", 6379),
Password: getEnvOrDefault("REDIS_PASSWORD", ""),
DB: getEnvIntOrDefault("REDIS_DB", 0),
Host: getEnvOrDefault("REDIS_HOST", "localhost"),
Port: getEnvIntOrDefault("REDIS_PORT", 6379),
Password: getEnvOrDefault("REDIS_PASSWORD", ""),
DB: getEnvIntOrDefault("REDIS_DB", 0),
EnableTLS: getEnvOrDefault("REDIS_ENABLE_TLS", "false") == "true",
},
Admin: AdminConfig{
Email: getEnvOrDefault("ADMIN_EMAIL", "admin@sub2api.local"),

View File

@@ -322,6 +322,9 @@ redis:
# Database number (0-15)
# 数据库编号0-15
db: 0
# Enable TLS/SSL connection
# 是否启用 TLS/SSL 连接
enable_tls: false
# =============================================================================
# Ops Monitoring (Optional)

View File

@@ -40,6 +40,7 @@ POSTGRES_DB=sub2api
# Leave empty for no password (default for local development)
REDIS_PASSWORD=
REDIS_DB=0
REDIS_ENABLE_TLS=false
# -----------------------------------------------------------------------------
# Admin Account

View File

@@ -376,6 +376,9 @@ redis:
# Database number (0-15)
# 数据库编号0-15
db: 0
# Enable TLS/SSL connection
# 是否启用 TLS/SSL 连接
enable_tls: false
# =============================================================================
# Ops Monitoring (Optional)

View File

@@ -56,6 +56,7 @@ services:
- REDIS_PORT=${REDIS_PORT:-6379}
- REDIS_PASSWORD=${REDIS_PASSWORD:-}
- REDIS_DB=${REDIS_DB:-0}
- REDIS_ENABLE_TLS=${REDIS_ENABLE_TLS:-false}
# =======================================================================
# Admin Account (auto-created on first run)

View File

@@ -62,6 +62,7 @@ services:
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD:-}
- REDIS_DB=${REDIS_DB:-0}
- REDIS_ENABLE_TLS=${REDIS_ENABLE_TLS:-false}
# =======================================================================
# Admin Account (auto-created on first run)

View File

@@ -31,6 +31,7 @@ export interface RedisConfig {
port: number
password: string
db: number
enable_tls: boolean
}
export interface AdminConfig {

View File

@@ -69,7 +69,9 @@ export default {
port: 'Port',
password: 'Password (optional)',
database: 'Database',
passwordPlaceholder: 'Password'
passwordPlaceholder: 'Password',
enableTls: 'Enable TLS',
enableTlsHint: 'Use TLS when connecting to Redis (public CA certs)'
},
admin: {
title: 'Admin Account',

View File

@@ -66,7 +66,9 @@ export default {
port: '端口',
password: '密码(可选)',
database: '数据库',
passwordPlaceholder: '密码'
passwordPlaceholder: '密码',
enableTls: '启用 TLS',
enableTlsHint: '连接 Redis 时使用 TLS公共 CA 证书)'
},
admin: {
title: '管理员账户',

View File

@@ -91,6 +91,18 @@
</div>
</div>
<div class="flex items-center justify-between rounded-xl border border-gray-200 p-3 dark:border-dark-700">
<div>
<p class="text-sm font-medium text-gray-900 dark:text-white">
{{ t("setup.redis.enableTls") }}
</p>
<p class="text-xs text-gray-500 dark:text-dark-400">
{{ t("setup.redis.enableTlsHint") }}
</p>
</div>
<Toggle v-model="formData.redis.enable_tls" />
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="input-label">{{ t('setup.database.username') }}</label>
@@ -517,7 +529,8 @@ const formData = reactive<InstallRequest>({
host: 'localhost',
port: 6379,
password: '',
db: 0
db: 0,
enable_tls: false
},
admin: {
email: '',