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.
pull/14/merge
Vasile Popescu 6 years ago
parent f8e90e0e04
commit 906f225899

2
.gitignore vendored

@ -1,4 +1,4 @@
public/ frontend/public/
output.log output.log
**/node_modules/ **/node_modules/
.DS_Store .DS_Store

@ -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_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) TTY_SENDER_SRC=$(wildcard ./tty-sender/*.go)
EXTRA_BUILD_DEPS=$(wildcard ./common/*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) all: $(TTY_SERVER) $(TTY_SENDER)
@echo "All done" @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) go build -o $@ $(TTY_SERVER_SRC)
$(TTY_SENDER): $(TTY_SENDER_SRC) $(EXTRA_BUILD_DEPS) $(TTY_SENDER): $(TTY_SENDER_SRC) $(EXTRA_BUILD_DEPS)
go build -o $@ $(TTY_SENDER_SRC) 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) tty-server/assets_bundle.go: $(TTY_SERVER_ASSETS)
go-bindata --prefix public -o $@ $^ go-bindata --prefix frontend/public/ -o $@ $^
frontend: FORCE frontend: FORCE
cd frontend && npm run build && cd - cd frontend && npm run build && cd -
clean: clean:
@rm -f $(TTY_SERVER) $(TTY_SENDER) rm -f $(TTY_SERVER) $(TTY_SENDER)
rm -fr frontend/out/
@echo "Cleaned" @echo "Cleaned"
runs: $(TTY_SERVER) 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) runc: $(TTY_SENDER)
@./$(TTY_SENDER) --logfile output.log ./$(TTY_SENDER) --logfile output.log --useTLS=false
test: test:
@go test github.com/elisescu/tty-share/testing -v @go test github.com/elisescu/tty-share/testing -v

@ -0,0 +1,10 @@
Routes
======
These are the routes the server will listen to:
* `/` - the main page which probably will be a redirect
* `/ws/<session id>` - will serve the websockets session
* `/s/<session id>` - 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

@ -4,9 +4,8 @@
"description": "", "description": "",
"main": "", "main": "",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "watch": "TTY_SHARE_ENV=development webpack --watch",
"build": "webpack", "build": "TTY_SHARE_ENV=production webpack"
"watch": "webpack --watch --hot"
}, },
"author": "", "author": "",
"license": "", "license": "",
@ -24,7 +23,9 @@
"source-map-loader": "0.2.2", "source-map-loader": "0.2.2",
"style-loader": "0.19.0", "style-loader": "0.19.0",
"webpack": "3.7.1", "webpack": "3.7.1",
"webpack-dev-server": "2.9.1",
"xterm": "2.9.2" "xterm": "2.9.2"
},
"devDependencies": {
"copy-webpack-plugin": "4.5.1"
} }
} }

@ -15,7 +15,7 @@
} }
console.log("Initial data", window.ttyInitialData) console.log("Initial data", window.ttyInitialData)
</script> </script>
<script src="/static/bundle.js"></script> <script src="/static/tty-receiver.js"></script>
</body> </body>
</html> </html>

@ -1,11 +1,16 @@
var path = require('path'); const webpack = require("webpack");
module.exports = { const copyWebpackPlugin = require('copy-webpack-plugin')
entry: './src/main.js',
const develBuild = process.env.TTY_SHARE_ENV === 'development';
let mainConfig = {
entry: {
'tty-receiver': './tty-receiver/main.js',
},
output: { output: {
path: __dirname, path: __dirname + '/public/',
filename: 'bundle.js' filename: '[name].js',
}, },
devtool: 'inline-source-map',
module: { module: {
rules: [ rules: [
{ {
@ -47,5 +52,21 @@ module.exports = {
use: ['source-map-loader'] use: ['source-map-loader']
} }
] ]
} },
}; 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;

@ -2,16 +2,24 @@ package main
import ( import (
"errors" "errors"
"fmt"
"html/template" "html/template"
"mime"
"net" "net"
"net/http" "net/http"
"os"
"path/filepath"
"sync" "sync"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
) )
const (
errorInvalidSession = iota
errorNotFound = iota
errorNotAllowed = iota
)
var log = MainLogger var log = MainLogger
// SessionTemplateModel used for templating // SessionTemplateModel used for templating
@ -41,6 +49,35 @@ type TTYProxyServer struct {
activeSessionsRWLock sync.RWMutex 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 // NewTTYProxyServer creates a new instance
func NewTTYProxyServer(config TTYProxyServerConfig) (server *TTYProxyServer) { func NewTTYProxyServer(config TTYProxyServerConfig) (server *TTYProxyServer) {
server = &TTYProxyServer{ server = &TTYProxyServer{
@ -51,29 +88,22 @@ func NewTTYProxyServer(config TTYProxyServerConfig) (server *TTYProxyServer) {
} }
routesHandler := mux.NewRouter() routesHandler := mux.NewRouter()
if config.FrontendPath != "" { routesHandler.PathPrefix("/static/").Handler(http.StripPrefix("/static/",
routesHandler.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.FileServer(http.Dir(config.FrontendPath)))) server.serveContent(w, r, r.URL.Path)
} 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.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) { 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) { 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) server.activeSessions = make(map[string]*ttyShareSession)
@ -85,14 +115,14 @@ func getWSPath(sessionID string) string {
return "/ws/" + sessionID 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) vars := mux.Vars(r)
sessionID := vars["sessionID"] sessionID := vars["sessionID"]
defer log.Debug("Finished WS connection for ", sessionID) defer log.Debug("Finished WS connection for ", sessionID)
// Validate incoming request. // Validate incoming request.
if r.Method != "GET" { if r.Method != "GET" {
w.WriteHeader(http.StatusMethodNotAllowed) w.WriteHeader(http.StatusForbidden)
return return
} }
@ -112,20 +142,14 @@ func websocketHandler(server *TTYProxyServer, w http.ResponseWriter, r *http.Req
if session == nil { if session == nil {
log.Error("WE connection for invalid sessionID: ", sessionID, ". Killing it.") 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 w.WriteHeader(http.StatusForbidden)
// control messages or data messages to go directly to the terminal.
conn.WriteMessage(websocket.TextMessage, []byte("$ access denied."))
return return
} }
session.HandleReceiver(newWSConnection(conn)) session.HandleReceiver(newWSConnection(conn))
} }
func defaultHandler(http.ResponseWriter, *http.Request) { func (server *TTYProxyServer) handleSession(w http.ResponseWriter, r *http.Request) {
log.Debug("Default handler ")
}
func sessionsHandler(server *TTYProxyServer, w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
sessionID := vars["sessionID"] sessionID := vars["sessionID"]
@ -133,31 +157,41 @@ func sessionsHandler(server *TTYProxyServer, w http.ResponseWriter, r *http.Requ
session := getSession(server, sessionID) session := getSession(server, sessionID)
// No valid session with this ID
if session == nil { if session == nil {
w.WriteHeader(http.StatusMethodNotAllowed) server.serveContent(w, r, "invalid-session.html")
return 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 { if err != nil {
fmt.Fprintf(w, err.Error()) panic("Cannot find the tty-receiver html template")
return }
}
t := template.New("index.html") t = template.New("tty-receiver.html")
_, err = t.Parse(string(templateDta)) _, err = t.Parse(string(templateDta))
} else {
t, err = template.ParseFiles(server.config.FrontendPath + string(os.PathSeparator) + "tty-receiver.in.html")
}
if err != nil { if err != nil {
fmt.Fprintf(w, err.Error()) panic("Cannot parse the tty-receiver html template")
return
} }
templateModel := SessionTemplateModel{ templateModel := SessionTemplateModel{
SessionID: sessionID, SessionID: sessionID,
Salt: "salt&pepper", Salt: "salt&pepper",
WSPath: getWSPath(sessionID), 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) { func addNewSession(server *TTYProxyServer, session *ttyShareSession) {

Loading…
Cancel
Save