parent
d379ffff9b
commit
9bb3c18df9
|
@ -0,0 +1,20 @@
|
||||||
|
kind: pipeline
|
||||||
|
name: default
|
||||||
|
steps:
|
||||||
|
- name: build
|
||||||
|
image: golang:1.17.2
|
||||||
|
commands:
|
||||||
|
- git tag $DRONE_TAG
|
||||||
|
- bash make.sh
|
||||||
|
- echo -n "latest,${DRONE_TAG#v}" > .tags
|
||||||
|
- name: gitea_release
|
||||||
|
image: plugins/gitea-release
|
||||||
|
settings:
|
||||||
|
api_key:
|
||||||
|
from_secret: gitea_api_key
|
||||||
|
base_url: https://kumoly.io
|
||||||
|
files: dist/*
|
||||||
|
checksum:
|
||||||
|
- sha256
|
||||||
|
trigger:
|
||||||
|
event: tag
|
|
@ -1,2 +1,3 @@
|
||||||
node_modules
|
node_modules
|
||||||
.parcel-cache
|
.parcel-cache
|
||||||
|
dist
|
29
README.md
29
README.md
|
@ -1 +1,28 @@
|
||||||
# goshell
|
# gterm
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```shell
|
||||||
|
Usage: gterm [options]
|
||||||
|
-addr string
|
||||||
|
address to bind (default ":8000")
|
||||||
|
-allow string
|
||||||
|
restrict ip
|
||||||
|
-dev
|
||||||
|
is development mode
|
||||||
|
-log-level int
|
||||||
|
log level (default 9)
|
||||||
|
-name string
|
||||||
|
the application title (default "gterm")
|
||||||
|
-shell string
|
||||||
|
the shell behind (default "bash")
|
||||||
|
-v show version
|
||||||
|
```
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo rm -f /usr/local/bin/gterm
|
||||||
|
sudo sh -c "curl -fsSL RELEASE_URL | tar -C /usr/local/bin/ -xz"
|
||||||
|
```
|
|
@ -2,27 +2,78 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"kumoly.io/lib/klog"
|
||||||
"kumoly.io/lib/ksrv"
|
"kumoly.io/lib/ksrv"
|
||||||
"kumoly.io/tools/gterm"
|
"kumoly.io/tools/gterm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var Version = "0.0.0"
|
||||||
|
var Build = "alpha"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
flagAllowIP string
|
||||||
flagAppName string = "gterm"
|
flagAppName string = "gterm"
|
||||||
flagAddr string
|
flagAddr string
|
||||||
flagShell string
|
flagShell string
|
||||||
|
flagLogLevel int
|
||||||
|
flagDev bool
|
||||||
|
flagVer bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
flag.StringVar(&flagAppName, "name", "gterm", "the application title")
|
||||||
flag.StringVar(&flagAddr, "addr", ":8000", "address to bind")
|
flag.StringVar(&flagAddr, "addr", ":8000", "address to bind")
|
||||||
flag.StringVar(&flagShell, "shell", "bash", "the shell behind")
|
flag.StringVar(&flagShell, "shell", "bash", "the shell behind")
|
||||||
|
flag.BoolVar(&flagDev, "dev", false, "is development mode")
|
||||||
|
flag.IntVar(&flagLogLevel, "log-level", 9, "log level")
|
||||||
|
flag.StringVar(&flagAllowIP, "allow", "", "restrict ip")
|
||||||
|
flag.BoolVar(&flagVer, "v", false, "show version")
|
||||||
|
|
||||||
|
flag.Usage = func() {
|
||||||
|
fmt.Fprintf(os.Stderr, "Usage: gterm [options]\n")
|
||||||
|
flag.PrintDefaults()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
if flagVer {
|
||||||
|
fmt.Printf("%v - %v\n", Version, Build)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.LEVEL = klog.Llevel(flagLogLevel)
|
||||||
|
klog.PROD = !flagDev
|
||||||
|
|
||||||
server := ksrv.New()
|
|
||||||
g := gterm.New()
|
g := gterm.New()
|
||||||
|
g.AppName = flagAppName
|
||||||
|
g.Cmd = flagShell
|
||||||
|
|
||||||
server.Handle(g).Listen(flagAddr).Serve()
|
server := &http.Server{
|
||||||
|
Addr: flagAddr,
|
||||||
|
Handler: Middleware(g),
|
||||||
|
}
|
||||||
|
server.ListenAndServe()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Middleware(next http.Handler) http.Handler {
|
||||||
|
log := klog.Sub(flagAppName)
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if flagAllowIP != "" {
|
||||||
|
if !ksrv.MatchIPGlob(ksrv.GetIP(r), flagAllowIP) {
|
||||||
|
w.WriteHeader(http.StatusForbidden)
|
||||||
|
w.Write([]byte("permission denied"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -5,7 +5,6 @@ go 1.17
|
||||||
require (
|
require (
|
||||||
github.com/creack/pty v1.1.17
|
github.com/creack/pty v1.1.17
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/rs/xid v1.3.0
|
|
||||||
kumoly.io/lib/klog v0.0.8
|
kumoly.io/lib/klog v0.0.8
|
||||||
kumoly.io/lib/ksrv v0.0.2-0.20211112060911-0d61b343a298
|
kumoly.io/lib/ksrv v0.0.2-0.20211112060911-0d61b343a298
|
||||||
)
|
)
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -4,8 +4,6 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4=
|
|
||||||
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4=
|
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4=
|
||||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
|
24
gterm.go
24
gterm.go
|
@ -14,14 +14,13 @@ import (
|
||||||
|
|
||||||
"github.com/creack/pty"
|
"github.com/creack/pty"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/rs/xid"
|
|
||||||
"kumoly.io/lib/klog"
|
"kumoly.io/lib/klog"
|
||||||
"kumoly.io/lib/ksrv"
|
"kumoly.io/lib/ksrv"
|
||||||
"kumoly.io/lib/ksrv/engine"
|
"kumoly.io/lib/ksrv/engine"
|
||||||
"kumoly.io/tools/gterm/public"
|
"kumoly.io/tools/gterm/public"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed index.html
|
//go:embed public/index.html
|
||||||
var index string
|
var index string
|
||||||
|
|
||||||
var tmpl *engine.Engine
|
var tmpl *engine.Engine
|
||||||
|
@ -68,10 +67,23 @@ func New() *GTerm {
|
||||||
// if g.BufferSize == 0 {
|
// if g.BufferSize == 0 {
|
||||||
// g.BufferSize = 512
|
// g.BufferSize = 512
|
||||||
// }
|
// }
|
||||||
|
go func() {
|
||||||
|
//print pool
|
||||||
|
log := klog.Sub("gterm")
|
||||||
|
for {
|
||||||
|
cons := []string{}
|
||||||
|
for k := range pool {
|
||||||
|
cons = append(cons, k)
|
||||||
|
}
|
||||||
|
log.Info("current connections: ", cons)
|
||||||
|
time.Sleep(5 * time.Minute)
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
return >erm{
|
return >erm{
|
||||||
AppName: "GTERM",
|
AppName: "GTERM",
|
||||||
Cmd: "bash",
|
Cmd: "bash",
|
||||||
Envs: os.Environ(),
|
Envs: append(os.Environ(), "TERM=xterm-256color"),
|
||||||
Timeout: 20 * time.Second,
|
Timeout: 20 * time.Second,
|
||||||
ErrorLimit: 10,
|
ErrorLimit: 10,
|
||||||
BufferSize: 512,
|
BufferSize: 512,
|
||||||
|
@ -111,7 +123,7 @@ func (g *GTerm) App(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GTerm) WS(w http.ResponseWriter, r *http.Request) {
|
func (g *GTerm) WS(w http.ResponseWriter, r *http.Request) {
|
||||||
id := ksrv.GetIP(r) + " : " + xid.New().String()
|
id := fmt.Sprintf("[%02d] %s", ctr(), ksrv.GetIP(r))
|
||||||
pool[id] = make(chan struct{}, 1)
|
pool[id] = make(chan struct{}, 1)
|
||||||
defer func() {
|
defer func() {
|
||||||
close(pool[id])
|
close(pool[id])
|
||||||
|
@ -226,7 +238,7 @@ func (g *GTerm) WS(w http.ResponseWriter, r *http.Request) {
|
||||||
if !ok {
|
if !ok {
|
||||||
dataType = "uunknown"
|
dataType = "uunknown"
|
||||||
}
|
}
|
||||||
l.Info(fmt.Sprintf("received %s (type: %v) message of size %v byte(s) from xterm.js with key sequence: %v", dataType, messageType, dataLength, dataBuffer))
|
l.Debug(fmt.Sprintf("received %s (type: %v) message of size %v byte(s) from xterm.js with key sequence: %v", dataType, messageType, dataLength, dataBuffer))
|
||||||
|
|
||||||
// process
|
// process
|
||||||
if dataLength == -1 { // invalid
|
if dataLength == -1 { // invalid
|
||||||
|
@ -243,7 +255,7 @@ func (g *GTerm) WS(w http.ResponseWriter, r *http.Request) {
|
||||||
l.Warn(fmt.Sprintf("failed to unmarshal received resize message '%s': %s", string(resizeMessage), err))
|
l.Warn(fmt.Sprintf("failed to unmarshal received resize message '%s': %s", string(resizeMessage), err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
l.InfoF(klog.H{"rows": ttySize.Rows, "columns": ttySize.Cols}, "resizing tty...")
|
l.DebugF(klog.H{"rows": ttySize.Rows, "columns": ttySize.Cols}, "resizing tty...")
|
||||||
if err := pty.Setsize(tty, &pty.Winsize{
|
if err := pty.Setsize(tty, &pty.Winsize{
|
||||||
Rows: ttySize.Rows,
|
Rows: ttySize.Rows,
|
||||||
Cols: ttySize.Cols,
|
Cols: ttySize.Cols,
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="terminal"></div>
|
<div id="terminal"></div>
|
||||||
<script src="./main.js"></script>
|
<script type="module" src="./main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
20
main.js
20
main.js
|
@ -7,7 +7,7 @@ import { WebLinksAddon } from 'xterm-addon-web-links';
|
||||||
import 'xterm/css/xterm.css'
|
import 'xterm/css/xterm.css'
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var terminal = new Terminal({
|
const terminal = new Terminal({
|
||||||
screenKeys: true,
|
screenKeys: true,
|
||||||
useStyle: true,
|
useStyle: true,
|
||||||
cursorBlink: true,
|
cursorBlink: true,
|
||||||
|
@ -16,19 +16,20 @@ import 'xterm/css/xterm.css'
|
||||||
screenReaderMode: true,
|
screenReaderMode: true,
|
||||||
cols: 128,
|
cols: 128,
|
||||||
});
|
});
|
||||||
terminal.open(document.getElementById("terminal"));
|
const fitAddon = new FitAddon();
|
||||||
|
terminal.loadAddon(fitAddon);
|
||||||
var protocol = (location.protocol === "https:") ? "wss://" : "ws://";
|
var protocol = (location.protocol === "https:") ? "wss://" : "ws://";
|
||||||
var url = protocol + location.host + "/ws"
|
var url = protocol + location.host + "/ws"
|
||||||
var ws = new WebSocket(url);
|
const ws = new WebSocket(url);
|
||||||
var attachAddon = new AttachAddon.AttachAddon(ws);
|
const attachAddon = new AttachAddon(ws);
|
||||||
var fitAddon = new FitAddon.FitAddon();
|
const webLinksAddon = new WebLinksAddon();
|
||||||
terminal.loadAddon(fitAddon);
|
|
||||||
var webLinksAddon = new WebLinksAddon.WebLinksAddon();
|
|
||||||
terminal.loadAddon(webLinksAddon);
|
terminal.loadAddon(webLinksAddon);
|
||||||
var unicode11Addon = new Unicode11Addon.Unicode11Addon();
|
const unicode11Addon = new Unicode11Addon();
|
||||||
terminal.loadAddon(unicode11Addon);
|
terminal.loadAddon(unicode11Addon);
|
||||||
var serializeAddon = new SerializeAddon.SerializeAddon();
|
const serializeAddon = new SerializeAddon();
|
||||||
terminal.loadAddon(serializeAddon);
|
terminal.loadAddon(serializeAddon);
|
||||||
|
terminal.open(document.getElementById("terminal"));
|
||||||
|
fitAddon.fit();
|
||||||
ws.onclose = function(event) {
|
ws.onclose = function(event) {
|
||||||
console.log(event);
|
console.log(event);
|
||||||
terminal.write('\r\n\nconnection has been terminated from the server-side (hit refresh to restart)\n')
|
terminal.write('\r\n\nconnection has been terminated from the server-side (hit refresh to restart)\n')
|
||||||
|
@ -50,6 +51,7 @@ import 'xterm/css/xterm.css'
|
||||||
console.log(event);
|
console.log(event);
|
||||||
});
|
});
|
||||||
window.onresize = function() {
|
window.onresize = function() {
|
||||||
|
console.log("resize")
|
||||||
fitAddon.fit();
|
fitAddon.fit();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
VERSION=$(git describe --tags --abbrev=0)
|
||||||
|
if [ $? -ne 0 ]; then VERSION=$DRONE_TAG; fi
|
||||||
|
BUILD=$(git rev-parse --short HEAD)
|
||||||
|
if [ $? -ne 0 ]; then BUILD=${DRONE_COMMIT:0:7}; fi
|
||||||
|
|
||||||
|
PROJ=gterm
|
||||||
|
DIST=dist
|
||||||
|
|
||||||
|
LDFLAGS="-ldflags \"-X main.Version=${VERSION} -X main.Build=${BUILD} -w -s\""
|
||||||
|
FAILURES=""
|
||||||
|
|
||||||
|
PLATFORMS="darwin/amd64 darwin/arm64"
|
||||||
|
PLATFORMS="$PLATFORMS linux/amd64"
|
||||||
|
|
||||||
|
for PLATFORM in $PLATFORMS; do
|
||||||
|
GOOS=${PLATFORM%/*}
|
||||||
|
GOARCH=${PLATFORM#*/}
|
||||||
|
BIN_FILENAME="${PROJ}"
|
||||||
|
CMD="GOOS=${GOOS} GOARCH=${GOARCH} go build ${LDFLAGS} -o ${DIST}/${BIN_FILENAME} cmd/gterm/main.go"
|
||||||
|
echo "${CMD}"
|
||||||
|
eval $CMD || FAILURES="${FAILURES} ${PLATFORM}"
|
||||||
|
sh -c "cd ${DIST} && tar -czf ${PROJ}-${VERSION}-${GOOS}-${GOARCH}.tar.gz ${BIN_FILENAME} && rm ${BIN_FILENAME}"
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "${FAILURES}" != "" ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "${SCRIPT_NAME} failed on: ${FAILURES}"
|
||||||
|
exit 1
|
||||||
|
fi
|
11
package.json
11
package.json
|
@ -1,10 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "gterm",
|
"name": "gterm",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "index.js",
|
|
||||||
"repository": "git@kumoly.io:tools/gterm.git",
|
"repository": "git@kumoly.io:tools/gterm.git",
|
||||||
"author": "Evan Chen <evanchen@kumoly.io>",
|
"author": "Evan Chen <evanchen@kumoly.io>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"build":"rm public/*.js public/*.css && yarn parcel build index.html"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"parcel": "^2.0.1",
|
"parcel": "^2.0.1",
|
||||||
"xterm": "^4.15.0",
|
"xterm": "^4.15.0",
|
||||||
|
@ -13,5 +15,12 @@
|
||||||
"xterm-addon-serialize": "^0.6.1",
|
"xterm-addon-serialize": "^0.6.1",
|
||||||
"xterm-addon-unicode11": "^0.3.0",
|
"xterm-addon-unicode11": "^0.3.0",
|
||||||
"xterm-addon-web-links": "^0.4.0"
|
"xterm-addon-web-links": "^0.4.0"
|
||||||
|
},
|
||||||
|
"targets":{
|
||||||
|
"default":{
|
||||||
|
"distDir":"public",
|
||||||
|
"publicUrl":"./",
|
||||||
|
"sourceMap": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,5 +2,5 @@ package public
|
||||||
|
|
||||||
import "embed"
|
import "embed"
|
||||||
|
|
||||||
//go:embed js favicon.ico
|
//go:embed *
|
||||||
var FS embed.FS
|
var FS embed.FS
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
||||||
|
.xterm{position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{border:0;height:0;left:-9999em;margin:0;opacity:0;overflow:hidden;padding:0;position:absolute;resize:none;top:0;white-space:nowrap;width:0;z-index:-5}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;bottom:0;cursor:default;left:0;overflow-y:scroll;position:absolute;right:0;top:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{left:0;position:absolute;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;left:-9999em;line-height:normal;position:absolute;top:0;visibility:hidden}.xterm{cursor:text}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility,.xterm .xterm-message{bottom:0;color:transparent;left:0;position:absolute;right:0;top:0;z-index:10}.xterm .live-region{height:1px;left:-9999px;overflow:hidden;position:absolute;width:1px}.xterm-dim{opacity:.5}.xterm-underline{text-decoration:underline}.xterm-strikethrough{text-decoration:line-through}
|
|
@ -0,0 +1 @@
|
||||||
|
<!DOCTYPE html><html><head><link rel="stylesheet" href="index.6999253a.css"><title>{{.AppName}}</title><style>body::-webkit-scrollbar,div::-webkit-scrollbar,html::-webkit-scrollbar{display:none;width:0}body,html{margin:0;overflow:hidden;padding:0}div#terminal{height:100%;left:0;position:absolute;top:0;width:100%}div#terminal div{height:100%}.xterm-screen,.xterm-viewport{height:100%;margin:0;padding:0}</style></head><body> <div id="terminal"></div> <script type="module" src="index.434ad47d.js"></script> </body></html>
|
Loading…
Reference in New Issue