Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support configurable base path for server #714

Merged
merged 2 commits into from
Nov 18, 2024
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
2 changes: 2 additions & 0 deletions docs/source/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Server Configuration
~~~~~~~~~~~~~~~~~~
- ``DAGU_HOST`` (``127.0.0.1``): Server binding host
- ``DAGU_PORT`` (``8080``): Server binding port
- ``DAGU_BASE_PATH`` (``""``): Base path to serve the application
yohamta marked this conversation as resolved.
Show resolved Hide resolved
- ``DAGU_TZ`` (``""``): Server timezone (default: system timezone)
- ``DAGU_CERT_FILE``: SSL certificate file path
- ``DAGU_KEY_FILE``: SSL key file path
Expand Down Expand Up @@ -59,6 +60,7 @@ Create ``admin.yaml`` in ``$HOME/.config/dagu/`` to override default settings. B
# Server Configuration
host: "127.0.0.1" # Web UI hostname
port: 8080 # Web UI port
basePath: "" # Base path to serve the application
tz: "Asia/Tokyo" # Timezone (e.g., "America/New_York")

# Directory Configuration
Expand Down
16 changes: 16 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"fmt"
"log"
"os"
"path"
"path/filepath"
"strconv"
"strings"
Expand Down Expand Up @@ -52,6 +53,7 @@ type Config struct {
IsAuthToken bool // Enable auth token for API
AuthToken string // Auth token for API
LatestStatusToday bool // Show latest status today or the latest status
BasePath string // Base path for the server
APIBaseURL string // Base URL for API
Debug bool // Enable debug mode (verbose logging)
LogFormat string // Log format
Expand Down Expand Up @@ -118,6 +120,18 @@ func Load() (*Config, error) {
cfg.Location = time.Local
}

if cfg.BasePath != "" {
cfg.BasePath = path.Clean(cfg.BasePath)

if !path.IsAbs(cfg.BasePath) {
cfg.BasePath = path.Join("/", cfg.BasePath)
}

if cfg.BasePath == "/" {
cfg.BasePath = ""
}
}

return &cfg, nil
}

Expand Down Expand Up @@ -168,6 +182,7 @@ func setupViper() error {
viper.SetDefault("host", "127.0.0.1")
viper.SetDefault("port", "8080")
viper.SetDefault("navbarTitle", "Dagu")
viper.SetDefault("basePath", "")
viper.SetDefault("apiBaseURL", "/api/v1")

// Set executable path
Expand Down Expand Up @@ -198,6 +213,7 @@ func bindEnvs() {
_ = viper.BindEnv("logEncodingCharset", "DAGU_LOG_ENCODING_CHARSET")
_ = viper.BindEnv("navbarColor", "DAGU_NAVBAR_COLOR")
_ = viper.BindEnv("navbarTitle", "DAGU_NAVBAR_TITLE")
_ = viper.BindEnv("basePath", "DAGU_BASE_PATH")
_ = viper.BindEnv("apiBaseURL", "DAGU_API_BASE_URL")
_ = viper.BindEnv("tz", "DAGU_TZ")

Expand Down
1 change: 1 addition & 0 deletions internal/frontend/frontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func New(cfg *config.Config, lg logger.Logger, cli client.Client) *server.Server
AssetsFS: assetsFS,
NavbarColor: cfg.NavbarColor,
NavbarTitle: cfg.NavbarTitle,
BasePath: cfg.BasePath,
APIBaseURL: cfg.APIBaseURL,
TimeZone: cfg.TZ,
}
Expand Down
18 changes: 14 additions & 4 deletions internal/frontend/middleware/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,15 @@ var (
authBasic *AuthBasic
authToken *AuthToken
appLogger logger.Logger
basePath string
)

type Options struct {
Handler http.Handler
AuthBasic *AuthBasic
AuthToken *AuthToken
Logger logger.Logger
BasePath string
}

type AuthBasic struct {
Expand All @@ -95,16 +97,24 @@ func Setup(opts *Options) {
authBasic = opts.AuthBasic
authToken = opts.AuthToken
appLogger = opts.Logger
basePath = opts.BasePath
}

func prefixChecker(next http.Handler) http.Handler {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, "/api") {
next.ServeHTTP(w, r)
} else {
defaultHandler.ServeHTTP(w, r)
if basePath != "" && r.URL.Path == "/" {
http.Redirect(w, r, basePath, http.StatusSeeOther)
return
}

http.StripPrefix(basePath, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, "/api") {
next.ServeHTTP(w, r)
} else {
defaultHandler.ServeHTTP(w, r)
}
})).ServeHTTP(w, r)
})
}

