refactor(errors): new error "helpers", following a more golang way
This commit is contained in:
20
internals/router/errors/400.go
Normal file
20
internals/router/errors/400.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package errors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ErrMissingParams struct {
|
||||||
|
defaultErr
|
||||||
|
Params []string `json:"params"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewErrMissingParams(params ...string) ErrMissingParams {
|
||||||
|
return ErrMissingParams{Params: params}
|
||||||
|
}
|
||||||
|
func (e ErrMissingParams) Error() string {
|
||||||
|
return fmt.Sprintf("Missing parameters: %s.", strings.Join(e.Params, ", "))
|
||||||
|
}
|
||||||
|
func (e ErrMissingParams) Status() int { return http.StatusBadRequest }
|
||||||
15
internals/router/errors/500.go
Normal file
15
internals/router/errors/500.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package errors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ErrInternal struct {
|
||||||
|
defaultErr
|
||||||
|
Err string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewErrInternal(err ...error) ErrInternal { return ErrInternal{Err: errors.Join(err...).Error()} }
|
||||||
|
func (e ErrInternal) Error() string { return e.Err }
|
||||||
|
func (e ErrInternal) Status() int { return http.StatusInternalServerError }
|
||||||
90
internals/router/errors/errors.templ
Normal file
90
internals/router/errors/errors.templ
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package errors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"extrovert/templates/layouts"
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Error interface {
|
||||||
|
Error() string
|
||||||
|
Status() int
|
||||||
|
ServeHTTP(w http.ResponseWriter, r *http.Request)
|
||||||
|
Component() templ.Component
|
||||||
|
JSON() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultErr struct{}
|
||||||
|
|
||||||
|
func (e defaultErr) Error() string {
|
||||||
|
return "Error: This method should have been overridden :')"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e defaultErr) Status() int {
|
||||||
|
return http.StatusNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e defaultErr) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if strings.Contains(r.Header.Get("Accept"), "text/html") {
|
||||||
|
w.Header().Set("Content-Type", "text/html")
|
||||||
|
|
||||||
|
err := e.Component().Render(r.Context(), w)
|
||||||
|
if err != nil {
|
||||||
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
w.Write([]byte("Unable to render error message, using JSON representation: " + e.JSON()))
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
_, err := w.Write([]byte(e.JSON()))
|
||||||
|
if err != nil {
|
||||||
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
w.Write([]byte("Unable to send error information due to: " + err.Error()))
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.WriteHeader(e.Status())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e defaultErr) JSON() string {
|
||||||
|
type jsonErr struct {
|
||||||
|
Error string `json:"error"`
|
||||||
|
Info any `json:"info"`
|
||||||
|
}
|
||||||
|
js, err := json.Marshal(jsonErr{
|
||||||
|
Error: e.Error(),
|
||||||
|
Info: e,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
js, _ = json.Marshal(jsonErr{
|
||||||
|
Error: "Unable to parse JSON of error",
|
||||||
|
Info: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(js)
|
||||||
|
}
|
||||||
|
|
||||||
|
templ (e defaultErr) Component() {
|
||||||
|
@layouts.Page("Error") {
|
||||||
|
<dialog open>
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<p>Error</p>
|
||||||
|
</header>
|
||||||
|
<p>
|
||||||
|
{ e.Error() }
|
||||||
|
</p>
|
||||||
|
<footer>
|
||||||
|
<a href={ templ.SafeURL("/") }>
|
||||||
|
<button>Return to homepage</button>
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
</article>
|
||||||
|
</dialog>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,13 +4,18 @@ import (
|
|||||||
"extrovert/templates/layouts"
|
"extrovert/templates/layouts"
|
||||||
"extrovert/components"
|
"extrovert/components"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"extrovert/internals/router/errors"
|
||||||
|
e "errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Homepage struct{}
|
type Homepage struct{}
|
||||||
|
|
||||||
func (h Homepage) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h Homepage) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
_ = h.page().Render(context.Background(), w)
|
err := h.page().Render(context.Background(), w)
|
||||||
|
if err != nil {
|
||||||
|
errors.NewErrInternal(e.New("Unable to render dashboard"), err).ServeHTTP(w, r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
templ (h Homepage) page() {
|
templ (h Homepage) page() {
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
e "errors"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"extrovert/internals"
|
"extrovert/internals/router/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AITxt struct{}
|
type AITxt struct{}
|
||||||
@@ -13,23 +14,24 @@ func (_ AITxt) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.Header().Add("Cache-Control", "max-age=604800, stale-while-revalidate=86400, stale-if-error=86400")
|
w.Header().Add("Cache-Control", "max-age=604800, stale-while-revalidate=86400, stale-if-error=86400")
|
||||||
w.Header().Add("CDN-Cache-Control", "max-age=604800")
|
w.Header().Add("CDN-Cache-Control", "max-age=604800")
|
||||||
|
|
||||||
error := internals.HttpErrorHelper(w)
|
list, err := http.Get("https://raw.githubusercontent.com/ai-robots-txt/ai.robots.txt/main/ai.txt")
|
||||||
|
if err != nil {
|
||||||
aiList, err := http.Get("https://raw.githubusercontent.com/ai-robots-txt/ai.robots.txt/main/ai.txt")
|
errors.NewErrInternal(e.New("Unable to fetch ai.txt list"), err).ServeHTTP(w, r)
|
||||||
if error("Error trying to create ai block list", err, http.StatusInternalServerError) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes, err := io.ReadAll(aiList.Body)
|
bytes, err := io.ReadAll(list.Body)
|
||||||
if error("Error trying to create ai block list", err, http.StatusInternalServerError) {
|
if err != nil {
|
||||||
return
|
errors.NewErrInternal(e.New("Unable to read dynamic ai.txt list"), err).ServeHTTP(w, r)
|
||||||
}
|
|
||||||
_, err = io.WriteString(w, string(bytes))
|
|
||||||
if error("Error trying to create ai block list", err, http.StatusInternalServerError) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Add("Content-Type", "text/plain")
|
w.Header().Add("Content-Type", "text/plain")
|
||||||
|
_, err = w.Write(bytes)
|
||||||
|
if err != nil {
|
||||||
|
errors.NewErrInternal(e.New("Unable to write ai.txt list"), err).ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type RobotsTxt struct{}
|
type RobotsTxt struct{}
|
||||||
@@ -38,19 +40,21 @@ func (_ RobotsTxt) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.Header().Add("Cache-Control", "max-age=604800, stale-while-revalidate=86400, stale-if-error=86400")
|
w.Header().Add("Cache-Control", "max-age=604800, stale-while-revalidate=86400, stale-if-error=86400")
|
||||||
w.Header().Add("CDN-Cache-Control", "max-age=604800")
|
w.Header().Add("CDN-Cache-Control", "max-age=604800")
|
||||||
|
|
||||||
error := internals.HttpErrorHelper(w)
|
list, err := http.Get("https://raw.githubusercontent.com/ai-robots-txt/ai.robots.txt/main/robots.txt")
|
||||||
aiList, err := http.Get("https://raw.githubusercontent.com/ai-robots-txt/ai.robots.txt/main/robots.txt")
|
if err != nil {
|
||||||
if error("Error trying to create robots block list", err, http.StatusInternalServerError) {
|
errors.NewErrInternal(e.New("Unable to fetch robots.txt list"), err).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes, err := io.ReadAll(aiList.Body)
|
bytes, err := io.ReadAll(list.Body)
|
||||||
if error("Error trying to create robots block list", err, http.StatusInternalServerError) {
|
if err != nil {
|
||||||
|
errors.NewErrInternal(e.New("Unable to read dynamic robots.txt list"), err).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = io.WriteString(w, string(bytes))
|
_, err = io.WriteString(w, string(bytes))
|
||||||
if error("Error trying to create robots block list", err, http.StatusInternalServerError) {
|
if err != nil {
|
||||||
|
errors.NewErrInternal(e.New("Unable to write robots.txt list"), err).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Add("Content-Type", "text/plain")
|
w.Header().Add("Content-Type", "text/plain")
|
||||||
|
|||||||
Reference in New Issue
Block a user