update
continuous-integration/drone/tag Build is passing Details

master v0.1.0
Evan Chen 2021-11-17 02:04:21 +08:00
parent d379ffff9b
commit 9bb3c18df9
16 changed files with 191 additions and 28 deletions

20
.drone.yml Normal file
View File

@ -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

3
.gitignore vendored
View File

@ -1,2 +1,3 @@
node_modules
.parcel-cache
.parcel-cache
dist

View File

@ -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"
```

View File

@ -2,27 +2,78 @@ package main
import (
"flag"
"fmt"
"net/http"
"os"
"kumoly.io/lib/klog"
"kumoly.io/lib/ksrv"
"kumoly.io/tools/gterm"
)
var Version = "0.0.0"
var Build = "alpha"
var (
flagAppName string = "gterm"
flagAddr string
flagShell string
flagAllowIP string
flagAppName string = "gterm"
flagAddr string
flagShell string
flagLogLevel int
flagDev bool
flagVer bool
)
func init() {
flag.StringVar(&flagAppName, "name", "gterm", "the application title")
flag.StringVar(&flagAddr, "addr", ":8000", "address to bind")
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() {
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.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
View File

@ -5,7 +5,6 @@ go 1.17
require (
github.com/creack/pty v1.1.17
github.com/gorilla/websocket v1.4.2
github.com/rs/xid v1.3.0
kumoly.io/lib/klog v0.0.8
kumoly.io/lib/ksrv v0.0.2-0.20211112060911-0d61b343a298
)

2
go.sum
View File

@ -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/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/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-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@ -14,14 +14,13 @@ import (
"github.com/creack/pty"
"github.com/gorilla/websocket"
"github.com/rs/xid"
"kumoly.io/lib/klog"
"kumoly.io/lib/ksrv"
"kumoly.io/lib/ksrv/engine"
"kumoly.io/tools/gterm/public"
)
//go:embed index.html
//go:embed public/index.html
var index string
var tmpl *engine.Engine
@ -68,10 +67,23 @@ func New() *GTerm {
// if g.BufferSize == 0 {
// 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 &GTerm{
AppName: "GTERM",
Cmd: "bash",
Envs: os.Environ(),
Envs: append(os.Environ(), "TERM=xterm-256color"),
Timeout: 20 * time.Second,
ErrorLimit: 10,
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) {
id := ksrv.GetIP(r) + " : " + xid.New().String()
id := fmt.Sprintf("[%02d] %s", ctr(), ksrv.GetIP(r))
pool[id] = make(chan struct{}, 1)
defer func() {
close(pool[id])
@ -226,7 +238,7 @@ func (g *GTerm) WS(w http.ResponseWriter, r *http.Request) {
if !ok {
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
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))
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{
Rows: ttySize.Rows,
Cols: ttySize.Cols,

View File

@ -41,7 +41,7 @@
<body>
<div id="terminal"></div>
<script src="./main.js"></script>
<script type="module" src="./main.js"></script>
</body>
</html>

20
main.js
View File

@ -7,7 +7,7 @@ import { WebLinksAddon } from 'xterm-addon-web-links';
import 'xterm/css/xterm.css'
(function() {
var terminal = new Terminal({
const terminal = new Terminal({
screenKeys: true,
useStyle: true,
cursorBlink: true,
@ -16,19 +16,20 @@ import 'xterm/css/xterm.css'
screenReaderMode: true,
cols: 128,
});
terminal.open(document.getElementById("terminal"));
const fitAddon = new FitAddon();
terminal.loadAddon(fitAddon);
var protocol = (location.protocol === "https:") ? "wss://" : "ws://";
var url = protocol + location.host + "/ws"
var ws = new WebSocket(url);
var attachAddon = new AttachAddon.AttachAddon(ws);
var fitAddon = new FitAddon.FitAddon();
terminal.loadAddon(fitAddon);
var webLinksAddon = new WebLinksAddon.WebLinksAddon();
const ws = new WebSocket(url);
const attachAddon = new AttachAddon(ws);
const webLinksAddon = new WebLinksAddon();
terminal.loadAddon(webLinksAddon);
var unicode11Addon = new Unicode11Addon.Unicode11Addon();
const unicode11Addon = new Unicode11Addon();
terminal.loadAddon(unicode11Addon);
var serializeAddon = new SerializeAddon.SerializeAddon();
const serializeAddon = new SerializeAddon();
terminal.loadAddon(serializeAddon);
terminal.open(document.getElementById("terminal"));
fitAddon.fit();
ws.onclose = function(event) {
console.log(event);
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);
});
window.onresize = function() {
console.log("resize")
fitAddon.fit();
};
};

29
make.sh Executable file
View File

@ -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

View File

@ -1,10 +1,12 @@
{
"name": "gterm",
"version": "1.0.0",
"main": "index.js",
"repository": "git@kumoly.io:tools/gterm.git",
"author": "Evan Chen <evanchen@kumoly.io>",
"license": "MIT",
"scripts": {
"build":"rm public/*.js public/*.css && yarn parcel build index.html"
},
"dependencies": {
"parcel": "^2.0.1",
"xterm": "^4.15.0",
@ -13,5 +15,12 @@
"xterm-addon-serialize": "^0.6.1",
"xterm-addon-unicode11": "^0.3.0",
"xterm-addon-web-links": "^0.4.0"
},
"targets":{
"default":{
"distDir":"public",
"publicUrl":"./",
"sourceMap": false
}
}
}

View File

@ -2,5 +2,5 @@ package public
import "embed"
//go:embed js favicon.ico
//go:embed *
var FS embed.FS

1
public/index.434ad47d.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -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}

1
public/index.html Normal file
View File

@ -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>

12
util.go
View File

@ -1 +1,13 @@
package gterm
import "sync"
var curCtr = 0
var ctrLck sync.Mutex
func ctr() int {
ctrLck.Lock()
defer ctrLck.Unlock()
curCtr++
return curCtr
}