package api import ( "fmt" "io" "net/http" "os" "path/filepath" "runtime/debug" "strings" "time" ) func (s *Server) handleWebUI(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } if !s.checkAuth(r) { http.Error(w, "unauthorized", http.StatusUnauthorized) return } if s.token != "" && s.isBearerAuthorized(r) { http.SetCookie(w, &http.Cookie{ Name: "clawgo_webui_token", Value: s.token, Path: "/", HttpOnly: true, Secure: requestUsesTLS(r), SameSite: http.SameSiteLaxMode, MaxAge: 86400, }) } if s.tryServeWebUIDist(w, r, "/index.html") { return } w.Header().Set("Content-Type", "text/html; charset=utf-8") _, _ = w.Write([]byte(webUIHTML)) } func (s *Server) handleWebUIAsset(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } if !s.checkAuth(r) { http.Error(w, "unauthorized", http.StatusUnauthorized) return } if strings.HasPrefix(r.URL.Path, "/api/") { http.NotFound(w, r) return } if r.URL.Path == "/" { s.handleWebUI(w, r) return } if s.tryServeWebUIDist(w, r, r.URL.Path) { return } if s.tryServeWebUIDist(w, r, "/index.html") { return } http.NotFound(w, r) } func (s *Server) tryServeWebUIDist(w http.ResponseWriter, r *http.Request, reqPath string) bool { dir := strings.TrimSpace(s.webUIDir) if dir == "" { return false } p := strings.TrimPrefix(reqPath, "/") if reqPath == "/" || reqPath == "/index.html" { p = "index.html" } p = filepath.Clean(strings.TrimPrefix(p, "/")) if strings.HasPrefix(p, "..") { return false } full := filepath.Join(dir, p) fi, err := os.Stat(full) if err != nil || fi.IsDir() { return false } http.ServeFile(w, r, full) return true } func (s *Server) handleWebUIUpload(w http.ResponseWriter, r *http.Request) { if !s.checkAuth(r) { http.Error(w, "unauthorized", http.StatusUnauthorized) return } if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } if err := r.ParseMultipartForm(32 << 20); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } f, h, err := r.FormFile("file") if err != nil { http.Error(w, "file required", http.StatusBadRequest) return } defer f.Close() dir := filepath.Join(os.TempDir(), "clawgo_webui_uploads") _ = os.MkdirAll(dir, 0755) name := fmt.Sprintf("%d_%s", time.Now().UnixNano(), filepath.Base(h.Filename)) path := filepath.Join(dir, name) out, err := os.Create(path) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer out.Close() if _, err := io.Copy(out, f); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } writeJSON(w, map[string]interface{}{"ok": true, "media": name, "name": h.Filename}) } func gatewayBuildVersion() string { if bi, ok := debug.ReadBuildInfo(); ok && bi != nil { ver := strings.TrimSpace(bi.Main.Version) rev := "" for _, s := range bi.Settings { if s.Key == "vcs.revision" { rev = s.Value break } } if len(rev) > 8 { rev = rev[:8] } if ver == "" || ver == "(devel)" { ver = "devel" } if rev != "" { return ver + "+" + rev } return ver } return "unknown" } func detectWebUIVersion(webUIDir string) string { _ = webUIDir return "dev" } const webUIHTML = `
Token: