Passed
Push — main ( 35bff0...003d10 )
by Yume
03:29 queued 01:21
created

main.setupInfrastructures   A

Complexity

Conditions 4

Size

Total Lines 23
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

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