summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--assets/templates/_base.template.html11
-rw-r--r--assets/templates/authenticate.template.html3
-rw-r--r--assets/templates/information.template.html3
-rw-r--r--cmd/portgate.go5
-rw-r--r--handlers/handler.go56
-rw-r--r--handlers/portgate.go45
-rw-r--r--path.go10
-rw-r--r--templates.go55
8 files changed, 165 insertions, 23 deletions
diff --git a/assets/templates/_base.template.html b/assets/templates/_base.template.html
new file mode 100644
index 0000000..2ed25d1
--- /dev/null
+++ b/assets/templates/_base.template.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Portgate</title>
+</head>
+<body>
+    <h2>Portgate</h2>
+    {{template "content"}}
+</body>
+</html>
\ No newline at end of file
diff --git a/assets/templates/authenticate.template.html b/assets/templates/authenticate.template.html
new file mode 100644
index 0000000..21f31e4
--- /dev/null
+++ b/assets/templates/authenticate.template.html
@@ -0,0 +1,3 @@
+{{define "content"}}
+<h1>This is the authentication page...</h1>
+{{end}}
\ No newline at end of file
diff --git a/assets/templates/information.template.html b/assets/templates/information.template.html
new file mode 100644
index 0000000..e87885c
--- /dev/null
+++ b/assets/templates/information.template.html
@@ -0,0 +1,3 @@
+{{define "content"}}
+<h1>This is the information page...</h1>
+{{end}}
\ No newline at end of file
diff --git a/cmd/portgate.go b/cmd/portgate.go
index afa756a..2bc0693 100644
--- a/cmd/portgate.go
+++ b/cmd/portgate.go
@@ -17,8 +17,11 @@ func main() {
 		log.Fatal("Failed to get Portgate config.")
 	}
 
+	// Load and Parse all Portgate templates
+	templates, _ := portgate.LoadAllTemplates()
+
 	// Create handler for requests
-	handler := handlers.NewRequestHandler(&config)
+	handler := handlers.NewRequestHandler(&config, templates)
 
 	// Start to listen to the outside world.
 	log.Print("Listening for requests on port 8080.")
diff --git a/handlers/handler.go b/handlers/handler.go
index 6484c61..56801a3 100644
--- a/handlers/handler.go
+++ b/handlers/handler.go
@@ -5,6 +5,7 @@ import (
 	"github.com/valyala/fasthttp"
 	"net/http"
 	"portgate"
+	"strings"
 )
 
 // RequestHandler keeps data relevant to the request handlers.
@@ -13,12 +14,29 @@ type RequestHandler struct {
 	config *portgate.Config
 	// HTTP Client for requesting resources from the destination host.
 	client fasthttp.Client
+	// Handler for static Portgate assets.
+	staticHandler fasthttp.RequestHandler
+	// Templates for Portgate pages.
+	templates portgate.Templates
 }
 
