about summary refs log tree commit diff stats
path: root/internal/server
diff options
context:
space:
mode:
authorAlan Pearce2024-05-15 12:44:03 +0200
committerAlan Pearce2024-05-15 12:44:03 +0200
commit2d5ee7b69cbe27e7e1abef7bf3451cb6455c4387 (patch)
treec0d321d6a9f1ea9de1447742175261ed231a3590 /internal/server
parent149cbe7681607eb48f47df14c7a39e6f289fb7b6 (diff)
downloadsearchix-2d5ee7b69cbe27e7e1abef7bf3451cb6455c4387.tar.lz
searchix-2d5ee7b69cbe27e7e1abef7bf3451cb6455c4387.tar.zst
searchix-2d5ee7b69cbe27e7e1abef7bf3451cb6455c4387.zip
feat: render HTML error pages
Diffstat (limited to 'internal/server')
-rw-r--r--internal/server/error.go43
-rw-r--r--internal/server/mux.go30
2 files changed, 61 insertions, 12 deletions
diff --git a/internal/server/error.go b/internal/server/error.go
new file mode 100644
index 0000000..51453f4
--- /dev/null
+++ b/internal/server/error.go
@@ -0,0 +1,43 @@
+package server
+
+import (
+	"log/slog"
+	"net/http"
+	"searchix/internal/config"
+)
+
+func createErrorHandler(
+	config *config.Config,
+) func(http.ResponseWriter, *http.Request, string, int) {
+	return func(w http.ResponseWriter, r *http.Request, message string, code int) {
+		var err error
+		if message == "" {
+			message = http.StatusText(code)
+		}
+		indexData := TemplateData{
+			ExtraHeadHTML: config.Web.ExtraHeadHTML,
+			Sources:       config.Importer.Sources,
+			Version:       *versionInfo,
+			Code:          code,
+			Message:       message,
+		}
+		w.WriteHeader(code)
+		if r.Header.Get("Fetch") == "true" {
+			err = templates["error"].ExecuteTemplate(w, "main", indexData)
+		} else {
+			err = templates["error"].Execute(w, indexData)
+		}
+		if err != nil {
+			slog.Error(
+				"error rendering error page template",
+				"error",
+				err,
+				"code",
+				code,
+				"message",
+				message,
+			)
+			http.Error(w, message, code)
+		}
+	}
+}
diff --git a/internal/server/mux.go b/internal/server/mux.go
index aa2c1a6..c1f36fe 100644
--- a/internal/server/mux.go
+++ b/internal/server/mux.go
@@ -48,6 +48,8 @@ type TemplateData struct {
 	SourceResult  *bleve.SearchResult
 	ExtraHeadHTML template.HTML
 	Version       VersionInfo
+	Code          int
+	Message       string
 }
 
 type ResultData[T options.NixOption] struct {
@@ -64,6 +66,8 @@ var versionInfo = &VersionInfo{
 	CommitSHA: config.CommitSHA,
 }
 
+var templates TemplateCollection
+
 func applyDevModeOverrides(config *config.Config) {
 	if len(config.Web.ContentSecurityPolicy.ScriptSrc) == 0 {
 		config.Web.ContentSecurityPolicy.ScriptSrc = config.Web.ContentSecurityPolicy.DefaultSrc
@@ -93,14 +97,16 @@ func NewMux(
 		Repanic: true,
 	})
 
-	templates, err := loadTemplates()
+	templates, err = loadTemplates()
 	if err != nil {
 		log.Panicf("could not load templates: %v", err)
 	}
 
+	errorHandler := createErrorHandler(config)
+
 	top := http.NewServeMux()
 	mux := http.NewServeMux()
-	mux.HandleFunc("/{$}", func(w http.ResponseWriter, _ *http.Request) {
+	mux.HandleFunc("/{$}", func(w http.ResponseWriter, r *http.Request) {
 		indexData := TemplateData{
 			ExtraHeadHTML: config.Web.ExtraHeadHTML,
 			Sources:       config.Importer.Sources,
@@ -108,7 +114,7 @@ func NewMux(
 		}
 		err := templates["index"].Execute(w, indexData)
 		if err != nil {
-			http.Error(w, err.Error(), http.StatusInternalServerError)
+			errorHandler(w, r, err.Error(), http.StatusInternalServerError)
 		}
 	})
 
@@ -118,7 +124,7 @@ func NewMux(
 
 		source := config.Importer.Sources[sourceKey]
 		if source == nil {
-			http.Error(w, "Source not found", http.StatusNotFound)
+			errorHandler(w, r, "Source not found", http.StatusNotFound)
 
 			return
 		}
@@ -133,18 +139,18 @@ func NewMux(
 			if pg != "" {
 				page, err = strconv.ParseUint(pg, 10, 64)
 				if err != nil || page == 0 {
-					http.Error(w, "Bad query string", http.StatusBadRequest)
+					errorHandler(w, r, "Bad query string", http.StatusBadRequest)
 				}
 			}
 			results, err := index.Search(ctx, sourceKey, qs, (page-1)*search.ResultsPerPage)
 			if err != nil {
 				if err == context.DeadlineExceeded {
-					http.Error(w, "Search timed out", http.StatusInternalServerError)
+					errorHandler(w, r, "Search timed out", http.StatusInternalServerError)
 
 					return
 				}
 				slog.Error("search error", "error", err)
-				http.Error(w, err.Error(), http.StatusInternalServerError)
+				errorHandler(w, r, err.Error(), http.StatusInternalServerError)
 			}
 
 			tdata := ResultData[options.NixOption]{
@@ -163,13 +169,13 @@ func NewMux(
 			if results.Total > hits {
 				q, err := url.ParseQuery(r.URL.RawQuery)
 				if err != nil {
-					http.Error(w, "Query string error", http.StatusBadRequest)
+					errorHandler(w, r, "Query string error", http.StatusBadRequest)
 
 					return
 				}
 
 				if page*search.ResultsPerPage > results.Total {
-					http.Error(w, "Not found", http.StatusNotFound)
+					errorHandler(w, r, "Not found", http.StatusNotFound)
 
 					return
 				}
@@ -198,12 +204,12 @@ func NewMux(
 			}
 			if err != nil {
 				slog.Error("template error", "template", "options", "error", err)
-				http.Error(w, err.Error(), http.StatusInternalServerError)
+				errorHandler(w, r, err.Error(), http.StatusInternalServerError)
 			}
 		} else {
 			sourceResult, err := index.GetSource(ctx, sourceKey)
 			if err != nil {
-				http.Error(w, err.Error(), http.StatusInternalServerError)
+				errorHandler(w, r, err.Error(), http.StatusInternalServerError)
 
 				return
 			}
@@ -216,7 +222,7 @@ func NewMux(
 				Version:       *versionInfo,
 			})
 			if err != nil {
-				http.Error(w, err.Error(), http.StatusInternalServerError)
+				errorHandler(w, r, err.Error(), http.StatusInternalServerError)
 
 				return
 			}