Skip to content

Commit 30868c2

Browse files
committed
Implement asset routes
1 parent e7ec584 commit 30868c2

File tree

5 files changed

+220
-87
lines changed

5 files changed

+220
-87
lines changed

Diff for: api/v1/api.gen.go

+72-72
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: api/v1/api.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,6 @@ components:
435435
DAGDetailTab:
436436
type: string
437437
description: "Indicates which part of the DAG to retrieve"
438-
default: status
439438
enum:
440439
- status
441440
- spec

Diff for: internal/frontend/assets.go

-10
This file was deleted.

Diff for: internal/frontend/server.go

+41-4
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,32 @@ import (
2626
)
2727

2828
type Server struct {
29-
api *api.API
30-
config *config.Config
31-
httpServer *http.Server
29+
api *api.API
30+
config *config.Config
31+
httpServer *http.Server
32+
funcsConfig funcsConfig
3233
}
3334

3435
func NewServer(
3536
api *api.API,
3637
cfg *config.Config,
3738
) *Server {
39+
var remoteNodes []string
40+
for _, node := range cfg.Server.RemoteNodes {
41+
remoteNodes = append(remoteNodes, node.Name)
42+
}
3843
return &Server{
3944
api: api,
4045
config: cfg,
46+
funcsConfig: funcsConfig{
47+
NavbarColor: cfg.UI.NavbarColor,
48+
NavbarTitle: cfg.UI.NavbarTitle,
49+
BasePath: cfg.Server.BasePath,
50+
APIBasePath: cfg.Server.APIBasePath,
51+
TZ: cfg.Global.TZ,
52+
MaxDashboardPageLimit: cfg.UI.MaxDashboardPageLimit,
53+
RemoteNodes: remoteNodes,
54+
},
4155
}
4256
}
4357

@@ -57,12 +71,14 @@ func (srv *Server) Serve(ctx context.Context) error {
5771
r.Use(httplog.RequestLogger(requestLogger))
5872
r.Use(withRecoverer)
5973
r.Use(cors.Handler(cors.Options{
60-
AllowedOrigins: []string{"*"},
74+
AllowedOrigins: []string{"*"}, // TODO: Update to specific origins
6175
AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
6276
AllowedHeaders: []string{"*"},
6377
AllowCredentials: true,
6478
}))
6579

80+
srv.routes(ctx, r)
81+
6682
basePath := path.Join(srv.config.Server.BasePath, "api/v1")
6783
if !strings.HasPrefix(basePath, "/") {
6884
basePath = "/" + basePath
@@ -100,6 +116,27 @@ func (srv *Server) Serve(ctx context.Context) error {
100116
return nil
101117
}
102118

119+
func (srv *Server) routes(ctx context.Context, r *chi.Mux) {
120+
// Always allow API routes to work
121+
if srv.config.Server.Headless {
122+
logger.Info(ctx, "Headless mode enabled: UI is disabled, but API remains active")
123+
124+
// Only register API routes, skip Web UI routes
125+
return
126+
}
127+
128+
// Serve assets (optional, remove if not needed)
129+
r.Get("/assets/*", func(w http.ResponseWriter, r *http.Request) {
130+
w.Header().Set("Cache-Control", "max-age=86400")
131+
http.FileServer(http.FS(assetsFS)).ServeHTTP(w, r)
132+
})
133+
134+
// Serve UI pages (disable when headless)
135+
r.Get("/*", func(w http.ResponseWriter, _ *http.Request) {
136+
srv.useTemplate(ctx, "index.gohtml", "index")(w, nil)
137+
})
138+
}
139+
103140
func (srv *Server) Shutdown(ctx context.Context) error {
104141
if srv.httpServer != nil {
105142
logger.Info(ctx, "Server is shutting down", "addr", srv.httpServer.Addr)

Diff for: internal/frontend/templates.go

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package frontend
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"embed"
7+
"io"
8+
"net/http"
9+
"path"
10+
"path/filepath"
11+
"strings"
12+
"text/template"
13+
14+
_ "embed"
15+
16+
"github.com/dagu-org/dagu/internal/build"
17+
"github.com/dagu-org/dagu/internal/logger"
18+
)
19+
20+
//go:embed templates/* assets/*
21+
var assetsFS embed.FS
22+
23+
// templatePath is the path to the templates directory.
24+
var templatePath = "templates/"
25+
26+
func (srv *Server) useTemplate(ctx context.Context, layout string, name string) func(http.ResponseWriter, any) {
27+
// Skip template rendering if headless
28+
if srv.config.Server.Headless {
29+
return func(w http.ResponseWriter, _ any) {
30+
http.Error(w, "Web UI is disabled in headless mode", http.StatusForbidden)
31+
}
32+
}
33+
34+
files := append(baseTemplates(), filepath.Join(templatePath, layout))
35+
tmpl, err := template.New(name).Funcs(
36+
defaultFunctions(srv.funcsConfig)).ParseFS(assetsFS, files...,
37+
)
38+
if err != nil {
39+
panic(err)
40+
}
41+
42+
return func(w http.ResponseWriter, data any) {
43+
var buf bytes.Buffer
44+
if err := tmpl.ExecuteTemplate(&buf, "base", data); err != nil {
45+
logger.Error(ctx, "Template execution failed", "err", err)
46+
http.Error(w, err.Error(), http.StatusInternalServerError)
47+
return
48+
}
49+
w.WriteHeader(http.StatusOK)
50+
_, _ = io.Copy(w, &buf)
51+
}
52+
}
53+
54+
type funcsConfig struct {
55+
NavbarColor string
56+
NavbarTitle string
57+
BasePath string
58+
APIBasePath string
59+
TZ string
60+
MaxDashboardPageLimit int
61+
RemoteNodes []string
62+
}
63+
64+
func defaultFunctions(cfg funcsConfig) template.FuncMap {
65+
return template.FuncMap{
66+
"defTitle": func(ip any) string {
67+
v, ok := ip.(string)
68+
if !ok || (ok && v == "") {
69+
return ""
70+
}
71+
return v
72+
},
73+
"version": func() string {
74+
return build.Version
75+
},
76+
"navbarColor": func() string {
77+
return cfg.NavbarColor
78+
},
79+
"navbarTitle": func() string {
80+
return cfg.NavbarTitle
81+
},
82+
"basePath": func() string {
83+
return cfg.BasePath
84+
},
85+
"apiURL": func() string {
86+
return path.Join(cfg.BasePath, cfg.APIBasePath)
87+
},
88+
"tz": func() string {
89+
return cfg.TZ
90+
},
91+
"maxDashboardPageLimit": func() int {
92+
return cfg.MaxDashboardPageLimit
93+
},
94+
"remoteNodes": func() string {
95+
return strings.Join(cfg.RemoteNodes, ",")
96+
},
97+
}
98+
}
99+
100+
func baseTemplates() []string {
101+
var templateFiles = []string{"base.gohtml"}
102+
ret := make([]string, 0, len(templateFiles))
103+
for _, t := range templateFiles {
104+
ret = append(ret, filepath.Join(templatePath, t))
105+
}
106+
return ret
107+
}

0 commit comments

Comments
 (0)