-func NewRequestHandler(config *portgate.Config) RequestHandler {
+// NewRequestHandler creates a new RequestHandler instance.
+func NewRequestHandler(config *portgate.Config, templates portgate.Templates) RequestHandler {
+	// Serves static Portgate files when called.
+	fs := fasthttp.FS{
+		Root: "./assets/static/",
+		PathRewrite: func(ctx *fasthttp.RequestCtx) []byte {
+			return []byte(strings.TrimPrefix(string(ctx.Path()), "/_portgate/static"))
+		},
+		PathNotFound: nil,
+	}
+	staticHandler := fs.NewRequestHandler()
+
 	return RequestHandler{
-		config: config,
-		client: fasthttp.Client{},
+		config:        config,
+		client:        fasthttp.Client{},
+		staticHandler: staticHandler,
+		templates:     templates,
 	}
 }
 
@@ -26,24 +44,26 @@ func NewRequestHandler(config *portgate.Config) RequestHandler {
 func (h *RequestHandler) HandleRequest(ctx *fasthttp.RequestCtx) {
 	path := portgate.ParsePath(string(ctx.Path()))
 
+	if path.IsPortgatePath() {
+		h.handlePortgateRequest(ctx, path)
+		return
+	}
+
 	if path.DestinationIdentifier == -1 {
-		// We were not given a port.
+		// We were not given a destination.
 
-		if path.ResourcePath == "/_portgate" {
-			h.handlePortgateRequest(ctx)
+		// Try to grab actual destination from Referer header.
+		// This can help us if the user followed an absolute link on a proxied page.
+		refererPath, err := portgate.ParsePathFromReferer(path, string(ctx.Request.Header.Referer()))
+		if err != nil || refererPath.DestinationIdentifier == -1 {
+			// The referer path also has no destination
+			h.handleUnknownRequest(ctx)
 		} else {
-			// Try to grab actual destination from Referer header.
-			refererPath, err := portgate.ParsePathFromReferer(path, string(ctx.Request.Header.Referer()))
-			if err != nil || refererPath.DestinationIdentifier == -1 {
-				// The referer path also has no destination
-				h.handleUnknownRequest(ctx)
-			} else {
-				// We found the destination from the referer path, so we
-				// redirect the user to the Portgate URL they should've requested.
-
-				portgateUrl := fmt.Sprintf("/%d%s", refererPath.DestinationIdentifier, refererPath.ResourcePath)
-				ctx.Redirect(portgateUrl, http.StatusTemporaryRedirect)
-			}
+			// We found the destination from the referer path, so we
+			// redirect the user to the Portgate URL they should've requested.
+
+			portgateUrl := fmt.Sprintf("/%d%s", refererPath.DestinationIdentifier, refererPath.ResourcePath)
+			ctx.Redirect(portgateUrl, http.StatusTemporaryRedirect)
 		}
 	} else {
 		// We were given a port, so we have to pass the request through to the destination host.
diff --git a/handlers/portgate.go b/handlers/portgate.go
index 9d4f3ef..1cd9bb8 100644
--- a/handlers/portgate.go
+++ b/handlers/portgate.go
@@ -1,10 +1,47 @@
 package handlers
 
-import "github.com/valyala/fasthttp"
+import (
+	"github.com/valyala/fasthttp"
+	"net/http"
+	"portgate"
+)
 
 // handlePortgateRequest handles all Portgate specific request for either showing Portgate
 // specific pages or handling creation of authorization tokens.
-func (h *RequestHandler) handlePortgateRequest(ctx *fasthttp.RequestCtx) {
-	// TODO: Implement authentication, authorization
-	_, _ = ctx.WriteString("Portgate request.")
+func (h *RequestHandler) handlePortgateRequest(ctx *fasthttp.RequestCtx, path portgate.Path) {
+	if path.IsPortgateStaticPath() {
+		h.staticHandler(ctx)
+	} else {
+		// TODO: Implement authentication, authorization
+		h.handlePortgateIndexRequest(ctx)
+	}
+}
+
+// handlePortgateIndexRequest delegates requests directed at /_portgate.
+func (h *RequestHandler) handlePortgateIndexRequest(ctx *fasthttp.RequestCtx) {
+	switch string(ctx.Method()) {
+	case "GET":
+		// If we received a GET request, the user wants to see either the login page,
+		// or an info page if they're already authenticated.
+		h.handlePortgatePageRequest(ctx)
+	case "POST":
+		// If we received a POST request, the user wants to get authorization to Portgate.
+		h.handleAuthenticateRequest(ctx)
+	}
+}
+
+// handlePortgatePageRequest renders the Portgate page with either the authentication page or
+// a basic information page.
+func (h *RequestHandler) handlePortgatePageRequest(ctx *fasthttp.RequestCtx) {
+	// We render the page template and pass it to the user.
+	ctx.Response.Header.SetContentType("text/html")
+	err := h.templates.ExecuteTemplate(ctx, "authenticate.template.html", nil)
+	if err != nil {
+		ctx.SetStatusCode(http.StatusInternalServerError)
+		_, _ = ctx.WriteString("An error occurred.")
+	}
+}
+
+func (h *RequestHandler) handleAuthenticateRequest(ctx *fasthttp.RequestCtx) {
+	// TODO
 }
diff --git a/path.go b/path.go
index 3aa7816..82a8353 100644
--- a/path.go
+++ b/path.go
@@ -68,3 +68,13 @@ func ParsePathFromReferer(p Path, r string) (Path, error) {
 		ResourcePath:          p.ResourcePath,
 	}, nil
 }
+
+// IsPortgatePath returns whether the Path goes to Portgate.
+func (p *Path) IsPortgatePath() bool {
+	return p.DestinationIdentifier == -1 && strings.HasPrefix(p.ResourcePath, "/_portgate")
+}
+
+// IsPortgateStaticPath returns whether the Path goes to the Portgate static files.
+func (p *Path) IsPortgateStaticPath() bool {
+	return p.DestinationIdentifier == -1 && strings.HasPrefix(p.ResourcePath, "/_portgate/static")
+}
diff --git a/templates.go b/templates.go
new file mode 100644
index 0000000..25cb480
--- /dev/null
+++ b/templates.go
@@ -0,0 +1,55 @@
+package portgate
+
+import (
+	"errors"
+	"html/template"
+	"io"
+	"io/fs"
+	"path/filepath"
+	"strings"
+)
+
+// Templates houses all the Portgate page templates and can render them with correct styling.
+type Templates struct {
+	templateMap map[string]*template.Template
+}
+
+// LoadAllTemplates loads all Portgate templates from ./assets/templates/ and bundles them
+// into a single Templates instance.
+func LoadAllTemplates() (Templates, error) {
+	templateMap := make(map[string]*template.Template)
+
+	// We walk through every file in the templates folder.
+	err := filepath.Walk("./assets/templates", func(path string, info fs.FileInfo, err error) error {
+		// We only care about files which are actual templates and are not of special use (like "_base")
+		if !strings.HasPrefix(info.Name(), "_") && strings.HasSuffix(path, ".template.html") {
+			// We bundle the templates together with the base template so that we can render them together later.
+			t, err := template.ParseFiles("./assets/templates/_base.template.html", path)
+			if err == nil {
+				// We keep the parsed template in the templateMap by it's filename.
+				templateMap[info.Name()] = t
+			}
+		}
+
+		return err
+	})
+
+	if err != nil {
+		return Templates{}, err
+	}
+
+	return Templates{
+		templateMap: templateMap,
+	}, nil
+}
+
+// ExecuteTemplate executes a single template with the given name and data and writes it to the given
+// io.Writer, which is usually fasthttp.RequestCtx.
+func (templates *Templates) ExecuteTemplate(w io.Writer, name string, data interface{}) error {
+	t := templates.templateMap[name]
+	if t == nil {
+		return errors.New("Unknown template name: " + name)
+	}
+
+	return t.ExecuteTemplate(w, "_base.template.html", data)
+}