Skip to content

Commit aeaf94c

Browse files
committed
feat(webkubectl): 集成webkubectl
1 parent cff4012 commit aeaf94c

File tree

13 files changed

+196
-46
lines changed

13 files changed

+196
-46
lines changed

.kube/config

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<html>
2+
<head><title>302 Found</title></head>
3+
<body bgcolor="white">
4+
<center><h1>302 Found</h1></center>
5+
<hr><center>bfe/1.0.8.18</center>
6+
</body>
7+
</html>

internal/api/v1/v1.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -272,5 +272,5 @@ func AddV1Route(app iris.Party) {
272272
proxy.Install(authParty)
273273
ws.Install(authParty)
274274
chart.Install(authParty)
275-
webkubectl.Install(authParty)
275+
webkubectl.Install(authParty, v1Party)
276276
}

internal/api/v1/webkubectl/types.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ type Session struct {
99
}
1010

1111
type SessionResponse struct {
12-
SessionId string `json:"sessionId"`
12+
Token string `json:"token"`
1313
}

internal/api/v1/webkubectl/webkubectl.go

+22-20
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"github.com/google/uuid"
1111
"github.com/kataras/iris/v12"
1212
"github.com/kataras/iris/v12/context"
13-
"k8s.io/client-go/rest"
1413
"k8s.io/client-go/tools/clientcmd"
1514
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
1615
)
@@ -31,15 +30,15 @@ func NewHandler() *Handler {
3130

3231
func (h *Handler) GetConfigFile() iris.Handler {
3332
return func(ctx *context.Context) {
34-
sessionId := ctx.URLParam("session")
33+
sessionId := ctx.URLParam("token")
3534
requireLen := len(uuid.New().String())
3635
if len(sessionId) != requireLen {
3736
ctx.StatusCode(iris.StatusBadRequest)
3837
ctx.Values().Set("message", fmt.Sprintf("sessionId length must be %d", requireLen))
3938
return
4039
}
41-
cfg := h.sessionCache.Get(sessionId)
42-
if cfg != nil {
40+
sess := h.sessionCache.Get(sessionId)
41+
if sess != nil {
4342
h.sessionCache.Delete(sessionId)
4443
} else {
4544
ctx.StatusCode(iris.StatusInternalServerError)
@@ -51,7 +50,7 @@ func (h *Handler) GetConfigFile() iris.Handler {
5150
ctx.Header("Content-Disposition", "attachment;filename=config")
5251
ctx.Header("Content-Transfer-Encoding", "binary")
5352

54-
cc := toCmdConfig(*cfg.config)
53+
cc := toCmdConfig(sess)
5554
bs, err := clientcmd.Write(*cc)
5655
if err != nil {
5756
ctx.StatusCode(iris.StatusInternalServerError)
@@ -62,20 +61,23 @@ func (h *Handler) GetConfigFile() iris.Handler {
6261
}
6362
}
6463

65-
func toCmdConfig(rc rest.Config) *clientcmdapi.Config {
64+
func toCmdConfig(sess *Session) *clientcmdapi.Config {
6665
cc := clientcmdapi.NewConfig()
67-
cc.Clusters["default"] = &clientcmdapi.Cluster{
68-
Server: rc.Host,
66+
cc.Clusters[sess.Cluster] = &clientcmdapi.Cluster{
67+
Server: sess.config.Host,
68+
InsecureSkipTLSVerify: true,
6969
}
70-
cc.AuthInfos["default"] = &clientcmdapi.AuthInfo{
71-
ClientCertificateData: rc.CAData,
72-
ClientKeyData: rc.KeyData,
73-
Token: rc.BearerToken,
70+
cc.AuthInfos[sess.User] = &clientcmdapi.AuthInfo{
71+
ClientCertificateData: sess.config.CertData,
72+
ClientKeyData: sess.config.KeyData,
73+
Token: sess.config.BearerToken,
7474
}
75-
cc.Contexts["default"] = &clientcmdapi.Context{
76-
Cluster: "default",
77-
AuthInfo: "default",
75+
contextName := fmt.Sprintf("%s@%s", sess.Cluster, sess.User)
76+
cc.Contexts[contextName] = &clientcmdapi.Context{
77+
Cluster: sess.Cluster,
78+
AuthInfo: sess.User,
7879
}
80+
cc.CurrentContext = contextName
7981
return cc
8082
}
8183

@@ -113,15 +115,15 @@ func (h *Handler) CreateSession() iris.Handler {
113115
cfg.CertData = rb.Certificate
114116
}
115117
sess.config = cfg
118+
sess.User = profile.Name
116119
sessionId := uuid.New().String()
117120
h.sessionCache.Put(sessionId, &sess)
118-
ctx.Values().Set("data", &SessionResponse{SessionId: sessionId})
121+
ctx.Values().Set("data", &SessionResponse{Token: sessionId})
119122
}
120123
}
121124

122-
func Install(parent iris.Party) {
125+
func Install(authParent, noAuthParty iris.Party) {
123126
handler := NewHandler()
124-
sp := parent.Party("/webkubectl")
125-
sp.Get("/session", handler.GetConfigFile())
126-
sp.Post("/session", handler.CreateSession())
127+
authParent.Post("/webkubectl/session", handler.CreateSession())
128+
noAuthParty.Get("/webkubectl/session", handler.GetConfigFile())
127129
}

internal/server/server.go

+44-7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"embed"
55
"fmt"
66
"net/http"
7+
"net/http/httputil"
8+
"net/url"
79
"os"
810
"path"
911
"strings"
@@ -74,17 +76,23 @@ func (e *KubePiSerer) setUpDB() {
7476
func (e *KubePiSerer) setUpStaticFile() {
7577
spaOption := iris.DirOptions{SPA: true, IndexName: "index.html"}
7678

79+
party := e.Party("/")
80+
party.Get("/", func(ctx *context.Context) {
81+
ctx.Redirect("/kubepi")
82+
})
83+
party.Use(iris.Compression)
7784
dashboardFS := iris.PrefixDir("web/dashboard", http.FS(EmbedWebDashboard))
78-
e.RegisterView(view.HTML(dashboardFS, ".html"))
79-
e.HandleDir("/dashboard/", dashboardFS, spaOption)
85+
party.RegisterView(view.HTML(dashboardFS, ".html"))
86+
party.HandleDir("/dashboard/", dashboardFS, spaOption)
8087

8188
terminalFS := iris.PrefixDir("web/terminal", http.FS(EmbedWebTerminal))
82-
e.RegisterView(view.HTML(terminalFS, ".html"))
83-
e.HandleDir("/terminal/", terminalFS, spaOption)
89+
party.RegisterView(view.HTML(terminalFS, ".html"))
90+
party.HandleDir("/terminal/", terminalFS, spaOption)
8491

8592
kubePiFS := iris.PrefixDir("web/kubepi", http.FS(EmbedWebKubePi))
86-
e.RegisterView(view.HTML(kubePiFS, ".html"))
87-
e.HandleDir("/kubepi/", kubePiFS, spaOption)
93+
party.RegisterView(view.HTML(kubePiFS, ".html"))
94+
party.HandleDir("/kubepi/", kubePiFS, spaOption)
95+
8896
}
8997

9098
func (e *KubePiSerer) setUpSession() {
@@ -104,6 +112,11 @@ func (e *KubePiSerer) setResultHandler() {
104112
isProxyPath := func() bool {
105113
p := ctx.GetCurrentRoute().Path()
106114
ss := strings.Split(p, "/")
115+
if len(ss) > 0 {
116+
if ss[0] == "webkubectl" {
117+
return true
118+
}
119+
}
107120
if len(ss) >= 3 {
108121
for i := range ss {
109122
if ss[i] == "proxy" || ss[i] == "ws" {
@@ -169,17 +182,41 @@ func (e *KubePiSerer) setUpErrHandler() {
169182
func (e *KubePiSerer) runMigrations() {
170183
migrate.RunMigrate(e.db, e.logger)
171184
}
185+
func (e *KubePiSerer) setWebkubectlProxy() {
186+
handler := func(ctx *context.Context) {
187+
p := ctx.Params().Get("p")
188+
if strings.Contains(p, "root") {
189+
ctx.Request().URL.Path = strings.ReplaceAll(ctx.Request().URL.Path, "root", "")
190+
ctx.Request().RequestURI = strings.ReplaceAll(ctx.Request().RequestURI, "root", "")
191+
}
192+
u, _ := url.Parse("http://localhost:8080")
193+
proxy := httputil.NewSingleHostReverseProxy(u)
194+
proxy.ModifyResponse = func(resp *http.Response) error {
195+
if resp.StatusCode == iris.StatusMovedPermanently {
196+
// 重定向重写
197+
if resp.Header.Get("Location") == "/webkubectl/" {
198+
resp.Header.Set("Location", "/webkubectl/root")
199+
}
200+
}
201+
return nil
202+
}
203+
proxy.ServeHTTP(ctx.ResponseWriter(), ctx.Request())
204+
}
205+
e.Any("/webkubectl/{p:path}", handler)
206+
e.Any("webkubectl", handler)
207+
}
172208

173209
func (e *KubePiSerer) bootstrap() *KubePiSerer {
174210
e.Application = iris.New()
175-
e.Application.Use(iris.Compression)
211+
//e.Application.Use(iris.Compression)
176212
e.setUpStaticFile()
177213
e.setUpConfig()
178214
e.setUpLogger()
179215
e.setUpDB()
180216
e.setUpSession()
181217
e.setResultHandler()
182218
e.setUpErrHandler()
219+
e.setWebkubectlProxy()
183220
e.runMigrations()
184221
return e
185222
}

thirdparty/gotty/.kube/config

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
apiVersion: v1
2+
clusters:
3+
- cluster:
4+
insecure-skip-tls-verify: true
5+
server: https://172.16.10.41:8443
6+
name: abcd
7+
contexts:
8+
- context:
9+
cluster: abcd
10+
user: admin
11+
name: abcd@admin
12+
current-context: abcd@admin
13+
kind: Config
14+
preferences: {}
15+
users:
16+
- name: admin
17+
user:
18+
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURBRENDQWVpZ0F3SUJBZ0lKQVBraVB1OVZxTjhHTUEwR0NTcUdTSWIzRFFFQkN3VUFNQlV4RXpBUkJnTlYKQkFNTUNtdDFZbVZ5Ym1WMFpYTXdJQmNOTWpFd056SXpNRFkwTkRFeVdoZ1BNakV5TVRBMk1qa3dOalEwTVRKYQpNRFF4R1RBWEJnTlZCQU1NRUd0MVltVnlibVYwWlhNdFlXUnRhVzR4RnpBVkJnTlZCQW9NRG5ONWMzUmxiVHB0CllYTjBaWEp6TUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF6eEhRTUN3OWh5STYKeGVaNEVmbXcwV0xqWE0rZnV6Q0t1bHByeXhQZVAzTVh3VmRrK2MybEUrZG9oTENDMDZ1QlBzVXJ1RVN6NGhvVApyQ1ZCOGpLb21UUWpXb2ExRWtUaDRBZUZHQUNjandNcU53Uk16TG9aeWxUSUs2eXdxNzNyVnlYSjQyVUVyR0pzCnpPd1ZpYVlaQ0k4MW13aWttaFBaaDhMSk1JZ1JSMzVtbXFBSmU0OTlZd2ZzWnRTNXljalUyaGd4dDduamxDVjkKQ1cxWEJUS1ppbWlNeDBWN3BidjBmaHp5UHpVcnBqM1p1azJtSzZzcUYyaFNpMFB5alVyWkFJSVNzYmtyOVRuUAo2MFhPZUt2Tkw2dk1Gc3F1aXpKbWZvUXh5R2UvbUoxSjRLRGgxNzAwN3pSa3ZiMVc0dnJtODFYQ1U0T0p5TmEwCmVnRTl6OG9iQ1FJREFRQUJvekl3TURBSkJnTlZIUk1FQWpBQU1BNEdBMVVkRHdFQi93UUVBd0lGb0RBVEJnTlYKSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBbHNLSmF0QzNsbFF5VjNBdwpZbUNMT1ZuTzkxQXlzTEhPa0xJQVAwblFYRldiQzBXeG1ja09uS2tseCsxZ29xV2o2a2Z2UC9xT3JJZitPaUcwClhGazJIQ21UM2pzTlA3ck9DUnZBUVpQQ3VLeVVUejJoYXQ4Z25LOFhIemU4QzBOTmxQaG94eE1ieVRWNXRKbUkKbFRkNFNpWlVrcWVUSU44OEQ5WkgvTWlVbHN2aTllNWg3U3BkdC9RcU9BNlp1T0V4RjI5dlMvU29hNDVTUnNuTgpaU3Y5WU84bUEvSS9iRHE3cFAyUVR2TjF1T1ZMTUF1K0QzQlNkQllwUU1BTnZkMkM1TkFBYmZVcUdBTldhZlhXClFNeHhDaE9ybjlWRWRYSG1DSzJSdUN4STRYVXdzZVo3dFpUNnJZV0NEc2p1T0ZCc2VDYTR6aEQ2c0pBM3RQeWgKNXQ5QXBBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
19+
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBenhIUU1DdzloeUk2eGVaNEVmbXcwV0xqWE0rZnV6Q0t1bHByeXhQZVAzTVh3VmRrCitjMmxFK2RvaExDQzA2dUJQc1VydUVTejRob1RyQ1ZCOGpLb21UUWpXb2ExRWtUaDRBZUZHQUNjandNcU53Uk0KekxvWnlsVElLNnl3cTczclZ5WEo0MlVFckdKc3pPd1ZpYVlaQ0k4MW13aWttaFBaaDhMSk1JZ1JSMzVtbXFBSgplNDk5WXdmc1p0UzV5Y2pVMmhneHQ3bmpsQ1Y5Q1cxWEJUS1ppbWlNeDBWN3BidjBmaHp5UHpVcnBqM1p1azJtCks2c3FGMmhTaTBQeWpVclpBSUlTc2JrcjlUblA2MFhPZUt2Tkw2dk1Gc3F1aXpKbWZvUXh5R2UvbUoxSjRLRGgKMTcwMDd6Umt2YjFXNHZybTgxWENVNE9KeU5hMGVnRTl6OG9iQ1FJREFRQUJBb0lCQUJQdkRoQ2xJYU14cTJERAp5QWxLOVRlakFtbzczbytobzZrKzdTT1duUTJVb2RQTit2MkZTMy80QmZySUYwQVRRWlR1WEVBWDlRMHVIM2l4CitZQThXaml0YVQzY3UrK3ZTK05LTmdqU2ZqL1NPUzBrcjlRQmtsd0UvMlFjOHNFRnZuTWNReXd0M0Y1UDkxTmUKNHMvbWVvQjFjTjZrM1Z1cno0UWZQMGo1d0ZnV2FhdEdab1hxeis3OXVwcHArY3p2bzRieUh5bmFEVTZjTVFlagp0c0RFMy9lcHpQNGxFaGU3VG5kVUo0RS9Qb2haN05LT1lVOTRQOUt2ZDBoVG5leWVEdERFVk5Va2x1RGo0TDh6Ck8vR2QyYVpsTjZsMjhCY1VMK3JSbGcydUJCaXJPdXlPckMzb3hhdFdxVm9jUlZVM0tJV0lNK05ib0FpTjNFQ2gKT1F5Mi93RUNnWUVBNk0wTEpXYXdCK09KOEZPMXFlMEtPZUJFckluY0U0NEFvNEFvMStTUzF2YlNvbkJQclRLSgpwNFN5REFqVnlLY0djbDlJOThaaTJiN1VqWWcyaU16cDNpSE1jQ2lmb3gxVW9oVkQ3Z0F4SXY0K0kzUFFVV3ZIClc3WmhrSzl2N285ZEtVNTI0Y1NFa0FqWmRCTEYrbFZ5Rmw2RTBmK0dOVzlCcVdUNEovY1NRdUVDZ1lFQTQ3UlgKQk8rY0VSUDZGZUEyM3piT3ZIZitod0dXbC9YYU0raHVVTVRhcGQza004bERUSDF5UGU5ZXNUNUJOTlkzRW5MegpVZGgva1ZwWjA3V044MkVHRWV5K1FPTmR5THVLdFFrRGFiVTdNUStYc2JLODhzVVZKQStjaHhPd3NiazRNTTl6Cm5obEU2bHp5d0VUUERQWXorcUd3dENFMC9EV2lwUGg5bUVOdkJTa0NnWUVBc3k0dWFBa1RiOTc0S2x1anRUaFcKNkpqdkFvWFJOOVBTTmdYTWFlN3AvajVYZk9OSTBCdlh5M0hjd0ZxRWdUM2RQbWFNZVBqZTJJMkNkN1RobElWegpjNTg4QlErZlh5S1NJM2FPcEQ3Tmt3dHd0RVROai8yT29jNU1aZHBDSXJHQm1PdGxvUHRxdEptN0ZSWGwvM21JCnFDTHp3OVlTYkVwSGxhTzdSTGRyL01FQ2dZQWh3dHBvSS9LNjRZQmgvL1dDMFFpRUR6S1E1OTVoTjVXYnZxbWsKbGh0cEhtWTRlMjNjQ0htSXoxWDE1Nm9aWUcyWDhhMDhCR0tkdFl5K0JCeEE2ckRRdFk1YjVwcEFLZlpkOHpFcgpXakNsakk5TVRKa1JVY0Zac0dyZjZENTVpMkhXY3R2TXBDaUFxemxlNHBUa2NoaXczNHBvbXNmVllVcDFOVmJCCjNiVzNTUUtCZ0V2YWVUQ1RWU0tTTjlqTE1aem5sd0R5YllEaTFHVDVMdVBtSGkrNlVaeFpKUFVvVjIweEw4Y2gKL2Z2bGk2NSs4RTdteDVGT3V0RVkyWUJKd01DOXpRczVabjVHSHMwUm45Sm1uNE83TzM1R05SSGpBdjdiK3J2NwpwMHBLeXRBT3ZQSTQ4U3FjM0xwSzVUc1E3ZCtEWThZYUw5TjVCV00vTmVFM3VMWW15bG5OCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==

thirdparty/gotty/init-kube.sh

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/bin/bash
2+
set -e
3+
4+
5+
6+
7+
8+
#if [ "${WELCOME_BANNER}" ]; then
9+
# echo ${WELCOME_BANNER}
10+
#fi
11+
#
12+
arg1=$1
13+
#
14+
#mkdir -p /nonexistent
15+
#mount -t tmpfs -o size=${SESSION_STORAGE_SIZE} tmpfs /nonexistent
16+
#cd /nonexistent
17+
#cp /root/.bashrc ./
18+
#cp /etc/vim/vimrc.local .vimrc
19+
#echo 'source /opt/kubectl-aliases/.kubectl_aliases' >> .bashrc
20+
#echo -e 'PS1="> "\nalias ll="ls -la"' >> .bashrc
21+
mkdir -p .kube
22+
#
23+
#export HOME=/nonexistent
24+
#
25+
echo "download kubeconfig file..."
26+
27+
code=`curl -w %{http_code} -s -o .kube/config http://localhost/api/v1/webkubectl/session?token=${arg1}`
28+
29+
if [[ $code == '200' ]];
30+
then
31+
echo "download kubeconfig success"
32+
else
33+
echo "download kubeconfig failed"
34+
exit
35+
fi
36+
37+
38+
#
39+
#chown -R nobody:nogroup .kube
40+
#export TMPDIR=/nonexistent
41+
#
42+
#envs=`env`
43+
#for env in ${envs[@]}; do
44+
# if [[ $env == GOTTY* ]];
45+
# then
46+
# unset ${env%%=*}
47+
# fi
48+
#done
49+
#
50+
#unset WELCOME_BANNER PPROF_ENABLED KUBECTL_INSECURE_SKIP_TLS_VERIFY SESSION_STORAGE_SIZE KUBECTL_VERSION
51+
#
52+
#exec su -s /bin/bash nobody
53+
exec /bin/bash

thirdparty/gotty/server/handlers.go

+1-14
Original file line numberDiff line numberDiff line change
@@ -127,20 +127,7 @@ func (server *Server) processWSConn(ctx context.Context, conn *websocket.Conn) e
127127
windowTitle := ""
128128
params := query.Query()
129129
params.Del("arg")
130-
arg := ""
131-
if len(params.Get("token")) > 0 {
132-
ttyParameter := server.cache.Get(params.Get("token"))
133-
cachedKey := params.Get("token")
134-
if ttyParameter != nil {
135-
windowTitle = ttyParameter.Title
136-
arg = ttyParameter.Arg
137-
server.cache.Delete(cachedKey)
138-
} else {
139-
arg = "ERROR:Invalid Token"
140-
}
141-
} else {
142-
arg = "ERROR:No Token Provided"
143-
}
130+
arg := params.Get("token")
144131
params.Add("arg", arg)
145132
log.Println("arg: " + arg)
146133
var slave Slave

thirdparty/gotty/server/server.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func (server *Server) Run(ctx context.Context, options ...RunOption) error {
115115
}
116116

117117
counter := newCounter(time.Duration(server.options.Timeout) * time.Second)
118-
path := "/terminal/"
118+
path := "/webkubectl/"
119119
customPath := os.Getenv("TERMINAL_PATH")
120120
if len(customPath) > 0 {
121121
path = customPath

web/dashboard/src/api/webkubectl.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {post} from "@/plugins/request";
2+
3+
const baseUrl = "/api/v1/webkubectl/session"
4+
5+
export function getTerminalSession(clusterName) {
6+
return post(`${baseUrl}`, {cluster: clusterName})
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<template>
2+
<div style="margin-right: 20px;margin-top: 5px">
3+
<el-button size="mini" @click="onOpenTerminal"><i class="el-icon-arrow-right"/>Terminal</el-button>
4+
</div>
5+
</template>
6+
7+
<script>
8+
import {getTerminalSession} from "@/api/webkubectl";
9+
10+
export default {
11+
name: "TerminalEnter",
12+
data() {
13+
return {}
14+
},
15+
methods: {
16+
onOpenTerminal() {
17+
const clusterName = this.$store.getters.cluster
18+
getTerminalSession(clusterName).then(data => {
19+
const token = data.data.token
20+
const url = `/webkubectl/root?token=${token}`
21+
console.log(url)
22+
open(url, "_black")
23+
})
24+
}
25+
}
26+
}
27+
</script>
28+
29+
<style scoped>
30+
31+
</style>

web/dashboard/src/business/app-layout/horizontal-layout/HorizontalHeader.vue

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
<div class="header-left">
44
<sidebar-toggle-button/>
55
<home></home>
6-
<project-switch style="margin-left: 20px" ></project-switch>
6+
<project-switch style="margin-left: 20px"></project-switch>
77
</div>
88
<div class="header-right">
9+
<TerminalEnter></TerminalEnter>
910
<language-switch></language-switch>
1011
<user-setting style="margin-left: 20px"></user-setting>
1112
</div>
@@ -18,10 +19,11 @@ import UserSetting from "@/business/app-layout/header-components/UserSetting"
1819
import Home from "@/business/app-layout/header-components/Home"
1920
import ProjectSwitch from "@/business/app-layout/header-components/ProjectSwitch";
2021
import LanguageSwitch from "@/business/app-layout/header-components/LanguageSwitch"
22+
import TerminalEnter from "@/business/app-layout/header-components/TerminalEnter";
2123
2224
export default {
2325
name: "HorizontalHeader",
24-
components: { LanguageSwitch, ProjectSwitch, Home, UserSetting, SidebarToggleButton}
26+
components: {TerminalEnter, LanguageSwitch, ProjectSwitch, Home, UserSetting, SidebarToggleButton}
2527
}
2628
</script>
2729

web/kubepi/vue.config.js

+5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ module.exports = {
2020
ws: true,
2121
secure: false,
2222
},
23+
'/webkubectl': {
24+
target: 'http://0.0.0.0:80',
25+
ws: true,
26+
secure: false,
27+
},
2328
'/dashboard': {
2429
target: 'http://0.0.0.0:4400',
2530
},

0 commit comments

Comments
 (0)