Add system for showing template render errors

Makes troubleshooting bad templates much easier. Need to convert
everything over.
This commit is contained in:
Eli Ribble 2025-11-10 22:16:23 +00:00
parent 7b48f6eddf
commit b4830f7684
No known key found for this signature in database
2 changed files with 52 additions and 14 deletions

View file

@ -173,7 +173,8 @@ func getRoot(w http.ResponseWriter, r *http.Request) {
return return
} }
if has { if has {
err = htmlDashboard(r.Context(), w, user) htmlDashboard(r.Context(), w, user)
return
} else { } else {
err = htmlOauthPrompt(w, user) err = htmlOauthPrompt(w, user)
} }

63
html.go
View file

@ -1,12 +1,14 @@
package main package main
import ( import (
"bytes"
"context" "context"
"errors" "errors"
"fmt" "fmt"
"html/template" "html/template"
"io" "io"
"log/slog" "log/slog"
"net/http"
"os" "os"
"strings" "strings"
"time" "time"
@ -42,6 +44,7 @@ var components = [...]string{"header"}
type BuiltTemplate struct { type BuiltTemplate struct {
files []string files []string
name string
template *template.Template template *template.Template
} }
@ -112,33 +115,39 @@ func extractInitials(name string) string {
return initials.String() return initials.String()
} }
func htmlDashboard(ctx context.Context, w io.Writer, user *models.User) error { func htmlDashboard(ctx context.Context, w http.ResponseWriter, user *models.User) {
org, err := user.Organization().One(ctx, PGInstance.BobDB) org, err := user.Organization().One(ctx, PGInstance.BobDB)
if err != nil { if err != nil {
return fmt.Errorf("Failed to get org: %v", err) respondError(w, "Failed to get org", err, http.StatusInternalServerError)
return
} }
var lastSync time.Time var lastSync time.Time
sync, err := org.FieldseekerSyncs(sm.OrderBy("created")).One(ctx, PGInstance.BobDB) sync, err := org.FieldseekerSyncs(sm.OrderBy("created")).One(ctx, PGInstance.BobDB)
if err != nil { if err != nil {
return fmt.Errorf("Failed to get sync: %v", err) respondError(w, "Failed to get syncs", err, http.StatusInternalServerError)
return
} else { } else {
lastSync = sync.Created lastSync = sync.Created
} }
inspectionCount, err := org.FSMosquitoinspections().Count(ctx, PGInstance.BobDB) inspectionCount, err := org.FSMosquitoinspections().Count(ctx, PGInstance.BobDB)
if err != nil { if err != nil {
return fmt.Errorf("Failed to get inspection count: %v", err) respondError(w, "Failed to get inspection count", err, http.StatusInternalServerError)
return
} }
sourceCount, err := org.FSPointlocations().Count(ctx, PGInstance.BobDB) sourceCount, err := org.FSPointlocations().Count(ctx, PGInstance.BobDB)
if err != nil { if err != nil {
return fmt.Errorf("Failed to get inspection count: %v", err) respondError(w, "Failed to get source count", err, http.StatusInternalServerError)
return
} }
serviceCount, err := org.FSServicerequests().Count(ctx, PGInstance.BobDB) serviceCount, err := org.FSServicerequests().Count(ctx, PGInstance.BobDB)
if err != nil { if err != nil {
return fmt.Errorf("Failed to get service count: %v", err) respondError(w, "Failed to get service count", err, http.StatusInternalServerError)
return
} }
recentRequests, err := org.FSServicerequests(sm.OrderBy("creationdate").Desc(), sm.Limit(10)).All(ctx, PGInstance.BobDB) recentRequests, err := org.FSServicerequests(sm.OrderBy("creationdate").Desc(), sm.Limit(10)).All(ctx, PGInstance.BobDB)
if err != nil { if err != nil {
return fmt.Errorf("Failed to get recent service: %v", err) respondError(w, "Failed to get recent service", err, http.StatusInternalServerError)
return
} }
requests := make([]ServiceRequestSummary, 0) requests := make([]ServiceRequestSummary, 0)
@ -162,7 +171,7 @@ func htmlDashboard(ctx context.Context, w io.Writer, user *models.User) error {
Username: user.Username, Username: user.Username,
}, },
} }
return dashboard.ExecuteTemplate(w, data) renderOrError(w, dashboard, data)
} }
func htmlOauthPrompt(w io.Writer, user *models.User) error { func htmlOauthPrompt(w io.Writer, user *models.User) error {
@ -292,9 +301,10 @@ func makeFuncMap() template.FuncMap {
} }
return funcMap return funcMap
} }
func newBuiltTemplate(files ...string) BuiltTemplate { func newBuiltTemplate(name string, files ...string) BuiltTemplate {
files_on_disk := true files_on_disk := true
for _, f := range files { all_files := append([]string{name}, files...)
for _, f := range all_files {
full_path := "templates/" + f + ".html" full_path := "templates/" + f + ".html"
_, err := os.Stat(full_path) _, err := os.Stat(full_path)
if err != nil { if err != nil {
@ -304,13 +314,15 @@ func newBuiltTemplate(files ...string) BuiltTemplate {
} }
if files_on_disk { if files_on_disk {
return BuiltTemplate{ return BuiltTemplate{
files: files, files: all_files,
name: name,
template: nil, template: nil,
} }
} }
return BuiltTemplate{ return BuiltTemplate{
files: files, files: all_files,
template: parseEmbedded(files), name: name,
template: parseEmbedded(all_files),
} }
} }
@ -369,3 +381,28 @@ func timeSince(t time.Time) string {
return fmt.Sprintf("%d days ago", int(days)) return fmt.Sprintf("%d days ago", int(days))
} }
} }
type Notification struct {
Created time.Time
Link string
Message string
}
func notificationsForUser(u *models.User) ([]Notification, error) {
return []Notification{Notification{
Created: time.Now(),
Link: "/foo/bar",
Message: "hey, your oauth is broken.",
}}, nil
}
func renderOrError(w http.ResponseWriter, template BuiltTemplate, context interface{}) {
buf := &bytes.Buffer{}
err := template.ExecuteTemplate(buf, context)
if err != nil {
slog.Error("Failed to render template", slog.String("err", err.Error()), slog.String("template", template.name))
respondError(w, "Failed to render template", err, http.StatusInternalServerError)
return
}
buf.WriteTo(w)
}