Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ type Config struct {
// Deprecated: Use UI.NavbarTitle instead
NavbarTitle string `mapstructure:"navbarTitle"`
// Deprecated: Use UI.MaxDashboardPageLimit instead
MaxDashboardPageLimit int `mapstructure:"maxDashboardPageLimit"`
MaxDashboardPageLimit int `mapstructure:"maxDashboardPageLimit"`
Headless bool `mapstructure:"headless"`
// Legacy fields for backward compatibility - End

// Other settings
Expand Down Expand Up @@ -112,6 +113,7 @@ type UI struct {
NavbarColor string `mapstructure:"navbarColor"`
NavbarTitle string `mapstructure:"navbarTitle"`
MaxDashboardPageLimit int `mapstructure:"maxDashboardPageLimit"`
Headless bool `mapstructure:"headless"`
}

// RemoteNode represents a remote node configuration
Expand Down Expand Up @@ -206,6 +208,9 @@ func (c *Config) migrateUISettings() {
if c.MaxDashboardPageLimit > 0 {
c.UI.MaxDashboardPageLimit = c.MaxDashboardPageLimit
}
if c.Headless {
c.UI.Headless = c.Headless
}
}

func (c *Config) cleanBasePath() {
Expand Down
1 change: 1 addition & 0 deletions internal/config/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ func (l *ConfigLoader) bindEnvironmentVariables() {
l.bindEnv("ui.logEncodingCharset", "UI_LOG_ENCODING_CHARSET")
l.bindEnv("ui.navbarColor", "UI_NAVBAR_COLOR")
l.bindEnv("ui.navbarTitle", "UI_NAVBAR_TITLE")
l.bindEnv("ui.headless", "UI_HEADLESS")

// UI configurations (legacy)
l.bindEnv("ui.maxDashboardPageLimit", "MAX_DASHBOARD_PAGE_LIMIT")
Expand Down
1 change: 1 addition & 0 deletions internal/frontend/frontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func New(cfg *config.Config, cli client.Client) *server.Server {
APIBaseURL: cfg.APIBaseURL,
TimeZone: cfg.TZ,
RemoteNodes: remoteNodes,
Headless: cfg.UI.Headless,
}

if cfg.Auth.Token.Enabled {
Expand Down
12 changes: 12 additions & 0 deletions internal/frontend/server/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,23 @@ import (
"context"
"net/http"

"github.com/dagu-org/dagu/internal/logger"
"github.com/go-chi/chi/v5"
)

func (svr *Server) defaultRoutes(ctx context.Context, r *chi.Mux) *chi.Mux {
// Always allow API routes to work
if svr.headless {
logger.Info(ctx, "Headless mode enabled: UI is disabled, but API remains active")

// Only register API routes, skip Web UI routes
return r
}

// Serve assets (optional, remove if not needed)
r.Get("/assets/*", svr.handleGetAssets())

// Serve UI pages (disable when headless)
r.Get("/*", svr.handleRequest(ctx))

return r
Expand Down
34 changes: 15 additions & 19 deletions internal/frontend/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/dagu-org/dagu/internal/frontend/gen/restapi"
"github.com/dagu-org/dagu/internal/logger"
"github.com/go-openapi/loads"
flags "github.com/jessevdk/go-flags"
"github.com/jessevdk/go-flags"

"github.com/dagu-org/dagu/internal/frontend/gen/restapi/operations"
pkgmiddleware "github.com/dagu-org/dagu/internal/frontend/middleware"
Expand All @@ -31,6 +31,7 @@ type Server struct {
server *restapi.Server
handlers []Handler
assets fs.FS
headless bool
}

type NewServerArgs struct {
Expand All @@ -42,7 +43,7 @@ type NewServerArgs struct {
Handlers []Handler
AssetsFS fs.FS

// Configuration for the frontend
Headless bool
NavbarColor string
NavbarTitle string
BasePath string
Expand Down Expand Up @@ -74,6 +75,7 @@ func New(params NewServerArgs) *Server {
tls: params.TLS,
handlers: params.Handlers,
assets: params.AssetsFS,
headless: params.Headless, // Assign headless mode flag
funcsConfig: funcsConfig{
NavbarColor: params.NavbarColor,
NavbarTitle: params.NavbarTitle,
Expand All @@ -98,11 +100,14 @@ func (svr *Server) Shutdown(ctx context.Context) {

func (svr *Server) Serve(ctx context.Context) (err error) {
loggerInstance := logger.FromContext(ctx)

// Setup middleware & routes
middlewareOptions := &pkgmiddleware.Options{
Handler: svr.defaultRoutes(ctx, chi.NewRouter()),
Handler: svr.defaultRoutes(ctx, chi.NewRouter()), // API remains active
BasePath: svr.funcsConfig.BasePath,
Logger: loggerInstance,
}

if svr.authToken != nil {
middlewareOptions.AuthToken = &pkgmiddleware.AuthToken{
Token: svr.authToken.Token,
Expand All @@ -116,6 +121,7 @@ func (svr *Server) Serve(ctx context.Context) (err error) {
}
pkgmiddleware.Setup(middlewareOptions)

// Load API spec (Always required)
swaggerSpec, err := loads.Analyzed(restapi.SwaggerJSON, "")
if err != nil {
logger.Error(ctx, "Failed to load API spec", "err", err)
Expand All @@ -124,35 +130,28 @@ func (svr *Server) Serve(ctx context.Context) (err error) {
api := operations.NewDaguAPI(swaggerSpec)
api.Logger = loggerInstance.Infof
for _, h := range svr.handlers {
h.Configure(api)
h.Configure(api) // Always configure API handlers
}

// Start API server
svr.server = restapi.NewServer(api)
defer svr.Shutdown(ctx)

svr.server.Host = svr.host
svr.server.Port = svr.port
svr.server.ConfigureAPI()

// Server run context
// Listen for system signals (CTRL+C, termination)
serverCtx, serverStopCtx := context.WithCancel(ctx)

// Listen for syscall signals for process to interrupt/quit
sig := make(chan os.Signal, 1)
signal.Notify(
sig, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT,
)
signal.Notify(sig, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
go func() {
<-sig

// Trigger graceful shutdown
err := svr.server.Shutdown()
if err != nil {
logger.Error(ctx, "Server shutdown", "err", err)
}
_ = svr.server.Shutdown()
serverStopCtx()
}()

// Run with or without TLS
if svr.tls != nil {
svr.server.TLSCertificate = flags.Filename(svr.tls.CertFile)
svr.server.TLSCertificateKey = flags.Filename(svr.tls.KeyFile)
Expand All @@ -167,10 +166,7 @@ func (svr *Server) Serve(ctx context.Context) (err error) {
logger.Error(ctx, "Server error", "err", err)
}

// Wait for server context to be stopped
<-serverCtx.Done()

logger.Info(ctx, "Server stopped")

return nil
}
7 changes: 7 additions & 0 deletions internal/frontend/server/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ var (
)

func (srv *Server) useTemplate(ctx context.Context, layout string, name string) func(http.ResponseWriter, any) {
// Skip template rendering if headless
if srv.headless {
return func(w http.ResponseWriter, _ any) {
http.Error(w, "Web UI is disabled in headless mode", http.StatusForbidden)
}
}

files := append(baseTemplates(), filepath.Join(templatePath, layout))
tmpl, err := template.New(name).Funcs(
defaultFunctions(srv.funcsConfig)).ParseFS(srv.assets, files...,
Expand Down