mirror of
https://github.com/sorenisanerd/gotty.git
synced 2024-11-09 23:34:26 +00:00
Merge profile and config files and support custom index file
This commit is contained in:
parent
25a5bc0b89
commit
d3e48aa3ae
19
Makefile
19
Makefile
@ -3,7 +3,7 @@ gotty: app/resource.go main.go app/*.go
|
|||||||
|
|
||||||
resource: app/resource.go
|
resource: app/resource.go
|
||||||
|
|
||||||
app/resource.go: bindata/static/hterm.js bindata/static/gotty.js bindata/static/index.html
|
app/resource.go: bindata/static/js/hterm.js bindata/static/js/gotty.js bindata/static/index.html
|
||||||
go-bindata -prefix bindata -pkg app -ignore=\\.gitkeep -o app/resource.go bindata/...
|
go-bindata -prefix bindata -pkg app -ignore=\\.gitkeep -o app/resource.go bindata/...
|
||||||
gofmt -w app/resource.go
|
gofmt -w app/resource.go
|
||||||
|
|
||||||
@ -13,12 +13,15 @@ bindata:
|
|||||||
bindata/static: bindata
|
bindata/static: bindata
|
||||||
mkdir bindata/static
|
mkdir bindata/static
|
||||||
|
|
||||||
bindata/static/hterm.js: bindata/static libapps/hterm/js/*.js
|
|
||||||
cd libapps && \
|
|
||||||
LIBDOT_SEARCH_PATH=`pwd` ./libdot/bin/concat.sh -i ./hterm/concat/hterm_all.concat -o ../bindata/static/hterm.js
|
|
||||||
|
|
||||||
bindata/static/gotty.js: bindata/static resources/gotty.js
|
|
||||||
cp resources/gotty.js bindata/static/gotty.js
|
|
||||||
|
|
||||||
bindata/static/index.html: bindata/static resources/index.html
|
bindata/static/index.html: bindata/static resources/index.html
|
||||||
cp resources/index.html bindata/static/index.html
|
cp resources/index.html bindata/static/index.html
|
||||||
|
|
||||||
|
bindata/static/js: bindata/static
|
||||||
|
mkdir bindata/static/js
|
||||||
|
|
||||||
|
bindata/static/js/hterm.js: bindata/static/js libapps/hterm/js/*.js
|
||||||
|
cd libapps && \
|
||||||
|
LIBDOT_SEARCH_PATH=`pwd` ./libdot/bin/concat.sh -i ./hterm/concat/hterm_all.concat -o ../bindata/static/js/hterm.js
|
||||||
|
|
||||||
|
bindata/static/js/gotty.js: bindata/static/js resources/gotty.js
|
||||||
|
cp resources/gotty.js bindata/static/js/gotty.js
|
||||||
|
32
README.md
32
README.md
@ -58,26 +58,36 @@ By default, gotty starts a web server at port 8080. Open the URL on your web bro
|
|||||||
--tls, -t Enable TLS/SSL [$GOTTY_TLS]
|
--tls, -t Enable TLS/SSL [$GOTTY_TLS]
|
||||||
--tls-crt "~/.gotty.key" TLS/SSL crt file path [$GOTTY_TLS_CRT]
|
--tls-crt "~/.gotty.key" TLS/SSL crt file path [$GOTTY_TLS_CRT]
|
||||||
--tls-key "~/.gotty.crt" TLS/SSL key file path [$GOTTY_TLS_KEY]
|
--tls-key "~/.gotty.crt" TLS/SSL key file path [$GOTTY_TLS_KEY]
|
||||||
--profile "~/.gotty.prf" Profile file path [$GOTTY_PROFILE]
|
--index Custom index file [$GOTTY_INDEX]
|
||||||
--title-format "GoTTY - {{ .Command }} ({{ .Hostname }})" Title format of browser window [$GOTTY_TITLE_FORMAT]
|
--title-format "GoTTY - {{ .Command }} ({{ .Hostname }})" Title format of browser window [$GOTTY_TITLE_FORMAT]
|
||||||
--reconnect Enable reconnection [$GOTTY_RECONNECT]
|
--reconnect Enable reconnection [$GOTTY_RECONNECT]
|
||||||
--reconnect-time "10" Time to reconnect [$GOTTY_RECONNECT_TIME]
|
--reconnect-time "10" Time to reconnect [$GOTTY_RECONNECT_TIME]
|
||||||
--once Accept only one client and exit on disconnection [$GOTTY_ONCE]
|
--once Accept only one client and exit on disconnection [$GOTTY_ONCE]
|
||||||
--config "~/.gotty" Config file path [$GOTTY_CONFIG]
|
--config "~/.gotty" Config file path [$GOTTY_CONFIG]
|
||||||
```
|
--version, -v print the version
|
||||||
|
|
||||||
### Profile File
|
|
||||||
|
|
||||||
You can customize your terminal (hterm) by providing a profile file to the `gotty` command, which is a HCL file that has a map of preference keys and values. Gotty loads a profile file at `~/.gotty` by default when it exists.
|
|
||||||
|
|
||||||
The following example makes the font size smaller and the background color a little bit blue.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
font-size = 5,
|
|
||||||
background-color = "rgb(16, 16, 32)"
|
### Config File
|
||||||
|
|
||||||
|
You can customize default options and your terminal (hterm) by providing a config file to the `gotty` command. Gotty loads a profile file at `~/.gotty` by default when it exists.
|
||||||
|
|
||||||
|
```
|
||||||
|
// Listen at port 9000 by default
|
||||||
|
port = "9000"
|
||||||
|
|
||||||
|
// Enable TSL/SSL by default
|
||||||
|
enable_tls = true
|
||||||
|
|
||||||
|
// hterm preferences
|
||||||
|
// Smaller font and a little bit bluer background color
|
||||||
|
preferences {
|
||||||
|
font_size = 5,
|
||||||
|
background_color = "rgb(16, 16, 32)"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Available preferences are listed in [the hterm source code](https://chromium.googlesource.com/apps/libapps/+/master/hterm/js/hterm_preference_manager.js)
|
Available hterm preference options are listed in [the hterm source code](https://chromium.googlesource.com/apps/libapps/+/master/hterm/js/hterm_preference_manager.js). Note that hifens (`-`) in the preference names must be replaced by underscores (`_`).
|
||||||
|
|
||||||
### Security Options
|
### Security Options
|
||||||
|
|
||||||
|
65
app/app.go
65
app/app.go
@ -32,7 +32,6 @@ type App struct {
|
|||||||
upgrader *websocket.Upgrader
|
upgrader *websocket.Upgrader
|
||||||
server *manners.GracefulServer
|
server *manners.GracefulServer
|
||||||
|
|
||||||
preferences map[string]interface{}
|
|
||||||
titleTemplate *template.Template
|
titleTemplate *template.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +43,7 @@ type Options struct {
|
|||||||
Credential string
|
Credential string
|
||||||
EnableRandomUrl bool
|
EnableRandomUrl bool
|
||||||
RandomUrlLength int
|
RandomUrlLength int
|
||||||
ProfileFile string
|
IndexFile string
|
||||||
EnableTLS bool
|
EnableTLS bool
|
||||||
TLSCrtFile string
|
TLSCrtFile string
|
||||||
TLSKeyFile string
|
TLSKeyFile string
|
||||||
@ -52,6 +51,7 @@ type Options struct {
|
|||||||
EnableReconnect bool
|
EnableReconnect bool
|
||||||
ReconnectTime int
|
ReconnectTime int
|
||||||
Once bool
|
Once bool
|
||||||
|
Preferences map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultOptions = Options{
|
var DefaultOptions = Options{
|
||||||
@ -62,7 +62,7 @@ var DefaultOptions = Options{
|
|||||||
Credential: "",
|
Credential: "",
|
||||||
EnableRandomUrl: false,
|
EnableRandomUrl: false,
|
||||||
RandomUrlLength: 8,
|
RandomUrlLength: 8,
|
||||||
ProfileFile: "~/.gotty.prf",
|
IndexFile: "",
|
||||||
EnableTLS: false,
|
EnableTLS: false,
|
||||||
TLSCrtFile: "~/.gotty.key",
|
TLSCrtFile: "~/.gotty.key",
|
||||||
TLSKeyFile: "~/.gotty.crt",
|
TLSKeyFile: "~/.gotty.crt",
|
||||||
@ -70,6 +70,7 @@ var DefaultOptions = Options{
|
|||||||
EnableReconnect: false,
|
EnableReconnect: false,
|
||||||
ReconnectTime: 10,
|
ReconnectTime: 10,
|
||||||
Once: false,
|
Once: false,
|
||||||
|
Preferences: make(map[string]interface{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(command []string, options *Options) (*App, error) {
|
func New(command []string, options *Options) (*App, error) {
|
||||||
@ -78,11 +79,6 @@ func New(command []string, options *Options) (*App, error) {
|
|||||||
return nil, errors.New("Title format string syntax error")
|
return nil, errors.New("Title format string syntax error")
|
||||||
}
|
}
|
||||||
|
|
||||||
prefMap, err := loadProfileFile(options)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &App{
|
return &App{
|
||||||
command: command,
|
command: command,
|
||||||
options: options,
|
options: options,
|
||||||
@ -93,7 +89,6 @@ func New(command []string, options *Options) (*App, error) {
|
|||||||
Subprotocols: []string{"gotty"},
|
Subprotocols: []string{"gotty"},
|
||||||
},
|
},
|
||||||
|
|
||||||
preferences: prefMap,
|
|
||||||
titleTemplate: titleTemplate,
|
titleTemplate: titleTemplate,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -126,12 +121,25 @@ func applyConfigFile(options *Options, filePath string) error {
|
|||||||
if val, ok := config[configName]; ok {
|
if val, ok := config[configName]; ok {
|
||||||
field, ok := o.FieldOk(name)
|
field, ok := o.FieldOk(name)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("No such field: " + name)
|
return errors.New("No such option: " + name)
|
||||||
}
|
}
|
||||||
err := field.Set(val)
|
|
||||||
|
var err error
|
||||||
|
if name == "Preferences" {
|
||||||
|
prefs := val.([]map[string]interface{})[0]
|
||||||
|
htermPrefs := make(map[string]interface{})
|
||||||
|
for key, value := range prefs {
|
||||||
|
htermPrefs[strings.Replace(key, "_", "-", -1)] = value
|
||||||
|
}
|
||||||
|
err = field.Set(htermPrefs)
|
||||||
|
} else {
|
||||||
|
err = field.Set(val)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,28 +154,6 @@ func ExpandHomeDir(path string) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadProfileFile(options *Options) (map[string]interface{}, error) {
|
|
||||||
prefString := []byte{}
|
|
||||||
prefPath := options.ProfileFile
|
|
||||||
if options.ProfileFile == DefaultOptions.ProfileFile {
|
|
||||||
prefPath = os.Getenv("HOME") + "/.gotty.prf"
|
|
||||||
}
|
|
||||||
if _, err := os.Stat(prefPath); os.IsNotExist(err) {
|
|
||||||
if options.ProfileFile != DefaultOptions.ProfileFile {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Printf("Loading profile path: %s", prefPath)
|
|
||||||
prefString, _ = ioutil.ReadFile(prefPath)
|
|
||||||
}
|
|
||||||
var prefMap map[string]interface{}
|
|
||||||
err := hcl.Decode(&prefMap, string(prefString))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return prefMap, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app *App) Run() error {
|
func (app *App) Run() error {
|
||||||
if app.options.PermitWrite {
|
if app.options.PermitWrite {
|
||||||
log.Printf("Permitting clients to write input to the PTY.")
|
log.Printf("Permitting clients to write input to the PTY.")
|
||||||
@ -190,7 +176,20 @@ func (app *App) Run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var siteMux = http.NewServeMux()
|
var siteMux = http.NewServeMux()
|
||||||
|
|
||||||
|
if app.options.IndexFile != "" {
|
||||||
|
log.Printf("Using index file at " + app.options.IndexFile)
|
||||||
|
indexHandler := http.HandlerFunc(
|
||||||
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.ServeFile(w, r, ExpandHomeDir(app.options.IndexFile))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
siteMux.Handle(path+"/", indexHandler)
|
||||||
|
} else {
|
||||||
siteMux.Handle(path+"/", http.StripPrefix(path+"/", staticHandler))
|
siteMux.Handle(path+"/", http.StripPrefix(path+"/", staticHandler))
|
||||||
|
}
|
||||||
|
|
||||||
|
siteMux.Handle(path+"/js/", http.StripPrefix(path+"/", staticHandler))
|
||||||
siteMux.Handle(path+"/ws", wsHandler)
|
siteMux.Handle(path+"/ws", wsHandler)
|
||||||
|
|
||||||
siteHandler := http.Handler(siteMux)
|
siteHandler := http.Handler(siteMux)
|
||||||
|
@ -126,7 +126,7 @@ func (context *clientContext) sendInitialize() error {
|
|||||||
}
|
}
|
||||||
writer.Close()
|
writer.Close()
|
||||||
|
|
||||||
prefs, _ := json.Marshal(context.app.preferences)
|
prefs, _ := json.Marshal(context.app.options.Preferences)
|
||||||
context.connection.WriteMessage(
|
context.connection.WriteMessage(
|
||||||
websocket.TextMessage,
|
websocket.TextMessage,
|
||||||
append([]byte{SetPreferences}, prefs...),
|
append([]byte{SetPreferences}, prefs...),
|
||||||
|
File diff suppressed because one or more lines are too long
4
main.go
4
main.go
@ -28,7 +28,7 @@ func main() {
|
|||||||
flag{"tls", "t", "Enable TLS/SSL"},
|
flag{"tls", "t", "Enable TLS/SSL"},
|
||||||
flag{"tls-crt", "", "TLS/SSL crt file path"},
|
flag{"tls-crt", "", "TLS/SSL crt file path"},
|
||||||
flag{"tls-key", "", "TLS/SSL key file path"},
|
flag{"tls-key", "", "TLS/SSL key file path"},
|
||||||
flag{"profile", "", "Profile file path"},
|
flag{"index", "", "Custom index file"},
|
||||||
flag{"title-format", "", "Title format of browser window"},
|
flag{"title-format", "", "Title format of browser window"},
|
||||||
flag{"reconnect", "", "Enable reconnection"},
|
flag{"reconnect", "", "Enable reconnection"},
|
||||||
flag{"reconnect-time", "", "Time to reconnect"},
|
flag{"reconnect-time", "", "Time to reconnect"},
|
||||||
@ -36,7 +36,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mappingHint := map[string]string{
|
mappingHint := map[string]string{
|
||||||
"profile": "ProfileFile",
|
"index": "IndexFile",
|
||||||
"tls": "EnableTLS",
|
"tls": "EnableTLS",
|
||||||
"tls-crt": "TLSCrtFile",
|
"tls-crt": "TLSCrtFile",
|
||||||
"tls-key": "TLSKeyFile",
|
"tls-key": "TLSKeyFile",
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
term.installKeyboard();
|
term.installKeyboard();
|
||||||
};
|
};
|
||||||
|
|
||||||
term.decorate(document.body);
|
term.decorate(document.getElementById("terminal"));
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onmessage = function(event) {
|
ws.onmessage = function(event) {
|
||||||
|
@ -2,10 +2,11 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>GoTTY</title>
|
<title>GoTTY</title>
|
||||||
<style>body {position: absolute; height: 100%; width: 100%; margin: 0px;}</style>
|
<style>body, #terminal {position: absolute; height: 100%; width: 100%; margin: 0px;}</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script src="hterm.js"></script>
|
<div id="terminal"></div>
|
||||||
<script src="gotty.js"></script>
|
<script src="./js/hterm.js"></script>
|
||||||
|
<script src="./js/gotty.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
Reference in New Issue
Block a user