├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── build.sh ├── conf ├── app.conf └── titletext ├── controllers ├── base.go ├── index.go └── server.go ├── doc ├── 1.png └── 2.png ├── go.mod ├── go.sum ├── k8-deploy.yaml ├── main.go ├── routers ├── commentsRouter_.go ├── commentsRouter_controllers.go └── router.go ├── static ├── assets │ ├── app.css │ ├── main.js │ ├── reconnecting-websocket │ │ └── reconnecting-websocket.min.js │ ├── style.css │ ├── svg │ │ ├── docker.svg │ │ ├── menu.svg │ │ └── terminal.svg │ └── xterm.js │ │ ├── .editorconfig │ │ ├── Dockerfile │ │ ├── LICENSE │ │ ├── Procfile.dev │ │ ├── README.md │ │ ├── addons │ │ ├── attach │ │ │ ├── attach.js │ │ │ ├── index.html │ │ │ └── package.json │ │ ├── fit │ │ │ ├── fit.js │ │ │ └── package.json │ │ ├── fullscreen │ │ │ ├── fullscreen.css │ │ │ ├── fullscreen.js │ │ │ └── package.json │ │ └── linkify │ │ │ ├── index.html │ │ │ ├── linkify.js │ │ │ └── package.json │ │ ├── bower.json │ │ ├── demo │ │ ├── app.js │ │ ├── index.html │ │ ├── main.js │ │ └── style.css │ │ ├── jsdoc.json │ │ ├── package.json │ │ ├── src │ │ ├── xterm.css │ │ └── xterm.js │ │ ├── test │ │ ├── addons │ │ │ ├── linkify-test.js │ │ │ └── test.js │ │ └── test.js │ │ └── xtermjs.png ├── build.css ├── build.css.map ├── build.js ├── build.js.map ├── config.js ├── frontend │ ├── boot.js │ ├── dashboard │ │ ├── Dashboard.js │ │ ├── DashboardController.js │ │ ├── DashboardService.js │ │ └── view │ │ │ └── dashboard.html │ ├── main.js │ └── utils │ │ ├── LogDecorator.js │ │ └── supplant.js └── js │ └── reload.min.js ├── tests └── default_test.go └── views ├── index.html └── terminal.html /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | kkk 8 | config 9 | k8 10 | # Test binary, build with `go test -c` 11 | *.test 12 | *.git 13 | *.idea 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | from golang 2 | MAINTAINER Eric 3 | COPY ./conf /opt/conf 4 | COPY ./static /opt/static 5 | COPY ./views /opt/views 6 | COPY ./k8-web-terminal /opt/k8-web-terminal 7 | WORKDIR /opt/ 8 | CMD ["./k8-web-terminal"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 吉超 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 本项目使用 beego + websocket 进行开发 2 | 3 | ``` 4 | _ __ __ ___ _____ 5 | /_\ /\ /\ \/ / / _ \\_ \ 6 | //_\\/ / \ \ / / /_)/ / /\/ 7 | / _ \ \_/ / \/ ___/\/ /_ 8 | \_/ \_/\___/_/\_\/ \____/ 9 | 10 | 🍭 A k8 web terminal TOOL 🍭 11 | ``` 12 | 13 | ## k8 web terminal 14 | 15 | 一个k8s web终端连接工具,在前后端分离或未分离项目中心中,也可以把此项目无缝集成,开箱即用。 16 | 17 | ## 实现细节 18 | 19 | 前端用xterm.js库,它是模拟一个terminal在浏览器中,并没有通讯能力。需要在前端建立websocket,连到自己开发的websocket服务端。服务端会基于k8s的remotecommand包,建立与container的ssh长连接,remotecommand包只需要我们提供3个回调,其中read是来获取terminal的输入,我们要做的就是读取websocket发来的数据,然后返回给read回调;write则是用来向terminal发送数据,我们要做的就是调用websocket把数据写到前端,然后前端把数据写给xterm即可;next是用来获取terminal的实际大小的,sshd服务端需要知道终端的大小,这样决定了它一行输出多少个字符就要输出一次换行符。所以,当浏览器窗口改变影响了terminal大小的时候,前端应该把最新的terminal大小发给服务端,然后在next回调中返回其大小。 20 | 21 | 下面是真实的效果 22 | 23 |  24 | 25 |  26 | 27 | ## 使用方式 28 | - 1 29 | 在k8s集群内部署:使用"k8-deploy.yaml"编排进行部署 30 | ```cgo 31 | [root@hub-01 ~]# kubectl apply -f k8-deploy.yaml 32 | deployment "terminal-xcbgj" created 33 | service "terminal" created 34 | [root@hub-01 ~]# kubectl get po 35 | NAME READY STATUS RESTARTS AGE 36 | terminal-xcbgj-78c7ff9ffc-kp99q 1/1 Running 0 26s 37 | 38 | - 2 39 | 在集群外部署: 需要有/root/.kube/config文件 40 | ```cgo 41 | [root@k8s-01 k8]# ls /root/.kube/config 42 | /root/.kube/config 43 | [root@k8s-01 k8]# ls 44 | conf k8-web-terminal static views 45 | [root@k8s-01 k8]# ./k8-web-terminal 46 | 2019/07/29 10:25:06.793 [I] [asm_amd64.s:1333] http server Running on http://:8089 47 | # 也可以手动指定文件路径 48 | ./k8-web-terminal --kubeconfig ./config 49 | 50 | 51 | ## 感谢以下框架的开源支持 52 | 53 | - [Beego] - 54 | 55 | ## MIT License 56 | 57 | ``` 58 | Copyright (c) 2019 Eric 59 | ``` -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | GOOS=linux go build -o bin/k8-web-terminal 3 | GOOS=darwin go build -o bin/k8-web-terminal-mac 4 | GOOS=windows go build -o bin/k8-web-terminal.exe -------------------------------------------------------------------------------- /conf/app.conf: -------------------------------------------------------------------------------- 1 | appname = k8-web-terminal 2 | httpport = 8080 3 | runmode = "${API_RUN_MODE||dev}" 4 | 5 | kubeapiserver = "" 6 | -------------------------------------------------------------------------------- /conf/titletext: -------------------------------------------------------------------------------- 1 | ********************************************************** 2 | * * * * * * * * * Welcome to K8 * * * * * * * * * 3 | ********************************************************** 4 | Note: if TERM is not set, you should 'export TERM=vt100' 5 | first before using interactive command. 6 | -------------------------------------------------------------------------------- /controllers/base.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "os" 5 | "k8s.io/client-go/kubernetes" 6 | "k8s.io/client-go/rest" 7 | "flag" 8 | "path/filepath" 9 | "k8s.io/client-go/tools/clientcmd" 10 | ) 11 | 12 | var Clientset *kubernetes.Clientset 13 | var Config *rest.Config 14 | 15 | func homeDir() string { 16 | if h := os.Getenv("HOME"); h != "" { 17 | return h 18 | } 19 | return os.Getenv("USERPROFILE") // windows 20 | } 21 | func init() { 22 | // 配置 k8s 集群外 kubeconfig 配置文件,默认位置 $HOME/.kube/config 23 | // 24 | var kubeconfig *string 25 | if home := homeDir(); home != "" { 26 | kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file") 27 | }else { 28 | kubeconfig = flag.String("kubeconfig", "", "(optional) absolute path to the kubeconfig file") 29 | } 30 | flag.Parse() 31 | 32 | //在 kubeconfig 中使用当前上下文环境,config 获取支持 url 和 path 方式 33 | Config, err =rest.InClusterConfig() 34 | if err != nil { 35 | Config, err = clientcmd.BuildConfigFromFlags("", *kubeconfig) 36 | if err != nil { 37 | panic(err.Error()) 38 | } 39 | Clientset, err = kubernetes.NewForConfig(Config) 40 | if err != nil { 41 | panic(err.Error()) 42 | } 43 | }else { 44 | Clientset, err = kubernetes.NewForConfig(Config) 45 | if err != nil { 46 | panic(err.Error()) 47 | } 48 | } 49 | 50 | 51 | //// 集群内方式 52 | //Config, err = rest.InClusterConfig() 53 | //if err != nil { 54 | // panic(err.Error()) 55 | //} 56 | // 57 | //Clientset, err = kubernetes.NewForConfig(Config) 58 | //if err != nil { 59 | // panic(err.Error()) 60 | //} 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /controllers/index.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/astaxie/beego" 5 | "log" 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | "strings" 8 | "k8s.io/api/core/v1" 9 | "time" 10 | ) 11 | 12 | 13 | type MainController struct { 14 | beego.Controller 15 | } 16 | 17 | 18 | func (this *MainController) URLMapping() { 19 | this.Mapping("Nodes",this.Nodes) 20 | this.Mapping("NodePods",this.NodePods) 21 | this.Mapping("ContainerTerminal",this.ContainerTerminal) 22 | } 23 | 24 | // @router / [get] 25 | func (c *MainController) Get() { 26 | c.TplName = "index.html" 27 | 28 | 29 | } 30 | 31 | 32 | // @Title 获取所有节点信息 33 | // @Description 获取所有节点信息 34 | // @Success 200 {string} 35 | // @Failure 404 body is empty 36 | // @router /api/nodes [get] 37 | func (c *MainController) Nodes() { 38 | resp, err := Clientset.CoreV1().Nodes().List(metav1.ListOptions{}) 39 | if err != nil { 40 | log.Fatal(err) 41 | } 42 | c.Data["json"] = resp.Items 43 | c.ServeJSON() 44 | } 45 | 46 | // @Title 获取节点上的所有容器 47 | // @Description 获取节点上的所有容器 48 | // @Success 200 {string} 49 | // @Failure 404 body is empty 50 | // @router /api/nodes/containers [get] 51 | func (c *MainController) NodePods() { 52 | nodeip := c.GetString("node") 53 | //resp, err := Clientset.CoreV1().Nodes().List(metav1.ListOptions{}) 54 | nodespod111, err := Clientset.CoreV1().Pods(v1.NamespaceAll).List(metav1.ListOptions{ 55 | FieldSelector: "spec.nodeName=" + nodeip, 56 | }) 57 | if err != nil { 58 | log.Fatal(err) 59 | } 60 | var mapss []interface{} 61 | for _, pod := range nodespod111.Items { 62 | pods := pod 63 | var ImageID string 64 | var ContainerID string 65 | var Created int64 66 | var Status string 67 | if pods.Status.ContainerStatuses != nil { 68 | ImageID = pods.Status.ContainerStatuses[0].ImageID 69 | ContainerID = strings.Join(strings.Split(pods.Status.ContainerStatuses[0].ContainerID,"docker://"),"") 70 | 71 | if pods.Status.ContainerStatuses[0].State.Running != nil { 72 | Created = pods.Status.ContainerStatuses[0].State.Running.StartedAt.Unix() 73 | }else { 74 | Created = time.Now().Unix() 75 | } 76 | } else { 77 | ImageID = "" 78 | ContainerID = "" 79 | 80 | } 81 | if pods.Status.Conditions != nil { 82 | if pods.Status.Conditions[1].Status == "True" { 83 | Status = "Ready" 84 | } else { 85 | Status = pods.Status.Conditions[1].Reason 86 | } 87 | } else { 88 | Status = "NotReady" 89 | } 90 | maps := map[string]interface{}{ 91 | "Name": pods.Name, 92 | "Namespace": pods.Namespace, 93 | "NodeName": pods.Spec.NodeName, 94 | "Labels": pods.ObjectMeta.Labels, 95 | "SelfLink": pods.ObjectMeta.SelfLink, 96 | "Uid": pods.ObjectMeta.UID, 97 | "Status": Status, 98 | "IP": pods.Status.PodIP, 99 | "Image": pods.Spec.Containers[0].Image, 100 | "AppName": pods.Spec.Containers[0].Name, 101 | "ImageID": ImageID, 102 | "Id": ContainerID, 103 | "Created": Created, 104 | "Command": pod.Spec.Containers[0].Command, 105 | 106 | } 107 | mapss = append(mapss, maps) 108 | } 109 | c.Data["json"] = mapss 110 | c.ServeJSON() 111 | } 112 | 113 | 114 | // @router /container/terminal [get] 115 | func (this *MainController) ContainerTerminal() { 116 | this.TplName = "terminal.html" 117 | } 118 | -------------------------------------------------------------------------------- /controllers/server.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "net/http" 5 | "github.com/gorilla/websocket" 6 | "sync" 7 | "k8s.io/client-go/tools/remotecommand" 8 | "k8s.io/client-go/rest" 9 | "k8s.io/api/core/v1" 10 | "fmt" 11 | "k8s.io/client-go/kubernetes/scheme" 12 | "github.com/astaxie/beego" 13 | "io/ioutil" 14 | "errors" 15 | ) 16 | type TSockjs struct { 17 | beego.Controller 18 | } 19 | 20 | 21 | var ( 22 | wsSocket *websocket.Conn 23 | msg *WsMessage 24 | copyData []byte 25 | wsConn *WsConnection 26 | sshReq *rest.Request 27 | podName string 28 | podNs string 29 | container string 30 | executor remotecommand.Executor 31 | handler *streamHandler 32 | err error 33 | msgType int 34 | data []byte 35 | ) 36 | 37 | // http升级websocket协议的配置 38 | var wsUpgrader = websocket.Upgrader{ 39 | // 允许所有CORS跨域请求 40 | CheckOrigin: func(r *http.Request) bool { 41 | return true 42 | }, 43 | } 44 | 45 | // websocket消息 46 | type WsMessage struct { 47 | MessageType int 48 | Data []byte 49 | } 50 | 51 | // 封装websocket连接 52 | type WsConnection struct { 53 | wsSocket *websocket.Conn // 底层websocket 54 | inChan chan *WsMessage // 读取队列 55 | outChan chan *WsMessage // 发送队列 56 | mutex sync.Mutex // 避免重复关闭管道 57 | isClosed bool 58 | closeChan chan byte // 关闭通知 59 | } 60 | 61 | // 读取协程 62 | func (wsConn *WsConnection) wsReadLoop() { 63 | for { 64 | // 读一条message 65 | if msgType, data, err = wsConn.wsSocket.ReadMessage(); err != nil { 66 | break 67 | } 68 | 69 | // 放入请求队列 70 | wsConn.inChan <- &WsMessage{ 71 | msgType, 72 | data, 73 | } 74 | 75 | //select { 76 | //case wsConn.inChan <- msg: 77 | //case <- wsConn.closeChan: 78 | // 79 | //} 80 | } 81 | } 82 | 83 | // 发送协程 84 | func (wsConn *WsConnection) wsWriteLoop() { 85 | // 服务端返回给页面的数据 86 | 87 | for { 88 | select { 89 | // 取一个应答 90 | case msg = <- wsConn.outChan: 91 | // 写给web websocket 92 | 93 | if err = wsConn.wsSocket.WriteMessage(msg.MessageType, msg.Data); err != nil { 94 | break 95 | } 96 | case <- wsConn.closeChan: 97 | wsConn.WsClose() 98 | } 99 | } 100 | 101 | 102 | } 103 | 104 | 105 | func InitWebsocket(resp http.ResponseWriter, req *http.Request) (wsConn *WsConnection, err error) { 106 | 107 | // 应答客户端告知升级连接为websocket 108 | if wsSocket, err = wsUpgrader.Upgrade(resp, req, nil); err != nil { 109 | return 110 | } 111 | wsConn = &WsConnection{ 112 | wsSocket: wsSocket, 113 | inChan: make(chan *WsMessage, 1000), 114 | outChan: make(chan *WsMessage, 1000), 115 | closeChan: make(chan byte), 116 | isClosed: false, 117 | } 118 | 119 | // 页面读入输入 协程 120 | go wsConn.wsReadLoop() 121 | // 服务端返回数据 协程 122 | go wsConn.wsWriteLoop() 123 | 124 | return 125 | } 126 | 127 | 128 | // 发送返回消息到协程 129 | func (wsConn *WsConnection) WsWrite(messageType int, data []byte) (err error) { 130 | 131 | 132 | select { 133 | case wsConn.outChan <- &WsMessage{messageType, data,}: 134 | 135 | case <- wsConn.closeChan: 136 | err = errors.New("WsWrite websocket closed") 137 | break 138 | } 139 | return 140 | } 141 | 142 | // 读取 页面消息到协程 143 | func (wsConn *WsConnection) WsRead() (msg *WsMessage, err error) { 144 | select { 145 | case msg = <- wsConn.inChan: 146 | return 147 | case <- wsConn.closeChan: 148 | err = errors.New("WsRead websocket closed") 149 | break 150 | } 151 | return 152 | } 153 | 154 | // 关闭连接 155 | func (wsConn *WsConnection) WsClose() { 156 | wsConn.wsSocket.Close() 157 | wsConn.mutex.Lock() 158 | defer wsConn.mutex.Unlock() 159 | if !wsConn.isClosed { 160 | wsConn.isClosed = true 161 | close(wsConn.closeChan) 162 | } 163 | } 164 | 165 | // ssh流式处理器 166 | type streamHandler struct { 167 | wsConn *WsConnection 168 | resizeEvent chan remotecommand.TerminalSize 169 | } 170 | 171 | // web终端发来的包 172 | type xtermMessage struct { 173 | MsgType string `json:"type"` // 类型:resize客户端调整终端, input客户端输入 174 | Input string `json:"input"` // msgtype=input情况下使用 175 | Rows uint16 `json:"rows"` // msgtype=resize情况下使用 176 | Cols uint16 `json:"cols"`// msgtype=resize情况下使用 177 | } 178 | 179 | 180 | // executor回调获取web是否resize 181 | func (handler *streamHandler) Next() (size *remotecommand.TerminalSize) { 182 | ret := <- handler.resizeEvent 183 | size = &ret 184 | return 185 | } 186 | 187 | // executor回调读取web端的输入 188 | func (handler *streamHandler) Read(p []byte) (size int, err error) { 189 | 190 | 191 | // 读web发来的输入 192 | if msg, err = handler.wsConn.WsRead(); err != nil { 193 | wsConn.WsClose() 194 | return 195 | } 196 | // 解析客户端请求 197 | //if err = json.Unmarshal([]byte(msg.Data), &xtermMsg); err != nil { 198 | // return 199 | //} 200 | 201 | xtermMsg := &xtermMessage{ 202 | //MsgType: string(msg.MessageType), 203 | Input: string(msg.Data), 204 | } 205 | // 放到channel里,等remotecommand executor调用我们的Next取走 206 | handler.resizeEvent <- remotecommand.TerminalSize{Width: xtermMsg.Cols, Height: xtermMsg.Rows} 207 | size = len(xtermMsg.Input) 208 | copy(p, xtermMsg.Input) 209 | return 210 | 211 | } 212 | 213 | // executor回调向web端输出 214 | func (handler *streamHandler) Write(p []byte) (size int, err error) { 215 | // 产生副本 216 | copyData = make([]byte, len(p)) 217 | copy(copyData, p) 218 | size = len(p) 219 | err = handler.wsConn.WsWrite(websocket.TextMessage, copyData) 220 | return 221 | } 222 | 223 | 224 | 225 | func isValidBash(isValidbash []string, shell string) bool { 226 | for _, isValidbash := range isValidbash { 227 | if isValidbash == shell { 228 | return true 229 | } 230 | } 231 | return false 232 | } 233 | func (t *TSockjs) ServeHTTP() { 234 | podNs = t.GetString("namespace") 235 | podName = t.GetString("name") 236 | container = t.GetString("container") 237 | 238 | if wsConn, err = InitWebsocket(t.Ctx.ResponseWriter, t.Ctx.Request); err != nil { 239 | fmt.Println("wsConn err",err) 240 | wsConn.WsClose() 241 | } 242 | 243 | datas ,_:= ioutil.ReadFile("conf/titletext") 244 | wsConn.WsWrite(websocket.TextMessage,datas ) 245 | 246 | fmt.Println(fmt.Sprintf("Namespace:%s podName:%s container:%s",podNs,podName,container)) 247 | validbashs := []string{"/bin/bash","/bin/sh" } 248 | var err error 249 | if isValidBash(validbashs, "") { 250 | cmds := []string{""} 251 | err = startProcess(podName,podNs, container,cmds) 252 | } else { 253 | for _, testShell := range validbashs { 254 | cmd := []string{testShell} 255 | if err = startProcess(podName,podNs, container, cmd);err != nil { 256 | continue 257 | } 258 | } 259 | } 260 | 261 | } 262 | 263 | 264 | 265 | func startProcess(podName string,podNs string,container string, cmd []string ) error { 266 | // URL: 267 | // https://172.16.0.143:6443/api/v1/namespaces/default/pods/nginx-deployment-5cbd8757f-d5qvx/exec?command=sh&container=nginx&stderr=true&stdin=true&stdout=true&tty=true 268 | sshReq = Clientset.CoreV1().RESTClient().Post(). 269 | Resource("pods"). 270 | Name(podName). 271 | Namespace(podNs). 272 | SubResource("exec"). 273 | VersionedParams(&v1.PodExecOptions{ 274 | Container: container, 275 | Command: cmd, 276 | Stdin: true, 277 | Stdout: true, 278 | Stderr: true, 279 | TTY: true, 280 | }, scheme.ParameterCodec) 281 | 282 | // 创建到容器的连接 283 | if executor, err = remotecommand.NewSPDYExecutor(Config, "POST", sshReq.URL()); err != nil { 284 | wsConn.WsClose() 285 | return err 286 | 287 | 288 | } 289 | 290 | // 配置与容器之间的数据流处理回调 291 | handler = &streamHandler{ wsConn: wsConn, resizeEvent: make(chan remotecommand.TerminalSize)} 292 | if err = executor.Stream(remotecommand.StreamOptions{ 293 | Stdin: handler, 294 | Stdout: handler, 295 | Stderr: handler, 296 | TerminalSizeQueue: handler, 297 | Tty: true, 298 | }); err != nil { 299 | fmt.Println("handler",err) 300 | return err 301 | 302 | } 303 | return err 304 | 305 | } 306 | 307 | 308 | -------------------------------------------------------------------------------- /doc/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcops/k8-web-terminal/3a1cc6da47a11915280f315435f52560f336e150/doc/1.png -------------------------------------------------------------------------------- /doc/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcops/k8-web-terminal/3a1cc6da47a11915280f315435f52560f336e150/doc/2.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module k8-web-terminal 2 | 3 | require ( 4 | github.com/astaxie/beego v1.12.0 5 | github.com/gorilla/websocket v1.4.0 6 | github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect 7 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 // indirect 8 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect 9 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563 // indirect 10 | k8s.io/api v0.0.0-20190726022912-69e1bce1dad5 11 | k8s.io/apimachinery v0.0.0-20190726022757-641a75999153 12 | k8s.io/client-go v11.0.0+incompatible 13 | k8s.io/utils v0.0.0-20190712204705-3dccf664f023 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= 3 | github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= 4 | github.com/OwnLocal/goes v1.0.0/go.mod h1:8rIFjBGTue3lCU0wplczcUgt9Gxgrkkrw7etMIcn8TM= 5 | github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 6 | github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 7 | github.com/astaxie/beego v1.12.0 h1:MRhVoeeye5N+Flul5PoVfD9CslfdoH+xqC/xvSQ5u2Y= 8 | github.com/astaxie/beego v1.12.0/go.mod h1:fysx+LZNZKnvh4GED/xND7jWtjCR6HzydR2Hh2Im57o= 9 | github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ= 10 | github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU= 11 | github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= 12 | github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE= 13 | github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= 14 | github.com/couchbase/go-couchbase v0.0.0-20181122212707-3e9b6e1258bb/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U= 15 | github.com/couchbase/gomemcached v0.0.0-20181122193126-5125a94a666c/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= 16 | github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= 17 | github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY= 18 | github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 19 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 20 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 21 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 22 | github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg= 23 | github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= 24 | github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= 25 | github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= 26 | github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= 27 | github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= 28 | github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 29 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 30 | github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 31 | github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= 32 | github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= 33 | github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= 34 | github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= 35 | github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= 36 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 37 | github.com/gogo/protobuf v0.0.0-20190410021324-65acae22fc9/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 38 | github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= 39 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 40 | github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 41 | github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 42 | github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= 43 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 44 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 45 | github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= 46 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 47 | github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= 48 | github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= 49 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 50 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 51 | github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= 52 | github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k= 53 | github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= 54 | github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= 55 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 56 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 57 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 58 | github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 59 | github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= 60 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 61 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 62 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 63 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 64 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 65 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 66 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 67 | github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 68 | github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= 69 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 70 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 71 | github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 72 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 73 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 74 | github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 75 | github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= 76 | github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 77 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 78 | github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 79 | github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= 80 | github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 81 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 82 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 83 | github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 84 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 85 | github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 h1:X+yvsM2yrEktyI+b2qND5gpH8YhURn0k8OCaeRnkINo= 86 | github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg= 87 | github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= 88 | github.com/siddontang/ledisdb v0.0.0-20181029004158-becf5f38d373/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg= 89 | github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA= 90 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 91 | github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 92 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 93 | github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE= 94 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 95 | github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 96 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 97 | github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= 98 | github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc= 99 | golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 100 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= 101 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 102 | golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 103 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 104 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 105 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 106 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 107 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU= 108 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 109 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= 110 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 111 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 112 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 113 | golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 114 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 115 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 116 | golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4= 117 | golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 118 | golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 119 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 120 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 121 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 122 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= 123 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 124 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 125 | golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 126 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 127 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 128 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 129 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 130 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 131 | gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o= 132 | gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 133 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 134 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 135 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 136 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 137 | k8s.io/api v0.0.0-20190726022912-69e1bce1dad5 h1:vSfC/FjyeuqXC/fjdNqZixNpeec4mEHJ68K3kzetm/M= 138 | k8s.io/api v0.0.0-20190726022912-69e1bce1dad5/go.mod h1:V6cpJ9D7WqSy0wqcE096gcbj+W//rshgQgmj1Shdwi8= 139 | k8s.io/apimachinery v0.0.0-20190726022757-641a75999153 h1:Mg7TTs6b/jjTI6dMHGZM9/dbvHDAPCxnKyqK2ag9pAs= 140 | k8s.io/apimachinery v0.0.0-20190726022757-641a75999153/go.mod h1:eXR4ljjmbwK6Ng0PKsXRySPXnTUy/qBUa6kPDeckhQ0= 141 | k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o= 142 | k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= 143 | k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= 144 | k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= 145 | k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= 146 | k8s.io/klog v0.3.1 h1:RVgyDHY/kFKtLqh67NvEWIgkMneNoIrdkN0CxDSQc68= 147 | k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= 148 | k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4= 149 | k8s.io/utils v0.0.0-20190712204705-3dccf664f023 h1:1H4Jyzb0z2X0GfBMTwRjnt5ejffRHrGftUgJcV/ZfDc= 150 | k8s.io/utils v0.0.0-20190712204705-3dccf664f023/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= 151 | sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= 152 | sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= 153 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 154 | -------------------------------------------------------------------------------- /k8-deploy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: extensions/v1beta1 3 | kind: Deployment 4 | metadata: 5 | annotations: 6 | deployment.kubernetes.io/revision: "1" 7 | description: "" 8 | generation: 1 9 | labels: 10 | app: terminal.default 11 | name: dopware-44-73-terminal 12 | name: terminal-xcbgj 13 | namespace: default 14 | spec: 15 | progressDeadlineSeconds: 600 16 | replicas: 1 17 | revisionHistoryLimit: 10 18 | selector: 19 | matchLabels: 20 | app: terminal.default 21 | name: dopware-44-73-terminal 22 | strategy: 23 | rollingUpdate: 24 | maxSurge: 25% 25 | maxUnavailable: 25% 26 | type: RollingUpdate 27 | template: 28 | metadata: 29 | annotations: 30 | description: "" 31 | labels: 32 | app: terminal.default 33 | appId: "572" 34 | name: dopware-44-73-terminal 35 | name: terminal-xcbgj 36 | namespace: default 37 | spec: 38 | containers: 39 | - name: terminal-xcbgj 40 | image: registry.cn-hangzhou.aliyuncs.com/jcops/terminal:v1 41 | imagePullPolicy: Always 42 | resources: 43 | limits: 44 | cpu: "1" 45 | memory: 2Gi 46 | requests: 47 | cpu: "1" 48 | memory: "1717986918" 49 | securityContext: 50 | privileged: false 51 | terminationMessagePath: /dev/termination-log 52 | terminationMessagePolicy: File 53 | dnsPolicy: ClusterFirst 54 | restartPolicy: Always 55 | schedulerName: default-scheduler 56 | securityContext: {} 57 | terminationGracePeriodSeconds: 30 58 | --- 59 | apiVersion: v1 60 | kind: Service 61 | metadata: 62 | labels: 63 | app: terminal.default 64 | name: dopware-44-73-terminal 65 | name: terminal 66 | namespace: default 67 | spec: 68 | externalTrafficPolicy: Cluster 69 | ports: 70 | - name: tcp-28088-8089-2jhl9 71 | nodePort: 28088 72 | port: 8080 73 | protocol: TCP 74 | targetPort: 8080 75 | selector: 76 | app: terminal.default 77 | name: dopware-44-73-terminal 78 | sessionAffinity: None 79 | type: NodePort 80 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "k8-web-terminal/routers" 5 | "github.com/astaxie/beego" 6 | _ "k8-web-terminal/controllers" 7 | 8 | ) 9 | 10 | func main() { 11 | beego.SetStaticPath("/assets", "./static/assets") 12 | beego.SetStaticPath("/public", "./static") 13 | beego.Run() 14 | } 15 | 16 | -------------------------------------------------------------------------------- /routers/commentsRouter_.go: -------------------------------------------------------------------------------- 1 | package routers 2 | 3 | import ( 4 | "github.com/astaxie/beego" 5 | "github.com/astaxie/beego/context/param" 6 | ) 7 | 8 | func init() { 9 | 10 | beego.GlobalControllerRouter["k8-web-terminal/controllers:MainController"] = append(beego.GlobalControllerRouter["k8-web-terminal/controllers:MainController"], 11 | beego.ControllerComments{ 12 | Method: "Get", 13 | Router: `/`, 14 | AllowHTTPMethods: []string{"get"}, 15 | MethodParams: param.Make(), 16 | Filters: nil, 17 | Params: nil}) 18 | 19 | beego.GlobalControllerRouter["k8-web-terminal/controllers:MainController"] = append(beego.GlobalControllerRouter["k8-web-terminal/controllers:MainController"], 20 | beego.ControllerComments{ 21 | Method: "Nodes", 22 | Router: `/api/nodes`, 23 | AllowHTTPMethods: []string{"get"}, 24 | MethodParams: param.Make(), 25 | Filters: nil, 26 | Params: nil}) 27 | 28 | beego.GlobalControllerRouter["k8-web-terminal/controllers:MainController"] = append(beego.GlobalControllerRouter["k8-web-terminal/controllers:MainController"], 29 | beego.ControllerComments{ 30 | Method: "NodePods", 31 | Router: `/api/nodes/containers`, 32 | AllowHTTPMethods: []string{"get"}, 33 | MethodParams: param.Make(), 34 | Filters: nil, 35 | Params: nil}) 36 | 37 | beego.GlobalControllerRouter["k8-web-terminal/controllers:MainController"] = append(beego.GlobalControllerRouter["k8-web-terminal/controllers:MainController"], 38 | beego.ControllerComments{ 39 | Method: "ContainerTerminal", 40 | Router: `/container/terminal`, 41 | AllowHTTPMethods: []string{"get"}, 42 | MethodParams: param.Make(), 43 | Filters: nil, 44 | Params: nil}) 45 | 46 | } 47 | -------------------------------------------------------------------------------- /routers/commentsRouter_controllers.go: -------------------------------------------------------------------------------- 1 | package routers 2 | 3 | import ( 4 | "github.com/astaxie/beego" 5 | "github.com/astaxie/beego/context/param" 6 | ) 7 | 8 | func init() { 9 | 10 | beego.GlobalControllerRouter["k8-web-terminal/controllers:MainController"] = append(beego.GlobalControllerRouter["k8-web-terminal/controllers:MainController"], 11 | beego.ControllerComments{ 12 | Method: "Get", 13 | Router: `/`, 14 | AllowHTTPMethods: []string{"get"}, 15 | MethodParams: param.Make(), 16 | Filters: nil, 17 | Params: nil}) 18 | 19 | beego.GlobalControllerRouter["k8-web-terminal/controllers:MainController"] = append(beego.GlobalControllerRouter["k8-web-terminal/controllers:MainController"], 20 | beego.ControllerComments{ 21 | Method: "Nodes", 22 | Router: `/api/nodes`, 23 | AllowHTTPMethods: []string{"get"}, 24 | MethodParams: param.Make(), 25 | Filters: nil, 26 | Params: nil}) 27 | 28 | beego.GlobalControllerRouter["k8-web-terminal/controllers:MainController"] = append(beego.GlobalControllerRouter["k8-web-terminal/controllers:MainController"], 29 | beego.ControllerComments{ 30 | Method: "NodePods", 31 | Router: `/api/nodes/containers`, 32 | AllowHTTPMethods: []string{"get"}, 33 | MethodParams: param.Make(), 34 | Filters: nil, 35 | Params: nil}) 36 | 37 | beego.GlobalControllerRouter["k8-web-terminal/controllers:MainController"] = append(beego.GlobalControllerRouter["k8-web-terminal/controllers:MainController"], 38 | beego.ControllerComments{ 39 | Method: "ContainerTerminal", 40 | Router: `/container/terminal`, 41 | AllowHTTPMethods: []string{"get"}, 42 | MethodParams: param.Make(), 43 | Filters: nil, 44 | Params: nil}) 45 | 46 | } 47 | -------------------------------------------------------------------------------- /routers/router.go: -------------------------------------------------------------------------------- 1 | package routers 2 | 3 | import ( 4 | "k8-web-terminal/controllers" 5 | "github.com/astaxie/beego" 6 | 7 | ) 8 | //api/nodes/containers?node=172.16.0.143 9 | func init() { 10 | beego.Include(&controllers.MainController{}) 11 | beego.Router("/container/terminal/shell/ws", &controllers.TSockjs{}, "get:ServeHTTP") 12 | 13 | //------------------------------------------------------------------------------------------------------------------ 14 | //beego.Handler("/container/terminal/shell/ws",&controllers.TSockjs{},true) 15 | //ns := beego.NewNamespace("/api", 16 | // beego.NSNamespace("/v1", beego.NSInclude(&controllers.MainController{} ), 17 | // ), 18 | // 19 | // ) 20 | ////注册 namespace 21 | //beego.AddNamespace(ns) 22 | } 23 | -------------------------------------------------------------------------------- /static/assets/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'Roboto', sans-serif; 3 | font-size:14px; 4 | height:100%; 5 | margin: 0px; 6 | padding: 0px; 7 | } 8 | 9 | /* Toolbar area */ 10 | 11 | .menu { 12 | background-color: transparent; 13 | border: none; 14 | height: 38px; 15 | margin: 16px; 16 | width: 36px; 17 | } 18 | 19 | 20 | /* Sidenav area */ 21 | 22 | md-list .md-button { 23 | color: inherit; 24 | font-weight: 500; 25 | text-align: left; 26 | width: 100%; 27 | padding-top:10px; 28 | padding-bottom:10px; 29 | text-transform: none 30 | } 31 | md-list .md-button.selected { 32 | color: #03a9f4; 33 | } 34 | 35 | md-sidenav md-list { 36 | padding: 0px 0px 8px 0px; 37 | } 38 | 39 | 40 | /* Primary content area */ 41 | 42 | #content { 43 | overflow: hidden; 44 | } 45 | 46 | #content { 47 | padding-left: 40px; 48 | padding-right: 40px; 49 | padding-top: 5px; 50 | } 51 | 52 | #content .md-button.share { 53 | background-color: transparent; 54 | border: none; 55 | width: 48px; 56 | height: 48px; 57 | margin: 8px auto 16px 0; 58 | position: absolute; 59 | top: 10px; 60 | right: 25px; 61 | } 62 | 63 | #content md-icon.avatar { 64 | margin-top: 0; 65 | } 66 | 67 | #content .md-button.share > md-icon { 68 | fill:black; 69 | width:36px; 70 | height:36px; 71 | } 72 | 73 | md-button.menuBtn > md-icon { 74 | fill:white; 75 | width:24px; 76 | height:24px; 77 | } 78 | 79 | #content .md-button.share:active > md-icon { 80 | background-color: #dadada; 81 | border-radius: 75%; 82 | padding:4px; 83 | transition: all 100ms ease-out 30ms; 84 | } 85 | 86 | #content img { 87 | display: block; 88 | height: auto; 89 | max-width: 500px; 90 | } 91 | 92 | /* Utils */ 93 | 94 | .content-wrapper { 95 | position: relative; 96 | } 97 | 98 | /* Typography support coming in 0.8.0 */ 99 | 100 | md-toolbar h1 { 101 | font-weight: inherit; 102 | color: #fff; 103 | font-size: 1.250em; 104 | margin-left: 24px; 105 | } 106 | 107 | .avatar { 108 | position: relative; 109 | width: 75px; 110 | height: 75px; 111 | border: 1px solid #ddd; 112 | border-radius: 50%; 113 | display: inline-block; 114 | overflow: hidden; 115 | margin: 0px; 116 | vertical-align: middle; 117 | margin-right: 10px; 118 | } 119 | 120 | 121 | md-bottom-sheet md-icon { 122 | margin-right:20px; 123 | } 124 | 125 | span.name { 126 | font-weight: bold; 127 | font-size:1.1em; 128 | padding-left:5px; 129 | } 130 | 131 | md-list-item .md-button { 132 | margin: 0; 133 | } 134 | 135 | 136 | md-list .md-button.selected { 137 | color: #03A9F7; 138 | background-color: #EFF1F2; 139 | } 140 | 141 | md-list md-list-item button.md-button:not(.selected):not([disabled]) { 142 | background-color: white; 143 | } 144 | 145 | 146 | 147 | #content { 148 | padding:25px; 149 | } 150 | #content md-icon.avatar { 151 | width:100px; 152 | height:100px; 153 | box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.14), 0 4px 5px 0 rgba(0, 0, 0, 0.098), 0 1px 10px 0 rgba(0, 0, 0, 0.084); 154 | } 155 | -------------------------------------------------------------------------------- /static/assets/main.js: -------------------------------------------------------------------------------- 1 | var term, 2 | protocol, 3 | socketURL, 4 | socket; 5 | 6 | var terminalContainer = document.getElementById('terminal-container'); 7 | 8 | //var optionElements = { 9 | // cursorBlink: document.querySelector('#option-cursor-blink') 10 | //}; 11 | // 12 | //optionElements.cursorBlink.addEventListener('change', createTerminal); 13 | 14 | var Query = function () { 15 | // This function is anonymous, is executed immediately and 16 | // the return value is assigned to QueryString! 17 | var query_string = {}; 18 | var query = window.location.search.substring(1); 19 | var vars = query.split("&"); 20 | for (var i=0;i 2) { 124 | term.write('\b \b'); 125 | } 126 | } else if (printable) { 127 | term.write(key); 128 | } 129 | }); 130 | 131 | term.on('paste', function (data, ev) { 132 | console.log(data) 133 | term.write(data); 134 | }); 135 | } 136 | 137 | -------------------------------------------------------------------------------- /static/assets/reconnecting-websocket/reconnecting-websocket.min.js: -------------------------------------------------------------------------------- 1 | !function(a,b){"function"==typeof define&&define.amd?define([],b):"undefined"!=typeof module&&module.exports?module.exports=b():a.ReconnectingWebSocket=b()}(this,function(){function a(b,c,d){function l(a,b){var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,!1,!1,b),c}var e={debug:!1,automaticOpen:!0,reconnectInterval:1e3,maxReconnectInterval:3e4,reconnectDecay:1.5,timeoutInterval:2e3};d||(d={});for(var f in e)this[f]="undefined"!=typeof d[f]?d[f]:e[f];this.url=b,this.reconnectAttempts=0,this.readyState=WebSocket.CONNECTING,this.protocol=null;var h,g=this,i=!1,j=!1,k=document.createElement("div");k.addEventListener("open",function(a){g.onopen(a)}),k.addEventListener("close",function(a){g.onclose(a)}),k.addEventListener("connecting",function(a){g.onconnecting(a)}),k.addEventListener("message",function(a){g.onmessage(a)}),k.addEventListener("error",function(a){g.onerror(a)}),this.addEventListener=k.addEventListener.bind(k),this.removeEventListener=k.removeEventListener.bind(k),this.dispatchEvent=k.dispatchEvent.bind(k),this.open=function(b){h=new WebSocket(g.url,c||[]),b||k.dispatchEvent(l("connecting")),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","attempt-connect",g.url);var d=h,e=setTimeout(function(){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","connection-timeout",g.url),j=!0,d.close(),j=!1},g.timeoutInterval);h.onopen=function(){clearTimeout(e),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onopen",g.url),g.protocol=h.protocol,g.readyState=WebSocket.OPEN,g.reconnectAttempts=0;var d=l("open");d.isReconnect=b,b=!1,k.dispatchEvent(d)},h.onclose=function(c){if(clearTimeout(e),h=null,i)g.readyState=WebSocket.CLOSED,k.dispatchEvent(l("close"));else{g.readyState=WebSocket.CONNECTING;var d=l("connecting");d.code=c.code,d.reason=c.reason,d.wasClean=c.wasClean,k.dispatchEvent(d),b||j||((g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onclose",g.url),k.dispatchEvent(l("close")));var e=g.reconnectInterval*Math.pow(g.reconnectDecay,g.reconnectAttempts);setTimeout(function(){g.reconnectAttempts++,g.open(!0)},e>g.maxReconnectInterval?g.maxReconnectInterval:e)}},h.onmessage=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onmessage",g.url,b.data);var c=l("message");c.data=b.data,k.dispatchEvent(c)},h.onerror=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onerror",g.url,b),k.dispatchEvent(l("error"))}},1==this.automaticOpen&&this.open(!1),this.send=function(b){if(h)return(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","send",g.url,b),h.send(b);throw"INVALID_STATE_ERR : Pausing to reconnect websocket"},this.close=function(a,b){"undefined"==typeof a&&(a=1e3),i=!0,h&&h.close(a,b)},this.refresh=function(){h&&h.close()}}return a.prototype.onopen=function(){},a.prototype.onclose=function(){},a.prototype.onconnecting=function(){},a.prototype.onmessage=function(){},a.prototype.onerror=function(){},a.debugAll=!1,a.CONNECTING=WebSocket.CONNECTING,a.OPEN=WebSocket.OPEN,a.CLOSING=WebSocket.CLOSING,a.CLOSED=WebSocket.CLOSED,a}); 2 | -------------------------------------------------------------------------------- /static/assets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: helvetica, sans-serif, arial; 3 | font-size: 1em; 4 | color: #111; 5 | } 6 | 7 | h1 { 8 | text-align: center; 9 | } 10 | 11 | #terminal-container { 12 | height: 700px; 13 | margin: 0 auto; 14 | padding: 2px; 15 | } 16 | 17 | #terminal-container .terminal { 18 | background-color: #111; 19 | color: #fafafa; 20 | padding: 2px; 21 | } 22 | 23 | #terminal-container .terminal:focus .terminal-cursor { 24 | background-color: #fafafa; 25 | } 26 | -------------------------------------------------------------------------------- /static/assets/svg/docker.svg: -------------------------------------------------------------------------------- 1 | 2 | 15 | 17 | 18 | 20 | image/svg+xml 21 | 23 | 24 | 25 | 26 | 28 | 48 | 52 | 56 | 62 | 68 | 74 | 80 | 86 | 92 | 98 | 104 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /static/assets/svg/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /static/assets/svg/terminal.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /static/assets/xterm.js/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | 9 | [*.js] 10 | max_line_length = 100 11 | 12 | [*.css] 13 | indent_size = 4 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /static/assets/xterm.js/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:4-onbuild 2 | MAINTAINER Paris Kasidiaris 3 | 4 | EXPOSE 3000 5 | -------------------------------------------------------------------------------- /static/assets/xterm.js/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, sourceLair Limited (https://github.com/sourcelair/) 2 | Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /static/assets/xterm.js/Procfile.dev: -------------------------------------------------------------------------------- 1 | web: npm start 2 | -------------------------------------------------------------------------------- /static/assets/xterm.js/README.md: -------------------------------------------------------------------------------- 1 | # xterm.js 2 | 3 |  4 | 5 | Xterm.js is a full xterm clone, written in JavaScript. 6 | 7 | It is used at [SourceLair](https://www.sourcelair.com/home) to help people develop their applications in their browsers. 8 | 9 | Xterm.js supplies a modular, event-based interface that lets developers build addons and themes on top of it. 10 | 11 |  12 | 13 | ## Demo 14 | 15 | To launch the demo simply run: 16 | 17 | ``` 18 | npm install 19 | npm start 20 | ``` 21 | 22 | Then open http://0.0.0.0:3000 in a web browser (use http://127.0.0.1:3000 is running under Windows). 23 | 24 | ## Addons 25 | 26 | Addons are JavaScript modules that attach functions to the `Terminal` prototype to extend its functionality. There are a handful available in the main repository in the `addons` directory, you can even write your own (though they may break when the internals of xterm.js change across versions). 27 | 28 | To use an addon, just include the JavaScript file after xterm.js and before the `Terminal` object has been instantiated. The function should then be exposed on the `Terminal` object: 29 | 30 | ```html 31 | 32 | 33 | ``` 34 | 35 | ```js 36 | var xterm = new Terminal(); 37 | // init code... 38 | xterm.linkify(); 39 | ``` 40 | 41 | ## Contribution and License Agreement 42 | 43 | If you contribute code to this project, you are implicitly allowing your code to be distributed under the MIT license. You are also implicitly verifying that all code is your original work. 44 | 45 | ## License 46 | 47 | Copyright (c) 2014-2016, SourceLair, Private Company ([www.sourcelair.com](https://www.sourcelair.com/home)) (MIT License) 48 | 49 | Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) 50 | -------------------------------------------------------------------------------- /static/assets/xterm.js/addons/attach/attach.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Implements the attach method, that 3 | * attaches the terminal to a WebSocket stream. 4 | * 5 | * The bidirectional argument indicates, whether the terminal should 6 | * send data to the socket as well and is true, by default. 7 | */ 8 | 9 | (function (attach) { 10 | if (typeof exports === 'object' && typeof module === 'object') { 11 | /* 12 | * CommonJS environment 13 | */ 14 | module.exports = attach(require('../../src/xterm')); 15 | } else if (typeof define == 'function') { 16 | /* 17 | * Require.js is available 18 | */ 19 | define(['../../src/xterm'], attach); 20 | } else { 21 | /* 22 | * Plain browser environment 23 | */ 24 | attach(this.Xterm); 25 | } 26 | })(function (Xterm) { 27 | 'use strict'; 28 | 29 | /** 30 | * This module provides methods for attaching a terminal to a WebSocket 31 | * stream. 32 | * 33 | * @module xterm/addons/attach/attach 34 | */ 35 | var exports = {}; 36 | 37 | /** 38 | * Attaches the given terminal to the given socket. 39 | * 40 | * @param {Xterm} term - The terminal to be attached to the given socket. 41 | * @param {WebSocket} socket - The socket to attach the current terminal. 42 | * @param {boolean} bidirectional - Whether the terminal should send data 43 | * to the socket as well. 44 | * @param {boolean} buffered - Whether the rendering of incoming data 45 | * should happen instantly or at a maximum 46 | * frequency of 1 rendering per 10ms. 47 | */ 48 | exports.attach = function (term, socket, bidirectional, buffered) { 49 | bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional; 50 | term.socket = socket; 51 | 52 | term._flushBuffer = function () { 53 | term.write(term._attachSocketBuffer); 54 | term._attachSocketBuffer = null; 55 | clearTimeout(term._attachSocketBufferTimer); 56 | term._attachSocketBufferTimer = null; 57 | }; 58 | 59 | term._pushToBuffer = function (data) { 60 | if (term._attachSocketBuffer) { 61 | term._attachSocketBuffer += data; 62 | } else { 63 | term._attachSocketBuffer = data; 64 | setTimeout(term._flushBuffer, 10); 65 | } 66 | }; 67 | 68 | term._getMessage = function (ev) { 69 | if (buffered) { 70 | term._pushToBuffer(ev.data); 71 | } else { 72 | term.write(ev.data); 73 | } 74 | }; 75 | 76 | term._sendData = function (data) { 77 | socket.send(data); 78 | }; 79 | 80 | socket.addEventListener('message', term._getMessage); 81 | 82 | if (bidirectional) { 83 | term.on('data', term._sendData); 84 | } 85 | 86 | socket.addEventListener('close', term.detach.bind(term, socket)); 87 | socket.addEventListener('error', term.detach.bind(term, socket)); 88 | }; 89 | 90 | /** 91 | * Detaches the given terminal from the given socket 92 | * 93 | * @param {Xterm} term - The terminal to be detached from the given socket. 94 | * @param {WebSocket} socket - The socket from which to detach the current 95 | * terminal. 96 | */ 97 | exports.detach = function (term, socket) { 98 | term.off('data', term._sendData); 99 | 100 | socket = (typeof socket == 'undefined') ? term.socket : socket; 101 | 102 | if (socket) { 103 | socket.removeEventListener('message', term._getMessage); 104 | } 105 | 106 | delete term.socket; 107 | }; 108 | 109 | /** 110 | * Attaches the current terminal to the given socket 111 | * 112 | * @param {WebSocket} socket - The socket to attach the current terminal. 113 | * @param {boolean} bidirectional - Whether the terminal should send data 114 | * to the socket as well. 115 | * @param {boolean} buffered - Whether the rendering of incoming data 116 | * should happen instantly or at a maximum 117 | * frequency of 1 rendering per 10ms. 118 | */ 119 | Xterm.prototype.attach = function (socket, bidirectional, buffered) { 120 | return exports.attach(this, socket, bidirectional, buffered); 121 | }; 122 | 123 | /** 124 | * Detaches the current terminal from the given socket. 125 | * 126 | * @param {WebSocket} socket - The socket from which to detach the current 127 | * terminal. 128 | */ 129 | Xterm.prototype.detach = function (socket) { 130 | return exports.detach(this, socket); 131 | }; 132 | 133 | return exports; 134 | }); 135 | -------------------------------------------------------------------------------- /static/assets/xterm.js/addons/attach/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 49 | 50 | 51 | 52 | 53 | 54 | xterm.js: socket attach 55 | 56 | 57 | Attach the terminal to a WebSocket terminal stream with ease. Perfect for attaching to your 58 | Docker containers. 59 | 60 | 61 | Socket information 62 | 63 | 64 | 68 | 69 | Attach 70 | 71 | 72 | 73 | 74 | 75 | 92 | 93 | -------------------------------------------------------------------------------- /static/assets/xterm.js/addons/attach/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xterm.attach", 3 | "main": "attach.js", 4 | "private": true 5 | } 6 | -------------------------------------------------------------------------------- /static/assets/xterm.js/addons/fit/fit.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Fit terminal columns and rows to the dimensions of its 3 | * DOM element. 4 | * 5 | * Approach: 6 | * - Rows: Truncate the division of the terminal parent element height 7 | * by the terminal row height 8 | * 9 | * - Columns: Truncate the division of the terminal parent element width by 10 | * the terminal character width (apply display: inline at the 11 | * terminal row and truncate its width with the current number 12 | * of columns) 13 | */ 14 | (function (fit) { 15 | if (typeof exports === 'object' && typeof module === 'object') { 16 | /* 17 | * CommonJS environment 18 | */ 19 | module.exports = fit(require('../../src/xterm')); 20 | } else if (typeof define == 'function') { 21 | /* 22 | * Require.js is available 23 | */ 24 | define(['../../src/xterm'], fit); 25 | } else { 26 | /* 27 | * Plain browser environment 28 | */ 29 | fit(this.Xterm); 30 | } 31 | })(function (Xterm) { 32 | /** 33 | * This module provides methods for fitting a terminal's size to a parent container. 34 | * 35 | * @module xterm/addons/fit/fit 36 | */ 37 | var exports = {}; 38 | 39 | exports.proposeGeometry = function (term) { 40 | var parentElementStyle = window.getComputedStyle(term.element.parentElement), 41 | parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')), 42 | parentElementWidth = parseInt(parentElementStyle.getPropertyValue('width')), 43 | elementStyle = window.getComputedStyle(term.element), 44 | elementPaddingVer = parseInt(elementStyle.getPropertyValue('padding-top')) + parseInt(elementStyle.getPropertyValue('padding-bottom')), 45 | elementPaddingHor = parseInt(elementStyle.getPropertyValue('padding-right')) + parseInt(elementStyle.getPropertyValue('padding-left')), 46 | availableHeight = parentElementHeight - elementPaddingVer, 47 | availableWidth = parentElementWidth - elementPaddingHor, 48 | container = term.rowContainer, 49 | subjectRow = term.rowContainer.firstElementChild, 50 | contentBuffer = subjectRow.innerHTML, 51 | characterHeight, 52 | rows, 53 | characterWidth, 54 | cols, 55 | geometry; 56 | 57 | subjectRow.style.display = 'inline'; 58 | subjectRow.innerHTML = 'W'; // Common character for measuring width, although on monospace 59 | characterWidth = subjectRow.getBoundingClientRect().width; 60 | subjectRow.style.display = ''; // Revert style before calculating height, since they differ. 61 | characterHeight = parseInt(subjectRow.offsetHeight); 62 | subjectRow.innerHTML = contentBuffer; 63 | 64 | rows = parseInt(availableHeight / characterHeight); 65 | cols = parseInt(availableWidth / characterWidth) - 1; 66 | 67 | geometry = {cols: cols, rows: rows}; 68 | return geometry; 69 | }; 70 | 71 | exports.fit = function (term) { 72 | var geometry = exports.proposeGeometry(term); 73 | 74 | term.resize(geometry.cols, geometry.rows); 75 | }; 76 | 77 | Xterm.prototype.proposeGeometry = function () { 78 | return exports.proposeGeometry(this); 79 | }; 80 | 81 | Xterm.prototype.fit = function () { 82 | return exports.fit(this); 83 | }; 84 | 85 | return exports; 86 | }); 87 | -------------------------------------------------------------------------------- /static/assets/xterm.js/addons/fit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xterm.fit", 3 | "main": "fit.js", 4 | "private": true 5 | } 6 | -------------------------------------------------------------------------------- /static/assets/xterm.js/addons/fullscreen/fullscreen.css: -------------------------------------------------------------------------------- 1 | .xterm.fullscreen { 2 | position: fixed; 3 | top: 0; 4 | bottom: 0; 5 | left: 0; 6 | right: 0; 7 | width: auto; 8 | height: auto; 9 | z-index: 255; 10 | } 11 | -------------------------------------------------------------------------------- /static/assets/xterm.js/addons/fullscreen/fullscreen.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Fullscreen addon for xterm.js 3 | * 4 | * Implements the toggleFullscreen function. 5 | * 6 | * If the `fullscreen` argument has been supplied, then 7 | * if it is true, the fullscreen mode gets turned on, 8 | * if it is false or null, the fullscreen mode gets turned off. 9 | * 10 | * If the `fullscreen` argument has not been supplied, the 11 | * fullscreen mode is being toggled. 12 | */ 13 | (function (fullscreen) { 14 | if (typeof exports === 'object' && typeof module === 'object') { 15 | /* 16 | * CommonJS environment 17 | */ 18 | module.exports = fullscreen(require('../../src/xterm')); 19 | } else if (typeof define == 'function') { 20 | /* 21 | * Require.js is available 22 | */ 23 | define(['../../src/xterm'], fullscreen); 24 | } else { 25 | /* 26 | * Plain browser environment 27 | */ 28 | fullscreen(this.Xterm); 29 | } 30 | })(function (Xterm) { 31 | var exports = {}; 32 | 33 | exports.toggleFullScreen = function (term, fullscreen) { 34 | var fn; 35 | 36 | if (typeof fullscreen == 'undefined') { 37 | fn = (term.element.classList.contains('fullscreen')) ? 'remove' : 'add'; 38 | } else if (!fullscreen) { 39 | fn = 'remove'; 40 | } else { 41 | fn = 'add'; 42 | } 43 | 44 | term.element.classList[fn]('fullscreen'); 45 | }; 46 | 47 | Xterm.prototype.toggleFullscreen = function (fullscreen) { 48 | exports.toggleFullScreen(this, fullscreen); 49 | }; 50 | 51 | return exports; 52 | }); 53 | -------------------------------------------------------------------------------- /static/assets/xterm.js/addons/fullscreen/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xterm.fullscreen", 3 | "main": "fullscreen.js", 4 | "private": true 5 | } 6 | -------------------------------------------------------------------------------- /static/assets/xterm.js/addons/linkify/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 22 | 23 | 24 | 25 | 35 | 36 | -------------------------------------------------------------------------------- /static/assets/xterm.js/addons/linkify/linkify.js: -------------------------------------------------------------------------------- 1 | (function (linkify) { 2 | if (typeof exports === 'object' && typeof module === 'object') { 3 | /* 4 | * CommonJS environment 5 | */ 6 | module.exports = linkify(require('../../src/xterm')); 7 | } else if (typeof define == 'function') { 8 | /* 9 | * Require.js is available 10 | */ 11 | define(['../../src/xterm'], linkify); 12 | } else { 13 | /* 14 | * Plain browser environment 15 | */ 16 | linkify(this.Xterm); 17 | } 18 | })(function (Xterm) { 19 | 'use strict'; 20 | 21 | /** 22 | * This module provides methods for convertings valid URL substrings 23 | * into HTML anchor elements (links), inside a terminal view. 24 | * 25 | * @module xterm/addons/linkify/linkify 26 | */ 27 | var exports = {}, 28 | protocolClause = '(https?:\\/\\/)', 29 | domainCharacterSet = '[\\da-z\\.-]+', 30 | negatedDomainCharacterSet = '[^\\da-z\\.-]+', 31 | domainBodyClause = '(' + domainCharacterSet + ')', 32 | tldClause = '([a-z\\.]{2,6})', 33 | ipClause = '((\\d{1,3}\\.){3}\\d{1,3})', 34 | portClause = '(:\\d{1,5})', 35 | hostClause = '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + ')' + portClause + '?', 36 | pathClause = '(\\/[\\/\\w\\.-]*)*', 37 | negatedPathCharacterSet = '[^\\/\\w\\.-]+', 38 | bodyClause = hostClause + pathClause, 39 | start = '(?:^|' + negatedDomainCharacterSet + ')(', 40 | end = ')($|' + negatedPathCharacterSet + ')', 41 | lenientUrlClause = start + protocolClause + '?' + bodyClause + end, 42 | strictUrlClause = start + protocolClause + bodyClause + end, 43 | lenientUrlRegex = new RegExp(lenientUrlClause), 44 | strictUrlRegex = new RegExp(strictUrlClause); 45 | 46 | /** 47 | * Converts all valid URLs found in the given terminal line into 48 | * hyperlinks. The terminal line can be either the HTML element itself 49 | * or the index of the termina line in the children of the terminal 50 | * rows container. 51 | * 52 | * @param {Xterm} terminal - The terminal that owns the given line. 53 | * @param {number|HTMLDivElement} line - The terminal line that should get 54 | * "linkified". 55 | * @param {boolean} lenient - The regex type that will be used to identify links. If lenient is 56 | * false, the regex requires a protocol clause. Defaults to true. 57 | * @emits linkify 58 | * @emits linkify:line 59 | */ 60 | exports.linkifyTerminalLine = function (terminal, line, lenient) { 61 | if (typeof line == 'number') { 62 | line = terminal.rowContainer.children[line]; 63 | } else if (! (line instanceof HTMLDivElement)) { 64 | var message = 'The "line" argument should be either a number'; 65 | message += ' or an HTMLDivElement'; 66 | 67 | throw new TypeError(message); 68 | } 69 | 70 | var buffer = document.createElement('span'), 71 | nodes = line.childNodes; 72 | 73 | for (var j=0; j' + url + '', 104 | newHTML = nodeHTML.replace(url, link); 105 | 106 | line.innerHTML = line.innerHTML.replace(nodeHTML, newHTML); 107 | } 108 | 109 | /** 110 | * This event gets emitted when conversion of all URL susbtrings 111 | * to HTML anchor elements (links) has finished, for a specific 112 | * line of the current Xterm instance. 113 | * 114 | * @event linkify:line 115 | */ 116 | terminal.emit('linkify:line', line); 117 | }; 118 | 119 | /** 120 | * Finds a link within a block of text. 121 | * 122 | * @param {string} text - The text to search . 123 | * @param {boolean} lenient - Whether to use the lenient search. 124 | * @return {string} A URL. 125 | */ 126 | exports.findLinkMatch = function (text, lenient) { 127 | var match = text.match(lenient ? lenientUrlRegex : strictUrlRegex); 128 | if (!match || match.length === 0) { 129 | return null; 130 | } 131 | return match[1]; 132 | } 133 | 134 | /** 135 | * Converts all valid URLs found in the terminal view into hyperlinks. 136 | * 137 | * @param {Xterm} terminal - The terminal that should get "linkified". 138 | * @param {boolean} lenient - The regex type that will be used to identify links. If lenient is 139 | * false, the regex requires a protocol clause. Defaults to true. 140 | * @emits linkify 141 | * @emits linkify:line 142 | */ 143 | exports.linkify = function (terminal, lenient) { 144 | var rows = terminal.rowContainer.children; 145 | 146 | lenient = (typeof lenient == "boolean") ? lenient : true; 147 | for (var i=0; i 2 | 3 | 4 | xterm.js demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | xterm.js: xterm, in the browser 16 | 17 | 18 | 19 | Options 20 | cursorBlink 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /static/assets/xterm.js/demo/main.js: -------------------------------------------------------------------------------- 1 | var term, 2 | protocol, 3 | socketURL, 4 | socket; 5 | 6 | var terminalContainer = document.getElementById('terminal-container'); 7 | var optionElements = { 8 | cursorBlink: document.querySelector('#option-cursor-blink') 9 | }; 10 | 11 | optionElements.cursorBlink.addEventListener('change', createTerminal); 12 | 13 | createTerminal(); 14 | 15 | function createTerminal() { 16 | while (terminalContainer.children.length) { 17 | terminalContainer.removeChild(terminalContainer.children[0]); 18 | } 19 | term = new Terminal({ 20 | cursorBlink: optionElements.cursorBlink.checked 21 | }); 22 | protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://'; 23 | socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/bash'; 24 | socket = new WebSocket(socketURL); 25 | 26 | term.open(terminalContainer); 27 | term.fit(); 28 | 29 | socket.onopen = runRealTerminal; 30 | socket.onclose = runFakeTerminal; 31 | socket.onerror = runFakeTerminal; 32 | } 33 | 34 | 35 | function runRealTerminal() { 36 | term.attach(socket); 37 | term._initialized = true; 38 | } 39 | 40 | function runFakeTerminal() { 41 | if (term._initialized) { 42 | return; 43 | } 44 | 45 | term._initialized = true; 46 | 47 | var shellprompt = '$ '; 48 | 49 | term.prompt = function () { 50 | term.write('\r\n' + shellprompt); 51 | }; 52 | 53 | term.writeln('Welcome to xterm.js'); 54 | term.writeln('This is a local terminal emulation, without a real terminal in the back-end.'); 55 | term.writeln('Type some keys and commands to play around.'); 56 | term.writeln(''); 57 | term.prompt(); 58 | 59 | term.on('key', function (key, ev) { 60 | var printable = ( 61 | !ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey 62 | ); 63 | 64 | if (ev.keyCode == 13) { 65 | term.prompt(); 66 | } else if (ev.keyCode == 8) { 67 | /* 68 | * Do not delete the prompt 69 | */ 70 | if (term.x > 2) { 71 | term.write('\b \b'); 72 | } 73 | } else if (printable) { 74 | term.write(key); 75 | } 76 | }); 77 | 78 | term.on('paste', function (data, ev) { 79 | term.write(data); 80 | }); 81 | } 82 | -------------------------------------------------------------------------------- /static/assets/xterm.js/demo/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: helvetica, sans-serif, arial; 3 | font-size: 1em; 4 | color: #111; 5 | } 6 | 7 | h1 { 8 | text-align: center; 9 | } 10 | 11 | #terminal-container { 12 | width: 960px; 13 | height: 600px; 14 | margin: 0 auto; 15 | padding: 2px; 16 | } 17 | 18 | #terminal-container .terminal { 19 | background-color: #111; 20 | color: #fafafa; 21 | padding: 2px; 22 | } 23 | 24 | #terminal-container .terminal:focus .terminal-cursor { 25 | background-color: #fafafa; 26 | } 27 | -------------------------------------------------------------------------------- /static/assets/xterm.js/jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": { 3 | "include": [ 4 | "src/xterm.js", 5 | "addons/attach/attach.js", 6 | "addons/fit/fit.js", 7 | "addons/fullscreen/fullscreen.js", 8 | "addons/linkify/linkify.js" 9 | ] 10 | }, 11 | "opts": { 12 | "readme": "README.md", 13 | "template": "node_modules/docdash", 14 | "encoding": "utf8", 15 | "destination": "docs/", 16 | "recurse": true, 17 | "verbose": true 18 | }, 19 | "templates": { 20 | "cleverLinks": false, 21 | "monospaceLinks": false 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /static/assets/xterm.js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xterm", 3 | "version": "0.33.0", 4 | "ignore": ["demo", "test", ".gitignore"], 5 | "main": "src/xterm.js", 6 | "repository": "https://github.com/sourcelair/xterm.js", 7 | "license": "MIT", 8 | "devDependencies": { 9 | "express": "4.13.4", 10 | "express-ws": "2.0.0-rc.1", 11 | "pty.js": "0.3.0", 12 | "mocha": "2.5.3", 13 | "chai": "3.5.0", 14 | "jsdoc": "3.4.0", 15 | "docdash": "0.4.0" 16 | }, 17 | "scripts": { 18 | "start": "node demo/app", 19 | "test": "mocha --recursive", 20 | "build:docs": "node_modules/.bin/jsdoc -c jsdoc.json" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /static/assets/xterm.js/src/xterm.css: -------------------------------------------------------------------------------- 1 | /** 2 | * xterm.js: xterm, in the browser 3 | * Copyright (c) 2014, sourceLair Limited (www.sourcelair.com (MIT License) 4 | * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) 5 | * https://github.com/chjj/term.js 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | * Originally forked from (with the author's permission): 26 | * Fabrice Bellard's javascript vt100 for jslinux: 27 | * http://bellard.org/jslinux/ 28 | * Copyright (c) 2011 Fabrice Bellard 29 | * The original design remains. The terminal itself 30 | * has been extended to include xterm CSI codes, among 31 | * other features. 32 | */ 33 | 34 | /* 35 | * Default style for xterm.js 36 | */ 37 | 38 | .terminal { 39 | background-color: #000; 40 | color: #fff; 41 | font-family: courier-new, courier, monospace; 42 | position: relative; 43 | } 44 | 45 | .terminal:focus { 46 | outline: none; 47 | } 48 | 49 | .terminal .terminal-cursor { 50 | background-color: #fff; 51 | color: #000; 52 | } 53 | 54 | .terminal:not(:focus) .terminal-cursor { 55 | outline: 1px solid #fff; 56 | outline-offset: -1px; 57 | background-color: transparent; 58 | } 59 | 60 | .terminal .terminal-cursor.blinking { 61 | animation: blink-cursor 1.2s infinite step-end; 62 | } 63 | 64 | @keyframes blink-cursor { 65 | 0% { 66 | background-color: #fff; 67 | color: #000; 68 | } 69 | 50% { 70 | background-color: transparent; 71 | color: #FFF; 72 | } 73 | } 74 | 75 | /* 76 | * Determine default colors for xterm.js 77 | */ 78 | .terminal .xterm-bold { 79 | font-weight: bold; 80 | } 81 | 82 | .terminal .xterm-underline { 83 | text-decoration: underline; 84 | } 85 | 86 | .terminal .xterm-blink { 87 | text-decoration: blink; 88 | } 89 | 90 | .terminal .xterm-hidden { 91 | visibility: hidden; 92 | } 93 | 94 | .terminal .xterm-color-0 { 95 | color: #2e3436; 96 | } 97 | 98 | .terminal .xterm-bg-color-0 { 99 | background-color: #2e3436; 100 | } 101 | 102 | .terminal .xterm-color-1 { 103 | color: #cc0000; 104 | } 105 | 106 | .terminal .xterm-bg-color-1 { 107 | background-color: #cc0000; 108 | } 109 | 110 | .terminal .xterm-color-2 { 111 | color: #4e9a06; 112 | } 113 | 114 | .terminal .xterm-bg-color-2 { 115 | background-color: #4e9a06; 116 | } 117 | 118 | .terminal .xterm-color-3 { 119 | color: #c4a000; 120 | } 121 | 122 | .terminal .xterm-bg-color-3 { 123 | background-color: #c4a000; 124 | } 125 | 126 | .terminal .xterm-color-4 { 127 | color: #3465a4; 128 | } 129 | 130 | .terminal .xterm-bg-color-4 { 131 | background-color: #3465a4; 132 | } 133 | 134 | .terminal .xterm-color-5 { 135 | color: #75507b; 136 | } 137 | 138 | .terminal .xterm-bg-color-5 { 139 | background-color: #75507b; 140 | } 141 | 142 | .terminal .xterm-color-6 { 143 | color: #06989a; 144 | } 145 | 146 | .terminal .xterm-bg-color-6 { 147 | background-color: #06989a; 148 | } 149 | 150 | .terminal .xterm-color-7 { 151 | color: #d3d7cf; 152 | } 153 | 154 | .terminal .xterm-bg-color-7 { 155 | background-color: #d3d7cf; 156 | } 157 | 158 | .terminal .xterm-color-8 { 159 | color: #555753; 160 | } 161 | 162 | .terminal .xterm-bg-color-8 { 163 | background-color: #555753; 164 | } 165 | 166 | .terminal .xterm-color-9 { 167 | color: #ef2929; 168 | } 169 | 170 | .terminal .xterm-bg-color-9 { 171 | background-color: #ef2929; 172 | } 173 | 174 | .terminal .xterm-color-10 { 175 | color: #8ae234; 176 | } 177 | 178 | .terminal .xterm-bg-color-10 { 179 | background-color: #8ae234; 180 | } 181 | 182 | .terminal .xterm-color-11 { 183 | color: #fce94f; 184 | } 185 | 186 | .terminal .xterm-bg-color-11 { 187 | background-color: #fce94f; 188 | } 189 | 190 | .terminal .xterm-color-12 { 191 | color: #729fcf; 192 | } 193 | 194 | .terminal .xterm-bg-color-12 { 195 | background-color: #729fcf; 196 | } 197 | 198 | .terminal .xterm-color-13 { 199 | color: #ad7fa8; 200 | } 201 | 202 | .terminal .xterm-bg-color-13 { 203 | background-color: #ad7fa8; 204 | } 205 | 206 | .terminal .xterm-color-14 { 207 | color: #34e2e2; 208 | } 209 | 210 | .terminal .xterm-bg-color-14 { 211 | background-color: #34e2e2; 212 | } 213 | 214 | .terminal .xterm-color-15 { 215 | color: #eeeeec; 216 | } 217 | 218 | .terminal .xterm-bg-color-15 { 219 | background-color: #eeeeec; 220 | } 221 | 222 | .terminal .xterm-color-16 { 223 | color: #000000; 224 | } 225 | 226 | .terminal .xterm-bg-color-16 { 227 | background-color: #000000; 228 | } 229 | 230 | .terminal .xterm-color-17 { 231 | color: #00005f; 232 | } 233 | 234 | .terminal .xterm-bg-color-17 { 235 | background-color: #00005f; 236 | } 237 | 238 | .terminal .xterm-color-18 { 239 | color: #000087; 240 | } 241 | 242 | .terminal .xterm-bg-color-18 { 243 | background-color: #000087; 244 | } 245 | 246 | .terminal .xterm-color-19 { 247 | color: #0000af; 248 | } 249 | 250 | .terminal .xterm-bg-color-19 { 251 | background-color: #0000af; 252 | } 253 | 254 | .terminal .xterm-color-20 { 255 | color: #0000d7; 256 | } 257 | 258 | .terminal .xterm-bg-color-20 { 259 | background-color: #0000d7; 260 | } 261 | 262 | .terminal .xterm-color-21 { 263 | color: #0000ff; 264 | } 265 | 266 | .terminal .xterm-bg-color-21 { 267 | background-color: #0000ff; 268 | } 269 | 270 | .terminal .xterm-color-22 { 271 | color: #005f00; 272 | } 273 | 274 | .terminal .xterm-bg-color-22 { 275 | background-color: #005f00; 276 | } 277 | 278 | .terminal .xterm-color-23 { 279 | color: #005f5f; 280 | } 281 | 282 | .terminal .xterm-bg-color-23 { 283 | background-color: #005f5f; 284 | } 285 | 286 | .terminal .xterm-color-24 { 287 | color: #005f87; 288 | } 289 | 290 | .terminal .xterm-bg-color-24 { 291 | background-color: #005f87; 292 | } 293 | 294 | .terminal .xterm-color-25 { 295 | color: #005faf; 296 | } 297 | 298 | .terminal .xterm-bg-color-25 { 299 | background-color: #005faf; 300 | } 301 | 302 | .terminal .xterm-color-26 { 303 | color: #005fd7; 304 | } 305 | 306 | .terminal .xterm-bg-color-26 { 307 | background-color: #005fd7; 308 | } 309 | 310 | .terminal .xterm-color-27 { 311 | color: #005fff; 312 | } 313 | 314 | .terminal .xterm-bg-color-27 { 315 | background-color: #005fff; 316 | } 317 | 318 | .terminal .xterm-color-28 { 319 | color: #008700; 320 | } 321 | 322 | .terminal .xterm-bg-color-28 { 323 | background-color: #008700; 324 | } 325 | 326 | .terminal .xterm-color-29 { 327 | color: #00875f; 328 | } 329 | 330 | .terminal .xterm-bg-color-29 { 331 | background-color: #00875f; 332 | } 333 | 334 | .terminal .xterm-color-30 { 335 | color: #008787; 336 | } 337 | 338 | .terminal .xterm-bg-color-30 { 339 | background-color: #008787; 340 | } 341 | 342 | .terminal .xterm-color-31 { 343 | color: #0087af; 344 | } 345 | 346 | .terminal .xterm-bg-color-31 { 347 | background-color: #0087af; 348 | } 349 | 350 | .terminal .xterm-color-32 { 351 | color: #0087d7; 352 | } 353 | 354 | .terminal .xterm-bg-color-32 { 355 | background-color: #0087d7; 356 | } 357 | 358 | .terminal .xterm-color-33 { 359 | color: #0087ff; 360 | } 361 | 362 | .terminal .xterm-bg-color-33 { 363 | background-color: #0087ff; 364 | } 365 | 366 | .terminal .xterm-color-34 { 367 | color: #00af00; 368 | } 369 | 370 | .terminal .xterm-bg-color-34 { 371 | background-color: #00af00; 372 | } 373 | 374 | .terminal .xterm-color-35 { 375 | color: #00af5f; 376 | } 377 | 378 | .terminal .xterm-bg-color-35 { 379 | background-color: #00af5f; 380 | } 381 | 382 | .terminal .xterm-color-36 { 383 | color: #00af87; 384 | } 385 | 386 | .terminal .xterm-bg-color-36 { 387 | background-color: #00af87; 388 | } 389 | 390 | .terminal .xterm-color-37 { 391 | color: #00afaf; 392 | } 393 | 394 | .terminal .xterm-bg-color-37 { 395 | background-color: #00afaf; 396 | } 397 | 398 | .terminal .xterm-color-38 { 399 | color: #00afd7; 400 | } 401 | 402 | .terminal .xterm-bg-color-38 { 403 | background-color: #00afd7; 404 | } 405 | 406 | .terminal .xterm-color-39 { 407 | color: #00afff; 408 | } 409 | 410 | .terminal .xterm-bg-color-39 { 411 | background-color: #00afff; 412 | } 413 | 414 | .terminal .xterm-color-40 { 415 | color: #00d700; 416 | } 417 | 418 | .terminal .xterm-bg-color-40 { 419 | background-color: #00d700; 420 | } 421 | 422 | .terminal .xterm-color-41 { 423 | color: #00d75f; 424 | } 425 | 426 | .terminal .xterm-bg-color-41 { 427 | background-color: #00d75f; 428 | } 429 | 430 | .terminal .xterm-color-42 { 431 | color: #00d787; 432 | } 433 | 434 | .terminal .xterm-bg-color-42 { 435 | background-color: #00d787; 436 | } 437 | 438 | .terminal .xterm-color-43 { 439 | color: #00d7af; 440 | } 441 | 442 | .terminal .xterm-bg-color-43 { 443 | background-color: #00d7af; 444 | } 445 | 446 | .terminal .xterm-color-44 { 447 | color: #00d7d7; 448 | } 449 | 450 | .terminal .xterm-bg-color-44 { 451 | background-color: #00d7d7; 452 | } 453 | 454 | .terminal .xterm-color-45 { 455 | color: #00d7ff; 456 | } 457 | 458 | .terminal .xterm-bg-color-45 { 459 | background-color: #00d7ff; 460 | } 461 | 462 | .terminal .xterm-color-46 { 463 | color: #00ff00; 464 | } 465 | 466 | .terminal .xterm-bg-color-46 { 467 | background-color: #00ff00; 468 | } 469 | 470 | .terminal .xterm-color-47 { 471 | color: #00ff5f; 472 | } 473 | 474 | .terminal .xterm-bg-color-47 { 475 | background-color: #00ff5f; 476 | } 477 | 478 | .terminal .xterm-color-48 { 479 | color: #00ff87; 480 | } 481 | 482 | .terminal .xterm-bg-color-48 { 483 | background-color: #00ff87; 484 | } 485 | 486 | .terminal .xterm-color-49 { 487 | color: #00ffaf; 488 | } 489 | 490 | .terminal .xterm-bg-color-49 { 491 | background-color: #00ffaf; 492 | } 493 | 494 | .terminal .xterm-color-50 { 495 | color: #00ffd7; 496 | } 497 | 498 | .terminal .xterm-bg-color-50 { 499 | background-color: #00ffd7; 500 | } 501 | 502 | .terminal .xterm-color-51 { 503 | color: #00ffff; 504 | } 505 | 506 | .terminal .xterm-bg-color-51 { 507 | background-color: #00ffff; 508 | } 509 | 510 | .terminal .xterm-color-52 { 511 | color: #5f0000; 512 | } 513 | 514 | .terminal .xterm-bg-color-52 { 515 | background-color: #5f0000; 516 | } 517 | 518 | .terminal .xterm-color-53 { 519 | color: #5f005f; 520 | } 521 | 522 | .terminal .xterm-bg-color-53 { 523 | background-color: #5f005f; 524 | } 525 | 526 | .terminal .xterm-color-54 { 527 | color: #5f0087; 528 | } 529 | 530 | .terminal .xterm-bg-color-54 { 531 | background-color: #5f0087; 532 | } 533 | 534 | .terminal .xterm-color-55 { 535 | color: #5f00af; 536 | } 537 | 538 | .terminal .xterm-bg-color-55 { 539 | background-color: #5f00af; 540 | } 541 | 542 | .terminal .xterm-color-56 { 543 | color: #5f00d7; 544 | } 545 | 546 | .terminal .xterm-bg-color-56 { 547 | background-color: #5f00d7; 548 | } 549 | 550 | .terminal .xterm-color-57 { 551 | color: #5f00ff; 552 | } 553 | 554 | .terminal .xterm-bg-color-57 { 555 | background-color: #5f00ff; 556 | } 557 | 558 | .terminal .xterm-color-58 { 559 | color: #5f5f00; 560 | } 561 | 562 | .terminal .xterm-bg-color-58 { 563 | background-color: #5f5f00; 564 | } 565 | 566 | .terminal .xterm-color-59 { 567 | color: #5f5f5f; 568 | } 569 | 570 | .terminal .xterm-bg-color-59 { 571 | background-color: #5f5f5f; 572 | } 573 | 574 | .terminal .xterm-color-60 { 575 | color: #5f5f87; 576 | } 577 | 578 | .terminal .xterm-bg-color-60 { 579 | background-color: #5f5f87; 580 | } 581 | 582 | .terminal .xterm-color-61 { 583 | color: #5f5faf; 584 | } 585 | 586 | .terminal .xterm-bg-color-61 { 587 | background-color: #5f5faf; 588 | } 589 | 590 | .terminal .xterm-color-62 { 591 | color: #5f5fd7; 592 | } 593 | 594 | .terminal .xterm-bg-color-62 { 595 | background-color: #5f5fd7; 596 | } 597 | 598 | .terminal .xterm-color-63 { 599 | color: #5f5fff; 600 | } 601 | 602 | .terminal .xterm-bg-color-63 { 603 | background-color: #5f5fff; 604 | } 605 | 606 | .terminal .xterm-color-64 { 607 | color: #5f8700; 608 | } 609 | 610 | .terminal .xterm-bg-color-64 { 611 | background-color: #5f8700; 612 | } 613 | 614 | .terminal .xterm-color-65 { 615 | color: #5f875f; 616 | } 617 | 618 | .terminal .xterm-bg-color-65 { 619 | background-color: #5f875f; 620 | } 621 | 622 | .terminal .xterm-color-66 { 623 | color: #5f8787; 624 | } 625 | 626 | .terminal .xterm-bg-color-66 { 627 | background-color: #5f8787; 628 | } 629 | 630 | .terminal .xterm-color-67 { 631 | color: #5f87af; 632 | } 633 | 634 | .terminal .xterm-bg-color-67 { 635 | background-color: #5f87af; 636 | } 637 | 638 | .terminal .xterm-color-68 { 639 | color: #5f87d7; 640 | } 641 | 642 | .terminal .xterm-bg-color-68 { 643 | background-color: #5f87d7; 644 | } 645 | 646 | .terminal .xterm-color-69 { 647 | color: #5f87ff; 648 | } 649 | 650 | .terminal .xterm-bg-color-69 { 651 | background-color: #5f87ff; 652 | } 653 | 654 | .terminal .xterm-color-70 { 655 | color: #5faf00; 656 | } 657 | 658 | .terminal .xterm-bg-color-70 { 659 | background-color: #5faf00; 660 | } 661 | 662 | .terminal .xterm-color-71 { 663 | color: #5faf5f; 664 | } 665 | 666 | .terminal .xterm-bg-color-71 { 667 | background-color: #5faf5f; 668 | } 669 | 670 | .terminal .xterm-color-72 { 671 | color: #5faf87; 672 | } 673 | 674 | .terminal .xterm-bg-color-72 { 675 | background-color: #5faf87; 676 | } 677 | 678 | .terminal .xterm-color-73 { 679 | color: #5fafaf; 680 | } 681 | 682 | .terminal .xterm-bg-color-73 { 683 | background-color: #5fafaf; 684 | } 685 | 686 | .terminal .xterm-color-74 { 687 | color: #5fafd7; 688 | } 689 | 690 | .terminal .xterm-bg-color-74 { 691 | background-color: #5fafd7; 692 | } 693 | 694 | .terminal .xterm-color-75 { 695 | color: #5fafff; 696 | } 697 | 698 | .terminal .xterm-bg-color-75 { 699 | background-color: #5fafff; 700 | } 701 | 702 | .terminal .xterm-color-76 { 703 | color: #5fd700; 704 | } 705 | 706 | .terminal .xterm-bg-color-76 { 707 | background-color: #5fd700; 708 | } 709 | 710 | .terminal .xterm-color-77 { 711 | color: #5fd75f; 712 | } 713 | 714 | .terminal .xterm-bg-color-77 { 715 | background-color: #5fd75f; 716 | } 717 | 718 | .terminal .xterm-color-78 { 719 | color: #5fd787; 720 | } 721 | 722 | .terminal .xterm-bg-color-78 { 723 | background-color: #5fd787; 724 | } 725 | 726 | .terminal .xterm-color-79 { 727 | color: #5fd7af; 728 | } 729 | 730 | .terminal .xterm-bg-color-79 { 731 | background-color: #5fd7af; 732 | } 733 | 734 | .terminal .xterm-color-80 { 735 | color: #5fd7d7; 736 | } 737 | 738 | .terminal .xterm-bg-color-80 { 739 | background-color: #5fd7d7; 740 | } 741 | 742 | .terminal .xterm-color-81 { 743 | color: #5fd7ff; 744 | } 745 | 746 | .terminal .xterm-bg-color-81 { 747 | background-color: #5fd7ff; 748 | } 749 | 750 | .terminal .xterm-color-82 { 751 | color: #5fff00; 752 | } 753 | 754 | .terminal .xterm-bg-color-82 { 755 | background-color: #5fff00; 756 | } 757 | 758 | .terminal .xterm-color-83 { 759 | color: #5fff5f; 760 | } 761 | 762 | .terminal .xterm-bg-color-83 { 763 | background-color: #5fff5f; 764 | } 765 | 766 | .terminal .xterm-color-84 { 767 | color: #5fff87; 768 | } 769 | 770 | .terminal .xterm-bg-color-84 { 771 | background-color: #5fff87; 772 | } 773 | 774 | .terminal .xterm-color-85 { 775 | color: #5fffaf; 776 | } 777 | 778 | .terminal .xterm-bg-color-85 { 779 | background-color: #5fffaf; 780 | } 781 | 782 | .terminal .xterm-color-86 { 783 | color: #5fffd7; 784 | } 785 | 786 | .terminal .xterm-bg-color-86 { 787 | background-color: #5fffd7; 788 | } 789 | 790 | .terminal .xterm-color-87 { 791 | color: #5fffff; 792 | } 793 | 794 | .terminal .xterm-bg-color-87 { 795 | background-color: #5fffff; 796 | } 797 | 798 | .terminal .xterm-color-88 { 799 | color: #870000; 800 | } 801 | 802 | .terminal .xterm-bg-color-88 { 803 | background-color: #870000; 804 | } 805 | 806 | .terminal .xterm-color-89 { 807 | color: #87005f; 808 | } 809 | 810 | .terminal .xterm-bg-color-89 { 811 | background-color: #87005f; 812 | } 813 | 814 | .terminal .xterm-color-90 { 815 | color: #870087; 816 | } 817 | 818 | .terminal .xterm-bg-color-90 { 819 | background-color: #870087; 820 | } 821 | 822 | .terminal .xterm-color-91 { 823 | color: #8700af; 824 | } 825 | 826 | .terminal .xterm-bg-color-91 { 827 | background-color: #8700af; 828 | } 829 | 830 | .terminal .xterm-color-92 { 831 | color: #8700d7; 832 | } 833 | 834 | .terminal .xterm-bg-color-92 { 835 | background-color: #8700d7; 836 | } 837 | 838 | .terminal .xterm-color-93 { 839 | color: #8700ff; 840 | } 841 | 842 | .terminal .xterm-bg-color-93 { 843 | background-color: #8700ff; 844 | } 845 | 846 | .terminal .xterm-color-94 { 847 | color: #875f00; 848 | } 849 | 850 | .terminal .xterm-bg-color-94 { 851 | background-color: #875f00; 852 | } 853 | 854 | .terminal .xterm-color-95 { 855 | color: #875f5f; 856 | } 857 | 858 | .terminal .xterm-bg-color-95 { 859 | background-color: #875f5f; 860 | } 861 | 862 | .terminal .xterm-color-96 { 863 | color: #875f87; 864 | } 865 | 866 | .terminal .xterm-bg-color-96 { 867 | background-color: #875f87; 868 | } 869 | 870 | .terminal .xterm-color-97 { 871 | color: #875faf; 872 | } 873 | 874 | .terminal .xterm-bg-color-97 { 875 | background-color: #875faf; 876 | } 877 | 878 | .terminal .xterm-color-98 { 879 | color: #875fd7; 880 | } 881 | 882 | .terminal .xterm-bg-color-98 { 883 | background-color: #875fd7; 884 | } 885 | 886 | .terminal .xterm-color-99 { 887 | color: #875fff; 888 | } 889 | 890 | .terminal .xterm-bg-color-99 { 891 | background-color: #875fff; 892 | } 893 | 894 | .terminal .xterm-color-100 { 895 | color: #878700; 896 | } 897 | 898 | .terminal .xterm-bg-color-100 { 899 | background-color: #878700; 900 | } 901 | 902 | .terminal .xterm-color-101 { 903 | color: #87875f; 904 | } 905 | 906 | .terminal .xterm-bg-color-101 { 907 | background-color: #87875f; 908 | } 909 | 910 | .terminal .xterm-color-102 { 911 | color: #878787; 912 | } 913 | 914 | .terminal .xterm-bg-color-102 { 915 | background-color: #878787; 916 | } 917 | 918 | .terminal .xterm-color-103 { 919 | color: #8787af; 920 | } 921 | 922 | .terminal .xterm-bg-color-103 { 923 | background-color: #8787af; 924 | } 925 | 926 | .terminal .xterm-color-104 { 927 | color: #8787d7; 928 | } 929 | 930 | .terminal .xterm-bg-color-104 { 931 | background-color: #8787d7; 932 | } 933 | 934 | .terminal .xterm-color-105 { 935 | color: #8787ff; 936 | } 937 | 938 | .terminal .xterm-bg-color-105 { 939 | background-color: #8787ff; 940 | } 941 | 942 | .terminal .xterm-color-106 { 943 | color: #87af00; 944 | } 945 | 946 | .terminal .xterm-bg-color-106 { 947 | background-color: #87af00; 948 | } 949 | 950 | .terminal .xterm-color-107 { 951 | color: #87af5f; 952 | } 953 | 954 | .terminal .xterm-bg-color-107 { 955 | background-color: #87af5f; 956 | } 957 | 958 | .terminal .xterm-color-108 { 959 | color: #87af87; 960 | } 961 | 962 | .terminal .xterm-bg-color-108 { 963 | background-color: #87af87; 964 | } 965 | 966 | .terminal .xterm-color-109 { 967 | color: #87afaf; 968 | } 969 | 970 | .terminal .xterm-bg-color-109 { 971 | background-color: #87afaf; 972 | } 973 | 974 | .terminal .xterm-color-110 { 975 | color: #87afd7; 976 | } 977 | 978 | .terminal .xterm-bg-color-110 { 979 | background-color: #87afd7; 980 | } 981 | 982 | .terminal .xterm-color-111 { 983 | color: #87afff; 984 | } 985 | 986 | .terminal .xterm-bg-color-111 { 987 | background-color: #87afff; 988 | } 989 | 990 | .terminal .xterm-color-112 { 991 | color: #87d700; 992 | } 993 | 994 | .terminal .xterm-bg-color-112 { 995 | background-color: #87d700; 996 | } 997 | 998 | .terminal .xterm-color-113 { 999 | color: #87d75f; 1000 | } 1001 | 1002 | .terminal .xterm-bg-color-113 { 1003 | background-color: #87d75f; 1004 | } 1005 | 1006 | .terminal .xterm-color-114 { 1007 | color: #87d787; 1008 | } 1009 | 1010 | .terminal .xterm-bg-color-114 { 1011 | background-color: #87d787; 1012 | } 1013 | 1014 | .terminal .xterm-color-115 { 1015 | color: #87d7af; 1016 | } 1017 | 1018 | .terminal .xterm-bg-color-115 { 1019 | background-color: #87d7af; 1020 | } 1021 | 1022 | .terminal .xterm-color-116 { 1023 | color: #87d7d7; 1024 | } 1025 | 1026 | .terminal .xterm-bg-color-116 { 1027 | background-color: #87d7d7; 1028 | } 1029 | 1030 | .terminal .xterm-color-117 { 1031 | color: #87d7ff; 1032 | } 1033 | 1034 | .terminal .xterm-bg-color-117 { 1035 | background-color: #87d7ff; 1036 | } 1037 | 1038 | .terminal .xterm-color-118 { 1039 | color: #87ff00; 1040 | } 1041 | 1042 | .terminal .xterm-bg-color-118 { 1043 | background-color: #87ff00; 1044 | } 1045 | 1046 | .terminal .xterm-color-119 { 1047 | color: #87ff5f; 1048 | } 1049 | 1050 | .terminal .xterm-bg-color-119 { 1051 | background-color: #87ff5f; 1052 | } 1053 | 1054 | .terminal .xterm-color-120 { 1055 | color: #87ff87; 1056 | } 1057 | 1058 | .terminal .xterm-bg-color-120 { 1059 | background-color: #87ff87; 1060 | } 1061 | 1062 | .terminal .xterm-color-121 { 1063 | color: #87ffaf; 1064 | } 1065 | 1066 | .terminal .xterm-bg-color-121 { 1067 | background-color: #87ffaf; 1068 | } 1069 | 1070 | .terminal .xterm-color-122 { 1071 | color: #87ffd7; 1072 | } 1073 | 1074 | .terminal .xterm-bg-color-122 { 1075 | background-color: #87ffd7; 1076 | } 1077 | 1078 | .terminal .xterm-color-123 { 1079 | color: #87ffff; 1080 | } 1081 | 1082 | .terminal .xterm-bg-color-123 { 1083 | background-color: #87ffff; 1084 | } 1085 | 1086 | .terminal .xterm-color-124 { 1087 | color: #af0000; 1088 | } 1089 | 1090 | .terminal .xterm-bg-color-124 { 1091 | background-color: #af0000; 1092 | } 1093 | 1094 | .terminal .xterm-color-125 { 1095 | color: #af005f; 1096 | } 1097 | 1098 | .terminal .xterm-bg-color-125 { 1099 | background-color: #af005f; 1100 | } 1101 | 1102 | .terminal .xterm-color-126 { 1103 | color: #af0087; 1104 | } 1105 | 1106 | .terminal .xterm-bg-color-126 { 1107 | background-color: #af0087; 1108 | } 1109 | 1110 | .terminal .xterm-color-127 { 1111 | color: #af00af; 1112 | } 1113 | 1114 | .terminal .xterm-bg-color-127 { 1115 | background-color: #af00af; 1116 | } 1117 | 1118 | .terminal .xterm-color-128 { 1119 | color: #af00d7; 1120 | } 1121 | 1122 | .terminal .xterm-bg-color-128 { 1123 | background-color: #af00d7; 1124 | } 1125 | 1126 | .terminal .xterm-color-129 { 1127 | color: #af00ff; 1128 | } 1129 | 1130 | .terminal .xterm-bg-color-129 { 1131 | background-color: #af00ff; 1132 | } 1133 | 1134 | .terminal .xterm-color-130 { 1135 | color: #af5f00; 1136 | } 1137 | 1138 | .terminal .xterm-bg-color-130 { 1139 | background-color: #af5f00; 1140 | } 1141 | 1142 | .terminal .xterm-color-131 { 1143 | color: #af5f5f; 1144 | } 1145 | 1146 | .terminal .xterm-bg-color-131 { 1147 | background-color: #af5f5f; 1148 | } 1149 | 1150 | .terminal .xterm-color-132 { 1151 | color: #af5f87; 1152 | } 1153 | 1154 | .terminal .xterm-bg-color-132 { 1155 | background-color: #af5f87; 1156 | } 1157 | 1158 | .terminal .xterm-color-133 { 1159 | color: #af5faf; 1160 | } 1161 | 1162 | .terminal .xterm-bg-color-133 { 1163 | background-color: #af5faf; 1164 | } 1165 | 1166 | .terminal .xterm-color-134 { 1167 | color: #af5fd7; 1168 | } 1169 | 1170 | .terminal .xterm-bg-color-134 { 1171 | background-color: #af5fd7; 1172 | } 1173 | 1174 | .terminal .xterm-color-135 { 1175 | color: #af5fff; 1176 | } 1177 | 1178 | .terminal .xterm-bg-color-135 { 1179 | background-color: #af5fff; 1180 | } 1181 | 1182 | .terminal .xterm-color-136 { 1183 | color: #af8700; 1184 | } 1185 | 1186 | .terminal .xterm-bg-color-136 { 1187 | background-color: #af8700; 1188 | } 1189 | 1190 | .terminal .xterm-color-137 { 1191 | color: #af875f; 1192 | } 1193 | 1194 | .terminal .xterm-bg-color-137 { 1195 | background-color: #af875f; 1196 | } 1197 | 1198 | .terminal .xterm-color-138 { 1199 | color: #af8787; 1200 | } 1201 | 1202 | .terminal .xterm-bg-color-138 { 1203 | background-color: #af8787; 1204 | } 1205 | 1206 | .terminal .xterm-color-139 { 1207 | color: #af87af; 1208 | } 1209 | 1210 | .terminal .xterm-bg-color-139 { 1211 | background-color: #af87af; 1212 | } 1213 | 1214 | .terminal .xterm-color-140 { 1215 | color: #af87d7; 1216 | } 1217 | 1218 | .terminal .xterm-bg-color-140 { 1219 | background-color: #af87d7; 1220 | } 1221 | 1222 | .terminal .xterm-color-141 { 1223 | color: #af87ff; 1224 | } 1225 | 1226 | .terminal .xterm-bg-color-141 { 1227 | background-color: #af87ff; 1228 | } 1229 | 1230 | .terminal .xterm-color-142 { 1231 | color: #afaf00; 1232 | } 1233 | 1234 | .terminal .xterm-bg-color-142 { 1235 | background-color: #afaf00; 1236 | } 1237 | 1238 | .terminal .xterm-color-143 { 1239 | color: #afaf5f; 1240 | } 1241 | 1242 | .terminal .xterm-bg-color-143 { 1243 | background-color: #afaf5f; 1244 | } 1245 | 1246 | .terminal .xterm-color-144 { 1247 | color: #afaf87; 1248 | } 1249 | 1250 | .terminal .xterm-bg-color-144 { 1251 | background-color: #afaf87; 1252 | } 1253 | 1254 | .terminal .xterm-color-145 { 1255 | color: #afafaf; 1256 | } 1257 | 1258 | .terminal .xterm-bg-color-145 { 1259 | background-color: #afafaf; 1260 | } 1261 | 1262 | .terminal .xterm-color-146 { 1263 | color: #afafd7; 1264 | } 1265 | 1266 | .terminal .xterm-bg-color-146 { 1267 | background-color: #afafd7; 1268 | } 1269 | 1270 | .terminal .xterm-color-147 { 1271 | color: #afafff; 1272 | } 1273 | 1274 | .terminal .xterm-bg-color-147 { 1275 | background-color: #afafff; 1276 | } 1277 | 1278 | .terminal .xterm-color-148 { 1279 | color: #afd700; 1280 | } 1281 | 1282 | .terminal .xterm-bg-color-148 { 1283 | background-color: #afd700; 1284 | } 1285 | 1286 | .terminal .xterm-color-149 { 1287 | color: #afd75f; 1288 | } 1289 | 1290 | .terminal .xterm-bg-color-149 { 1291 | background-color: #afd75f; 1292 | } 1293 | 1294 | .terminal .xterm-color-150 { 1295 | color: #afd787; 1296 | } 1297 | 1298 | .terminal .xterm-bg-color-150 { 1299 | background-color: #afd787; 1300 | } 1301 | 1302 | .terminal .xterm-color-151 { 1303 | color: #afd7af; 1304 | } 1305 | 1306 | .terminal .xterm-bg-color-151 { 1307 | background-color: #afd7af; 1308 | } 1309 | 1310 | .terminal .xterm-color-152 { 1311 | color: #afd7d7; 1312 | } 1313 | 1314 | .terminal .xterm-bg-color-152 { 1315 | background-color: #afd7d7; 1316 | } 1317 | 1318 | .terminal .xterm-color-153 { 1319 | color: #afd7ff; 1320 | } 1321 | 1322 | .terminal .xterm-bg-color-153 { 1323 | background-color: #afd7ff; 1324 | } 1325 | 1326 | .terminal .xterm-color-154 { 1327 | color: #afff00; 1328 | } 1329 | 1330 | .terminal .xterm-bg-color-154 { 1331 | background-color: #afff00; 1332 | } 1333 | 1334 | .terminal .xterm-color-155 { 1335 | color: #afff5f; 1336 | } 1337 | 1338 | .terminal .xterm-bg-color-155 { 1339 | background-color: #afff5f; 1340 | } 1341 | 1342 | .terminal .xterm-color-156 { 1343 | color: #afff87; 1344 | } 1345 | 1346 | .terminal .xterm-bg-color-156 { 1347 | background-color: #afff87; 1348 | } 1349 | 1350 | .terminal .xterm-color-157 { 1351 | color: #afffaf; 1352 | } 1353 | 1354 | .terminal .xterm-bg-color-157 { 1355 | background-color: #afffaf; 1356 | } 1357 | 1358 | .terminal .xterm-color-158 { 1359 | color: #afffd7; 1360 | } 1361 | 1362 | .terminal .xterm-bg-color-158 { 1363 | background-color: #afffd7; 1364 | } 1365 | 1366 | .terminal .xterm-color-159 { 1367 | color: #afffff; 1368 | } 1369 | 1370 | .terminal .xterm-bg-color-159 { 1371 | background-color: #afffff; 1372 | } 1373 | 1374 | .terminal .xterm-color-160 { 1375 | color: #d70000; 1376 | } 1377 | 1378 | .terminal .xterm-bg-color-160 { 1379 | background-color: #d70000; 1380 | } 1381 | 1382 | .terminal .xterm-color-161 { 1383 | color: #d7005f; 1384 | } 1385 | 1386 | .terminal .xterm-bg-color-161 { 1387 | background-color: #d7005f; 1388 | } 1389 | 1390 | .terminal .xterm-color-162 { 1391 | color: #d70087; 1392 | } 1393 | 1394 | .terminal .xterm-bg-color-162 { 1395 | background-color: #d70087; 1396 | } 1397 | 1398 | .terminal .xterm-color-163 { 1399 | color: #d700af; 1400 | } 1401 | 1402 | .terminal .xterm-bg-color-163 { 1403 | background-color: #d700af; 1404 | } 1405 | 1406 | .terminal .xterm-color-164 { 1407 | color: #d700d7; 1408 | } 1409 | 1410 | .terminal .xterm-bg-color-164 { 1411 | background-color: #d700d7; 1412 | } 1413 | 1414 | .terminal .xterm-color-165 { 1415 | color: #d700ff; 1416 | } 1417 | 1418 | .terminal .xterm-bg-color-165 { 1419 | background-color: #d700ff; 1420 | } 1421 | 1422 | .terminal .xterm-color-166 { 1423 | color: #d75f00; 1424 | } 1425 | 1426 | .terminal .xterm-bg-color-166 { 1427 | background-color: #d75f00; 1428 | } 1429 | 1430 | .terminal .xterm-color-167 { 1431 | color: #d75f5f; 1432 | } 1433 | 1434 | .terminal .xterm-bg-color-167 { 1435 | background-color: #d75f5f; 1436 | } 1437 | 1438 | .terminal .xterm-color-168 { 1439 | color: #d75f87; 1440 | } 1441 | 1442 | .terminal .xterm-bg-color-168 { 1443 | background-color: #d75f87; 1444 | } 1445 | 1446 | .terminal .xterm-color-169 { 1447 | color: #d75faf; 1448 | } 1449 | 1450 | .terminal .xterm-bg-color-169 { 1451 | background-color: #d75faf; 1452 | } 1453 | 1454 | .terminal .xterm-color-170 { 1455 | color: #d75fd7; 1456 | } 1457 | 1458 | .terminal .xterm-bg-color-170 { 1459 | background-color: #d75fd7; 1460 | } 1461 | 1462 | .terminal .xterm-color-171 { 1463 | color: #d75fff; 1464 | } 1465 | 1466 | .terminal .xterm-bg-color-171 { 1467 | background-color: #d75fff; 1468 | } 1469 | 1470 | .terminal .xterm-color-172 { 1471 | color: #d78700; 1472 | } 1473 | 1474 | .terminal .xterm-bg-color-172 { 1475 | background-color: #d78700; 1476 | } 1477 | 1478 | .terminal .xterm-color-173 { 1479 | color: #d7875f; 1480 | } 1481 | 1482 | .terminal .xterm-bg-color-173 { 1483 | background-color: #d7875f; 1484 | } 1485 | 1486 | .terminal .xterm-color-174 { 1487 | color: #d78787; 1488 | } 1489 | 1490 | .terminal .xterm-bg-color-174 { 1491 | background-color: #d78787; 1492 | } 1493 | 1494 | .terminal .xterm-color-175 { 1495 | color: #d787af; 1496 | } 1497 | 1498 | .terminal .xterm-bg-color-175 { 1499 | background-color: #d787af; 1500 | } 1501 | 1502 | .terminal .xterm-color-176 { 1503 | color: #d787d7; 1504 | } 1505 | 1506 | .terminal .xterm-bg-color-176 { 1507 | background-color: #d787d7; 1508 | } 1509 | 1510 | .terminal .xterm-color-177 { 1511 | color: #d787ff; 1512 | } 1513 | 1514 | .terminal .xterm-bg-color-177 { 1515 | background-color: #d787ff; 1516 | } 1517 | 1518 | .terminal .xterm-color-178 { 1519 | color: #d7af00; 1520 | } 1521 | 1522 | .terminal .xterm-bg-color-178 { 1523 | background-color: #d7af00; 1524 | } 1525 | 1526 | .terminal .xterm-color-179 { 1527 | color: #d7af5f; 1528 | } 1529 | 1530 | .terminal .xterm-bg-color-179 { 1531 | background-color: #d7af5f; 1532 | } 1533 | 1534 | .terminal .xterm-color-180 { 1535 | color: #d7af87; 1536 | } 1537 | 1538 | .terminal .xterm-bg-color-180 { 1539 | background-color: #d7af87; 1540 | } 1541 | 1542 | .terminal .xterm-color-181 { 1543 | color: #d7afaf; 1544 | } 1545 | 1546 | .terminal .xterm-bg-color-181 { 1547 | background-color: #d7afaf; 1548 | } 1549 | 1550 | .terminal .xterm-color-182 { 1551 | color: #d7afd7; 1552 | } 1553 | 1554 | .terminal .xterm-bg-color-182 { 1555 | background-color: #d7afd7; 1556 | } 1557 | 1558 | .terminal .xterm-color-183 { 1559 | color: #d7afff; 1560 | } 1561 | 1562 | .terminal .xterm-bg-color-183 { 1563 | background-color: #d7afff; 1564 | } 1565 | 1566 | .terminal .xterm-color-184 { 1567 | color: #d7d700; 1568 | } 1569 | 1570 | .terminal .xterm-bg-color-184 { 1571 | background-color: #d7d700; 1572 | } 1573 | 1574 | .terminal .xterm-color-185 { 1575 | color: #d7d75f; 1576 | } 1577 | 1578 | .terminal .xterm-bg-color-185 { 1579 | background-color: #d7d75f; 1580 | } 1581 | 1582 | .terminal .xterm-color-186 { 1583 | color: #d7d787; 1584 | } 1585 | 1586 | .terminal .xterm-bg-color-186 { 1587 | background-color: #d7d787; 1588 | } 1589 | 1590 | .terminal .xterm-color-187 { 1591 | color: #d7d7af; 1592 | } 1593 | 1594 | .terminal .xterm-bg-color-187 { 1595 | background-color: #d7d7af; 1596 | } 1597 | 1598 | .terminal .xterm-color-188 { 1599 | color: #d7d7d7; 1600 | } 1601 | 1602 | .terminal .xterm-bg-color-188 { 1603 | background-color: #d7d7d7; 1604 | } 1605 | 1606 | .terminal .xterm-color-189 { 1607 | color: #d7d7ff; 1608 | } 1609 | 1610 | .terminal .xterm-bg-color-189 { 1611 | background-color: #d7d7ff; 1612 | } 1613 | 1614 | .terminal .xterm-color-190 { 1615 | color: #d7ff00; 1616 | } 1617 | 1618 | .terminal .xterm-bg-color-190 { 1619 | background-color: #d7ff00; 1620 | } 1621 | 1622 | .terminal .xterm-color-191 { 1623 | color: #d7ff5f; 1624 | } 1625 | 1626 | .terminal .xterm-bg-color-191 { 1627 | background-color: #d7ff5f; 1628 | } 1629 | 1630 | .terminal .xterm-color-192 { 1631 | color: #d7ff87; 1632 | } 1633 | 1634 | .terminal .xterm-bg-color-192 { 1635 | background-color: #d7ff87; 1636 | } 1637 | 1638 | .terminal .xterm-color-193 { 1639 | color: #d7ffaf; 1640 | } 1641 | 1642 | .terminal .xterm-bg-color-193 { 1643 | background-color: #d7ffaf; 1644 | } 1645 | 1646 | .terminal .xterm-color-194 { 1647 | color: #d7ffd7; 1648 | } 1649 | 1650 | .terminal .xterm-bg-color-194 { 1651 | background-color: #d7ffd7; 1652 | } 1653 | 1654 | .terminal .xterm-color-195 { 1655 | color: #d7ffff; 1656 | } 1657 | 1658 | .terminal .xterm-bg-color-195 { 1659 | background-color: #d7ffff; 1660 | } 1661 | 1662 | .terminal .xterm-color-196 { 1663 | color: #ff0000; 1664 | } 1665 | 1666 | .terminal .xterm-bg-color-196 { 1667 | background-color: #ff0000; 1668 | } 1669 | 1670 | .terminal .xterm-color-197 { 1671 | color: #ff005f; 1672 | } 1673 | 1674 | .terminal .xterm-bg-color-197 { 1675 | background-color: #ff005f; 1676 | } 1677 | 1678 | .terminal .xterm-color-198 { 1679 | color: #ff0087; 1680 | } 1681 | 1682 | .terminal .xterm-bg-color-198 { 1683 | background-color: #ff0087; 1684 | } 1685 | 1686 | .terminal .xterm-color-199 { 1687 | color: #ff00af; 1688 | } 1689 | 1690 | .terminal .xterm-bg-color-199 { 1691 | background-color: #ff00af; 1692 | } 1693 | 1694 | .terminal .xterm-color-200 { 1695 | color: #ff00d7; 1696 | } 1697 | 1698 | .terminal .xterm-bg-color-200 { 1699 | background-color: #ff00d7; 1700 | } 1701 | 1702 | .terminal .xterm-color-201 { 1703 | color: #ff00ff; 1704 | } 1705 | 1706 | .terminal .xterm-bg-color-201 { 1707 | background-color: #ff00ff; 1708 | } 1709 | 1710 | .terminal .xterm-color-202 { 1711 | color: #ff5f00; 1712 | } 1713 | 1714 | .terminal .xterm-bg-color-202 { 1715 | background-color: #ff5f00; 1716 | } 1717 | 1718 | .terminal .xterm-color-203 { 1719 | color: #ff5f5f; 1720 | } 1721 | 1722 | .terminal .xterm-bg-color-203 { 1723 | background-color: #ff5f5f; 1724 | } 1725 | 1726 | .terminal .xterm-color-204 { 1727 | color: #ff5f87; 1728 | } 1729 | 1730 | .terminal .xterm-bg-color-204 { 1731 | background-color: #ff5f87; 1732 | } 1733 | 1734 | .terminal .xterm-color-205 { 1735 | color: #ff5faf; 1736 | } 1737 | 1738 | .terminal .xterm-bg-color-205 { 1739 | background-color: #ff5faf; 1740 | } 1741 | 1742 | .terminal .xterm-color-206 { 1743 | color: #ff5fd7; 1744 | } 1745 | 1746 | .terminal .xterm-bg-color-206 { 1747 | background-color: #ff5fd7; 1748 | } 1749 | 1750 | .terminal .xterm-color-207 { 1751 | color: #ff5fff; 1752 | } 1753 | 1754 | .terminal .xterm-bg-color-207 { 1755 | background-color: #ff5fff; 1756 | } 1757 | 1758 | .terminal .xterm-color-208 { 1759 | color: #ff8700; 1760 | } 1761 | 1762 | .terminal .xterm-bg-color-208 { 1763 | background-color: #ff8700; 1764 | } 1765 | 1766 | .terminal .xterm-color-209 { 1767 | color: #ff875f; 1768 | } 1769 | 1770 | .terminal .xterm-bg-color-209 { 1771 | background-color: #ff875f; 1772 | } 1773 | 1774 | .terminal .xterm-color-210 { 1775 | color: #ff8787; 1776 | } 1777 | 1778 | .terminal .xterm-bg-color-210 { 1779 | background-color: #ff8787; 1780 | } 1781 | 1782 | .terminal .xterm-color-211 { 1783 | color: #ff87af; 1784 | } 1785 | 1786 | .terminal .xterm-bg-color-211 { 1787 | background-color: #ff87af; 1788 | } 1789 | 1790 | .terminal .xterm-color-212 { 1791 | color: #ff87d7; 1792 | } 1793 | 1794 | .terminal .xterm-bg-color-212 { 1795 | background-color: #ff87d7; 1796 | } 1797 | 1798 | .terminal .xterm-color-213 { 1799 | color: #ff87ff; 1800 | } 1801 | 1802 | .terminal .xterm-bg-color-213 { 1803 | background-color: #ff87ff; 1804 | } 1805 | 1806 | .terminal .xterm-color-214 { 1807 | color: #ffaf00; 1808 | } 1809 | 1810 | .terminal .xterm-bg-color-214 { 1811 | background-color: #ffaf00; 1812 | } 1813 | 1814 | .terminal .xterm-color-215 { 1815 | color: #ffaf5f; 1816 | } 1817 | 1818 | .terminal .xterm-bg-color-215 { 1819 | background-color: #ffaf5f; 1820 | } 1821 | 1822 | .terminal .xterm-color-216 { 1823 | color: #ffaf87; 1824 | } 1825 | 1826 | .terminal .xterm-bg-color-216 { 1827 | background-color: #ffaf87; 1828 | } 1829 | 1830 | .terminal .xterm-color-217 { 1831 | color: #ffafaf; 1832 | } 1833 | 1834 | .terminal .xterm-bg-color-217 { 1835 | background-color: #ffafaf; 1836 | } 1837 | 1838 | .terminal .xterm-color-218 { 1839 | color: #ffafd7; 1840 | } 1841 | 1842 | .terminal .xterm-bg-color-218 { 1843 | background-color: #ffafd7; 1844 | } 1845 | 1846 | .terminal .xterm-color-219 { 1847 | color: #ffafff; 1848 | } 1849 | 1850 | .terminal .xterm-bg-color-219 { 1851 | background-color: #ffafff; 1852 | } 1853 | 1854 | .terminal .xterm-color-220 { 1855 | color: #ffd700; 1856 | } 1857 | 1858 | .terminal .xterm-bg-color-220 { 1859 | background-color: #ffd700; 1860 | } 1861 | 1862 | .terminal .xterm-color-221 { 1863 | color: #ffd75f; 1864 | } 1865 | 1866 | .terminal .xterm-bg-color-221 { 1867 | background-color: #ffd75f; 1868 | } 1869 | 1870 | .terminal .xterm-color-222 { 1871 | color: #ffd787; 1872 | } 1873 | 1874 | .terminal .xterm-bg-color-222 { 1875 | background-color: #ffd787; 1876 | } 1877 | 1878 | .terminal .xterm-color-223 { 1879 | color: #ffd7af; 1880 | } 1881 | 1882 | .terminal .xterm-bg-color-223 { 1883 | background-color: #ffd7af; 1884 | } 1885 | 1886 | .terminal .xterm-color-224 { 1887 | color: #ffd7d7; 1888 | } 1889 | 1890 | .terminal .xterm-bg-color-224 { 1891 | background-color: #ffd7d7; 1892 | } 1893 | 1894 | .terminal .xterm-color-225 { 1895 | color: #ffd7ff; 1896 | } 1897 | 1898 | .terminal .xterm-bg-color-225 { 1899 | background-color: #ffd7ff; 1900 | } 1901 | 1902 | .terminal .xterm-color-226 { 1903 | color: #ffff00; 1904 | } 1905 | 1906 | .terminal .xterm-bg-color-226 { 1907 | background-color: #ffff00; 1908 | } 1909 | 1910 | .terminal .xterm-color-227 { 1911 | color: #ffff5f; 1912 | } 1913 | 1914 | .terminal .xterm-bg-color-227 { 1915 | background-color: #ffff5f; 1916 | } 1917 | 1918 | .terminal .xterm-color-228 { 1919 | color: #ffff87; 1920 | } 1921 | 1922 | .terminal .xterm-bg-color-228 { 1923 | background-color: #ffff87; 1924 | } 1925 | 1926 | .terminal .xterm-color-229 { 1927 | color: #ffffaf; 1928 | } 1929 | 1930 | .terminal .xterm-bg-color-229 { 1931 | background-color: #ffffaf; 1932 | } 1933 | 1934 | .terminal .xterm-color-230 { 1935 | color: #ffffd7; 1936 | } 1937 | 1938 | .terminal .xterm-bg-color-230 { 1939 | background-color: #ffffd7; 1940 | } 1941 | 1942 | .terminal .xterm-color-231 { 1943 | color: #ffffff; 1944 | } 1945 | 1946 | .terminal .xterm-bg-color-231 { 1947 | background-color: #ffffff; 1948 | } 1949 | 1950 | .terminal .xterm-color-232 { 1951 | color: #080808; 1952 | } 1953 | 1954 | .terminal .xterm-bg-color-232 { 1955 | background-color: #080808; 1956 | } 1957 | 1958 | .terminal .xterm-color-233 { 1959 | color: #121212; 1960 | } 1961 | 1962 | .terminal .xterm-bg-color-233 { 1963 | background-color: #121212; 1964 | } 1965 | 1966 | .terminal .xterm-color-234 { 1967 | color: #1c1c1c; 1968 | } 1969 | 1970 | .terminal .xterm-bg-color-234 { 1971 | background-color: #1c1c1c; 1972 | } 1973 | 1974 | .terminal .xterm-color-235 { 1975 | color: #262626; 1976 | } 1977 | 1978 | .terminal .xterm-bg-color-235 { 1979 | background-color: #262626; 1980 | } 1981 | 1982 | .terminal .xterm-color-236 { 1983 | color: #303030; 1984 | } 1985 | 1986 | .terminal .xterm-bg-color-236 { 1987 | background-color: #303030; 1988 | } 1989 | 1990 | .terminal .xterm-color-237 { 1991 | color: #3a3a3a; 1992 | } 1993 | 1994 | .terminal .xterm-bg-color-237 { 1995 | background-color: #3a3a3a; 1996 | } 1997 | 1998 | .terminal .xterm-color-238 { 1999 | color: #444444; 2000 | } 2001 | 2002 | .terminal .xterm-bg-color-238 { 2003 | background-color: #444444; 2004 | } 2005 | 2006 | .terminal .xterm-color-239 { 2007 | color: #4e4e4e; 2008 | } 2009 | 2010 | .terminal .xterm-bg-color-239 { 2011 | background-color: #4e4e4e; 2012 | } 2013 | 2014 | .terminal .xterm-color-240 { 2015 | color: #585858; 2016 | } 2017 | 2018 | .terminal .xterm-bg-color-240 { 2019 | background-color: #585858; 2020 | } 2021 | 2022 | .terminal .xterm-color-241 { 2023 | color: #626262; 2024 | } 2025 | 2026 | .terminal .xterm-bg-color-241 { 2027 | background-color: #626262; 2028 | } 2029 | 2030 | .terminal .xterm-color-242 { 2031 | color: #6c6c6c; 2032 | } 2033 | 2034 | .terminal .xterm-bg-color-242 { 2035 | background-color: #6c6c6c; 2036 | } 2037 | 2038 | .terminal .xterm-color-243 { 2039 | color: #767676; 2040 | } 2041 | 2042 | .terminal .xterm-bg-color-243 { 2043 | background-color: #767676; 2044 | } 2045 | 2046 | .terminal .xterm-color-244 { 2047 | color: #808080; 2048 | } 2049 | 2050 | .terminal .xterm-bg-color-244 { 2051 | background-color: #808080; 2052 | } 2053 | 2054 | .terminal .xterm-color-245 { 2055 | color: #8a8a8a; 2056 | } 2057 | 2058 | .terminal .xterm-bg-color-245 { 2059 | background-color: #8a8a8a; 2060 | } 2061 | 2062 | .terminal .xterm-color-246 { 2063 | color: #949494; 2064 | } 2065 | 2066 | .terminal .xterm-bg-color-246 { 2067 | background-color: #949494; 2068 | } 2069 | 2070 | .terminal .xterm-color-247 { 2071 | color: #9e9e9e; 2072 | } 2073 | 2074 | .terminal .xterm-bg-color-247 { 2075 | background-color: #9e9e9e; 2076 | } 2077 | 2078 | .terminal .xterm-color-248 { 2079 | color: #a8a8a8; 2080 | } 2081 | 2082 | .terminal .xterm-bg-color-248 { 2083 | background-color: #a8a8a8; 2084 | } 2085 | 2086 | .terminal .xterm-color-249 { 2087 | color: #b2b2b2; 2088 | } 2089 | 2090 | .terminal .xterm-bg-color-249 { 2091 | background-color: #b2b2b2; 2092 | } 2093 | 2094 | .terminal .xterm-color-250 { 2095 | color: #bcbcbc; 2096 | } 2097 | 2098 | .terminal .xterm-bg-color-250 { 2099 | background-color: #bcbcbc; 2100 | } 2101 | 2102 | .terminal .xterm-color-251 { 2103 | color: #c6c6c6; 2104 | } 2105 | 2106 | .terminal .xterm-bg-color-251 { 2107 | background-color: #c6c6c6; 2108 | } 2109 | 2110 | .terminal .xterm-color-252 { 2111 | color: #d0d0d0; 2112 | } 2113 | 2114 | .terminal .xterm-bg-color-252 { 2115 | background-color: #d0d0d0; 2116 | } 2117 | 2118 | .terminal .xterm-color-253 { 2119 | color: #dadada; 2120 | } 2121 | 2122 | .terminal .xterm-bg-color-253 { 2123 | background-color: #dadada; 2124 | } 2125 | 2126 | .terminal .xterm-color-254 { 2127 | color: #e4e4e4; 2128 | } 2129 | 2130 | .terminal .xterm-bg-color-254 { 2131 | background-color: #e4e4e4; 2132 | } 2133 | 2134 | .terminal .xterm-color-255 { 2135 | color: #eeeeee; 2136 | } 2137 | 2138 | .terminal .xterm-bg-color-255 { 2139 | background-color: #eeeeee; 2140 | } 2141 | 2142 | /** 2143 | * All terminal rows should have explicitly declared height, 2144 | * in order to allow child elements to adjust. 2145 | */ 2146 | .terminal .xterm-rows > div { 2147 | line-height: normal; 2148 | } 2149 | -------------------------------------------------------------------------------- /static/assets/xterm.js/test/addons/linkify-test.js: -------------------------------------------------------------------------------- 1 | var assert = require('chai').assert; 2 | var Terminal = require('../../src/xterm'); 3 | var linkify = require('../../addons/linkify/linkify'); 4 | 5 | describe('linkify addon', function () { 6 | var xterm; 7 | 8 | describe('API', function () { 9 | it('should define Terminal.prototype.linkify', function () { 10 | assert.isDefined(Terminal.prototype.linkify); 11 | }); 12 | it('should define Terminal.prototype.linkifyTerminalLine', function () { 13 | assert.isDefined(Terminal.prototype.linkifyTerminalLine); 14 | }); 15 | }); 16 | 17 | describe('findUrlMatchOnLine', function () { 18 | describe('strict regex', function () { 19 | it('should match when the entire text is a match', function () { 20 | assert.equal(linkify.findLinkMatch('http://github.com', false), 'http://github.com'); 21 | assert.equal(linkify.findLinkMatch('http://127.0.0.1', false), 'http://127.0.0.1'); 22 | }); 23 | it('should match simple domains', function () { 24 | assert.equal(linkify.findLinkMatch('foo http://github.com bar', false), 'http://github.com'); 25 | assert.equal(linkify.findLinkMatch('foo http://www.github.com bar', false), 'http://www.github.com'); 26 | assert.equal(linkify.findLinkMatch('foo https://github.com bar', false), 'https://github.com'); 27 | assert.equal(linkify.findLinkMatch('foo https://www.github.com bar', false), 'https://www.github.com'); 28 | }); 29 | it('should match web addresses with alpha paths', function () { 30 | assert.equal(linkify.findLinkMatch('foo http://github.com/a/b/c bar', false), 'http://github.com/a/b/c'); 31 | assert.equal(linkify.findLinkMatch('foo http://www.github.com/a/b/c bar', false), 'http://www.github.com/a/b/c'); 32 | }); 33 | it('should not include whitespace surrounding a match', function () { 34 | assert.equal(linkify.findLinkMatch(' http://github.com', false), 'http://github.com'); 35 | assert.equal(linkify.findLinkMatch('http://github.com ', false), 'http://github.com'); 36 | assert.equal(linkify.findLinkMatch(' http://github.com ', false), 'http://github.com'); 37 | }); 38 | it('should match IP addresses', function () { 39 | assert.equal(linkify.findLinkMatch('foo http://127.0.0.1 bar', false), 'http://127.0.0.1'); 40 | assert.equal(linkify.findLinkMatch('foo https://127.0.0.1 bar', false), 'https://127.0.0.1'); 41 | }); 42 | it('should match ports on both domains and IP addresses', function () { 43 | assert.equal(linkify.findLinkMatch('foo http://127.0.0.1:8080 bar', false), 'http://127.0.0.1:8080'); 44 | assert.equal(linkify.findLinkMatch('foo http://www.github.com:8080 bar', false), 'http://www.github.com:8080'); 45 | }); 46 | }); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /static/assets/xterm.js/test/addons/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('chai').assert; 2 | var Terminal = require('../../src/xterm'); 3 | 4 | describe('xterm.js addons', function() { 5 | it('should load addons with Terminal.loadAddon', function () { 6 | Terminal.loadAddon('attach'); 7 | // Test that function was loaded successfully 8 | assert.equal(typeof Terminal.prototype.attach, 'function'); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /static/assets/xterm.js/test/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('chai').assert; 2 | var Terminal = require('../src/xterm'); 3 | 4 | describe('xterm.js', function() { 5 | var xterm; 6 | 7 | beforeEach(function () { 8 | xterm = new Terminal(); 9 | }); 10 | 11 | describe('evaluateKeyEscapeSequence', function() { 12 | it('should return the correct escape sequence for unmodified keys', function() { 13 | // Backspace 14 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 8 }).key, '\x7f'); // ^? 15 | // Tab 16 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 9 }).key, '\t'); 17 | // Return/enter 18 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 13 }).key, '\r'); // CR 19 | // Escape 20 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 27 }).key, '\x1b'); 21 | // Page up, page down 22 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 33 }).key, '\x1b[5~'); // CSI 5 ~ 23 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 34 }).key, '\x1b[6~'); // CSI 6 ~ 24 | // End, Home 25 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 35 }).key, '\x1bOF'); // SS3 F 26 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 36 }).key, '\x1bOH'); // SS3 H 27 | // Left, up, right, down arrows 28 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 37 }).key, '\x1b[D'); // CSI D 29 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 38 }).key, '\x1b[A'); // CSI A 30 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 39 }).key, '\x1b[C'); // CSI C 31 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 40 }).key, '\x1b[B'); // CSI B 32 | // Insert 33 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 45 }).key, '\x1b[2~'); // CSI 2 ~ 34 | // Delete 35 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 46 }).key, '\x1b[3~'); // CSI 3 ~ 36 | // F1-F12 37 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 112 }).key, '\x1bOP'); // SS3 P 38 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 113 }).key, '\x1bOQ'); // SS3 Q 39 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 114 }).key, '\x1bOR'); // SS3 R 40 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 115 }).key, '\x1bOS'); // SS3 S 41 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 116 }).key, '\x1b[15~'); // CSI 1 5 ~ 42 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 117 }).key, '\x1b[17~'); // CSI 1 7 ~ 43 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 118 }).key, '\x1b[18~'); // CSI 1 8 ~ 44 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 119 }).key, '\x1b[19~'); // CSI 1 9 ~ 45 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 120 }).key, '\x1b[20~'); // CSI 2 0 ~ 46 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 121 }).key, '\x1b[21~'); // CSI 2 1 ~ 47 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 122 }).key, '\x1b[23~'); // CSI 2 3 ~ 48 | assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 123 }).key, '\x1b[24~'); // CSI 2 4 ~ 49 | }); 50 | it('should return \\x1b[5D for ctrl+left', function() { 51 | assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 37 }).key, '\x1b[5D'); // CSI 5 D 52 | }); 53 | it('should return \\x1b[5C for ctrl+right', function() { 54 | assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 39 }).key, '\x1b[5C'); // CSI 5 C 55 | }); 56 | }); 57 | 58 | describe('evaluateCopiedTextProcessing', function () { 59 | it('should strip trailing whitespaces and replace nbsps with spaces', function () { 60 | var nonBreakingSpace = String.fromCharCode(160), 61 | copiedText = 'echo' + nonBreakingSpace + 'hello' + nonBreakingSpace, 62 | processedText = Terminal.prepareCopiedTextForClipboard(copiedText); 63 | 64 | // No trailing spaces 65 | assert.equal(processedText.match(/\s+$/), null); 66 | 67 | // No non-breaking space 68 | assert.equal(processedText.indexOf(nonBreakingSpace), -1); 69 | }); 70 | }); 71 | 72 | describe('Third level shift', function() { 73 | var ev = { 74 | preventDefault: function() {}, 75 | stopPropagation: function() {} 76 | }; 77 | 78 | beforeEach(function() { 79 | xterm.handler = function() {}; 80 | xterm.showCursor = function() {}; 81 | xterm.clearSelection = function() {}; 82 | }); 83 | 84 | describe('On Mac OS', function() { 85 | beforeEach(function() { 86 | xterm.isMac = true; 87 | }); 88 | 89 | it('should not interfere with the alt key on keyDown', function() { 90 | assert.equal( 91 | xterm.keyDown(Object.assign({}, ev, { altKey: true, keyCode: 81 })), 92 | true 93 | ); 94 | assert.equal( 95 | xterm.keyDown(Object.assign({}, ev, { altKey: true, keyCode: 192 })), 96 | true 97 | ); 98 | }); 99 | 100 | it('should interefere with the alt + arrow keys', function() { 101 | assert.equal( 102 | xterm.keyDown(Object.assign({}, ev, { altKey: true, keyCode: 37 })), 103 | false 104 | ); 105 | assert.equal( 106 | xterm.keyDown(Object.assign({}, ev, { altKey: true, keyCode: 39 })), 107 | false 108 | ); 109 | }); 110 | 111 | it('should emit key with alt + key on keyPress', function(done) { 112 | var keys = ['@', '@', '\\', '\\', '|', '|']; 113 | 114 | xterm.on('keypress', function(key) { 115 | if (key) { 116 | var index = keys.indexOf(key); 117 | assert(index !== -1, "Emitted wrong key: " + key); 118 | keys.splice(index, 1); 119 | } 120 | if (keys.length === 0) done(); 121 | }); 122 | 123 | xterm.keyPress(Object.assign({}, ev, { altKey: true, keyCode: 64 })); // @ 124 | // Firefox 125 | xterm.keyPress(Object.assign({}, ev, { altKey: true, charCode: 64, keyCode: 0 })); 126 | xterm.keyPress(Object.assign({}, ev, { altKey: true, keyCode: 92 })); // \ 127 | xterm.keyPress(Object.assign({}, ev, { altKey: true, charCode: 92, keyCode: 0 })); 128 | xterm.keyPress(Object.assign({}, ev, { altKey: true, keyCode: 124 })); // | 129 | xterm.keyPress(Object.assign({}, ev, { altKey: true, charCode: 124, keyCode: 0 })); 130 | }); 131 | }); 132 | 133 | describe('On MS Windows', function() { 134 | beforeEach(function() { 135 | xterm.isMSWindows = true; 136 | }); 137 | 138 | it('should not interfere with the alt + ctrl key on keyDown', function() { 139 | assert.equal( 140 | xterm.keyDown(Object.assign({}, ev, { altKey: true, ctrlKey: true, keyCode: 81 })), 141 | true 142 | ); 143 | assert.equal( 144 | xterm.keyDown(Object.assign({}, ev, { altKey: true, ctrlKey: true, keyCode: 192 })), 145 | true 146 | ); 147 | }); 148 | 149 | it('should interefere with the alt + ctrl + arrow keys', function() { 150 | assert.equal( 151 | xterm.keyDown(Object.assign({}, ev, { altKey: true, ctrlKey: true, keyCode: 37 })), 152 | false 153 | ); 154 | assert.equal( 155 | xterm.keyDown(Object.assign({}, ev, { altKey: true, ctrlKey: true, keyCode: 39 })), 156 | false 157 | ); 158 | }); 159 | 160 | it('should emit key with alt + ctrl + key on keyPress', function(done) { 161 | var keys = ['@', '@', '\\', '\\', '|', '|']; 162 | 163 | xterm.on('keypress', function(key) { 164 | if (key) { 165 | var index = keys.indexOf(key); 166 | assert(index !== -1, "Emitted wrong key: " + key); 167 | keys.splice(index, 1); 168 | } 169 | if (keys.length === 0) done(); 170 | }); 171 | 172 | xterm.keyPress( 173 | Object.assign({}, ev, { altKey: true, ctrlKey: true, keyCode: 64 }) 174 | ); // @ 175 | xterm.keyPress( 176 | Object.assign({}, ev, { altKey: true, ctrlKey: true, charCode: 64, keyCode: 0 }) 177 | ); 178 | xterm.keyPress( 179 | Object.assign({}, ev, { altKey: true, ctrlKey: true, keyCode: 92 }) 180 | ); // \ 181 | xterm.keyPress( 182 | Object.assign({}, ev, { altKey: true, ctrlKey: true, charCode: 92, keyCode: 0 }) 183 | ); 184 | xterm.keyPress( 185 | Object.assign({}, ev, { altKey: true, ctrlKey: true, keyCode: 124 }) 186 | ); // | 187 | xterm.keyPress( 188 | Object.assign({}, ev, { altKey: true, ctrlKey: true, charCode: 124, keyCode: 0 }) 189 | ); 190 | }); 191 | }); 192 | }); 193 | }); 194 | -------------------------------------------------------------------------------- /static/assets/xterm.js/xtermjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcops/k8-web-terminal/3a1cc6da47a11915280f315435f52560f336e150/static/assets/xterm.js/xtermjs.png -------------------------------------------------------------------------------- /static/config.js: -------------------------------------------------------------------------------- 1 | System.config({ 2 | baseURL: "/public", 3 | defaultJSExtensions: true, 4 | transpiler: "babel", 5 | babelOptions: { 6 | "optional": [ 7 | "runtime", 8 | "optimisation.modules.system" 9 | ] 10 | }, 11 | paths: { 12 | "github:*": "jspm_packages/github/*", 13 | "npm:*": "jspm_packages/npm/*", 14 | "app/*": "frontend/*.js", 15 | "dashboard/*": "frontend/dashboard/*.js", 16 | "utils/*": "frontend/utils/*.js" 17 | }, 18 | separateCSS: true, 19 | 20 | map: { 21 | "angular": "github:angular/bower-angular@1.5.7", 22 | "angular-animate": "github:angular/bower-angular-animate@1.5.5", 23 | "angular-aria": "github:angular/bower-angular-aria@1.5.5", 24 | "angular-material": "github:angular/bower-material@master", 25 | "angular-messages": "github:angular/bower-angular-messages@1.5.5", 26 | "babel": "npm:babel-core@5.8.38", 27 | "babel-runtime": "npm:babel-runtime@5.8.38", 28 | "core-js": "npm:core-js@1.2.6", 29 | "css": "github:systemjs/plugin-css@0.1.23", 30 | "json": "github:systemjs/plugin-json@0.1.2", 31 | "text": "github:systemjs/plugin-text@0.0.4", 32 | "traceur": "github:jmcriffey/bower-traceur@0.0.92", 33 | "traceur-runtime": "github:jmcriffey/bower-traceur-runtime@0.0.92", 34 | "github:angular/bower-angular-animate@1.5.5": { 35 | "angular": "github:angular/bower-angular@1.5.7" 36 | }, 37 | "github:angular/bower-angular-aria@1.5.5": { 38 | "angular": "github:angular/bower-angular@1.5.7" 39 | }, 40 | "github:angular/bower-angular-messages@1.5.5": { 41 | "angular": "github:angular/bower-angular@1.5.7" 42 | }, 43 | "github:angular/bower-material@master": { 44 | "angular": "github:angular/bower-angular@1.5.7", 45 | "angular-animate": "github:angular/bower-angular-animate@1.5.5", 46 | "angular-aria": "github:angular/bower-angular-aria@1.5.5", 47 | "angular-messages": "github:angular/bower-angular-messages@1.5.5", 48 | "css": "github:systemjs/plugin-css@0.1.23" 49 | }, 50 | "github:jspm/nodelibs-assert@0.1.0": { 51 | "assert": "npm:assert@1.4.1" 52 | }, 53 | "github:jspm/nodelibs-buffer@0.1.0": { 54 | "buffer": "npm:buffer@3.6.0" 55 | }, 56 | "github:jspm/nodelibs-path@0.1.0": { 57 | "path-browserify": "npm:path-browserify@0.0.0" 58 | }, 59 | "github:jspm/nodelibs-process@0.1.2": { 60 | "process": "npm:process@0.11.5" 61 | }, 62 | "github:jspm/nodelibs-util@0.1.0": { 63 | "util": "npm:util@0.10.3" 64 | }, 65 | "github:jspm/nodelibs-vm@0.1.0": { 66 | "vm-browserify": "npm:vm-browserify@0.0.4" 67 | }, 68 | "npm:assert@1.4.1": { 69 | "assert": "github:jspm/nodelibs-assert@0.1.0", 70 | "buffer": "github:jspm/nodelibs-buffer@0.1.0", 71 | "process": "github:jspm/nodelibs-process@0.1.2", 72 | "util": "npm:util@0.10.3" 73 | }, 74 | "npm:babel-runtime@5.8.38": { 75 | "process": "github:jspm/nodelibs-process@0.1.2" 76 | }, 77 | "npm:buffer@3.6.0": { 78 | "base64-js": "npm:base64-js@0.0.8", 79 | "child_process": "github:jspm/nodelibs-child_process@0.1.0", 80 | "fs": "github:jspm/nodelibs-fs@0.1.2", 81 | "ieee754": "npm:ieee754@1.1.6", 82 | "isarray": "npm:isarray@1.0.0", 83 | "process": "github:jspm/nodelibs-process@0.1.2" 84 | }, 85 | "npm:core-js@1.2.6": { 86 | "fs": "github:jspm/nodelibs-fs@0.1.2", 87 | "path": "github:jspm/nodelibs-path@0.1.0", 88 | "process": "github:jspm/nodelibs-process@0.1.2", 89 | "systemjs-json": "github:systemjs/plugin-json@0.1.2" 90 | }, 91 | "npm:inherits@2.0.1": { 92 | "util": "github:jspm/nodelibs-util@0.1.0" 93 | }, 94 | "npm:path-browserify@0.0.0": { 95 | "process": "github:jspm/nodelibs-process@0.1.2" 96 | }, 97 | "npm:process@0.11.5": { 98 | "assert": "github:jspm/nodelibs-assert@0.1.0", 99 | "fs": "github:jspm/nodelibs-fs@0.1.2", 100 | "vm": "github:jspm/nodelibs-vm@0.1.0" 101 | }, 102 | "npm:util@0.10.3": { 103 | "inherits": "npm:inherits@2.0.1", 104 | "process": "github:jspm/nodelibs-process@0.1.2" 105 | }, 106 | "npm:vm-browserify@0.0.4": { 107 | "indexof": "npm:indexof@0.0.1" 108 | } 109 | } 110 | }); 111 | -------------------------------------------------------------------------------- /static/frontend/boot.js: -------------------------------------------------------------------------------- 1 | // Load the Angular Material CSS associated with ngMaterial 2 | // then load the main.css to provide overrides, etc. 3 | 4 | import 'angular-material/angular-material.css!' 5 | import 'assets/app.css!' 6 | 7 | // Load Angular libraries 8 | 9 | import angular from 'angular' 10 | import material from 'angular-material' 11 | 12 | // Load custom application modules 13 | 14 | import main from 'app/main' 15 | 16 | // Load loggers for injection and pre-angular debugging 17 | 18 | import { LogDecorator, ExternalLogger } from 'utils/LogDecorator'; 19 | 20 | 21 | /** 22 | * Manually bootstrap the application when AngularJS and 23 | * the application classes have been loaded. 24 | */ 25 | angular 26 | .element( document ) 27 | .ready( function() { 28 | 29 | let appName = 'kubernetes-web-terminal'; 30 | let $log = new ExternalLogger(); 31 | 32 | $log = $log.getInstance( "BOOTSTRAP" ); 33 | $log.debug( "Initializing '{0}'", [ appName ] ); 34 | 35 | let body = document.getElementsByTagName("body")[0]; 36 | let app = angular 37 | .module( appName, [ material, main ] ) 38 | .config( ['$provide', LogDecorator] ); 39 | 40 | angular.bootstrap( body, [ app.name ], { strictDi: false }) 41 | 42 | }); 43 | -------------------------------------------------------------------------------- /static/frontend/dashboard/Dashboard.js: -------------------------------------------------------------------------------- 1 | const URL_ICON_DOCKER = 'assets/svg/docker.svg'; 2 | const URL_ICON_MENU = 'assets/svg/menu.svg'; 3 | const URL_ICON_TERMINAL = 'assets/svg/terminal.svg'; 4 | 5 | // Load the custom app ES6 modules 6 | 7 | import DashboardController from 'dashboard/DashboardController' 8 | import DashboardService from 'dashboard/DashboardService' 9 | 10 | import { 11 | ExternalLogger 12 | } 13 | from 'utils/LogDecorator'; 14 | 15 | let $log = new ExternalLogger(); 16 | $log = $log.getInstance("BOOTSTRAP"); 17 | $log.debug("Configuring 'Dashboard' module"); 18 | 19 | // Define the Angular 'Dashboard' module 20 | 21 | let moduleName = angular 22 | .module("dashboard", []) 23 | .service("dashboardService", DashboardService) 24 | .controller("DashboardController", DashboardController) 25 | .config(($mdIconProvider, $mdThemingProvider) => { 26 | $log.debug( "Configuring $mdIconProvider" ); 27 | 28 | // Register `dashboard` iconset & icons for $mdIcon service lookups 29 | $mdIconProvider 30 | .defaultIconSet( URL_ICON_DOCKER, 75 ) 31 | .icon('menu' , URL_ICON_MENU, 24) 32 | .icon('terminal', URL_ICON_TERMINAL, 24) 33 | .icon('docker', URL_ICON_DOCKER, 75); 34 | 35 | 36 | $mdThemingProvider.theme('dark-grey').backgroundPalette('grey').dark(); 37 | $mdThemingProvider.theme('dark-orange').backgroundPalette('orange').dark(); 38 | $mdThemingProvider.theme('dark-purple').backgroundPalette('deep-purple').dark(); 39 | $mdThemingProvider.theme('dark-blue').backgroundPalette('blue').dark(); 40 | 41 | }) 42 | .name; 43 | 44 | export default moduleName; 45 | -------------------------------------------------------------------------------- /static/frontend/dashboard/DashboardController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Main App Controller for the Angular Material Starter App 3 | * @param $scope 4 | * @param $mdSidenav 5 | * @param avatarsService 6 | * @constructor 7 | */ 8 | function DashboardController( dashboardService, $mdSidenav, $mdBottomSheet, $log ) { 9 | 10 | $log = $log.getInstance( "SessionController" ); 11 | $log.debug( "instanceOf() "); 12 | 13 | var self = this; 14 | 15 | self.selected = null; 16 | self.containers = []; 17 | self.nodes = [ ]; 18 | self.selectNode = selectNode; 19 | self.toggleList = toggleNodesList; 20 | // self.terminal = terminal; 21 | 22 | // Load all nodes 23 | 24 | dashboardService 25 | .loadNodes() 26 | .then(function(payload) { 27 | self.nodes = [].concat(payload.data); 28 | 29 | self.selected = payload.data[0]; 30 | 31 | dashboardService 32 | .loadContainers(self.selected.status.addresses[0].address) 33 | .then(function(payload) { 34 | self.containers = payload.data 35 | }); 36 | }); 37 | 38 | // ********************************* 39 | // Internal methods 40 | // ********************************* 41 | 42 | /** 43 | * Hide or Show the 'left' sideNav area 44 | */ 45 | function toggleNodesList() { 46 | $log.debug( "toggleNodesList() "); 47 | $mdSidenav('left').toggle(); 48 | } 49 | 50 | /** 51 | * Show Containers 52 | * @param menuId 53 | */ 54 | function selectNode ( node ) { 55 | $log.debug( "selectNode( {metadata.name} ) ", node); 56 | 57 | self.selected = angular.isNumber(node) ? $scope.nodes[node] : node; 58 | 59 | dashboardService 60 | .loadContainers(node.status.addresses[0].address) 61 | .then(function(payload) { 62 | self.containers = payload.data 63 | }); 64 | 65 | self.toggleList(); 66 | } 67 | 68 | // function terminal(container) { 69 | // $log.debug("terminal") 70 | // 71 | // var node = self.selected; 72 | // $log.debug("node: {metadata.name} ip: {address} ", node.status.addresses[0]); 73 | // $log.debug("containerId {Id}", container); 74 | // } 75 | 76 | } 77 | 78 | export default [ 79 | 'dashboardService', '$mdSidenav', '$mdBottomSheet', '$log', 80 | DashboardController 81 | ]; 82 | 83 | -------------------------------------------------------------------------------- /static/frontend/dashboard/DashboardService.js: -------------------------------------------------------------------------------- 1 | function DashboardService($log, $http) { 2 | var users = [{ 3 | Names: ['dopware'], 4 | Id: 'beyond', 5 | }]; 6 | 7 | $log = $log.getInstance("DashboardService"); 8 | $log.debug("instanceOf() "); 9 | 10 | // Promise-based API 11 | return { 12 | loadNodes: function() { 13 | $log.debug("loadNodes()"); 14 | return $http.get('/api/nodes'); 15 | }, 16 | loadContainers: function(node) { 17 | $log.debug("loadContainers()"); 18 | return $http.get('/api/nodes/containers?node=' + node); 19 | } 20 | }; 21 | } 22 | 23 | 24 | export default ['$log', '$http', DashboardService]; 25 | -------------------------------------------------------------------------------- /static/frontend/dashboard/view/dashboard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | K8 web terminal 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | {{it.metadata.name}} 20 | {{it.status.addresses[0].address}} 21 | {{it.status.nodeInfo.osImage}} 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | {{it.Id | limitTo: 11}} 36 | name: {{it.Name}} 37 | appname: {{it.AppName}} 38 | image: {{it.Image}} 39 | command: {{it.Command}} 40 | created: {{it.Created*1000 | date:'medium'}} 41 | status: {{it.Status}} 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /static/frontend/main.js: -------------------------------------------------------------------------------- 1 | import { ExternalLogger } from 'utils/LogDecorator'; 2 | import dashboard from 'dashboard/Dashboard'; 3 | 4 | let $log = new ExternalLogger(); 5 | $log = $log.getInstance( "BOOTSTRAP" ); 6 | $log.debug( "Configuring 'main' module" ); 7 | 8 | export default angular.module('main', [dashboard]).name; 9 | -------------------------------------------------------------------------------- /static/frontend/utils/LogDecorator.js: -------------------------------------------------------------------------------- 1 | import './supplant'; 2 | 3 | export { LogDecorator, ExternalLogger } 4 | 5 | /** 6 | * Decorate the $log to use inject the LogEnhancer features. 7 | * 8 | * @param {object} $provide The log console. 9 | * @returns {object} promise. 10 | * @private 11 | */ 12 | function LogDecorator($provide) { 13 | // Register our $log decorator with AngularJS $provider 14 | 15 | $provide.decorator('$log', ["$delegate", function ($delegate) { 16 | 17 | return enhanceLog($delegate); 18 | 19 | }]); 20 | } 21 | 22 | 23 | function ExternalLogger() 24 | { 25 | /** 26 | * Determines if the requested console logging method is available, since it is not with IE. 27 | * 28 | * @param {Function} method The request console logging method. 29 | * @returns {object} Indicates if the console logging method is available. 30 | * @private 31 | */ 32 | var prepareLogToConsole = function (method) 33 | { 34 | var console = window.console, 35 | isFunction = function (fn) 36 | { 37 | return(typeof (fn) == typeof (Function)); 38 | }, 39 | isAvailableConsoleFor = function (method) 40 | { 41 | var isPhantomJS = new BrowserDetect().browser != "PhantomJS"; 42 | 43 | // NOTE: Tried using this for less logging in the console/terminal, but then logging in IDE is 44 | // wiped out as well return console && console[method] && isFunction(console[method]) && isPhantomJS; 45 | 46 | return console && console[method] && isFunction(console[method]); 47 | }, 48 | logFn = function (message) 49 | { 50 | if(isAvailableConsoleFor(method)) 51 | { 52 | try 53 | { 54 | console[method](message); 55 | 56 | } 57 | catch(e) 58 | {} 59 | } 60 | }; 61 | 62 | return logFn; 63 | }, 64 | $log = { 65 | log : prepareLogToConsole("log"), 66 | info : prepareLogToConsole("info"), 67 | warn : prepareLogToConsole("warn"), 68 | debug: prepareLogToConsole("debug"), 69 | error: prepareLogToConsole("error") 70 | }; 71 | 72 | // Publish instance of $log simulator; with enhanced functionality 73 | return new enhanceLog($log); 74 | } 75 | 76 | 77 | function enhanceLog($log) { 78 | var separator = "::", 79 | 80 | /** 81 | * Cached DateTime formatter 82 | */ 83 | dateFormatter = new DateTime(), 84 | detector = new BrowserDetect(), 85 | 86 | 87 | /** 88 | * Capture the original $log functions; for use in enhancedLogFn() 89 | */ 90 | _$log = (function ($log) { 91 | return { 92 | log: $log.log, 93 | info: $log.info, 94 | warn: $log.warn, 95 | debug: $log.debug, 96 | error: $log.error 97 | }; 98 | })($log), 99 | 100 | /** 101 | * Chrome Dev tools supports color logging 102 | * @see https://developers.google.com/chrome-developer-tools/docs/console#styling_console_output_with_css 103 | */ 104 | colorify = function (message, colorCSS) { 105 | var isChrome = detector.browser == "Chrome", 106 | canColorize = isChrome && (colorCSS !== undefined); 107 | 108 | return canColorize ? ["%c" + message, colorCSS] : [message]; 109 | }, 110 | 111 | /** 112 | * Partial application to pre-capture a logger function 113 | */ 114 | prepareLogFn = function (logFn, className, colorCSS) { 115 | /** 116 | * Invoke the specified `logFn` with the supplant functionality... 117 | */ 118 | var supplant = String.supplant; 119 | var enhancedLogFn = function () { 120 | try { 121 | var args = Array.prototype.slice.call(arguments); 122 | 123 | // prepend a timestamp and optional classname to the original output message 124 | args[0] = supplant("{0} - {1}{2}", [dateFormatter.now(), className, args[0]]); 125 | args = colorify(supplant.apply(null, args), colorCSS); 126 | 127 | logFn.apply(null, args); 128 | } 129 | catch (error) { 130 | $log.error("LogEnhancer ERROR: " + error); 131 | } 132 | 133 | }; 134 | 135 | // Only needed to support angular-mocks expectations 136 | enhancedLogFn.logs = []; 137 | 138 | return enhancedLogFn; 139 | }, 140 | 141 | /** 142 | * Support to generate class-specific logger instance with classname only 143 | */ 144 | getInstance = function (className, colorCSS, customSeparator) { 145 | className = (className !== undefined) ? className + (customSeparator || separator) : ""; 146 | 147 | var instance = { 148 | log: prepareLogFn(_$log.log, className, colorCSS), 149 | info: prepareLogFn(_$log.info, className, colorCSS), 150 | warn: prepareLogFn(_$log.warn, className, colorCSS), 151 | debug: prepareLogFn(_$log.debug, className, colorCSS), 152 | error: prepareLogFn(_$log.error, className) // NO styling of ERROR messages 153 | }; 154 | 155 | // Attach instance specific tryCatch() functionality... 156 | instance.tryCatch = makeTryCatch(instance.error, instance); 157 | 158 | return instance; 159 | }; 160 | 161 | // Add special method to AngularJS $log 162 | $log.getInstance = getInstance; 163 | 164 | return $log; 165 | } 166 | 167 | 168 | /** 169 | * Implement a tryCatch() method that logs exceptions for method invocations AND 170 | * promise rejection activity. 171 | * 172 | * @param notifyFn Function used to log.debug exception information 173 | * @param scope Object Receiver for the notifyFn invocation 174 | * 175 | * @return Function used to guard and invoke the targeted actionFn 176 | */ 177 | function makeTryCatch(notifyFn, scope) { 178 | /** 179 | * Report error (with stack trace if possible) to the logger function 180 | */ 181 | var reportError = function (reason) { 182 | if (notifyFn != null) { 183 | var error = (reason && reason.stack) ? reason : null, 184 | message = reason != null ? String(reason) : ""; 185 | 186 | if (error != null) { 187 | message = error.message + "\n" + error.stack; 188 | } 189 | 190 | notifyFn.apply(scope, [message]); 191 | } 192 | 193 | return reason; 194 | }, 195 | /** 196 | * Publish the tryCatch() guard 'n report function 197 | */ 198 | tryCatch = function (actionFn, scope, args) { 199 | try { 200 | // Invoke the targeted `actionFn` 201 | var result = angular.isFunction(actionFn) ? actionFn.apply(scope, args || []) : String(actionFn), 202 | promise = (angular.isObject(result) && result.then) ? result : null; 203 | 204 | if (promise != null) { 205 | // Catch and report any promise rejection reason... 206 | promise.then(null, reportError); 207 | } 208 | 209 | actionFn = null; 210 | return result; 211 | 212 | } 213 | catch (e) { 214 | actionFn = null; 215 | throw reportError(e); 216 | } 217 | 218 | }; 219 | 220 | return tryCatch; 221 | } 222 | 223 | 224 | /** 225 | * @author Thomas Burleson 226 | * @author StackOverflow - Harto, http://stackoverflow.com/questions/2315408/how-do-i-format-a-timestamp-in-javascript-to-display-it-in-graphs-utc-is-fine 227 | * @description 228 | * 229 | * DateTime utility class that spits out UTC timestamp strings usually used in a reporting, print-capable process. 230 | */ 231 | function DateTime() { 232 | /** 233 | * Creates a date timestamp string. 234 | */ 235 | var buildTimeString = function (date, format) { 236 | format = format || "%h:%m:%s:%z"; 237 | 238 | function pad(value, isMilliSeconds) { 239 | if (typeof (isMilliSeconds) === "undefined") { 240 | isMilliSeconds = false; 241 | } 242 | if (isMilliSeconds) { 243 | if (value < 10) { 244 | value = "00" + value; 245 | } 246 | else if (value < 100) { 247 | value = "0" + value; 248 | } 249 | } 250 | return (value.toString().length < 2) ? "0" + value : value; 251 | } 252 | 253 | return format.replace(/%([a-zA-Z])/g, function (_, fmtCode) { 254 | switch (fmtCode) { 255 | case "Y": 256 | return date.getFullYear(); 257 | case "M": 258 | return pad(date.getMonth() + 1); 259 | case "d": 260 | return pad(date.getDate()); 261 | case "h": 262 | return pad(date.getHours()); 263 | case "m": 264 | return pad(date.getMinutes()); 265 | case "s": 266 | return pad(date.getSeconds()); 267 | case "z": 268 | return pad(date.getMilliseconds(), true); 269 | default: 270 | throw new Error("Unsupported format code: " + fmtCode); 271 | } 272 | }); 273 | }; 274 | 275 | // Publish API for DateTime utils 276 | return { 277 | now : function () { 278 | return buildTimeString(new Date()); 279 | } 280 | }; 281 | 282 | } 283 | 284 | function BrowserDetect() { 285 | 286 | // NOTE: It's important to list PhantomJS first since it has the same browser information as Safari 287 | this.dataBrowser = [ 288 | { 289 | string: "PhantomJS", 290 | subString: "PhantomJS", 291 | identity: "PhantomJS", 292 | versionSearch: "PhantomJS" 293 | }, 294 | { 295 | string: navigator.userAgent, 296 | subString: "Chrome", 297 | identity: "Chrome" 298 | }, 299 | { 300 | string: navigator.userAgent, 301 | subString: "OmniWeb", 302 | versionSearch: "OmniWeb/", 303 | identity: "OmniWeb" 304 | }, 305 | { 306 | string: navigator.vendor, 307 | subString: "Apple", 308 | identity: "Safari", 309 | versionSearch: "Version" 310 | }, 311 | { 312 | prop: window.opera, 313 | identity: "Opera", 314 | versionSearch: "Version" 315 | }, 316 | { 317 | string: navigator.vendor, 318 | subString: "iCab", 319 | identity: "iCab" 320 | }, 321 | { 322 | string: navigator.vendor, 323 | subString: "KDE", 324 | identity: "Konqueror" 325 | }, 326 | { 327 | string: navigator.userAgent, 328 | subString: "Firefox", 329 | identity: "Firefox" 330 | }, 331 | { 332 | string: navigator.vendor, 333 | subString: "Camino", 334 | identity: "Camino" 335 | }, 336 | { // for newer Netscapes (6+) 337 | string: navigator.userAgent, 338 | subString: "Netscape", 339 | identity: "Netscape" 340 | }, 341 | { 342 | string: navigator.userAgent, 343 | subString: "MSIE", 344 | identity: "Explorer", 345 | versionSearch: "MSIE" 346 | }, 347 | { 348 | string: navigator.userAgent, 349 | subString: "Gecko", 350 | identity: "Mozilla", 351 | versionSearch: "rv" 352 | }, 353 | { 354 | // for older Netscapes (4-) 355 | string: navigator.userAgent, 356 | subString: "Mozilla", 357 | identity: "Netscape", 358 | versionSearch: "Mozilla" 359 | }], 360 | this.dataOS = [ 361 | { 362 | string: navigator.platform, 363 | subString: "Win", 364 | identity: "Windows" 365 | }, 366 | { 367 | string: navigator.platform, 368 | subString: "Mac", 369 | identity: "Mac" 370 | }, 371 | { 372 | string: navigator.userAgent, 373 | subString: "iPhone", 374 | identity: "iPhone/iPod" 375 | }, 376 | { 377 | string: navigator.platform, 378 | subString: "Linux", 379 | identity: "Linux" 380 | }]; 381 | 382 | this.init(); 383 | } 384 | 385 | BrowserDetect.prototype = { 386 | 387 | /** 388 | * Sets the browser version and OS(Operating Systems) uses {@link mindspace.utils:BrowserDetect#searchString searchString} 389 | * and {@link mindspace.utils:BrowserDetect#searchVersion searchVersion} internally 390 | */ 391 | init: function () 392 | { 393 | this.browser = this.searchString(this.dataBrowser) || "An unknown browser"; 394 | this.version = this.searchVersion(navigator.userAgent) || this.searchVersion(navigator.appVersion) || 395 | "an unknown version"; 396 | this.OS = this.searchString(this.dataOS) || "an unknown OS"; 397 | 398 | return this; 399 | }, 400 | 401 | /** 402 | * Checks whether the browser is IE8. Root element(html) is already set with class='ie8 403 | * this function uses the same class reference and provides the status. 404 | */ 405 | isIE8: function () 406 | { 407 | return (document.documentElement.hasAttribute("class")) && (document.documentElement.getAttribute("class") === "ie8"); 408 | }, 409 | 410 | /** 411 | * User for determining the browser and OS based on the input provided by the data param. 412 | * Also sets the versionSearchString parameter which would be used by 413 | * {@link mindspace.utils:BrowserDetect#searchVersion searchVersion} 414 | */ 415 | searchString: function (data) 416 | { 417 | for(var i = 0; i < data.length; i++) 418 | { 419 | var dataString = data[i].string; 420 | var dataProp = data[i].prop; 421 | 422 | this.versionSearchString = data[i].versionSearch || data[i].identity; 423 | if(dataString) 424 | { 425 | if(dataString.indexOf(data[i].subString) != -1) 426 | { 427 | return data[i].identity; 428 | } 429 | } 430 | else if(dataProp) 431 | { 432 | return data[i].identity; 433 | } 434 | } 435 | }, 436 | 437 | /** 438 | * User for determining the browser version based on input string 439 | */ 440 | searchVersion: function (dataString) 441 | { 442 | var index = dataString.indexOf(this.versionSearchString); 443 | if(index == -1) 444 | { 445 | return; 446 | } 447 | return parseFloat(dataString.substring(index + this.versionSearchString.length + 1)); 448 | } 449 | 450 | }; 451 | -------------------------------------------------------------------------------- /static/frontend/utils/supplant.js: -------------------------------------------------------------------------------- 1 | export default supplant; 2 | 3 | 4 | /** 5 | * @author Thomas Burleson 6 | * @date November, 2013 7 | * @description 8 | * 9 | * String supplant global utility (similar to but more powerful than sprintf() ). 10 | * 11 | * Usages: 12 | * 13 | * var user = { 14 | * first : "Thomas", 15 | * last : "Burleson", 16 | * address : { 17 | * city : "West Des Moines", 18 | * state: "Iowa" 19 | * }, 20 | * contact : { 21 | * email : "ThomasBurleson@Gmail.com" 22 | * url : "http://www.gridlinked.info" 23 | * } 24 | * }, 25 | * message = "Hello Mr. {first} {last}. How's life in {address.city}, {address.state} ?"; 26 | * 27 | * return supplant( message, user ); 28 | * 29 | * 30 | * NOTE: this supplant() method is from Crockfords `Remedial Javascript` 31 | * 32 | */ 33 | function supplant( template, values, pattern ) { 34 | pattern = pattern || /\{([^\{\}]*)\}/g; 35 | 36 | return template.replace(pattern, function(a, b) { 37 | var p = b.split('.'), 38 | r = values; 39 | 40 | try { 41 | for (var s in p) { r = r[p[s]]; } 42 | } catch(e){ 43 | r = a; 44 | } 45 | 46 | return (typeof r === 'string' || typeof r === 'number') ? r : a; 47 | }); 48 | } 49 | 50 | 51 | // supplant() method from Crockfords `Remedial Javascript` 52 | Function.prototype.method = function (name, func) { 53 | this.prototype[name] = func; 54 | return this; 55 | }; 56 | 57 | String.method("supplant", function( values, pattern ) { 58 | var self = this; 59 | return supplant(self, values, pattern); 60 | }); 61 | 62 | String.supplant = supplant; 63 | 64 | -------------------------------------------------------------------------------- /static/js/reload.min.js: -------------------------------------------------------------------------------- 1 | function b(a){var c=new WebSocket(a);c.onclose=function(){setTimeout(function(){b(a)},2E3)};c.onmessage=function(){location.reload()}}try{if(window.WebSocket)try{b("ws://localhost:12450/reload")}catch(a){console.error(a)}else console.log("Your browser does not support WebSockets.")}catch(a){console.error("Exception during connecting to Reload:",a)}; 2 | -------------------------------------------------------------------------------- /tests/default_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | "runtime" 8 | "path/filepath" 9 | _ "k8/routers" 10 | 11 | "github.com/astaxie/beego" 12 | . "github.com/smartystreets/goconvey/convey" 13 | ) 14 | 15 | func init() { 16 | _, file, _, _ := runtime.Caller(0) 17 | apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".." + string(filepath.Separator)))) 18 | beego.TestBeegoInit(apppath) 19 | } 20 | 21 | 22 | // TestBeego is a sample to run an endpoint test 23 | func TestBeego(t *testing.T) { 24 | r, _ := http.NewRequest("GET", "/", nil) 25 | w := httptest.NewRecorder() 26 | beego.BeeApp.Handlers.ServeHTTP(w, r) 27 | 28 | beego.Trace("testing", "TestBeego", "Code[%d]\n%s", w.Code, w.Body.String()) 29 | 30 | Convey("Subject: Test Station Endpoint\n", t, func() { 31 | Convey("Status Code Should Be 200", func() { 32 | So(w.Code, ShouldEqual, 200) 33 | }) 34 | Convey("The Result Should Not Be Empty", func() { 35 | So(w.Body.Len(), ShouldBeGreaterThan, 0) 36 | }) 37 | }) 38 | } 39 | 40 | -------------------------------------------------------------------------------- /views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | K8 web terminal 9 | 10 | 11 | 12 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 31 | 32 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /views/terminal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | K8 web terminal 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | --------------------------------------------------------------------------------
57 | Attach the terminal to a WebSocket terminal stream with ease. Perfect for attaching to your 58 | Docker containers. 59 |
{{it.status.nodeInfo.osImage}}