buhapp-backend/internal/config/config.go
ga c30faf02ef Sprint 6: security hardening
1. CORS whitelist (was '*'): only buhapp.mygoodservice.ru, t.me, web.telegram.org
2. Rate limit on /auth/login (10/min/IP) and /auth/register (5/hour/IP)
3. TGHandler: removed unused JWTSecret, added WebhookSecret, real secret check
4. Login: constant-time bcrypt on user enumeration (dummy hash)
5. TgUsername saved in users (was lost)
6. User.IsVerified=true for Telegram users
7. Register: 409 instead of 500 on duplicate email
8. Bumped version to 0.4.0
2026-08-20 20:42:22 +00:00

133 lines
3.3 KiB
Go

package config
import (
"fmt"
"os"
"strconv"
"time"
"github.com/joho/godotenv"
)
type Config struct {
AppEnv string
AppPort int
AppURL string
PostgresHost string
PostgresPort int
PostgresUser string
PostgresPassword string
PostgresDB string
PostgresSSLMode string
RedisHost string
RedisPort int
RedisPassword string
RedisDB int
MinIOEndpoint string
MinIOAccessKey string
MinIOSecretKey string
MinIOBucket string
MinIOUseSSL bool
JWTSecret string
JWTAccessTTL time.Duration
JWTRefreshTTL time.Duration
TermsVersion string
PrivacyVersion string
DisclaimerVersion string
// Telegram
TelegramBotToken string
TelegramWebhookURL string
TelegramWebhookSecret string
}
func Load() (*Config, error) {
_ = godotenv.Load()
cfg := &Config{
AppEnv: getEnv("APP_ENV", "development"),
AppPort: getEnvInt("APP_PORT", 8080),
AppURL: getEnv("APP_URL", "http://localhost:8080"),
PostgresHost: getEnv("POSTGRES_HOST", "localhost"),
PostgresPort: getEnvInt("POSTGRES_PORT", 5432),
PostgresUser: getEnv("POSTGRES_USER", "buhapp"),
PostgresPassword: getEnv("POSTGRES_PASSWORD", "buhapp_secret"),
PostgresDB: getEnv("POSTGRES_DB", "buhapp"),
PostgresSSLMode: getEnv("POSTGRES_SSLMODE", "disable"),
RedisHost: getEnv("REDIS_HOST", "localhost"),
RedisPort: getEnvInt("REDIS_PORT", 6379),
RedisPassword: getEnv("REDIS_PASSWORD", ""),
RedisDB: getEnvInt("REDIS_DB", 0),
MinIOEndpoint: getEnv("MINIO_ENDPOINT", "localhost:9000"),
MinIOAccessKey: getEnv("MINIO_ACCESS_KEY", "buhapp_minio"),
MinIOSecretKey: getEnv("MINIO_SECRET_KEY", "buhapp_minio_secret"),
MinIOBucket: getEnv("MINIO_BUCKET", "buhapp"),
MinIOUseSSL: getEnvBool("MINIO_USE_SSL", false),
JWTSecret: getEnv("JWT_SECRET", "change-me"),
JWTAccessTTL: time.Duration(getEnvInt("JWT_ACCESS_TTL", 3600)) * time.Second,
JWTRefreshTTL: time.Duration(getEnvInt("JWT_REFRESH_TTL", 2592000)) * time.Second,
TermsVersion: getEnv("TERMS_VERSION", "1.0"),
PrivacyVersion: getEnv("PRIVACY_VERSION", "1.0"),
DisclaimerVersion: getEnv("DISCLAIMER_VERSION", "1.0"),
TelegramBotToken: getEnv("TELEGRAM_BOT_TOKEN", ""),
TelegramWebhookURL: getEnv("TELEGRAM_WEBHOOK_URL", ""),
TelegramWebhookSecret: getEnv("TELEGRAM_WEBHOOK_SECRET", ""),
}
if cfg.JWTSecret == "change-me" {
return nil, fmt.Errorf("JWT_SECRET must be set")
}
return cfg, nil
}
func (c *Config) PostgresDSN() string {
return fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s",
c.PostgresUser, c.PostgresPassword,
c.PostgresHost, c.PostgresPort,
c.PostgresDB, c.PostgresSSLMode)
}
func (c *Config) RedisAddr() string {
return fmt.Sprintf("%s:%d", c.RedisHost, c.RedisPort)
}
func (c *Config) JwTAccessTTL() time.Duration { return c.JWTAccessTTL }
func (c *Config) JwtRefreshTTL() time.Duration { return c.JWTRefreshTTL }
func getEnv(key, def string) string {
if v, ok := os.LookupEnv(key); ok {
return v
}
return def
}
func getEnvInt(key string, def int) int {
if v, ok := os.LookupEnv(key); ok {
if n, err := strconv.Atoi(v); err == nil {
return n
}
}
return def
}
func getEnvBool(key string, def bool) bool {
if v, ok := os.LookupEnv(key); ok {
if b, err := strconv.ParseBool(v); err == nil {
return b
}
}
return def
}