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
**/node_modules/
.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_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

@ -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": "",
"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"
}
}

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

@ -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']
}
]
}
};
},
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 (
"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) {

Loading…
Cancel
Save