diff --git a/Makefile b/Makefile index 2e844ba..22c386a 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ docker: docker build . -t gotty-bash:$(VERSION) .PHONY: asset -asset: bindata/static/js/gotty-bundle.js bindata/static/index.html bindata/static/favicon.png bindata/static/css/index.css bindata/static/css/xterm.css bindata/static/css/xterm_customize.css +asset: bindata/static/js/gotty-bundle.js bindata/static/index.html bindata/static/favicon.png bindata/static/css/index.css bindata/static/css/xterm.css bindata/static/css/xterm_customize.css bindata/static/manifest.json bindata/static/icon_192.png go-bindata -prefix bindata -pkg server -ignore=\\.gitkeep -o server/asset.go bindata/... gofmt -w server/asset.go @@ -26,9 +26,15 @@ bindata/static: bindata bindata/static/index.html: bindata/static resources/index.html cp resources/index.html bindata/static/index.html +bindata/static/manifest.json: bindata/static resources/manifest.json + cp resources/manifest.json bindata/static/manifest.json + bindata/static/favicon.png: bindata/static resources/favicon.png cp resources/favicon.png bindata/static/favicon.png +bindata/static/icon_192.png: bindata/static resources/icon_192.png + cp resources/icon_192.png bindata/static/icon_192.png + bindata/static/js: bindata/static mkdir -p bindata/static/js diff --git a/resources/icon_192.png b/resources/icon_192.png new file mode 100644 index 0000000..3a336b7 Binary files /dev/null and b/resources/icon_192.png differ diff --git a/resources/index.html b/resources/index.html index 67c1c0f..9f6331e 100644 --- a/resources/index.html +++ b/resources/index.html @@ -2,6 +2,7 @@ {{ .title }} + diff --git a/resources/manifest.json b/resources/manifest.json new file mode 100644 index 0000000..8a53b58 --- /dev/null +++ b/resources/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "{{ .title }}", + "name": "{{ .title }}", + "start_url": "./", + "icons": [ + { + "src": "./icon_192.png", + "type": "image/png", + "sizes": "192x192" + } + ], + "display": "minimal-ui", + "theme_color": "#000000", + "background_color": "#000000" +} diff --git a/server/asset.go b/server/asset.go index affe9d2..d34c06d 100644 --- a/server/asset.go +++ b/server/asset.go @@ -61,7 +61,16 @@ func static_favicon_png() ([]byte, error) { ) } -var _static_index_html = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x91\x51\x6a\xc3\x30\x0c\x86\xdf\x77\x0a\xcd\xef\x8b\x2f\xa0\xe4\x2a\x25\xb5\x95\x44\xad\x63\x07\x4b\x09\xcd\x4a\xef\x3e\x3c\xb7\x30\xe8\x60\xa3\x4f\x16\xfa\xbf\xef\xc7\xd8\xf8\xee\x93\xd3\x7d\x21\x98\x74\x0e\xdd\x1b\xd6\x03\x00\x27\xea\x7d\x19\x00\x50\x59\x03\x75\xd7\x2b\x34\xdf\x13\xdc\x6e\x68\xeb\xae\xe6\x81\xe3\x19\x32\x85\xd6\xb0\x4b\xd1\x40\xe9\x6b\x0d\xcf\xfd\x48\x76\x89\xa3\x81\x29\xd3\xd0\x9a\xa1\xdf\x4a\xde\x94\xd5\x93\x29\xba\x07\x92\x89\x48\x1f\x78\x63\x9d\x88\xe5\xe8\xe9\xd2\x38\x11\x03\xf6\xdf\xd2\x45\x29\xcf\x2f\x49\x07\xb7\x8a\xa6\x99\x3f\xe9\x87\x8e\xf6\xf1\x18\x78\x4c\x7e\xbf\x37\x7a\xde\x80\x7d\x6b\x8a\xc6\xb1\x0f\xa6\x43\xeb\x79\xbb\xa7\xe2\x32\x2f\x0a\x92\x5d\xe9\xef\x57\x9d\x0e\x9a\xce\x14\x9b\x93\x14\xb0\xc6\xbf\xb2\x2e\xc5\x81\xc7\xbf\xb9\x93\xd8\x31\xa9\xee\x1f\xc7\x35\xfa\x40\x4f\x02\xda\x7a\x59\xb4\xf5\x4f\xbf\x02\x00\x00\xff\xff\x92\x6e\x9e\x42\xeb\x01\x00\x00") +var _static_icon_192_png = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x24\x51\x69\x50\x53\x67\x1b\x7d\x43\x44\x04\x24\x7e\x7c\x62\xc0\x90\x9b\x84\xdc\x35\xf7\xe6\xde\x9b\x1b\xb6\x6c\x45\x83\xa1\xb8\x0d\x09\x10\x40\xc2\x62\x4c\xa6\x68\x13\x97\x48\x8d\x49\x25\x53\xe3\x12\xac\xe3\xd6\x1f\xd8\x89\xd5\x5a\x1d\x95\xea\xa8\x08\x83\x58\xc7\xad\x09\xca\xd8\x4e\x5b\x27\x9d\x5a\xed\x50\xb5\x51\xdb\x19\xa6\xca\xb8\x54\x94\x7f\xed\xdc\xe4\xc7\xf3\xe7\x9c\xe7\x3d\xe7\x3c\xef\xd9\x6d\xab\xab\xcd\xcb\x91\xe4\x00\x00\xf2\x96\x2c\xb6\x34\x00\x00\x62\xfc\xcc\x12\x02\x00\x3e\xc8\x38\x52\x0e\x80\xe0\xa4\x6d\xb9\xbd\x46\x2a\x95\x32\x0c\x23\x91\x48\x50\x14\xc5\x30\x0c\x41\x90\xba\xba\xba\x95\x2b\x57\x2a\x14\x0a\x99\x4c\x26\x97\xcb\x95\x4a\x25\x04\x41\x25\x25\x25\x46\xa3\x71\xfe\xfc\xf9\x62\xb1\x58\xad\x56\xab\x54\x2a\x82\x20\x70\x1c\xa7\x28\x8a\x24\x49\x9a\xa6\x59\x96\x05\xe7\x0c\xe0\x8c\xc1\xe9\x74\x1a\x0c\x06\x30\x60\xcc\xfc\x42\xd7\xd9\xd9\x89\x22\x48\x43\x43\x43\x51\x51\x91\x5e\xaf\x6f\x6a\x6a\x6a\x6f\x6f\xef\xe8\xe8\xa8\xac\xac\xd4\x6a\xb5\x36\x9b\x6d\xd5\xaa\x55\x6d\x6d\x6d\x56\xab\x75\xc5\x8a\x15\x8d\x8d\x8d\xad\xad\xad\x1a\x9c\x26\x64\x48\x73\x73\xb3\xdd\x6e\x2f\x28\x28\x60\xd5\x4c\x61\x61\x61\x71\x71\xb1\xc5\x62\x29\x2f\x2f\x2f\x2d\x2d\x5d\xba\x74\x69\x45\x45\x85\x4e\xa7\x53\xb8\x2a\xca\xca\xca\x34\x1a\x0d\xc7\x71\x30\x82\xf8\xfd\x7e\x52\x89\x2b\x9a\xb4\x0c\x45\xe3\x72\x84\x21\x69\x15\x46\xb8\x5c\x2e\xb4\x04\xc1\x50\x14\x63\x54\x14\x41\x2a\x9c\x15\x72\xa3\x8a\x46\x29\x79\xa3\x36\xdf\xa7\x45\x11\x14\xb2\x32\xa8\x0a\x97\x37\x70\x60\xc8\x88\x33\x64\x20\x10\x20\x58\x8a\xe4\x68\x70\xc9\x04\xc3\x30\x81\xe2\x2c\xc3\xe6\x6f\xd4\x3a\x1c\x8e\x82\x2e\x8d\x86\xa0\x19\x54\xad\x74\x94\xcb\xea\x59\x86\x63\x55\x25\x18\x59\xc5\xa1\x18\x46\x69\x69\xa9\x95\x56\x23\xa4\x06\x53\x13\x0a\xb4\xa5\xa5\x05\x0c\x18\x70\x14\x23\x71\x95\x5a\x45\xa1\x34\x01\x2e\x18\xd5\x65\xac\x12\x86\x71\x19\xa2\xd2\xa8\xa9\x52\x66\xc6\x31\x03\x06\xa3\x12\x1b\x2d\xaf\xd7\x80\x11\x23\x18\x36\x52\x30\x21\x3c\xa6\xcf\x5f\xcf\x65\x1e\xd2\x41\x36\x16\x5c\x34\xd1\xa4\xda\xed\x76\xb3\x14\xe3\x1e\x1f\x63\x00\x00\xd9\xbe\xc5\xad\x1f\x01\xc0\xc0\xfc\x08\x82\xa7\x16\x6c\x06\x20\x63\x78\x89\xc5\x6c\x0f\x8e\x4f\x3e\x6c\xb1\xf6\x99\x41\xe4\xf1\xc1\xcc\x93\xad\x13\x6f\x99\xb7\xcc\xe6\x9a\xc1\x5c\xd7\x8b\xeb\xef\x1e\x5d\x75\xdd\x5f\xb0\xee\xb5\x6c\xc3\x8f\x5d\xd7\xb9\x43\x1b\xba\xba\x8f\x1e\xde\xf6\xf0\xf0\x3f\x8f\xd6\xce\xd2\x5c\x3d\xd5\x18\xdd\xff\x7d\x11\xc4\xd1\x77\xc7\xd7\x11\xfa\x98\x40\xb4\xd8\xb4\xf0\x8f\x73\x37\x0b\xf6\xee\x8b\x8a\x3d\x93\xed\x3a\x1e\x79\x2f\x89\xbf\xb1\x50\x5b\x86\xae\x8d\xce\xdb\x4b\xf6\x89\x3d\x93\xd5\x69\xd8\xc9\xbe\x49\xac\x7f\x30\x2a\x7e\xbe\xeb\xc6\x8d\xde\xe2\xcb\xe1\xec\xb8\x7f\x91\x77\x6a\xc7\x97\x42\x69\x32\x7c\x57\xf8\xc6\x55\x56\xfc\x6e\x48\x28\x7f\x25\x85\x83\x7b\x0a\x13\xcb\xd6\xd5\xb3\x27\x1c\xcf\x40\x96\x4e\xf1\x6d\xf8\xac\x6e\x67\x6d\x74\xe7\xee\x68\x6f\xe5\x74\x66\x16\x2f\x36\xfd\x2f\x80\x0e\x6f\xfc\xdf\x6d\x6e\xde\x41\x41\x60\x70\xee\xc3\xb8\xbf\xda\x3b\x95\x1b\xe0\xb9\x33\x59\x13\xd1\x5d\x3d\xd3\x61\x8f\x60\xf5\x4b\xe1\xf9\xb8\xdf\xec\x9d\xda\x25\xe0\x09\x7d\x66\x68\xd4\x6f\xcf\x0d\xee\xfb\x7a\x62\xf6\x40\x9e\xc8\x17\x16\x76\xe7\x24\x86\xb2\x7f\x7f\x95\xf7\x6b\x46\x9d\xbb\xa8\x23\x3e\xb2\xc8\xdb\x93\xe4\x0d\xa7\x00\xf4\x6c\xe3\xdc\xda\x7a\x3a\x89\x1c\x3d\x46\x5f\x16\xad\x99\xfc\x6a\x6b\xb0\x3b\x2c\x94\x7e\x16\x0c\xef\xaf\x32\xc5\x3a\xec\x33\x43\x63\x55\xcf\xcd\x3d\xbe\x91\xc4\x78\x8d\x8f\x27\x6a\x63\x33\xaf\x8d\xf6\x1c\x19\xcb\x0e\xdd\x5e\x6d\xba\xf9\xf1\xa6\x19\x2d\xbd\xf7\x0c\x31\x81\xe8\x3b\x78\xe1\xed\xd0\xec\x27\x80\x79\x92\x2b\x7a\xaa\xdd\xff\x98\xf3\xec\x38\xbb\xe3\x5e\xe7\xf6\xf4\xcd\x9f\x9b\xe7\xe4\x25\xa1\x66\xfb\x6f\x91\x92\x07\xef\x0f\x09\x96\xa5\xdc\x0f\x84\x3f\xb9\x76\x6b\xef\x56\xc3\xf6\x6e\xf3\x1d\xb3\x17\x4a\x82\xac\x3f\x0b\x06\xe7\xfd\x74\x7a\x4f\x54\xec\x39\xcf\xbf\x33\x65\xc8\xaa\x0b\x4d\x92\x84\x94\x6f\xe1\x60\xca\x7e\x4b\x58\xd4\xf1\x72\xcd\x68\xf4\xd3\x0f\x8f\xdf\xbb\xb3\x2d\xbd\xb4\x29\x7e\x22\x3f\x2f\xff\x97\xea\xf3\xfe\x54\xc0\xd4\x86\xb9\xf1\xd6\x45\x5b\x5a\x33\x7f\x30\xe7\x4a\xb2\xbe\xda\xcd\x03\x23\x0f\x04\x22\xdf\xc2\xbf\x05\xcb\x8e\xe7\xca\xbc\x3f\xff\x9f\xa3\xef\x3a\xe8\x40\x4a\x37\xb6\x7c\xe2\xb8\xa4\x4f\xd9\x1f\xe1\xff\xeb\x75\x6e\xa2\x7c\xd8\x9e\x71\xb1\xf9\x07\x28\x95\x34\x14\x66\xc6\xf4\xa9\x23\x9d\x82\x4d\x71\xb7\xd8\xa3\xec\x8f\xe4\x24\xba\x53\x0d\x11\xa4\x93\x93\xa6\xb3\xcf\x7c\xaa\xec\x8f\xa4\xab\x3c\x00\xa0\x2b\x5d\x97\x24\x4f\xe1\xfe\x88\xa3\x6d\x4e\xba\xae\xb5\x3b\xab\xae\x34\x59\xfb\x78\xe8\xec\x5f\xbc\x4d\xc3\x9c\x44\xf1\xea\xfb\xf8\xc4\x85\x6f\x5e\xa4\x42\x78\x62\x1b\x8a\xd6\xf7\x76\x85\x86\x4f\x9c\x06\x00\x80\x25\x35\x75\x96\x81\x6a\xe7\xf6\xff\x02\x00\x00\xff\xff\x21\xeb\x41\x71\xb1\x04\x00\x00") + +func static_icon_192_png() ([]byte, error) { + return bindata_read( + _static_icon_192_png, + "static/icon_192.png", + ) +} + +var _static_index_html = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x92\xd1\x6e\xf3\x20\x0c\x85\xef\xff\xa7\xf0\xcf\xfd\xe0\x05\x48\x5e\xa5\xa2\xe0\x24\x4e\x09\x44\xb1\x13\x35\xab\xfa\xee\x13\xa3\xdd\x26\x65\xd2\xa6\x5d\x61\x7c\xce\xe7\x83\x90\xed\xff\x90\xbd\xec\x33\xc2\x20\x53\x6c\xff\xd9\x7a\x00\xd8\x01\x5d\x28\x05\x80\x15\x92\x88\xed\xed\x06\xfa\xbd\x82\xfb\xdd\x9a\xda\xab\x7a\xa4\x74\x81\x05\x63\xa3\x26\x97\xa8\x43\x16\x05\xc3\x82\xdd\xe7\x5d\x8f\x9c\x93\x3a\xd8\xc9\xe7\xa4\xa0\xc4\x37\x8a\x26\xd7\xa3\x99\x53\xff\x64\x3b\xb7\x15\x5d\x97\xd6\x81\x64\xd9\x23\xf2\x80\xf8\x11\xa5\x8d\x67\x36\x94\x02\x5e\xb5\x67\x56\x60\x7e\x0d\x5d\x05\x97\xe9\x4f\xd0\xc9\xaf\x2c\x79\xa2\x57\xfc\x82\x5b\xf3\xfc\x3b\x7b\xce\x61\x7f\x4c\x0c\xb4\x01\x85\x46\x15\x8c\x92\x8b\xaa\xb5\x26\xd0\xf6\x50\xd9\x2f\x34\x0b\xf0\xe2\xcb\x7c\xb7\xca\x70\x92\x7c\xc1\xa4\x47\x2e\xc6\x2a\x7f\xeb\xf5\x39\x75\xd4\xff\xec\x1b\xd9\xf4\x59\x64\x7f\x39\xaf\x29\x44\x3c\x00\xd6\xd4\xc7\x5a\x53\x57\xe0\x2d\x00\x00\xff\xff\xeb\xaf\xab\x1a\x1a\x02\x00\x00") func static_index_html() ([]byte, error) { return bindata_read( @@ -79,6 +88,15 @@ func static_js_gotty_bundle_js() ([]byte, error) { ) } +var _static_manifest_json = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x74\xce\xcd\x8a\xc3\x20\x10\xc0\xf1\xbb\x4f\x31\xb8\xd7\xdd\x64\x93\x5b\xf2\x2a\xa5\x88\x35\x92\x0c\xf5\x23\xa8\x81\xa6\xe2\xbb\x17\xc7\x53\x29\xf5\xe0\xc1\xdf\x5f\xc7\xcc\x00\x78\xdc\x7c\x48\xc2\x49\xab\xf9\x0c\x3c\x67\xe8\x12\x26\xa3\xa1\x14\xfe\x5b\xfd\xbb\xc4\x24\x43\x12\x47\x30\x95\xbb\xbe\x1d\xa2\xf2\x2e\xf2\x19\x2e\x0c\x00\x20\xd3\x5e\xdb\xa0\x5a\x55\x5d\x0c\xd3\xd8\xed\x6e\xa5\x1b\xc4\xe9\xdc\x69\x08\x5a\xb9\xea\xfe\x8d\x22\x3e\x75\x7d\x90\x0f\xd3\xf8\x18\xa6\x91\x13\x14\x06\x70\xa5\x81\x0b\xc6\xdd\xc8\xb3\x16\x16\x1d\x5a\x69\xfe\x0e\x6c\x7f\x49\x9b\xb6\x5a\x28\x6f\x7c\xa8\xfc\xf3\x4f\xab\xd9\x4d\xaa\xfb\x1a\xfc\xe1\x96\xcf\x80\x15\xf6\x0a\x00\x00\xff\xff\x14\x28\x68\x81\x1b\x01\x00\x00") + +func static_manifest_json() ([]byte, error) { + return bindata_read( + _static_manifest_json, + "static/manifest.json", + ) +} + // Asset loads and returns the asset for the given name. // It returns an error if the asset could not be found or // could not be loaded. @@ -105,8 +123,10 @@ var _bindata = map[string]func() ([]byte, error){ "static/css/xterm.css": static_css_xterm_css, "static/css/xterm_customize.css": static_css_xterm_customize_css, "static/favicon.png": static_favicon_png, + "static/icon_192.png": static_icon_192_png, "static/index.html": static_index_html, "static/js/gotty-bundle.js": static_js_gotty_bundle_js, + "static/manifest.json": static_manifest_json, } // AssetDir returns the file names below a certain @@ -156,10 +176,12 @@ var _bintree = &_bintree_t{nil, map[string]*_bintree_t{ "xterm.css": &_bintree_t{static_css_xterm_css, map[string]*_bintree_t{}}, "xterm_customize.css": &_bintree_t{static_css_xterm_customize_css, map[string]*_bintree_t{}}, }}, - "favicon.png": &_bintree_t{static_favicon_png, map[string]*_bintree_t{}}, - "index.html": &_bintree_t{static_index_html, map[string]*_bintree_t{}}, + "favicon.png": &_bintree_t{static_favicon_png, map[string]*_bintree_t{}}, + "icon_192.png": &_bintree_t{static_icon_192_png, map[string]*_bintree_t{}}, + "index.html": &_bintree_t{static_index_html, map[string]*_bintree_t{}}, "js": &_bintree_t{nil, map[string]*_bintree_t{ "gotty-bundle.js": &_bintree_t{static_js_gotty_bundle_js, map[string]*_bintree_t{}}, }}, + "manifest.json": &_bintree_t{static_manifest_json, map[string]*_bintree_t{}}, }}, }} diff --git a/server/handlers.go b/server/handlers.go index 4eb5e02..d4a0ba3 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -169,6 +169,40 @@ func (server *Server) processWSConn(ctx context.Context, conn *websocket.Conn) e } func (server *Server) handleIndex(w http.ResponseWriter, r *http.Request) { + indexVars, err := server.indexVariables(r) + if err != nil { + http.Error(w, "Internal Server Error", 500) + return + } + + indexBuf := new(bytes.Buffer) + err = server.indexTemplate.Execute(indexBuf, indexVars) + if err != nil { + http.Error(w, "Internal Server Error", 500) + return + } + + w.Write(indexBuf.Bytes()) +} + +func (server *Server) handleManifest(w http.ResponseWriter, r *http.Request) { + indexVars, err := server.indexVariables(r) + if err != nil { + http.Error(w, "Internal Server Error", 500) + return + } + + indexBuf := new(bytes.Buffer) + err = server.manifestTemplate.Execute(indexBuf, indexVars) + if err != nil { + http.Error(w, "Internal Server Error", 500) + return + } + + w.Write(indexBuf.Bytes()) +} + +func (server *Server) indexVariables(r *http.Request) (map[string]interface{}, error) { titleVars := server.titleVariables( []string{"server", "master"}, map[string]map[string]interface{}{ @@ -182,22 +216,13 @@ func (server *Server) handleIndex(w http.ResponseWriter, r *http.Request) { titleBuf := new(bytes.Buffer) err := server.titleTemplate.Execute(titleBuf, titleVars) if err != nil { - http.Error(w, "Internal Server Error", 500) - return + return nil, err } indexVars := map[string]interface{}{ "title": titleBuf.String(), } - - indexBuf := new(bytes.Buffer) - err = server.indexTemplate.Execute(indexBuf, indexVars) - if err != nil { - http.Error(w, "Internal Server Error", 500) - return - } - - w.Write(indexBuf.Bytes()) + return indexVars, err } func (server *Server) handleAuthToken(w http.ResponseWriter, r *http.Request) { diff --git a/server/server.go b/server/server.go index 434e1d3..b4253cf 100644 --- a/server/server.go +++ b/server/server.go @@ -28,9 +28,10 @@ type Server struct { factory Factory options *Options - upgrader *websocket.Upgrader - indexTemplate *template.Template - titleTemplate *noesctmpl.Template + upgrader *websocket.Upgrader + indexTemplate *template.Template + titleTemplate *noesctmpl.Template + manifestTemplate *template.Template } // New creates a new instance of Server. @@ -52,6 +53,15 @@ func New(factory Factory, options *Options) (*Server, error) { panic("index template parse failed") // must be valid } + manifestData, err := Asset("static/manifest.json") + if err != nil { + panic("manifest not found") // must be in bindata + } + manifestTemplate, err := template.New("manifest").Parse(string(manifestData)) + if err != nil { + panic("manifest template parse failed") // must be valid + } + titleTemplate, err := noesctmpl.New("title").Parse(options.TitleFormat) if err != nil { return nil, errors.Wrapf(err, "failed to parse window title format `%s`", options.TitleFormat) @@ -78,8 +88,9 @@ func New(factory Factory, options *Options) (*Server, error) { Subprotocols: webtty.Protocols, CheckOrigin: originChekcer, }, - indexTemplate: indexTemplate, - titleTemplate: titleTemplate, + indexTemplate: indexTemplate, + titleTemplate: titleTemplate, + manifestTemplate: manifestTemplate, }, nil } @@ -190,7 +201,9 @@ func (server *Server) setupHandlers(ctx context.Context, cancel context.CancelFu siteMux.Handle(pathPrefix+"js/", http.StripPrefix(pathPrefix, staticFileHandler)) siteMux.Handle(pathPrefix+"favicon.png", http.StripPrefix(pathPrefix, staticFileHandler)) siteMux.Handle(pathPrefix+"css/", http.StripPrefix(pathPrefix, staticFileHandler)) + siteMux.Handle(pathPrefix+"icon_192.png", http.StripPrefix(pathPrefix, staticFileHandler)) + siteMux.HandleFunc(pathPrefix+"manifest.json", server.handleManifest) siteMux.HandleFunc(pathPrefix+"auth_token.js", server.handleAuthToken) siteMux.HandleFunc(pathPrefix+"config.js", server.handleConfig)