Expand Down
7 changes: 5 additions & 2 deletions internal/frontend/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type NewServerArgs struct {
// Configuration for the frontend
NavbarColor string
NavbarTitle string
BasePath string
APIBaseURL string
TimeZone string
}
Expand Down Expand Up @@ -92,6 +93,7 @@ func New(params NewServerArgs) *Server {
funcsConfig: funcsConfig{
NavbarColor: params.NavbarColor,
NavbarTitle: params.NavbarTitle,
BasePath: params.BasePath,
APIBaseURL: params.APIBaseURL,
TZ: params.TimeZone,
},
Expand All @@ -110,8 +112,9 @@ func (svr *Server) Shutdown() {

func (svr *Server) Serve(ctx context.Context) (err error) {
middlewareOptions := &pkgmiddleware.Options{
Handler: svr.defaultRoutes(chi.NewRouter()),
Logger: svr.logger,
Handler: svr.defaultRoutes(chi.NewRouter()),
Logger: svr.logger,
BasePath: svr.funcsConfig.BasePath,
}
if svr.authToken != nil {
middlewareOptions.AuthToken = &pkgmiddleware.AuthToken{
Expand Down
7 changes: 6 additions & 1 deletion internal/frontend/server/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"bytes"
"io"
"net/http"
"path"
"path/filepath"
"text/template"

Expand Down Expand Up @@ -56,6 +57,7 @@ func (srv *Server) useTemplate(
type funcsConfig struct {
NavbarColor string
NavbarTitle string
BasePath string
APIBaseURL string
TZ string
}
Expand All @@ -78,8 +80,11 @@ func defaultFunctions(cfg funcsConfig) template.FuncMap {
"navbarTitle": func() string {
return cfg.NavbarTitle
},
"basePath": func() string {
return cfg.BasePath
},
"apiURL": func() string {
return cfg.APIBaseURL
return path.Join(cfg.BasePath, cfg.APIBaseURL)
},
"tz": func() string {
return cfg.TZ
Expand Down
3 changes: 2 additions & 1 deletion internal/frontend/templates/base.gohtml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
function getConfig() {
return {
apiURL: "{{ apiURL }}",
basePath: "{{ basePath }}",
title: "{{ navbarTitle }}",
navbarColor: "{{ navbarColor }}",
version: "{{ version }}",
tz: "{{ tz }}",
};
}
</script>
<script defer="defer" src="/assets/bundle.js?v={{ version }}"></script>
<script defer="defer" src="{{ basePath }}/assets/bundle.js?v={{ version }}"></script>
</head>
<body>
{{template "content" .}}
Expand Down
2 changes: 1 addition & 1 deletion ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function App({ config }: Props) {
>
<ConfigContext.Provider value={config}>
<UserPreferencesProvider>
<BrowserRouter>
<BrowserRouter basename={config.basePath}>
<Layout {...config}>
<Routes>
<Route path="/" element={<Dashboard />} />
Expand Down
1 change: 1 addition & 0 deletions ui/src/contexts/ConfigContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createContext, useContext } from 'react';

export type Config = {
apiURL: string;
basePath: string;
title: string;
navbarColor: string;
tz: string;
Expand Down
2 changes: 1 addition & 1 deletion ui/webpack.prod.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ module.exports = merge(common, {
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/assets/',
publicPath: 'auto',
yohamta marked this conversation as resolved.
Show resolved Hide resolved
clean: true,
},
});
Loading