From 906f2258990a957f450c69e9a4b0aa71f0bc0d07 Mon Sep 17 00:00:00 2001 From: Vasile Popescu Date: Sat, 12 May 2018 18:36:08 +0200 Subject: [PATCH] Refactor the frontend structure Change the Makefile and webpack config to reflect the new structure of where we keep the frontend generated files. Use the 404 static pages when requests are made to paths not found. Add a small doc describing the available http routes. --- .gitignore | 2 +- Makefile | 25 ++-- doc/http_routes.md | 10 ++ frontend/package.json | 9 +- .../{index.html => tty-receiver.in.html} | 2 +- frontend/webpack.config.js | 37 ++++-- tty-server/server.go | 116 +++++++++++------- 7 files changed, 128 insertions(+), 73 deletions(-) create mode 100644 doc/http_routes.md rename frontend/templates/{index.html => tty-receiver.in.html} (89%) diff --git a/.gitignore b/.gitignore index ead350d..26ee814 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -public/ +frontend/public/ output.log **/node_modules/ .DS_Store diff --git a/Makefile b/Makefile index d0be0b9..07fead2 100644 --- a/Makefile +++ b/Makefile @@ -9,44 +9,33 @@ TTY_SENDER=tty_sender TTY_SERVER_SRC=$(filter-out ./tty-server/assets_bundle.go, $(wildcard ./tty-server/*.go)) ./tty-server/assets_bundle.go TTY_SENDER_SRC=$(wildcard ./tty-sender/*.go) EXTRA_BUILD_DEPS=$(wildcard ./common/*go) -TTY_SERVER_ASSETS=$(addprefix ./public/templates/,$(notdir $(wildcard ./frontend/templates/*))) public/bundle.js +TTY_SERVER_ASSETS=$(wildcard frontend/public/*) all: $(TTY_SERVER) $(TTY_SENDER) @echo "All done" -$(TTY_SERVER): $(TTY_SERVER_SRC) $(EXTRA_BUILD_DEPS) $(TTY_SERVER_ASSETS) +$(TTY_SERVER): $(TTY_SERVER_SRC) $(EXTRA_BUILD_DEPS) go build -o $@ $(TTY_SERVER_SRC) $(TTY_SENDER): $(TTY_SENDER_SRC) $(EXTRA_BUILD_DEPS) go build -o $@ $(TTY_SENDER_SRC) -# TODO: perhaps replace all these paths with variables? -frontend/bundle.js: $(wildcard ./frontend/src/*) - cd frontend && npm run build && cd - - -public/bundle.js: frontend/bundle.js - mkdir -p $(dir $@) - cp $^ $@ - -public/templates/%: frontend/templates/% - mkdir -p $(dir $@) - cp $^ $@ - tty-server/assets_bundle.go: $(TTY_SERVER_ASSETS) - go-bindata --prefix public -o $@ $^ + go-bindata --prefix frontend/public/ -o $@ $^ frontend: FORCE cd frontend && npm run build && cd - clean: - @rm -f $(TTY_SERVER) $(TTY_SENDER) + rm -f $(TTY_SERVER) $(TTY_SENDER) + rm -fr frontend/out/ @echo "Cleaned" runs: $(TTY_SERVER) - @./$(TTY_SERVER) --url http://localhost:9090 --web_address :9090 --sender_address :7654 + ./$(TTY_SERVER) --url http://localhost:9090 --web_address :9090 --sender_address :7654 runc: $(TTY_SENDER) - @./$(TTY_SENDER) --logfile output.log + ./$(TTY_SENDER) --logfile output.log --useTLS=false test: @go test github.com/elisescu/tty-share/testing -v diff --git a/doc/http_routes.md b/doc/http_routes.md new file mode 100644 index 0000000..1325922 --- /dev/null +++ b/doc/http_routes.md @@ -0,0 +1,10 @@ +Routes +====== + +These are the routes the server will listen to: + +* `/` - the main page which probably will be a redirect +* `/ws/` - will serve the websockets session +* `/s/` - will serve the tty-receiver webpage, which will make some further requests for + the resources +* `/static/` - serving the static resources: 404 page, js and css files diff --git a/frontend/package.json b/frontend/package.json index 76459ad..1f75f48 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,9 +4,8 @@ "description": "", "main": "", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "build": "webpack", - "watch": "webpack --watch --hot" + "watch": "TTY_SHARE_ENV=development webpack --watch", + "build": "TTY_SHARE_ENV=production webpack" }, "author": "", "license": "", @@ -24,7 +23,9 @@ "source-map-loader": "0.2.2", "style-loader": "0.19.0", "webpack": "3.7.1", - "webpack-dev-server": "2.9.1", "xterm": "2.9.2" + }, + "devDependencies": { + "copy-webpack-plugin": "4.5.1" } } diff --git a/frontend/templates/index.html b/frontend/templates/tty-receiver.in.html similarity index 89% rename from frontend/templates/index.html rename to frontend/templates/tty-receiver.in.html index a1a6179..2fc2e5d 100644 --- a/frontend/templates/index.html +++ b/frontend/templates/tty-receiver.in.html @@ -15,7 +15,7 @@ } console.log("Initial data", window.ttyInitialData) - + diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js index 985758e..e9dc1b8 100644 --- a/frontend/webpack.config.js +++ b/frontend/webpack.config.js @@ -1,11 +1,16 @@ -var path = require('path'); -module.exports = { - entry: './src/main.js', +const webpack = require("webpack"); +const copyWebpackPlugin = require('copy-webpack-plugin') + +const develBuild = process.env.TTY_SHARE_ENV === 'development'; + +let mainConfig = { + entry: { + 'tty-receiver': './tty-receiver/main.js', + }, output: { - path: __dirname, - filename: 'bundle.js' + path: __dirname + '/public/', + filename: '[name].js', }, - devtool: 'inline-source-map', module: { rules: [ { @@ -47,5 +52,21 @@ module.exports = { use: ['source-map-loader'] } ] - } -}; \ No newline at end of file + }, + plugins: [ + new copyWebpackPlugin([ + 'static', + 'templates', + ], { + debug: 'info', + }), + ], +}; + +if (develBuild) { + mainConfig.devtool = 'inline-source-map'; +} else { + mainConfig.plugins.push(new webpack.optimize.UglifyJsPlugin( { minimize: true })); +} + +module.exports = mainConfig; diff --git a/tty-server/server.go b/tty-server/server.go index d850b7a..9db011c 100644 --- a/tty-server/server.go +++ b/tty-server/server.go @@ -2,16 +2,24 @@ package main import ( "errors" - "fmt" "html/template" + "mime" "net" "net/http" + "os" + "path/filepath" "sync" "github.com/gorilla/mux" "github.com/gorilla/websocket" ) +const ( + errorInvalidSession = iota + errorNotFound = iota + errorNotAllowed = iota +) + var log = MainLogger // SessionTemplateModel used for templating @@ -41,6 +49,35 @@ type TTYProxyServer struct { activeSessionsRWLock sync.RWMutex } +func (server *TTYProxyServer) serveContent(w http.ResponseWriter, r *http.Request, name string) { + // If a path to the frontend resources was passed, serve from there, otherwise, serve from the + // builtin bundle + if server.config.FrontendPath == "" { + file, err := Asset(name) + + if err != nil { + w.WriteHeader(http.StatusNotFound) + return + } + + ctype := mime.TypeByExtension(filepath.Ext(name)) + if ctype == "" { + ctype = http.DetectContentType(file) + } + w.Header().Set("Content-Type", ctype) + w.Write(file) + } else { + _, err := os.Open(server.config.FrontendPath + string(os.PathSeparator) + name) + + if err != nil { + w.WriteHeader(http.StatusNotFound) + return + } + + http.ServeFile(w, r, name) + } +} + // NewTTYProxyServer creates a new instance func NewTTYProxyServer(config TTYProxyServerConfig) (server *TTYProxyServer) { server = &TTYProxyServer{ @@ -51,29 +88,22 @@ func NewTTYProxyServer(config TTYProxyServerConfig) (server *TTYProxyServer) { } routesHandler := mux.NewRouter() - if config.FrontendPath != "" { - routesHandler.PathPrefix("/static/").Handler(http.StripPrefix("/static/", - http.FileServer(http.Dir(config.FrontendPath)))) - } else { - // Serve the bundled assets - routesHandler.PathPrefix("/static/").Handler(http.StripPrefix("/static/", - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - data, err := Asset(r.URL.Path) - if err != nil { - w.WriteHeader(http.StatusNotFound) - return - } - w.Write(data) - log.Infof("Delivered %s from the bundle", r.URL.Path) - }))) - } + routesHandler.PathPrefix("/static/").Handler(http.StripPrefix("/static/", + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + server.serveContent(w, r, r.URL.Path) + }))) - routesHandler.HandleFunc("/", defaultHandler) + routesHandler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + server.serveContent(w, r, "index.html") + }) routesHandler.HandleFunc("/s/{sessionID}", func(w http.ResponseWriter, r *http.Request) { - sessionsHandler(server, w, r) + server.handleSession(w, r) }) routesHandler.HandleFunc("/ws/{sessionID}", func(w http.ResponseWriter, r *http.Request) { - websocketHandler(server, w, r) + server.handleWebsocket(w, r) + }) + routesHandler.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + server.serveContent(w, r, "404.html") }) server.activeSessions = make(map[string]*ttyShareSession) @@ -85,14 +115,14 @@ func getWSPath(sessionID string) string { return "/ws/" + sessionID } -func websocketHandler(server *TTYProxyServer, w http.ResponseWriter, r *http.Request) { +func (server *TTYProxyServer) handleWebsocket(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) sessionID := vars["sessionID"] defer log.Debug("Finished WS connection for ", sessionID) // Validate incoming request. if r.Method != "GET" { - w.WriteHeader(http.StatusMethodNotAllowed) + w.WriteHeader(http.StatusForbidden) return } @@ -112,20 +142,14 @@ func websocketHandler(server *TTYProxyServer, w http.ResponseWriter, r *http.Req if session == nil { log.Error("WE connection for invalid sessionID: ", sessionID, ". Killing it.") - // TODO: Create a proper way to communicate with the remote WS end, so that the server can send - // control messages or data messages to go directly to the terminal. - conn.WriteMessage(websocket.TextMessage, []byte("$ access denied.")) + w.WriteHeader(http.StatusForbidden) return } session.HandleReceiver(newWSConnection(conn)) } -func defaultHandler(http.ResponseWriter, *http.Request) { - log.Debug("Default handler ") -} - -func sessionsHandler(server *TTYProxyServer, w http.ResponseWriter, r *http.Request) { +func (server *TTYProxyServer) handleSession(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) sessionID := vars["sessionID"] @@ -133,31 +157,41 @@ func sessionsHandler(server *TTYProxyServer, w http.ResponseWriter, r *http.Requ session := getSession(server, sessionID) + // No valid session with this ID if session == nil { - w.WriteHeader(http.StatusMethodNotAllowed) + server.serveContent(w, r, "invalid-session.html") return } - templateDta, err := Asset("templates/index.html") + var t *template.Template + var err error + if server.config.FrontendPath == "" { + templateDta, err := Asset("tty-receiver.in.html") - if err != nil { - fmt.Fprintf(w, err.Error()) - return - } + if err != nil { + panic("Cannot find the tty-receiver html template") + } - t := template.New("index.html") - _, err = t.Parse(string(templateDta)) + t = template.New("tty-receiver.html") + _, err = t.Parse(string(templateDta)) + } else { + t, err = template.ParseFiles(server.config.FrontendPath + string(os.PathSeparator) + "tty-receiver.in.html") + } if err != nil { - fmt.Fprintf(w, err.Error()) - return + panic("Cannot parse the tty-receiver html template") } + templateModel := SessionTemplateModel{ SessionID: sessionID, Salt: "salt&pepper", WSPath: getWSPath(sessionID), } - t.Execute(w, templateModel) + err = t.Execute(w, templateModel) + + if err != nil { + panic("Cannot execute the tty-receiver html template") + } } func addNewSession(server *TTYProxyServer, session *ttyShareSession) {