mirror of
https://github.com/niusmallnan/steve.git
synced 2025-07-04 18:26:18 +00:00
K-EXPLORER: add embed ui mode
This commit is contained in:
parent
8c327e08ad
commit
327be56d3a
2
go.mod
2
go.mod
@ -1,6 +1,6 @@
|
|||||||
module github.com/rancher/steve
|
module github.com/rancher/steve
|
||||||
|
|
||||||
go 1.13
|
go 1.16
|
||||||
|
|
||||||
replace (
|
replace (
|
||||||
github.com/crewjam/saml => github.com/rancher/saml v0.0.0-20180713225824-ce1532152fde
|
github.com/crewjam/saml => github.com/rancher/saml v0.0.0-20180713225824-ce1532152fde
|
||||||
|
97
pkg/ui/embed.go
Normal file
97
pkg/ui/embed.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
// +build embed
|
||||||
|
|
||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// content holds our static web server content.
|
||||||
|
//go:embed ui/*
|
||||||
|
var staticContent embed.FS
|
||||||
|
|
||||||
|
type fsFunc func(name string) (fs.File, error)
|
||||||
|
|
||||||
|
func (f fsFunc) Open(name string) (fs.File, error) {
|
||||||
|
return f(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func pathExist(path string) bool {
|
||||||
|
path = formatPath(path)
|
||||||
|
_, err := staticContent.Open(path)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func openFile(path string) (fs.File, error) {
|
||||||
|
path = formatPath(path)
|
||||||
|
file, err := staticContent.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("openEmbedFile %s err: %v", path, err)
|
||||||
|
}
|
||||||
|
return file, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatPath(path string) string {
|
||||||
|
// To replace _nuxt/_cluster/...
|
||||||
|
// For embed, If a pattern names a directory,
|
||||||
|
// all files in the subtree rooted at that directory are embedded (recursively),
|
||||||
|
// except that files with names beginning with ‘.’ or ‘_’ are excluded.
|
||||||
|
return strings.ReplaceAll(path, "_", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func serveEmbed(basePath string) http.Handler {
|
||||||
|
handler := fsFunc(func(name string) (fs.File, error) {
|
||||||
|
logrus.Debugf("serveEmbed name: %s", name)
|
||||||
|
assetPath := filepath.Join(basePath, name)
|
||||||
|
logrus.Debugf("serveEmbed final path: %s", assetPath)
|
||||||
|
return openFile(assetPath)
|
||||||
|
})
|
||||||
|
|
||||||
|
return http.FileServer(http.FS(handler))
|
||||||
|
}
|
||||||
|
|
||||||
|
func serveEmbedIndex(basePath string) http.Handler {
|
||||||
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
path := filepath.Join(basePath, "dashboard", "index.html")
|
||||||
|
logrus.Debugf("serveEmbedIndex : %s", path)
|
||||||
|
f, _ := staticContent.Open(path)
|
||||||
|
io.Copy(rw, f)
|
||||||
|
f.Close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Handler) ServeAsset() http.Handler {
|
||||||
|
return u.middleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
serveEmbed(u.pathSetting()).ServeHTTP(rw, req)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Handler) ServeFaviconDashboard() http.Handler {
|
||||||
|
return u.middleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
serveEmbed(filepath.Join(u.pathSetting(), "dashboard")).ServeHTTP(rw, req)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Handler) IndexFileOnNotFound() http.Handler {
|
||||||
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
path := filepath.Join(u.pathSetting(), req.URL.Path)
|
||||||
|
if pathExist(path) {
|
||||||
|
u.ServeAsset().ServeHTTP(rw, req)
|
||||||
|
} else {
|
||||||
|
u.IndexFile().ServeHTTP(rw, req)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Handler) IndexFile() http.Handler {
|
||||||
|
return u.indexMiddleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
serveEmbedIndex(u.pathSetting()).ServeHTTP(rw, req)
|
||||||
|
}))
|
||||||
|
}
|
42
pkg/ui/external.go
Normal file
42
pkg/ui/external.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// +build !embed
|
||||||
|
|
||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (u *Handler) ServeAsset() http.Handler {
|
||||||
|
return u.middleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
http.FileServer(http.Dir(u.pathSetting())).ServeHTTP(rw, req)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Handler) ServeFaviconDashboard() http.Handler {
|
||||||
|
return u.middleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
http.FileServer(http.Dir(filepath.Join(u.pathSetting(), "dashboard"))).ServeHTTP(rw, req)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Handler) IndexFileOnNotFound() http.Handler {
|
||||||
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
// we ignore directories here because we want those to come from the CDN when running in that mode
|
||||||
|
if stat, err := os.Stat(filepath.Join(u.pathSetting(), req.URL.Path)); err == nil && !stat.IsDir() {
|
||||||
|
u.ServeAsset().ServeHTTP(rw, req)
|
||||||
|
} else {
|
||||||
|
u.IndexFile().ServeHTTP(rw, req)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Handler) IndexFile() http.Handler {
|
||||||
|
return u.indexMiddleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
if path, isURL := u.path(); isURL {
|
||||||
|
_ = serveIndex(rw, path)
|
||||||
|
} else {
|
||||||
|
http.ServeFile(rw, req, filepath.Join(path, "index.html"))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
@ -5,14 +5,16 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/rancher/apiserver/pkg/middleware"
|
"github.com/rancher/apiserver/pkg/middleware"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultPath = "./ui"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
insecureClient = &http.Client{
|
insecureClient = &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
@ -24,10 +26,6 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
defaultPath = "./ui"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StringSetting func() string
|
type StringSetting func() string
|
||||||
type BoolSetting func() bool
|
type BoolSetting func() bool
|
||||||
|
|
||||||
@ -104,17 +102,6 @@ func NewUIHandler(opts *Options) *Handler {
|
|||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Handler) canDownload(url string) bool {
|
|
||||||
u.downloadOnce.Do(func() {
|
|
||||||
if err := serveIndex(ioutil.Discard, url); err == nil {
|
|
||||||
u.downloadSuccess = true
|
|
||||||
} else {
|
|
||||||
logrus.Errorf("Failed to download %s, falling back to packaged UI", url)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return u.downloadSuccess
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *Handler) path() (path string, isURL bool) {
|
func (u *Handler) path() (path string, isURL bool) {
|
||||||
switch u.offlineSetting() {
|
switch u.offlineSetting() {
|
||||||
case "dynamic":
|
case "dynamic":
|
||||||
@ -132,37 +119,15 @@ func (u *Handler) path() (path string, isURL bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Handler) ServeAsset() http.Handler {
|
func (u *Handler) canDownload(url string) bool {
|
||||||
return u.middleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
u.downloadOnce.Do(func() {
|
||||||
http.FileServer(http.Dir(u.pathSetting())).ServeHTTP(rw, req)
|
if err := serveIndex(ioutil.Discard, url); err == nil {
|
||||||
}))
|
u.downloadSuccess = true
|
||||||
}
|
|
||||||
|
|
||||||
func (u *Handler) ServeFaviconDashboard() http.Handler {
|
|
||||||
return u.middleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
||||||
http.FileServer(http.Dir(filepath.Join(u.pathSetting(), "dashboard"))).ServeHTTP(rw, req)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *Handler) IndexFileOnNotFound() http.Handler {
|
|
||||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
||||||
// we ignore directories here because we want those to come from the CDN when running in that mode
|
|
||||||
if stat, err := os.Stat(filepath.Join(u.pathSetting(), req.URL.Path)); err == nil && !stat.IsDir() {
|
|
||||||
u.ServeAsset().ServeHTTP(rw, req)
|
|
||||||
} else {
|
} else {
|
||||||
u.IndexFile().ServeHTTP(rw, req)
|
logrus.Errorf("Failed to download %s, falling back to packaged UI", url)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
return u.downloadSuccess
|
||||||
|
|
||||||
func (u *Handler) IndexFile() http.Handler {
|
|
||||||
return u.indexMiddleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
||||||
if path, isURL := u.path(); isURL {
|
|
||||||
_ = serveIndex(rw, path)
|
|
||||||
} else {
|
|
||||||
http.ServeFile(rw, req, filepath.Join(path, "index.html"))
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveIndex(resp io.Writer, url string) error {
|
func serveIndex(resp io.Writer, url string) error {
|
||||||
|
@ -5,10 +5,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
"github.com/rancher/steve/pkg/version"
|
||||||
|
|
||||||
var (
|
|
||||||
UIOffline = "dynamic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func New(path string) http.Handler {
|
func New(path string) http.Handler {
|
||||||
@ -20,7 +17,13 @@ func New(path string) http.Handler {
|
|||||||
return path
|
return path
|
||||||
},
|
},
|
||||||
Offline: func() string {
|
Offline: func() string {
|
||||||
return UIOffline
|
if path != "" {
|
||||||
|
return "true"
|
||||||
|
}
|
||||||
|
return "dynamic"
|
||||||
|
},
|
||||||
|
ReleaseSetting: func() bool {
|
||||||
|
return version.IsRelease()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,12 +1,23 @@
|
|||||||
package version
|
package version
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Version = "dev"
|
Version = "dev"
|
||||||
GitCommit = "HEAD"
|
GitCommit = "HEAD"
|
||||||
|
|
||||||
|
// K-EXPLORER
|
||||||
|
releasePattern = regexp.MustCompile("^v[0-9]")
|
||||||
)
|
)
|
||||||
|
|
||||||
func FriendlyVersion() string {
|
func FriendlyVersion() string {
|
||||||
return fmt.Sprintf("%s (%s)", Version, GitCommit)
|
return fmt.Sprintf("%s (%s)", Version, GitCommit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsRelease() bool {
|
||||||
|
return !strings.Contains(Version, "dev") && releasePattern.MatchString(Version)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user