140 lines
3.9 KiB
Go
140 lines
3.9 KiB
Go
package config
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"os"
|
||
"strconv"
|
||
"time"
|
||
|
||
"github.com/jackc/pgx/v5/pgxpool"
|
||
"github.com/joho/godotenv"
|
||
)
|
||
|
||
// Config содержит конфигурацию приложения
|
||
type Config struct {
|
||
// Database
|
||
DatabaseURL string
|
||
|
||
// Server
|
||
ServerHost string
|
||
ServerPort string
|
||
AppEnv string
|
||
|
||
// JWT
|
||
JWTSecret string
|
||
JWTAccessTokenTTL time.Duration
|
||
JWTRefreshTokenTTL time.Duration
|
||
|
||
// CORS
|
||
CORSAllowedOrigins []string
|
||
CORSAllowedMethods []string
|
||
CORSAllowedHeaders []string
|
||
|
||
// Rate Limiting
|
||
RateLimitRequestsPerMinute int
|
||
RateLimitBurst int
|
||
|
||
// Matching Algorithm
|
||
MatchingDefaultRadiusMeters int
|
||
MatchingDefaultLimit int
|
||
}
|
||
|
||
// Load загружает конфигурацию из переменных окружения
|
||
func Load() (*Config, error) {
|
||
// Загружаем .env файл если он существует
|
||
_ = godotenv.Load()
|
||
|
||
cfg := &Config{
|
||
DatabaseURL: getDatabaseURL(),
|
||
ServerHost: getEnv("APP_HOST", "0.0.0.0"),
|
||
ServerPort: getEnv("APP_PORT", "8080"),
|
||
AppEnv: getEnv("APP_ENV", "development"),
|
||
|
||
JWTSecret: getEnv("JWT_SECRET", "change_me_to_secure_random_string_min_32_chars"),
|
||
JWTAccessTokenTTL: parseDuration(getEnv("JWT_ACCESS_TOKEN_EXPIRY", "15m")),
|
||
JWTRefreshTokenTTL: parseDuration(getEnv("JWT_REFRESH_TOKEN_EXPIRY", "168h")), // 7 days
|
||
|
||
RateLimitRequestsPerMinute: getEnvInt("RATE_LIMIT_REQUESTS_PER_MINUTE", 60),
|
||
RateLimitBurst: getEnvInt("RATE_LIMIT_BURST", 10),
|
||
|
||
MatchingDefaultRadiusMeters: getEnvInt("MATCHING_DEFAULT_RADIUS_METERS", 10000),
|
||
MatchingDefaultLimit: getEnvInt("MATCHING_DEFAULT_LIMIT", 20),
|
||
}
|
||
|
||
return cfg, nil
|
||
}
|
||
|
||
// NewDBPool создает новый пул соединений с БД
|
||
func NewDBPool(ctx context.Context, databaseURL string) (*pgxpool.Pool, error) {
|
||
config, err := pgxpool.ParseConfig(databaseURL)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to parse database URL: %w", err)
|
||
}
|
||
|
||
// Настройка пула соединений
|
||
config.MaxConns = 25
|
||
config.MinConns = 5
|
||
config.MaxConnLifetime = time.Hour
|
||
config.MaxConnIdleTime = time.Minute * 30
|
||
config.HealthCheckPeriod = time.Minute
|
||
|
||
pool, err := pgxpool.NewWithConfig(ctx, config)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to create connection pool: %w", err)
|
||
}
|
||
|
||
// Проверка соединения
|
||
if err := pool.Ping(ctx); err != nil {
|
||
return nil, fmt.Errorf("failed to ping database: %w", err)
|
||
}
|
||
|
||
return pool, nil
|
||
}
|
||
|
||
// getDatabaseURL собирает DATABASE_URL из отдельных переменных или использует готовый
|
||
func getDatabaseURL() string {
|
||
// Если задан DATABASE_URL, используем его
|
||
if url := os.Getenv("DATABASE_URL"); url != "" {
|
||
return url
|
||
}
|
||
|
||
// Иначе собираем из отдельных переменных
|
||
user := getEnv("DB_USER", "volontery")
|
||
password := getEnv("DB_PASSWORD", "volontery")
|
||
host := getEnv("DB_HOST", "localhost")
|
||
port := getEnv("DB_PORT", "5432")
|
||
name := getEnv("DB_NAME", "volontery_db")
|
||
sslmode := getEnv("DB_SSLMODE", "disable")
|
||
|
||
return fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=%s",
|
||
user, password, host, port, name, sslmode)
|
||
}
|
||
|
||
// getEnv получает переменную окружения или возвращает значение по умолчанию
|
||
func getEnv(key, defaultValue string) string {
|
||
if value := os.Getenv(key); value != "" {
|
||
return value
|
||
}
|
||
return defaultValue
|
||
}
|
||
|
||
// getEnvInt получает целочисленную переменную окружения
|
||
func getEnvInt(key string, defaultValue int) int {
|
||
if value := os.Getenv(key); value != "" {
|
||
if intVal, err := strconv.Atoi(value); err == nil {
|
||
return intVal
|
||
}
|
||
}
|
||
return defaultValue
|
||
}
|
||
|
||
// parseDuration парсит duration строку
|
||
func parseDuration(s string) time.Duration {
|
||
d, err := time.ParseDuration(s)
|
||
if err != nil {
|
||
return 15 * time.Minute // default
|
||
}
|
||
return d
|
||
}
|