diff --git a/backend/internal/setup/setup.go b/backend/internal/setup/setup.go index 51baf3dfe6..a2c4e2847a 100644 --- a/backend/internal/setup/setup.go +++ b/backend/internal/setup/setup.go @@ -28,6 +28,7 @@ const ( InstallLockFile = ".installed" defaultUserConcurrency = 5 simpleModeAdminConcurrency = 30 + defaultMigrationTimeout = 60 * time.Second ) func setupDefaultAdminConcurrency() int { @@ -73,12 +74,13 @@ func GetInstallLockPath() string { // SetupConfig holds the setup configuration type SetupConfig struct { - Database DatabaseConfig `json:"database" yaml:"database"` - Redis RedisConfig `json:"redis" yaml:"redis"` - Admin AdminConfig `json:"admin" yaml:"-"` // Not stored in config file - Server ServerConfig `json:"server" yaml:"server"` - JWT JWTConfig `json:"jwt" yaml:"jwt"` - Timezone string `json:"timezone" yaml:"timezone"` // e.g. "Asia/Shanghai", "UTC" + Database DatabaseConfig `json:"database" yaml:"database"` + Redis RedisConfig `json:"redis" yaml:"redis"` + Admin AdminConfig `json:"admin" yaml:"-"` // Not stored in config file + Server ServerConfig `json:"server" yaml:"server"` + JWT JWTConfig `json:"jwt" yaml:"jwt"` + Timezone string `json:"timezone" yaml:"timezone"` // e.g. "Asia/Shanghai", "UTC" + MigrationTimeoutSeconds int `json:"migration_timeout_seconds" yaml:"migration_timeout_seconds,omitempty"` } type DatabaseConfig struct { @@ -350,11 +352,18 @@ func initializeDatabase(cfg *SetupConfig) error { } }() - migrationCtx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + migrationCtx, cancel := context.WithTimeout(context.Background(), cfg.migrationTimeout()) defer cancel() return repository.ApplyMigrations(migrationCtx, db) } +func (cfg *SetupConfig) migrationTimeout() time.Duration { + if cfg != nil && cfg.MigrationTimeoutSeconds > 0 { + return time.Duration(cfg.MigrationTimeoutSeconds) * time.Second + } + return defaultMigrationTimeout +} + func createAdminUser(cfg *SetupConfig) (bool, string, error) { dsn := fmt.Sprintf( "host=%s port=%d user=%s password=%s dbname=%s sslmode=%s", @@ -578,7 +587,8 @@ func AutoSetupFromEnv() error { Secret: getEnvOrDefault("JWT_SECRET", ""), ExpireHour: getEnvIntOrDefault("JWT_EXPIRE_HOUR", 24), }, - Timezone: tz, + Timezone: tz, + MigrationTimeoutSeconds: getEnvIntOrDefault("SETUP_MIGRATION_TIMEOUT_SECONDS", 0), } // Generate JWT secret if not provided diff --git a/backend/internal/setup/setup_test.go b/backend/internal/setup/setup_test.go index a2aa2f4cc1..b95c162bda 100644 --- a/backend/internal/setup/setup_test.go +++ b/backend/internal/setup/setup_test.go @@ -4,6 +4,7 @@ import ( "os" "strings" "testing" + "time" ) func TestDecideAdminBootstrap(t *testing.T) { @@ -70,6 +71,22 @@ func TestSetupDefaultAdminConcurrency(t *testing.T) { }) } +func TestSetupMigrationTimeout(t *testing.T) { + t.Run("uses default timeout when unset", func(t *testing.T) { + cfg := &SetupConfig{} + if got := cfg.migrationTimeout(); got != 60*time.Second { + t.Fatalf("migrationTimeout()=%s, want 60s", got) + } + }) + + t.Run("uses configured timeout", func(t *testing.T) { + cfg := &SetupConfig{MigrationTimeoutSeconds: 300} + if got := cfg.migrationTimeout(); got != 300*time.Second { + t.Fatalf("migrationTimeout()=%s, want 300s", got) + } + }) +} + func TestWriteConfigFileKeepsDefaultUserConcurrency(t *testing.T) { t.Setenv("RUN_MODE", "simple") t.Setenv("DATA_DIR", t.TempDir()) diff --git a/deploy/.env.example b/deploy/.env.example index 59e4b44b91..d8892dbecf 100644 --- a/deploy/.env.example +++ b/deploy/.env.example @@ -196,6 +196,13 @@ JWT_EXPIRE_HOUR=24 # - =0: 回退使用 JWT_EXPIRE_HOUR JWT_ACCESS_TOKEN_EXPIRE_MINUTES=0 +# ----------------------------------------------------------------------------- +# Setup Configuration +# ----------------------------------------------------------------------------- +# Database migration timeout during initial setup, in seconds. +# Leave 0 to use the built-in default of 60 seconds. +SETUP_MIGRATION_TIMEOUT_SECONDS=0 + # ----------------------------------------------------------------------------- # TOTP (2FA) Configuration # TOTP(双因素认证)配置 diff --git a/deploy/docker-compose.dev.yml b/deploy/docker-compose.dev.yml index 7755fdbeff..07e89e0b51 100644 --- a/deploy/docker-compose.dev.yml +++ b/deploy/docker-compose.dev.yml @@ -38,6 +38,7 @@ services: - ADMIN_EMAIL=${ADMIN_EMAIL:-admin@sub2api.local} - ADMIN_PASSWORD=${ADMIN_PASSWORD:-} - JWT_SECRET=${JWT_SECRET:-} + - SETUP_MIGRATION_TIMEOUT_SECONDS=${SETUP_MIGRATION_TIMEOUT_SECONDS:-0} - TOTP_ENCRYPTION_KEY=${TOTP_ENCRYPTION_KEY:-} - TZ=${TZ:-Asia/Shanghai} # OpenAI HTTP upstream protocol/timeout diff --git a/deploy/docker-compose.local.yml b/deploy/docker-compose.local.yml index b15be2402d..21f46e3760 100644 --- a/deploy/docker-compose.local.yml +++ b/deploy/docker-compose.local.yml @@ -94,6 +94,11 @@ services: - JWT_SECRET=${JWT_SECRET:-} - JWT_EXPIRE_HOUR=${JWT_EXPIRE_HOUR:-24} + # ======================================================================= + # Setup Configuration + # ======================================================================= + - SETUP_MIGRATION_TIMEOUT_SECONDS=${SETUP_MIGRATION_TIMEOUT_SECONDS:-0} + # ======================================================================= # TOTP (2FA) Configuration # ======================================================================= diff --git a/deploy/docker-compose.standalone.yml b/deploy/docker-compose.standalone.yml index 32afb28d6c..2e1d335624 100644 --- a/deploy/docker-compose.standalone.yml +++ b/deploy/docker-compose.standalone.yml @@ -76,6 +76,11 @@ services: - JWT_SECRET=${JWT_SECRET:-} - JWT_EXPIRE_HOUR=${JWT_EXPIRE_HOUR:-24} + # ======================================================================= + # Setup Configuration + # ======================================================================= + - SETUP_MIGRATION_TIMEOUT_SECONDS=${SETUP_MIGRATION_TIMEOUT_SECONDS:-0} + # ======================================================================= # Timezone Configuration # ======================================================================= diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index fd682a87e6..3da3bcbfb0 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -90,6 +90,11 @@ services: - JWT_SECRET=${JWT_SECRET:-} - JWT_EXPIRE_HOUR=${JWT_EXPIRE_HOUR:-24} + # ======================================================================= + # Setup Configuration + # ======================================================================= + - SETUP_MIGRATION_TIMEOUT_SECONDS=${SETUP_MIGRATION_TIMEOUT_SECONDS:-0} + # ======================================================================= # TOTP (2FA) Configuration # =======================================================================