Files
ds2api/internal/webui/build.go

104 lines
2.5 KiB
Go

package webui
import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"ds2api/internal/config"
)
const (
defaultBuildTimeout = 5 * time.Minute
)
func EnsureBuiltOnStartup() {
if !shouldAutoBuild() {
return
}
staticDir := resolveStaticAdminDir(config.StaticAdminDir())
if hasBuiltUI(staticDir) {
return
}
if err := buildWebUI(staticDir); err != nil {
config.Logger.Warn("[webui] auto build failed", "error", err)
return
}
if hasBuiltUI(staticDir) {
config.Logger.Info("[webui] auto build completed", "dir", staticDir)
return
}
config.Logger.Warn("[webui] auto build finished but output missing", "dir", staticDir)
}
func shouldAutoBuild() bool {
raw := strings.TrimSpace(os.Getenv("DS2API_AUTO_BUILD_WEBUI"))
if raw == "" {
return !config.IsVercel()
}
switch strings.ToLower(raw) {
case "1", "true", "yes", "on":
return true
case "0", "false", "no", "off":
return false
default:
return !config.IsVercel()
}
}
func hasBuiltUI(staticDir string) bool {
if strings.TrimSpace(staticDir) == "" {
return false
}
indexPath := filepath.Join(staticDir, "index.html")
st, err := os.Stat(indexPath)
return err == nil && !st.IsDir()
}
func buildWebUI(staticDir string) error {
if _, err := exec.LookPath("npm"); err != nil {
return fmt.Errorf("npm not found in PATH: %w", err)
}
if strings.TrimSpace(staticDir) == "" {
return errors.New("static admin dir is empty")
}
config.Logger.Info("[webui] static files missing, running npm build")
ctx, cancel := context.WithTimeout(context.Background(), defaultBuildTimeout)
defer cancel()
if _, err := os.Stat(filepath.Join("webui", "node_modules")); err != nil {
if !os.IsNotExist(err) {
return err
}
installCmd := exec.CommandContext(ctx, "npm", "ci", "--prefix", "webui")
installCmd.Stdout = os.Stdout
installCmd.Stderr = os.Stderr
if err := installCmd.Run(); err != nil {
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
return fmt.Errorf("webui npm ci timed out after %s", defaultBuildTimeout)
}
return err
}
}
if err := os.MkdirAll(staticDir, 0o755); err != nil {
return err
}
cmd := exec.CommandContext(ctx, "npm", "run", "build", "--prefix", "webui", "--", "--outDir", staticDir, "--emptyOutDir")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
return fmt.Errorf("webui build timed out after %s", defaultBuildTimeout)
}
return err
}
return nil
}