diff --git a/.gitignore b/.gitignore index 0100a13..cb79b93 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,7 @@ secret* tmp # version files -internal/assets/version \ No newline at end of file +internal/assets/version + +# data directory +data \ No newline at end of file diff --git a/cmd/root.go b/cmd/root.go index 8e0245d..2b0c172 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -33,6 +33,7 @@ var rootCmd = &cobra.Command{ conf.GoogleClientSecret = utils.GetSecret(conf.GoogleClientSecret, conf.GoogleClientSecretFile) conf.GenericClientSecret = utils.GetSecret(conf.GenericClientSecret, conf.GenericClientSecretFile) + // Validate config validator := validator.New() err = validator.Struct(conf) diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 07e6e7e..f391a49 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -19,6 +19,11 @@ export default defineConfig({ changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, ""), }, + "/resources": { + target: "http://tinyauth-backend:3000/resources", + changeOrigin: true, + rewrite: (path) => path.replace(/^\/resources/, ""), + }, }, allowedHosts: true, }, diff --git a/internal/bootstrap/app_bootstrap.go b/internal/bootstrap/app_bootstrap.go index f452f25..54838d1 100644 --- a/internal/bootstrap/app_bootstrap.go +++ b/internal/bootstrap/app_bootstrap.go @@ -20,7 +20,6 @@ type Controller interface { type Middleware interface { Middleware() gin.HandlerFunc Init() error - Name() string } type Service interface { @@ -103,6 +102,7 @@ func (app *BootstrapApp) Setup() error { err := ldapService.Init() if err != nil { + log.Warn().Err(err).Msg("Failed to initialize LDAP service, continuing without LDAP") ldapService = nil } } @@ -120,6 +120,7 @@ func (app *BootstrapApp) Setup() error { for _, svc := range services { if svc != nil { + log.Debug().Str("service", fmt.Sprintf("%T", svc)).Msg("Initializing service") err := svc.Init() if err != nil { return err @@ -142,7 +143,13 @@ func (app *BootstrapApp) Setup() error { // Create engine engine := gin.New() - router := engine.Group("/api") + + if config.Version != "development" { + gin.SetMode(gin.ReleaseMode) + } + + router := engine.Group("/") + apiRouter := router.Group("/api") // Create middlewares var middlewares []Middleware @@ -151,18 +158,16 @@ func (app *BootstrapApp) Setup() error { Domain: domain, }, authService, oauthBrokerService) - uiMiddleware := middleware.NewUIMiddleware(middleware.UIMiddlewareConfig{ - ResourcesDir: app.Config.ResourcesDir, - }) + uiMiddleware := middleware.NewUIMiddleware() zerologMiddleware := middleware.NewZerologMiddleware() middlewares = append(middlewares, contextMiddleware, uiMiddleware, zerologMiddleware) for _, middleware := range middlewares { - log.Debug().Str("middleware", middleware.Name()).Msg("Initializing middleware") + log.Debug().Str("middleware", fmt.Sprintf("%T", middleware)).Msg("Initializing middleware") err := middleware.Init() if err != nil { - return fmt.Errorf("failed to initialize %s middleware: %w", middleware.Name(), err) + return fmt.Errorf("failed to initialize %s middleware: %T", middleware, err) } router.Use(middleware.Middleware()) } @@ -177,24 +182,28 @@ func (app *BootstrapApp) Setup() error { ForgotPasswordMessage: app.Config.FogotPasswordMessage, BackgroundImage: app.Config.BackgroundImage, OAuthAutoRedirect: app.Config.OAuthAutoRedirect, - }, router) + }, apiRouter) oauthController := controller.NewOAuthController(controller.OAuthControllerConfig{ AppURL: app.Config.AppURL, SecureCookie: app.Config.SecureCookie, CSRFCookieName: csrfCookieName, RedirectCookieName: redirectCookieName, - }, router, authService, oauthBrokerService) + }, apiRouter, authService, oauthBrokerService) proxyController := controller.NewProxyController(controller.ProxyControllerConfig{ AppURL: app.Config.AppURL, - }, router, dockerService, authService) + }, apiRouter, dockerService, authService) userController := controller.NewUserController(controller.UserControllerConfig{ Domain: domain, - }, router, authService) + }, apiRouter, authService) - healthController := controller.NewHealthController(router) + resourcesController := controller.NewResourcesController(controller.ResourcesControllerConfig{ + ResourcesDir: app.Config.ResourcesDir, + }, router) + + healthController := controller.NewHealthController(apiRouter) // Setup routes controller := []Controller{ @@ -203,10 +212,11 @@ func (app *BootstrapApp) Setup() error { proxyController, userController, healthController, + resourcesController, } for _, ctrl := range controller { - log.Debug().Msgf("Setting up %T routes", ctrl) + log.Debug().Msgf("Setting up %T controller", ctrl) ctrl.SetupRoutes() } diff --git a/internal/controller/health_controller.go b/internal/controller/health_controller.go index 2330fb1..842b3d3 100644 --- a/internal/controller/health_controller.go +++ b/internal/controller/health_controller.go @@ -14,6 +14,7 @@ func NewHealthController(router *gin.RouterGroup) *HealthController { func (controller *HealthController) SetupRoutes() { controller.Router.GET("/health", controller.healthHandler) + controller.Router.HEAD("/health", controller.healthHandler) } func (controller *HealthController) healthHandler(c *gin.Context) { diff --git a/internal/controller/resources_controller.go b/internal/controller/resources_controller.go new file mode 100644 index 0000000..f0c2009 --- /dev/null +++ b/internal/controller/resources_controller.go @@ -0,0 +1,32 @@ +package controller + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +type ResourcesControllerConfig struct { + ResourcesDir string +} + +type ResourcesController struct { + Config ResourcesControllerConfig + Router *gin.RouterGroup +} + +func NewResourcesController(config ResourcesControllerConfig, router *gin.RouterGroup) *ResourcesController { + return &ResourcesController{ + Config: config, + Router: router, + } +} + +func (controller *ResourcesController) SetupRoutes() { + controller.Router.GET("/resources/*resource", controller.resourcesHandler) +} + +func (controller *ResourcesController) resourcesHandler(c *gin.Context) { + fileServer := http.StripPrefix("/resources", http.FileServer(http.Dir(controller.Config.ResourcesDir))) + fileServer.ServeHTTP(c.Writer, c.Request) +} diff --git a/internal/middleware/context_middleware.go b/internal/middleware/context_middleware.go index a83d465..62f2a64 100644 --- a/internal/middleware/context_middleware.go +++ b/internal/middleware/context_middleware.go @@ -32,10 +32,6 @@ func (m *ContextMiddleware) Init() error { return nil } -func (m *ContextMiddleware) Name() string { - return "ContextMiddleware" -} - func (m *ContextMiddleware) Middleware() gin.HandlerFunc { return func(c *gin.Context) { cookie, err := m.Auth.GetSessionCookie(c) diff --git a/internal/middleware/ui_middleware.go b/internal/middleware/ui_middleware.go index 0ce139b..6c03e4f 100644 --- a/internal/middleware/ui_middleware.go +++ b/internal/middleware/ui_middleware.go @@ -4,28 +4,19 @@ import ( "io/fs" "net/http" "os" - "path/filepath" "strings" "tinyauth/internal/assets" "github.com/gin-gonic/gin" ) -type UIMiddlewareConfig struct { - ResourcesDir string -} - type UIMiddleware struct { - Config UIMiddlewareConfig - UIFS fs.FS - UIFileServer http.Handler - ResourcesFileServer http.Handler + UIFS fs.FS + UIFileServer http.Handler } -func NewUIMiddleware(config UIMiddlewareConfig) *UIMiddleware { - return &UIMiddleware{ - Config: config, - } +func NewUIMiddleware() *UIMiddleware { + return &UIMiddleware{} } func (m *UIMiddleware) Init() error { @@ -37,15 +28,10 @@ func (m *UIMiddleware) Init() error { m.UIFS = ui m.UIFileServer = http.FileServer(http.FS(ui)) - m.ResourcesFileServer = http.FileServer(http.Dir(m.Config.ResourcesDir)) return nil } -func (m *UIMiddleware) Name() string { - return "UIMiddleware" -} - func (m *UIMiddleware) Middleware() gin.HandlerFunc { return func(c *gin.Context) { switch strings.Split(c.Request.URL.Path, "/")[1] { @@ -53,24 +39,7 @@ func (m *UIMiddleware) Middleware() gin.HandlerFunc { c.Next() return case "resources": - requestFilePath := m.Config.ResourcesDir + strings.TrimPrefix(c.Request.URL.Path, "/resources/") - - if !filepath.IsLocal(requestFilePath) { - c.Status(404) - c.Abort() - return - } - - _, err := os.Stat(requestFilePath) - - if os.IsNotExist(err) { - c.Status(404) - c.Abort() - return - } - - m.ResourcesFileServer.ServeHTTP(c.Writer, c.Request) - c.Abort() + c.Next() return default: _, err := fs.Stat(m.UIFS, strings.TrimPrefix(c.Request.URL.Path, "/")) diff --git a/internal/middleware/zerolog_middleware.go b/internal/middleware/zerolog_middleware.go index 79c5d70..95f5821 100644 --- a/internal/middleware/zerolog_middleware.go +++ b/internal/middleware/zerolog_middleware.go @@ -26,10 +26,6 @@ func (m *ZerologMiddleware) Init() error { return nil } -func (m *ZerologMiddleware) Name() string { - return "ZerologMiddleware" -} - func (m *ZerologMiddleware) logPath(path string) bool { for _, prefix := range loggerSkipPathsPrefix { if strings.HasPrefix(path, prefix) { diff --git a/internal/utils/other_utils.go b/internal/utils/app_utils.go similarity index 81% rename from internal/utils/other_utils.go rename to internal/utils/app_utils.go index 1716725..1ed8d4c 100644 --- a/internal/utils/other_utils.go +++ b/internal/utils/app_utils.go @@ -7,7 +7,6 @@ import ( "tinyauth/internal/config" "github.com/gin-gonic/gin" - "github.com/traefik/paerser/parser" "github.com/rs/zerolog" ) @@ -39,17 +38,6 @@ func ParseFileToLine(content string) string { return strings.Join(users, ",") } -func GetLabels(labels map[string]string) (config.Labels, error) { - var labelsParsed config.Labels - - err := parser.Decode(labels, &labelsParsed, "tinyauth", "tinyauth.users", "tinyauth.allowed", "tinyauth.headers", "tinyauth.domain", "tinyauth.basic", "tinyauth.oauth", "tinyauth.ip") - if err != nil { - return config.Labels{}, err - } - - return labelsParsed, nil -} - func Filter[T any](slice []T, test func(T) bool) (res []T) { for _, value := range slice { if test(value) { diff --git a/internal/utils/header_utils.go b/internal/utils/label_utils.go similarity index 63% rename from internal/utils/header_utils.go rename to internal/utils/label_utils.go index 1192de5..a01685b 100644 --- a/internal/utils/header_utils.go +++ b/internal/utils/label_utils.go @@ -2,8 +2,22 @@ package utils import ( "strings" + "tinyauth/internal/config" + + "github.com/traefik/paerser/parser" ) +func GetLabels(labels map[string]string) (config.Labels, error) { + var labelsParsed config.Labels + + err := parser.Decode(labels, &labelsParsed, "tinyauth", "tinyauth.users", "tinyauth.allowed", "tinyauth.headers", "tinyauth.domain", "tinyauth.basic", "tinyauth.oauth", "tinyauth.ip") + if err != nil { + return config.Labels{}, err + } + + return labelsParsed, nil +} + func ParseHeaders(headers []string) map[string]string { headerMap := make(map[string]string) for _, header := range headers { diff --git a/internal/utils/sec_utils.go b/internal/utils/security_utils.go similarity index 100% rename from internal/utils/sec_utils.go rename to internal/utils/security_utils.go diff --git a/main.go b/main.go index 27792d8..eac789e 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,6 @@ import ( ) func main() { - log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}).With().Timestamp().Logger().Level(zerolog.FatalLevel) + log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}).With().Timestamp().Caller().Logger().Level(zerolog.FatalLevel) cmd.Execute() }