all repos

onasty @ 312d08fc09acfbc338373ec3b6257fc6d6fb4118

a one-time notes service

onasty/internal/config/config.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
refactor(config): make config singleton (#202), 9 months ago
1
package config
2
3
import (
4
	"errors"
5
	"os"
6
	"strconv"
7
	"strings"
8
	"sync"
9
	"time"
10
)
11
12
var (
13
	instance *Config
14
	once     sync.Once
15
)
16
17
// Environment represents current app environment.
18
type Environment string
19
20
func (e Environment) IsDevMode() bool {
21
	return e == "debug" || e == "test"
22
}
23
24
type Config struct {
25
	AppEnv  Environment
26
	AppURL  string
27
	NatsURL string
28
29
	CORSAllowedOrigins []string
30
	CORSMaxAge         time.Duration
31
32
	HTTPPort            int
33
	HTTPWriteTimeout    time.Duration
34
	HTTPReadTimeout     time.Duration
35
	HTTPHeaderMaxSizeMb int
36
37
	PostgresDSN      string
38
	PasswordSalt     string
39
	NotePasswordSalt string
40
41
	RedisAddr     string
42
	RedisPassword string
43
	RedisDB       int
44
45
	CacheUsersTTL time.Duration
46
	CacheNoteTTL  time.Duration
47
48
	JwtSigningKey      string
49
	JwtAccessTokenTTL  time.Duration
50
	JwtRefreshTokenTTL time.Duration
51
52
	GoogleClientID    string
53
	GoogleSecret      string
54
	GoogleRedirectURL string
55
56
	GitHubClientID    string
57
	GitHubSecret      string
58
	GitHubRedirectURL string
59
60
	VerificationTokenTTL  time.Duration
61
	ResetPasswordTokenTTL time.Duration
62
	ChangeEmailTokenTTL   time.Duration
63
64
	MetricsEnabled bool
65
	MetricsPort    int
66
67
	LogLevel    string
68
	LogFormat   string
69
	LogShowLine bool
70
71
	RateLimiterRPS       int
72
	RateLimiterBurst     int
73
	RateLimiterTTL       time.Duration
74
	SlowRateLimiterRPS   int
75
	SlowRateLimiterBurst int
76
	SlowRateLimiterTTL   time.Duration
77
}
78
79
func NewConfig() *Config {
80
	once.Do(func() {
81
		instance = &Config{
82
			AppEnv:  Environment(getenvOrDefault("APP_ENV", "debug")),
83
			AppURL:  getenvOrDefault("APP_URL", ""),
84
			NatsURL: getenvOrDefault("NATS_URL", ""),
85
86
			CORSAllowedOrigins: strings.Split(getenvOrDefault("CORS_ALLOWED_ORIGINS", "*"), ","),
87
			CORSMaxAge:         mustParseDuration(getenvOrDefault("CORS_MAX_AGE", "12h")),
88
89
			HTTPPort:            mustGetenvOrDefaultInt("HTTP_PORT", 3000),
90
			HTTPWriteTimeout:    mustParseDuration(getenvOrDefault("HTTP_WRITE_TIMEOUT", "10s")),
91
			HTTPReadTimeout:     mustParseDuration(getenvOrDefault("HTTP_READ_TIMEOUT", "10s")),
92
			HTTPHeaderMaxSizeMb: mustGetenvOrDefaultInt("HTTP_HEADER_MAX_SIZE_MB", 1),
93
94
			PostgresDSN:      getenvOrDefault("POSTGRESQL_DSN", ""),
95
			PasswordSalt:     getenvOrDefault("PASSWORD_SALT", ""),
96
			NotePasswordSalt: getenvOrDefault("NOTE_PASSWORD_SALT", ""),
97
98
			RedisAddr:     getenvOrDefault("REDIS_ADDR", ""),
99
			RedisPassword: getenvOrDefault("REDIS_PASSWORD", ""),
100
			RedisDB:       mustGetenvOrDefaultInt(getenvOrDefault("REDIS_DB", "0"), 0),
101
102
			CacheUsersTTL: mustParseDuration(getenvOrDefault("CACHE_USERS_TTL", "1h")),
103
			CacheNoteTTL:  mustParseDuration(getenvOrDefault("CACHE_NOTE_TTL", "1h")),
104
105
			JwtSigningKey: getenvOrDefault("JWT_SIGNING_KEY", ""),
106
			JwtAccessTokenTTL: mustParseDuration(
107
				getenvOrDefault("JWT_ACCESS_TOKEN_TTL", "15m"),
108
			),
109
			JwtRefreshTokenTTL: mustParseDuration(
110
				getenvOrDefault("JWT_REFRESH_TOKEN_TTL", "24h"),
111
			),
112
113
			GoogleClientID:    getenvOrDefault("GOOGLE_CLIENTID", ""),
114
			GoogleSecret:      getenvOrDefault("GOOGLE_SECRET", ""),
115
			GoogleRedirectURL: getenvOrDefault("GOOGLE_REDIRECTURL", ""),
116
117
			GitHubClientID:    getenvOrDefault("GITHUB_CLIENTID", ""),
118
			GitHubSecret:      getenvOrDefault("GITHUB_SECRET", ""),
119
			GitHubRedirectURL: getenvOrDefault("GITHUB_REDIRECTURL", ""),
120
121
			VerificationTokenTTL: mustParseDuration(
122
				getenvOrDefault("VERIFICATION_TOKEN_TTL", "24h"),
123
			),
124
			ResetPasswordTokenTTL: mustParseDuration(
125
				getenvOrDefault("RESET_PASSWORD_TOKEN_TTL", "1h"),
126
			),
127
			ChangeEmailTokenTTL: mustParseDuration(
128
				getenvOrDefault("CHANGE_EMAIL_TOKEN_TTL", "24h"),
129
			),
130
131
			MetricsPort:    mustGetenvOrDefaultInt("METRICS_PORT", 3001),
132
			MetricsEnabled: getenvOrDefault("METRICS_ENABLED", "true") == "true",
133
134
			LogLevel:    getenvOrDefault("LOG_LEVEL", "debug"),
135
			LogFormat:   getenvOrDefault("LOG_FORMAT", "json"),
136
			LogShowLine: getenvOrDefault("LOG_SHOW_LINE", "true") == "true",
137
138
			RateLimiterRPS:       mustGetenvOrDefaultInt("RATELIMITER_RPS", 100),
139
			RateLimiterBurst:     mustGetenvOrDefaultInt("RATELIMITER_BURST", 10),
140
			RateLimiterTTL:       mustParseDuration(getenvOrDefault("RATELIMITER_TTL", "1m")),
141
			SlowRateLimiterRPS:   mustGetenvOrDefaultInt("SLOW_RATELIMITER_RPS", 2),
142
			SlowRateLimiterBurst: mustGetenvOrDefaultInt("SLOW_RATELIMITER_BURST", 2),
143
			SlowRateLimiterTTL:   mustParseDuration(getenvOrDefault("SLOW_RATELIMITER_TTL", "1m")),
144
		}
145
	})
146
	return instance
147
}
148
149
func getenvOrDefault(key, def string) string {
150
	if v, ok := os.LookupEnv(key); ok {
151
		return v
152
	}
153
	return def
154
}
155
156
func mustGetenvOrDefaultInt(key string, def int) int {
157
	if v, ok := os.LookupEnv(key); ok {
158
		r, err := strconv.Atoi(v)
159
		if err != nil {
160
			panic(err)
161
		}
162
		return r
163
	}
164
	return def
165
}
166
167
func mustParseDuration(dur string) time.Duration {
168
	d, err := time.ParseDuration(dur)
169
	if err != nil {
170
		panic(errors.Join(errors.New("cannot time.ParseDuration"), err)) //nolint:err113
171
	}
172
173
	return d
174
}