refactor: move resource handling to a controller

This commit is contained in:
Stavros
2025-08-26 12:22:10 +03:00
parent 03af18fd15
commit 645c555cf0
13 changed files with 86 additions and 71 deletions

5
.gitignore vendored
View File

@@ -23,4 +23,7 @@ secret*
tmp tmp
# version files # version files
internal/assets/version internal/assets/version
# data directory
data

View File

@@ -33,6 +33,7 @@ var rootCmd = &cobra.Command{
conf.GoogleClientSecret = utils.GetSecret(conf.GoogleClientSecret, conf.GoogleClientSecretFile) conf.GoogleClientSecret = utils.GetSecret(conf.GoogleClientSecret, conf.GoogleClientSecretFile)
conf.GenericClientSecret = utils.GetSecret(conf.GenericClientSecret, conf.GenericClientSecretFile) conf.GenericClientSecret = utils.GetSecret(conf.GenericClientSecret, conf.GenericClientSecretFile)
// Validate config
validator := validator.New() validator := validator.New()
err = validator.Struct(conf) err = validator.Struct(conf)

View File

@@ -19,6 +19,11 @@ export default defineConfig({
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""), rewrite: (path) => path.replace(/^\/api/, ""),
}, },
"/resources": {
target: "http://tinyauth-backend:3000/resources",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/resources/, ""),
},
}, },
allowedHosts: true, allowedHosts: true,
}, },

View File

