Completed
Push — main ( d205ff...d7083f )
by Yume
24s queued 13s
created

main.setupInfrastructures   B

Complexity

Conditions 5

Size

Total Lines 39
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 28
nop 1
dl 0
loc 39
rs 8.7413
c 0
b 0
f 0
1
package main
2
3
import (
4
	"context"
5
	"fmt"
6
	"log"
7
	"log/slog"
8
	"os"
9
	"os/signal"
10
	"time"
11
12
	"github.com/bytedance/gopkg/util/gctuner"
13
	"github.com/getsentry/sentry-go"
14
	v2 "github.com/memnix/memnix-rest/app/v2"
15
	"github.com/memnix/memnix-rest/cmd/v2/config"
16
	"github.com/memnix/memnix-rest/infrastructures"
17
	"github.com/memnix/memnix-rest/pkg/crypto"
18
	"github.com/memnix/memnix-rest/pkg/json"
19
	"github.com/memnix/memnix-rest/pkg/jwt"
20
	"github.com/memnix/memnix-rest/pkg/logger"
21
	"github.com/memnix/memnix-rest/pkg/oauth"
22
	"github.com/pkg/errors"
23
)
24
25
func main() {
26
	configPath := config.GetConfigPath(config.IsDevelopment())
27
28
	cfg, err := config.LoadConfig(configPath)
29
	if err != nil {
30
		log.Fatalf("❌ Error loading config: %s", err.Error())
31
	}
32
33
	logger.GetLogger().SetLogLevel(cfg.Log.GetSlogLevel()).CreateGlobalHandler()
34
35
	setup(cfg)
36
37
	e := v2.CreateEchoInstance(cfg.Server)
38
39
	if cfg.Log.GetSlogLevel().Level() == slog.LevelDebug {
40
		slog.Debug("🔧 Debug mode enabled")
41
	}
42
43
	slog.Info("starting server 🚀", slog.String("version", cfg.Server.AppVersion))
44
45
	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
46
	defer stop()
47
48
	go func() {
49
		if err = e.Start(); err != nil {
50
			slog.Error("error starting server", slog.Any("error", err))
51
			os.Exit(1)
52
		}
53
	}()
54
55
	const shutdownTimeout = 10 * time.Second
56
57
	<-ctx.Done()
58
	_, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
59
	defer cancel()
60
61
	slog.Info("shutting down server")
62
63
	if err = shutdown(); err != nil {
64
		slog.Error("error shutting down server", slog.Any("error", err))
65
	}
66
67
	slog.Info("server stopped")
68
}
69
70
func setup(cfg *config.Config) {
71
	setupCrypto(cfg)
72
73
	setupInfrastructures(cfg)
74
75
	gcTuning()
76
77
	setupJwt(cfg)
78
79
	setupOAuth(cfg)
80
81
	slog.Info("✅ setup completed!")
82
}
83
84
func shutdown() error {
85
	e := v2.GetEchoInstance()
86
87
	slog.Info("🔒 Server shutting down...")
88
89
	var shutdownError error
90
91
	if err := e.Shutdown(context.Background()); err != nil {
92
		// Add the error to the shutdownError
93
		shutdownError = errors.Wrap(shutdownError, err.Error())
94
	}
95
96
	slog.Info("🧹 Running cleanup tasks...")
97
98
	infrastructures.GetPgxConnInstance().ClosePgx()
99
100
	slog.Info("✅ Disconnected from database")
101
102
	err := infrastructures.GetRedisManagerInstance().CloseRedis()
103
	if err != nil {
104
		shutdownError = errors.Wrap(shutdownError, err.Error())
105
	} else {
106
		slog.Info("✅ Disconnected from Redis")
107
	}
108
109
	sentry.Flush(config.SentryFlushTimeout)
110
111
	slog.Info("✅ Disconnected from Sentry")
112
113
	slog.Info("✅ Cleanup tasks completed!")
114
115
	return shutdownError
116
}
117
118
func setupJwt(cfg *config.Config) {
119
	// Parse the keys
120
	if err := crypto.GetKeyManagerInstance().ParseEd25519Key(); err != nil {
121
		log.Fatal("❌ Error parsing keys", slog.Any("error", err))
122
	}
123
124
	// Create the JWT instance
125
	jwtInstance := jwt.NewJWTInstance(cfg.Auth.JWTHeaderLen, cfg.Auth.JWTExpiration, crypto.GetKeyManagerInstance().GetPublicKey(), crypto.GetKeyManagerInstance().GetPrivateKey())
126
127
	jwt.GetJwtInstance().SetJwt(jwtInstance)
128
129
	slog.Info("✅ Created JWT instance")
130
}
131
132
func setupCrypto(cfg *config.Config) {
133
	crypto.GetCryptoHelperInstance().SetCryptoHelper(crypto.NewBcryptCrypto(cfg.Auth.Bcryptcost))
134
135
	slog.Info("✅ Created Crypto instance")
136
}
137
138
func setupOAuth(cfg *config.Config) {
139
	oauth.GetJSONHelperInstance().SetJSONHelper(json.NewJSON(&json.NativeJSON{}))
140
141
	oauthConfig := oauth.GlobalConfig{
142
		CallbackURL: cfg.Server.Host,
143
		FrontendURL: cfg.Server.FrontendURL,
144
	}
145
146
	oauth.SetOauthConfig(oauthConfig)
147
148
	oauth.InitGithub(cfg.Auth.Github)
149
	oauth.InitDiscord(cfg.Auth.Discord)
150
151
	slog.Info("✅ Created OAuth instance")
152
}
153
154
func setupInfrastructures(cfg *config.Config) {
155
	err := infrastructures.NewPgxConnInstance(infrastructures.PgxConfig{
156
		DSN: cfg.Pgx.DSN,
157
	}).ConnectPgx()
158
	if err != nil {
159
		log.Fatal("❌ Error connecting to database", slog.Any("error", err))
160
	}
161
162
	slog.Info("✅ Connected to database")
163
164
	// Redis connection
165
	err = infrastructures.NewRedisInstance(cfg.Redis).ConnectRedis()
166
	if err != nil {
167
		log.Fatal("❌ Error connecting to Redis")
168
	}
169
	slog.Info("✅ Connected to Redis")
170
171
	// Connect to the tracer
172
	err = infrastructures.NewTracerInstance(infrastructures.SentryConfig{
173
		DSN:                cfg.Sentry.DSN,
174
		Environment:        cfg.Sentry.Environment,
175
		Debug:              cfg.Sentry.Debug,
176
		Release:            cfg.Sentry.Release,
177
		TracesSampleRate:   cfg.Sentry.TracesSampleRate,
178
		ProfilesSampleRate: cfg.Sentry.ProfilesSampleRate,
179
		Name:               "fiber-rest",
180
		WithStacktrace:     false,
181
	}).ConnectTracer()
182
	if err != nil {
183
		log.Fatal("❌ Error connecting to Tracer", slog.Any("error", err))
184
	}
185
	slog.Info("✅ Created Tracer")
186
187
	ristrettoInstance := infrastructures.CreateRistrettoInstance(cfg.Ristretto)
188
189
	if err = ristrettoInstance.CreateRistrettoCache(); err != nil {
190
		log.Fatal("❌ Error creating Ristretto cache", slog.Any("error", err))
191
	}
192
	slog.Info("✅ Created Ristretto cache")
193
}
194
195
func gcTuning() {
196
	var limit float64 = 4 * config.GCLimit
197
	// Set the GC threshold to 70% of the limit
198
	threshold := uint64(limit * config.GCThresholdPercent)
199
200
	gctuner.Tuning(threshold)
201
202
	slog.Info(fmt.Sprintf("🔧 GC Tuning - Limit: %.2f GB, Threshold: %d bytes, GC Percent: %d, Min GC Percent: %d, Max GC Percent: %d",
203
		limit/(config.GCLimit),
204
		threshold,
205
		gctuner.GetGCPercent(),
206
		gctuner.GetMinGCPercent(),
207
		gctuner.GetMaxGCPercent()))
208
209
	slog.Info("✅ GC Tuning completed!")
210
}
211