Skip to content

Commit 78f97c8

Browse files
authored
refactor: split bootstrap to smaller files for better readability (#518)
* refactor: split bootstrap to smaller files for better readability * chore: rename setup routes to setup router * fix: assign configured providers to app context
1 parent 3961589 commit 78f97c8

3 files changed

Lines changed: 253 additions & 192 deletions

File tree

internal/bootstrap/app_bootstrap.go

Lines changed: 48 additions & 192 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,26 @@ import (
1313
"time"
1414
"tinyauth/internal/config"
1515
"tinyauth/internal/controller"
16-
"tinyauth/internal/middleware"
1716
"tinyauth/internal/model"
18-
"tinyauth/internal/service"
1917
"tinyauth/internal/utils"
2018

21-
"github.com/gin-gonic/gin"
2219
"github.com/rs/zerolog/log"
2320
"gorm.io/gorm"
2421
)
2522

26-
type Controller interface {
27-
SetupRoutes()
28-
}
29-
30-
type Middleware interface {
31-
Middleware() gin.HandlerFunc
32-
Init() error
33-
}
34-
35-
type Service interface {
36-
Init() error
37-
}
38-
3923
type BootstrapApp struct {
40-
config config.Config
41-
uuid string
24+
config config.Config
25+
context struct {
26+
uuid string
27+
cookieDomain string
28+
sessionCookieName string
29+
csrfCookieName string
30+
redirectCookieName string
31+
users []config.User
32+
oauthProviders map[string]config.OAuthServiceConfig
33+
configuredProviders []controller.Provider
34+
}
35+
services Services
4236
}
4337

4438
func NewBootstrapApp(config config.Config) *BootstrapApp {
@@ -55,111 +49,51 @@ func (app *BootstrapApp) Setup() error {
5549
return err
5650
}
5751

52+
app.context.users = users
53+
5854
// Get OAuth configs
5955
oauthProviders, err := utils.GetOAuthProvidersConfig(os.Environ(), os.Args, app.config.AppURL)
6056

6157
if err != nil {
6258
return err
6359
}
6460

61+
app.context.oauthProviders = oauthProviders
62+
6563
// Get cookie domain
6664
cookieDomain, err := utils.GetCookieDomain(app.config.AppURL)
6765

6866
if err != nil {
6967
return err
7068
}
7169

70+
app.context.cookieDomain = cookieDomain
71+
7272
// Cookie names
7373
appUrl, _ := url.Parse(app.config.AppURL) // Already validated
74-
uuid := utils.GenerateUUID(appUrl.Hostname())
75-
app.uuid = uuid
76-
cookieId := strings.Split(uuid, "-")[0]
77-
sessionCookieName := fmt.Sprintf("%s-%s", config.SessionCookieName, cookieId)
78-
csrfCookieName := fmt.Sprintf("%s-%s", config.CSRFCookieName, cookieId)
79-
redirectCookieName := fmt.Sprintf("%s-%s", config.RedirectCookieName, cookieId)
74+
app.context.uuid = utils.GenerateUUID(appUrl.Hostname())
75+
cookieId := strings.Split(app.context.uuid, "-")[0]
76+
app.context.sessionCookieName = fmt.Sprintf("%s-%s", config.SessionCookieName, cookieId)
77+
app.context.csrfCookieName = fmt.Sprintf("%s-%s", config.CSRFCookieName, cookieId)
78+
app.context.redirectCookieName = fmt.Sprintf("%s-%s", config.RedirectCookieName, cookieId)
8079

8180
// Dumps
8281
log.Trace().Interface("config", app.config).Msg("Config dump")
83-
log.Trace().Interface("users", users).Msg("Users dump")
84-
log.Trace().Interface("oauthProviders", oauthProviders).Msg("OAuth providers dump")
85-
log.Trace().Str("cookieDomain", cookieDomain).Msg("Cookie domain")
86-
log.Trace().Str("sessionCookieName", sessionCookieName).Msg("Session cookie name")
87-
log.Trace().Str("csrfCookieName", csrfCookieName).Msg("CSRF cookie name")
88-
log.Trace().Str("redirectCookieName", redirectCookieName).Msg("Redirect cookie name")
89-
90-
// Create configs
91-
authConfig := service.AuthServiceConfig{
92-
Users: users,
93-
OauthWhitelist: app.config.OAuthWhitelist,
94-
SessionExpiry: app.config.SessionExpiry,
95-
SecureCookie: app.config.SecureCookie,
96-
CookieDomain: cookieDomain,
97-
LoginTimeout: app.config.LoginTimeout,
98-
LoginMaxRetries: app.config.LoginMaxRetries,
99-
SessionCookieName: sessionCookieName,
100-
}
101-
102-
// Setup services
103-
var ldapService *service.LdapService
104-
105-
if app.config.LdapAddress != "" {
106-
ldapConfig := service.LdapServiceConfig{
107-
Address: app.config.LdapAddress,
108-
BindDN: app.config.LdapBindDN,
109-
BindPassword: app.config.LdapBindPassword,
110-
BaseDN: app.config.LdapBaseDN,
111-
Insecure: app.config.LdapInsecure,
112-
SearchFilter: app.config.LdapSearchFilter,
113-
}
114-
115-
ldapService = service.NewLdapService(ldapConfig)
82+
log.Trace().Interface("users", app.context.users).Msg("Users dump")
83+
log.Trace().Interface("oauthProviders", app.context.oauthProviders).Msg("OAuth providers dump")
84+
log.Trace().Str("cookieDomain", app.context.cookieDomain).Msg("Cookie domain")
85+
log.Trace().Str("sessionCookieName", app.context.sessionCookieName).Msg("Session cookie name")
86+
log.Trace().Str("csrfCookieName", app.context.csrfCookieName).Msg("CSRF cookie name")
87+
log.Trace().Str("redirectCookieName", app.context.redirectCookieName).Msg("Redirect cookie name")
11688

117-
err := ldapService.Init()
118-
119-
if err != nil {
120-
log.Warn().Err(err).Msg("Failed to initialize LDAP service, continuing without LDAP")
121-
ldapService = nil
122-
}
123-
}
124-
125-
// Bootstrap database
126-
databaseService := service.NewDatabaseService(service.DatabaseServiceConfig{
127-
DatabasePath: app.config.DatabasePath,
128-
})
129-
130-
log.Debug().Str("service", fmt.Sprintf("%T", databaseService)).Msg("Initializing service")
131-
132-
err = databaseService.Init()
89+
// Services
90+
services, err := app.initServices()
13391

13492
if err != nil {
135-
return fmt.Errorf("failed to initialize database service: %w", err)
93+
return fmt.Errorf("failed to initialize services: %w", err)
13694
}
13795

138-
database := databaseService.GetDatabase()
139-
140-
// Create services
141-
dockerService := service.NewDockerService()
142-
aclsService := service.NewAccessControlsService(dockerService)
143-
authService := service.NewAuthService(authConfig, dockerService, ldapService, database)
144-
oauthBrokerService := service.NewOAuthBrokerService(oauthProviders)
145-
146-
// Initialize services (order matters)
147-
services := []Service{
148-
dockerService,
149-
aclsService,
150-
authService,
151-
oauthBrokerService,
152-
}
153-
154-
for _, svc := range services {
155-
if svc != nil {
156-
log.Debug().Str("service", fmt.Sprintf("%T", svc)).Msg("Initializing service")
157-
err := svc.Init()
158-
if err != nil {
159-
return err
160-
}
161-
}
162-
}
96+
app.services = services
16397

16498
// Configured providers
16599
configuredProviders := make([]controller.Provider, 0)
@@ -176,7 +110,7 @@ func (app *BootstrapApp) Setup() error {
176110
return configuredProviders[i].Name < configuredProviders[j].Name
177111
})
178112

179-
if authService.UserAuthConfigured() || ldapService != nil {
113+
if services.authService.UserAuthConfigured() {
180114
configuredProviders = append(configuredProviders, controller.Provider{
181115
Name: "Username",
182116
ID: "username",
@@ -190,106 +124,27 @@ func (app *BootstrapApp) Setup() error {
190124
return fmt.Errorf("no authentication providers configured")
191125
}
192126

193-
// Create engine
194-
engine := gin.New()
195-
engine.Use(gin.Recovery())
196-
197-
if len(app.config.TrustedProxies) > 0 {
198-
err := engine.SetTrustedProxies(strings.Split(app.config.TrustedProxies, ","))
199-
200-
if err != nil {
201-
return fmt.Errorf("failed to set trusted proxies: %w", err)
202-
}
203-
}
204-
205-
// Create middlewares
206-
var middlewares []Middleware
207-
208-
contextMiddleware := middleware.NewContextMiddleware(middleware.ContextMiddlewareConfig{
209-
CookieDomain: cookieDomain,
210-
}, authService, oauthBrokerService)
211-
212-
uiMiddleware := middleware.NewUIMiddleware()
213-
zerologMiddleware := middleware.NewZerologMiddleware()
127+
app.context.configuredProviders = configuredProviders
214128

215-
middlewares = append(middlewares, contextMiddleware, uiMiddleware, zerologMiddleware)
129+
// Setup router
130+
router, err := app.setupRouter()
216131

217-
for _, middleware := range middlewares {
218-
log.Debug().Str("middleware", fmt.Sprintf("%T", middleware)).Msg("Initializing middleware")
219-
err := middleware.Init()
220-
if err != nil {
221-
return fmt.Errorf("failed to initialize middleware %T: %w", middleware, err)
222-
}
223-
engine.Use(middleware.Middleware())
224-
}
225-
226-
// Create routers
227-
mainRouter := engine.Group("")
228-
apiRouter := engine.Group("/api")
229-
230-
// Create controllers
231-
contextController := controller.NewContextController(controller.ContextControllerConfig{
232-
Providers: configuredProviders,
233-
Title: app.config.Title,
234-
AppURL: app.config.AppURL,
235-
CookieDomain: cookieDomain,
236-
ForgotPasswordMessage: app.config.ForgotPasswordMessage,
237-
BackgroundImage: app.config.BackgroundImage,
238-
OAuthAutoRedirect: app.config.OAuthAutoRedirect,
239-
DisableUIWarnings: app.config.DisableUIWarnings,
240-
}, apiRouter)
241-
242-
oauthController := controller.NewOAuthController(controller.OAuthControllerConfig{
243-
AppURL: app.config.AppURL,
244-
SecureCookie: app.config.SecureCookie,
245-
CSRFCookieName: csrfCookieName,
246-
RedirectCookieName: redirectCookieName,
247-
CookieDomain: cookieDomain,
248-
}, apiRouter, authService, oauthBrokerService)
249-
250-
proxyController := controller.NewProxyController(controller.ProxyControllerConfig{
251-
AppURL: app.config.AppURL,
252-
}, apiRouter, aclsService, authService)
253-
254-
userController := controller.NewUserController(controller.UserControllerConfig{
255-
CookieDomain: cookieDomain,
256-
}, apiRouter, authService)
257-
258-
resourcesController := controller.NewResourcesController(controller.ResourcesControllerConfig{
259-
ResourcesDir: app.config.ResourcesDir,
260-
ResourcesDisabled: app.config.DisableResources,
261-
}, mainRouter)
262-
263-
healthController := controller.NewHealthController(apiRouter)
264-
265-
// Setup routes
266-
controller := []Controller{
267-
contextController,
268-
oauthController,
269-
proxyController,
270-
userController,
271-
healthController,
272-
resourcesController,
132+
if err != nil {
133+
return fmt.Errorf("failed to setup routes: %w", err)
273134
}
274135

275-
for _, ctrl := range controller {
276-
log.Debug().Msgf("Setting up %T controller", ctrl)
277-
ctrl.SetupRoutes()
278-
}
136+
// Start DB cleanup routine
137+
log.Debug().Msg("Starting database cleanup routine")
138+
go app.dbCleanup(services.databaseService.GetDatabase())
279139

280140
// If analytics are not disabled, start heartbeat
281141
if !app.config.DisableAnalytics {
282142
log.Debug().Msg("Starting heartbeat routine")
283143
go app.heartbeat()
284144
}
285145

286-
// Start DB cleanup routine
287-
log.Debug().Msg("Starting database cleanup routine")
288-
go app.dbCleanup(database)
289-
290146
// If we have an socket path, bind to it
291147
if app.config.SocketPath != "" {
292-
// Remove existing socket file
293148
if _, err := os.Stat(app.config.SocketPath); err == nil {
294149
log.Info().Msgf("Removing existing socket file %s", app.config.SocketPath)
295150
err := os.Remove(app.config.SocketPath)
@@ -298,9 +153,8 @@ func (app *BootstrapApp) Setup() error {
298153
}
299154
}
300155

301-
// Start server with unix socket
302156
log.Info().Msgf("Starting server on unix socket %s", app.config.SocketPath)
303-
if err := engine.RunUnix(app.config.SocketPath); err != nil {
157+
if err := router.RunUnix(app.config.SocketPath); err != nil {
304158
log.Fatal().Err(err).Msg("Failed to start server")
305159
}
306160

@@ -310,7 +164,7 @@ func (app *BootstrapApp) Setup() error {
310164
// Start server
311165
address := fmt.Sprintf("%s:%d", app.config.Address, app.config.Port)
312166
log.Info().Msgf("Starting server on %s", address)
313-
if err := engine.Run(address); err != nil {
167+
if err := router.Run(address); err != nil {
314168
log.Fatal().Err(err).Msg("Failed to start server")
315169
}
316170

@@ -328,7 +182,7 @@ func (app *BootstrapApp) heartbeat() {
328182

329183
var body heartbeat
330184

331-
body.UUID = app.uuid
185+
body.UUID = app.context.uuid
332186
body.Version = config.Version
333187

334188
bodyJson, err := json.Marshal(body)
@@ -338,7 +192,9 @@ func (app *BootstrapApp) heartbeat() {
338192
return
339193
}
340194

341-
client := &http.Client{}
195+
client := &http.Client{
196+
Timeout: time.Duration(10) * time.Second, // The server should never take more than 10 seconds to respond
197+
}
342198

343199
heartbeatURL := config.ApiServer + "/v1/instances/heartbeat"
344200

0 commit comments

Comments
 (0)