-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver.go
181 lines (151 loc) · 4.29 KB
/
server.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package EpicServer
import (
"context"
"fmt"
"net/http"
"os"
"time"
"github.com/gin-gonic/gin"
_ "github.com/joho/godotenv/autoload"
)
// Server represents the main server instance containing all necessary dependencies
type Server struct {
Config *Config
Engine *gin.Engine
Logger Logger
Hooks Hooks
PublicPaths map[string]bool
AuthConfigs map[string]*Auth
Db map[string]interface{}
Cache map[string]interface{}
srv *http.Server
cancel context.CancelFunc
}
// NewServer creates and initializes a new server instance with the provided configuration
// It applies all configurations and app layers in the order they are provided
// Panics if no secret key is set
func NewServer(p1 []Option) *Server {
// generate sensible default config
config := defaultConfig()
for _, opt := range p1 {
// loop through each option and apply whatever functionality has been defined
opt(config)
}
if len(config.SecretKey) == 0 {
panic("server secret key is required")
}
// We then define an initial setup for the server instance
s := &Server{
Config: config,
Engine: gin.New(),
Logger: defaultLogger(os.Stdout),
Db: make(map[string]interface{}),
Cache: make(map[string]interface{}),
}
s.Hooks = defaultHooks(s)
return s
}
// UpdateAppLayer allows adding new application layers to an existing server instance
func (s *Server) UpdateAppLayer(p1 []AppLayer) {
for _, opt := range p1 {
opt(s)
}
}
// Start initiates the server on the configured host and port
func (s *Server) Start() error {
// Create a new context with cancellation
ctx, cancel := context.WithCancel(context.Background())
s.cancel = cancel
s.srv = &http.Server{
Addr: fmt.Sprintf("%s:%d", s.Config.Server.Host, s.Config.Server.Port),
Handler: s.Engine,
}
go func() {
// Wait for the context to be canceled
<-ctx.Done()
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
defer shutdownCancel()
s.srv.Shutdown(shutdownCtx)
}()
// Start the server and return any errors
return s.srv.ListenAndServe()
}
// Stop gracefully stops the server
func (s *Server) Stop() error {
if s.cancel != nil {
s.cancel() // Call the cancel function to stop the server
fmt.Println("Server context canceled")
}
return nil
}
// setting up default config with sensible defaults
func defaultConfig() *Config {
c := &Config{}
c.Server.Host = "localhost"
c.Server.Port = 3000
return c
}
// set up default hooks
func defaultHooks(s *Server) Hooks {
h := Hooks{}
h.Auth = &DefaultAuthHooks{
s: s,
}
return h
}
// Need an option to provide methods that make changes to the engine
// We expose access to the underlying app layer to make changes directory to the config
// AppLayer defines a function type that can modify the server configuration
type AppLayer func(*Server)
// WithHealthCheck creates a basic health check endpoint at the specified path
// Returns 200 OK when the server is running
func WithHealthCheck(path string) AppLayer {
return func(s *Server) {
s.Engine.GET(path, func(ctx *gin.Context) {
ctx.Status(200)
})
}
}
// WithCompression adds compression middleware to the server
func WithCompression() AppLayer {
return func(s *Server) {
s.Engine.Use(CompressMiddleware)
}
}
// WithRemoveWWW adds middleware to remove the www. prefix from domain names
func WithRemoveWWW() AppLayer {
return func(s *Server) {
s.Engine.Use(RemoveWWWMiddleware())
}
}
// WithCors configures CORS settings for the specified origins
func WithCors(origins []string) AppLayer {
return func(s *Server) {
s.Engine.Use(CorsMiddleware(origins))
}
}
// WithEnvironment sets the Gin framework's running mode
// Accepts: "development", "production", or "test"
func WithEnvironment(environment string) AppLayer {
return func(s *Server) {
if environment == "development" {
gin.SetMode(gin.DebugMode)
} else if environment == "production" {
gin.SetMode(gin.ReleaseMode)
} else {
gin.SetMode(gin.TestMode)
}
}
}
// WithTrustedProxies configures the trusted proxy addresses for the server
func WithTrustedProxies(proxies []string) AppLayer {
return func(s *Server) {
s.Engine.SetTrustedProxies(proxies)
}
}
// WithHttp2 enables HTTP/2 support for the server
func WithHttp2() AppLayer {
return func(s *Server) {
s.Engine.UseH2C = true
}
}