Skip to content

Commit a16a596

Browse files
committed
feat: add configurable dashboard path and consolidate tests
Add DASHBOARD_PATH env var to support flexible dashboard locations in tests
1 parent 9b8bb61 commit a16a596

6 files changed

Lines changed: 54 additions & 35 deletions

File tree

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
prod-example:
22
cd docker && docker-compose --env-file .env.example -f docker-compose.yml up --build
3+
34
test:
4-
cd ./server && make test && cd ../dashboard && bun run test
5+
@echo "Running dashboard linting and type checking..."
6+
cd ./dashboard && bun run lint && bun run tsc
7+
@echo "\nRunning Go unit tests..."
8+
cd ./server && DASHBOARD_PATH=../dashboard/dist go test -v ./... --count=2

server/e2e/e2e_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ import (
2121
func testConfig() *config.Config {
2222
return &config.Config{
2323
Server: config.ServerConfig{
24-
Host: "127.0.0.1",
25-
Port: "0",
24+
Host: "127.0.0.1",
25+
Port: "0",
26+
DashboardPath: "", // Empty for tests - dashboard not required
2627
},
2728
Database: config.DatabaseConfig{
2829
Driver: "sqlite",

server/internal/config/config.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ type Config struct {
1818
}
1919

2020
type ServerConfig struct {
21-
Host string
22-
Port string
23-
BasePath string // Base path for all routes (e.g., "/app" or "/")
21+
Host string
22+
Port string
23+
BasePath string // Base path for all routes (e.g., "/app" or "/")
24+
DashboardPath string // Path to dashboard files (defaults to "dashboard")
2425
}
2526

2627
type DatabaseConfig struct {
@@ -49,9 +50,10 @@ func Load() *Config {
4950
}
5051
return &Config{
5152
Server: ServerConfig{
52-
Host: getEnv("SERVER_HOST", "0.0.0.0"),
53-
Port: getEnv("SERVER_PORT", "8080"),
54-
BasePath: basePath,
53+
Host: getEnv("SERVER_HOST", "0.0.0.0"),
54+
Port: getEnv("SERVER_PORT", "8080"),
55+
BasePath: basePath,
56+
DashboardPath: getEnv("DASHBOARD_PATH", "dashboard"),
5557
},
5658
Database: DatabaseConfig{
5759
Driver: getEnv("DB_DRIVER", "sqlite"),

server/internal/dashboard/dashboard.go

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,21 @@ import (
99
"strings"
1010
)
1111

12-
// StaticDir is the directory containing built dashboard files (set at build time)
13-
const StaticDir = "dashboard"
14-
1512
// Config holds the runtime configuration for the dashboard
1613
type Config struct {
17-
BasePath string
18-
APIUrl string
19-
GraphQLUrl string
14+
BasePath string
15+
APIUrl string
16+
GraphQLUrl string
17+
DashboardPath string // Path to dashboard files (defaults to "dashboard")
2018
}
2119

2220
// DefaultConfig returns the default configuration
2321
func DefaultConfig() Config {
2422
return Config{
25-
BasePath: "/",
26-
APIUrl: "/api",
27-
GraphQLUrl: "/graphql",
23+
BasePath: "/",
24+
APIUrl: "/api",
25+
GraphQLUrl: "/graphql",
26+
DashboardPath: "dashboard",
2827
}
2928
}
3029

@@ -39,6 +38,11 @@ window.__ENV__ = {
3938

4039
// Handler returns an http.Handler that serves the dashboard with the given config
4140
func Handler(cfg Config) http.Handler {
41+
// Set default dashboard path if not provided
42+
if cfg.DashboardPath == "" {
43+
cfg.DashboardPath = "dashboard"
44+
}
45+
4246
// Normalize base path - keep empty string for root, or ensure it starts with / and has no trailing /
4347
if cfg.BasePath != "" {
4448
if !strings.HasPrefix(cfg.BasePath, "/") {
@@ -51,10 +55,14 @@ func Handler(cfg Config) http.Handler {
5155
tmpl := template.Must(template.New("config.js").Parse(configJSTemplate))
5256

5357
// Read index.html and prepare it with base path replacements
54-
indexPath := filepath.Join(StaticDir, "index.html")
58+
indexPath := filepath.Join(cfg.DashboardPath, "index.html")
5559
indexHTML, err := os.ReadFile(indexPath)
5660
if err != nil {
57-
panic("Failed to read index.html: " + err.Error())
61+
// Return a minimal handler that serves a placeholder
62+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
63+
w.WriteHeader(http.StatusServiceUnavailable)
64+
w.Write([]byte("Dashboard not available"))
65+
})
5866
}
5967
// Replace {{BASE_PATH}} placeholder with actual base path
6068
indexContent := string(indexHTML)
@@ -68,7 +76,7 @@ func Handler(cfg Config) http.Handler {
6876
}
6977

7078
// Create file server for static files
71-
fileServer := http.FileServer(http.Dir(StaticDir))
79+
fileServer := http.FileServer(http.Dir(cfg.DashboardPath))
7280

7381
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
7482
// Serve dynamically generated config.js
@@ -88,7 +96,7 @@ func Handler(cfg Config) http.Handler {
8896
}
8997

9098
// Try to check if file exists on disk
91-
filePath := filepath.Join(StaticDir, filepath.Clean(urlPath))
99+
filePath := filepath.Join(cfg.DashboardPath, filepath.Clean(urlPath))
92100
if info, err := os.Stat(filePath); err == nil && !info.IsDir() {
93101
// Special handling for index.html - serve with BASE_PATH replacements
94102
if urlPath == "index.html" {

server/internal/handlers/health_handlers.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@ import (
55
"os"
66
"path/filepath"
77

8-
"github.com/lovely-eye/server/internal/dashboard"
98
"github.com/uptrace/bun"
109
)
1110
type HealthHandler struct {
12-
db *bun.DB
11+
db *bun.DB
12+
dashboardPath string
1313
}
14-
func NewHealthHandler(db *bun.DB) *HealthHandler {
14+
func NewHealthHandler(db *bun.DB, dashboardPath string) *HealthHandler {
1515
return &HealthHandler{
16-
db: db,
16+
db: db,
17+
dashboardPath: dashboardPath,
1718
}
1819
}
1920
func (h *HealthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -26,11 +27,13 @@ func (h *HealthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
2627
return
2728
}
2829

29-
// Check dashboard files exist
30-
if _, err := os.Stat(filepath.Join(dashboard.StaticDir, "index.html")); err != nil {
31-
w.WriteHeader(http.StatusServiceUnavailable)
32-
w.Write([]byte(`{"status":"unhealthy","error":"dashboard files not found"}`))
33-
return
30+
// Check dashboard files exist (skip check if dashboard path is empty for tests)
31+
if h.dashboardPath != "" {
32+
if _, err := os.Stat(filepath.Join(h.dashboardPath, "index.html")); err != nil {
33+
w.WriteHeader(http.StatusServiceUnavailable)
34+
w.Write([]byte(`{"status":"unhealthy","error":"dashboard files not found"}`))
35+
return
36+
}
3437
}
3538

3639
w.WriteHeader(http.StatusOK)

server/internal/server/server.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,17 @@ func New(cfg *config.Config) (*Server, error) {
120120
w.Header().Set("Access-Control-Allow-Origin", "*")
121121
w.Write(trackerJS)
122122
})
123-
hh := handlers.NewHealthHandler(db)
123+
hh := handlers.NewHealthHandler(db, cfg.Server.DashboardPath)
124124

125125
// Health check (always at root for load balancers)
126126
mux.Handle("GET /health", hh)
127127

128128
// Setup dashboard handler with runtime config
129129
dashboardCfg := dashboard.Config{
130-
BasePath: basePath,
131-
APIUrl: basePath + "/api",
132-
GraphQLUrl: basePath + "/graphql",
130+
BasePath: basePath,
131+
APIUrl: basePath + "/api",
132+
GraphQLUrl: basePath + "/graphql",
133+
DashboardPath: cfg.Server.DashboardPath,
133134
}
134135
dashboardHandler := dashboard.Handler(dashboardCfg)
135136

0 commit comments

Comments
 (0)