/
nacre.go
129 lines (118 loc) · 3.13 KB
/
nacre.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package nacre
import (
"encoding/json"
"fmt"
"net"
"os"
"strconv"
"time"
"github.com/go-redis/redis/v9"
)
// Root is the root struct defining the nacre server dependencies.
type Root struct {
Cfg Config
Hub Hub
HTTP *HTTPServer
TCP *TCPServer
}
// DefaultServer returns a Root nacre instance with the default configuration and setup.
func DefaultServer(cfg Config) (Root, error) {
redisClient := redis.NewClient(&redis.Options{
Addr: net.JoinHostPort(cfg.Redis.Host, cfg.Redis.Port),
Password: cfg.Redis.Password,
DB: 0,
})
hub := NewRedisHub(redisClient, cfg.App.MaxRedisStreamLen, cfg.App.MaxStreamPersistence)
rateLimiter := NewInMemoryRateLimiter()
tcpServer, err := NewTCPServer(cfg.App.TCPAddr, cfg.App.BaseURL, hub, rateLimiter)
if err != nil {
return Root{}, err
}
httpServer := NewHTTPServer(cfg.App.HTTPAddr, hub, rateLimiter)
return Root{
Cfg: cfg,
Hub: hub,
HTTP: httpServer,
TCP: tcpServer,
}, nil
}
// RedisConfig exposes Redis-specific configuration options.
type RedisConfig struct {
Host string
Port string
Password string
}
// AppConfig exposes Nacre-specific configuration options.
type AppConfig struct {
TCPAddr string
HTTPAddr string
BaseURL string
MaxRedisStreamLen int
MaxStreamPersistence time.Duration
}
// Config is the root structure containing Nacre configuration.
type Config struct {
Redis RedisConfig
App AppConfig
}
// ParseConfig returns the default Nacre configuration merged with overridden configurations
// from environment variables.
func ParseConfig() (Config, error) {
cfg := Config{
Redis: RedisConfig{
Host: "localhost",
Port: "6379",
Password: "",
},
App: AppConfig{
TCPAddr: ":1337",
HTTPAddr: ":8080",
BaseURL: "http://localhost:8080",
MaxRedisStreamLen: 1_000,
MaxStreamPersistence: time.Hour * 24,
},
}
if v := os.Getenv("NACRE_TCP_ADDR"); v != "" {
cfg.App.TCPAddr = v
}
if v := os.Getenv("NACRE_HTTP_ADDR"); v != "" {
cfg.App.HTTPAddr = v
}
if v := os.Getenv("NACRE_BASE_URL"); v != "" {
cfg.App.BaseURL = v
}
if v := os.Getenv("NACRE_MAX_STREAM_LEN"); v != "" {
maxLen, err := strconv.Atoi(v)
if err != nil {
return cfg, fmt.Errorf("NACRE_MAX_STREAM_LEN invalid: %w", err)
}
cfg.App.MaxRedisStreamLen = maxLen
}
if v := os.Getenv("NACRE_MAX_STREAM_PERSISTENCE"); v != "" {
persistDur, err := time.ParseDuration(v)
if err != nil {
return cfg, fmt.Errorf("NACRE_MAX_STREAM_PERSISTENCE invalid: %w", err)
}
cfg.App.MaxStreamPersistence = persistDur
}
if v := os.Getenv("NACRE_REDIS_HOST"); v != "" {
cfg.Redis.Host = v
}
if v := os.Getenv("NACRE_REDIS_PORT"); v != "" {
cfg.Redis.Port = v
}
if v := os.Getenv("NACRE_REDIS_PASSWORD"); v != "" {
cfg.Redis.Password = v
}
return cfg, nil
}
func (c Config) String() string { return c.JSONString() }
// JSONString returns a JSON-like representation of the configuration.
func (c Config) JSONString() string {
c.Redis.Password = "**REDACTED**"
raw, err := json.MarshalIndent(c, "(cfg)", "\t")
if err != nil {
panic(err)
}
return string(raw)
}