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
|
||||
.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 (
|
||||
"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 (
|
||||
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()
|
||||
|
||||
server := ksrv.New()
|
||||
g := gterm.New()
|
||||
|
||||
server.Handle(g).Listen(flagAddr).Serve()
|
||||
if flagVer {
|
||||
fmt.Printf("%v - %v\n", Version, Build)
|
||||
return
|
||||
}
|
||||
|
||||
klog.LEVEL = klog.Llevel(flagLogLevel)
|
||||
klog.PROD = !flagDev
|
||||
|
||||
g := gterm.New()
|
||||
g.AppName = flagAppName
|
||||
g.Cmd = flagShell
|
||||
|
||||
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 (
|
||||
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
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/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=
|
||||
|
|
24
gterm.go
24
gterm.go
|
@ -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 >erm{
|
||||
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,
|
||||
|
|
|
@ -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
20
main.js
|
@ -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();
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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",
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,5 +2,5 @@ package public
|
|||
|
||||
import "embed"
|
||||
|
||||
//go:embed js favicon.ico
|
||||
//go:embed *
|
||||
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