@@ -20,7 +20,6 @@ type Controller interface {
type Middleware interface { type Middleware interface {
Middleware() gin.HandlerFunc Middleware() gin.HandlerFunc
Init() error Init() error
Name() string
} }
type Service interface { type Service interface {
@@ -103,6 +102,7 @@ func (app *BootstrapApp) Setup() error {
err := ldapService.Init() err := ldapService.Init()
if err != nil { if err != nil {
log.Warn().Err(err).Msg("Failed to initialize LDAP service, continuing without LDAP")
ldapService = nil ldapService = nil
} }
} }
@@ -120,6 +120,7 @@ func (app *BootstrapApp) Setup() error {
for _, svc := range services { for _, svc := range services {
if svc != nil { if svc != nil {
log.Debug().Str("service", fmt.Sprintf("%T", svc)).Msg("Initializing service")
err := svc.Init() err := svc.Init()
if err != nil { if err != nil {
return err return err
@@ -142,7 +143,13 @@ func (app *BootstrapApp) Setup() error {
// Create engine // Create engine
engine := gin.New() engine := gin.New()
router := engine.Group("/api")
if config.Version != "development" {
gin.SetMode(gin.ReleaseMode)
}
router := engine.Group("/")
apiRouter := router.Group("/api")
// Create middlewares // Create middlewares
var middlewares []Middleware var middlewares []Middleware
@@ -151,18 +158,16 @@ func (app *BootstrapApp) Setup() error {
Domain: domain, Domain: domain,
}, authService, oauthBrokerService) }, authService, oauthBrokerService)
uiMiddleware := middleware.NewUIMiddleware(middleware.UIMiddlewareConfig{ uiMiddleware := middleware.NewUIMiddleware()
ResourcesDir: app.Config.ResourcesDir,
})
zerologMiddleware := middleware.NewZerologMiddleware() zerologMiddleware := middleware.NewZerologMiddleware()
middlewares = append(middlewares, contextMiddleware, uiMiddleware, zerologMiddleware) middlewares = append(middlewares, contextMiddleware, uiMiddleware, zerologMiddleware)
for _, middleware := range middlewares { 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() err := middleware.Init()
if err != nil { 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()) router.Use(middleware.Middleware())
} }
@@ -177,24 +182,28 @@ func (app *BootstrapApp) Setup() error {
ForgotPasswordMessage: app.Config.FogotPasswordMessage, ForgotPasswordMessage: app.Config.FogotPasswordMessage,
BackgroundImage: app.Config.BackgroundImage, BackgroundImage: app.Config.BackgroundImage,
OAuthAutoRedirect: app.Config.OAuthAutoRedirect, OAuthAutoRedirect: app.Config.OAuthAutoRedirect,
}, router) }, apiRouter)
oauthController := controller.NewOAuthController(controller.OAuthControllerConfig{ oauthController := controller.NewOAuthController(controller.OAuthControllerConfig{
AppURL: app.Config.AppURL, AppURL: app.Config.AppURL,
SecureCookie: app.Config.SecureCookie, SecureCookie: app.Config.SecureCookie,
CSRFCookieName: csrfCookieName, CSRFCookieName: csrfCookieName,
RedirectCookieName: redirectCookieName, RedirectCookieName: redirectCookieName,
}, router, authService, oauthBrokerService) }, apiRouter, authService, oauthBrokerService)
proxyController := controller.NewProxyController(controller.ProxyControllerConfig{ proxyController := controller.NewProxyController(controller.ProxyControllerConfig{
AppURL: app.Config.AppURL, AppURL: app.Config.AppURL,
}, router, dockerService, authService) }, apiRouter, dockerService, authService)
userController := controller.NewUserController(controller.UserControllerConfig{ userController := controller.NewUserController(controller.UserControllerConfig{
Domain: domain, 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 // Setup routes
controller := []Controller{ controller := []Controller{
@@ -203,10 +212,11 @@ func (app *BootstrapApp) Setup() error {
proxyController, proxyController,
userController, userController,
healthController, healthController,
resourcesController,
} }
for _, ctrl := range controller { for _, ctrl := range controller {
log.Debug().Msgf("Setting up %T routes", ctrl) log.Debug().Msgf("Setting up %T controller", ctrl)
ctrl.SetupRoutes() ctrl.SetupRoutes()
} }

View File

@@ -14,6 +14,7 @@ func NewHealthController(router *gin.RouterGroup) *HealthController {
func (controller *HealthController) SetupRoutes() { func (controller *HealthController) SetupRoutes() {
controller.Router.GET("/health", controller.healthHandler) controller.Router.GET("/health", controller.healthHandler)
controller.Router.HEAD("/health", controller.healthHandler)
} }
func (controller *HealthController) healthHandler(c *gin.Context) { func (controller *HealthController) healthHandler(c *gin.Context) {

View File

@@ -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)
}

View File

@@ -32,10 +32,6 @@ func (m *ContextMiddleware) Init() error {
return nil return nil
} }
func (m *ContextMiddleware) Name() string {
return "ContextMiddleware"
}
func (m *ContextMiddleware) Middleware() gin.HandlerFunc { func (m *ContextMiddleware) Middleware() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
cookie, err := m.Auth.GetSessionCookie(c) cookie, err := m.Auth.GetSessionCookie(c)

View File

@@ -4,28 +4,19 @@ import (
"io/fs" "io/fs"
"net/http" "net/http"
"os" "os"
"path/filepath"
"strings" "strings"
"tinyauth/internal/assets" "tinyauth/internal/assets"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
type UIMiddlewareConfig struct {
ResourcesDir string
}
type UIMiddleware struct { type UIMiddleware struct {
Config UIMiddlewareConfig UIFS fs.FS
UIFS fs.FS UIFileServer http.Handler
UIFileServer http.Handler
ResourcesFileServer http.Handler
} }
func NewUIMiddleware(config UIMiddlewareConfig) *UIMiddleware { func NewUIMiddleware() *UIMiddleware {
return &UIMiddleware{ return &UIMiddleware{}
Config: config,
}
} }
func (m *UIMiddleware) Init() error { func (m *UIMiddleware) Init() error {
@@ -37,15 +28,10 @@ func (m *UIMiddleware) Init() error {
m.UIFS = ui m.UIFS = ui
m.UIFileServer = http.FileServer(http.FS(ui)) m.UIFileServer = http.FileServer(http.FS(ui))
m.ResourcesFileServer = http.FileServer(http.Dir(m.Config.ResourcesDir))
return nil return nil
} }
func (m *UIMiddleware) Name() string {
return "UIMiddleware"
}
func (m *UIMiddleware) Middleware() gin.HandlerFunc { func (m *UIMiddleware) Middleware() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
switch strings.Split(c.Request.URL.Path, "/")[1] { switch strings.Split(c.Request.URL.Path, "/")[1] {
@@ -53,24 +39,7 @@ func (m *UIMiddleware) Middleware() gin.HandlerFunc {
c.Next() c.Next()
return return
case "resources": case "resources":
requestFilePath := m.Config.ResourcesDir + strings.TrimPrefix(c.Request.URL.Path, "/resources/") c.Next()
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()
return return
default: default:
_, err := fs.Stat(m.UIFS, strings.TrimPrefix(c.Request.URL.Path, "/")) _, err := fs.Stat(m.UIFS, strings.TrimPrefix(c.Request.URL.Path, "/"))

View File

@@ -26,10 +26,6 @@ func (m *ZerologMiddleware) Init() error {
return nil return nil
} }
func (m *ZerologMiddleware) Name() string {
return "ZerologMiddleware"
}
func (m *ZerologMiddleware) logPath(path string) bool { func (m *ZerologMiddleware) logPath(path string) bool {
for _, prefix := range loggerSkipPathsPrefix { for _, prefix := range loggerSkipPathsPrefix {
if strings.HasPrefix(path, prefix) { if strings.HasPrefix(path, prefix) {

View File

@@ -7,7 +7,6 @@ import (
"tinyauth/internal/config" "tinyauth/internal/config"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/traefik/paerser/parser"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
@@ -39,17 +38,6 @@ func ParseFileToLine(content string) string {
return strings.Join(users, ",") 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) { func Filter[T any](slice []T, test func(T) bool) (res []T) {
for _, value := range slice { for _, value := range slice {
if test(value) { if test(value) {

View File

@@ -2,8 +2,22 @@ package utils
import ( import (
"strings" "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 { func ParseHeaders(headers []string) map[string]string {
headerMap := make(map[string]string) headerMap := make(map[string]string)
for _, header := range headers { for _, header := range headers {

View File

@@ -10,6 +10,6 @@ import (
) )
func main() { 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() cmd.Execute()
} }