diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 28d51711..7d2b1f1b 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -16,7 +16,7 @@ jobs: - name: Set up Go uses: WillAbides/setup-go-faster@v1.14.0 with: - go-version: 1.17 + go-version: 1.21 - run: ls ${{ github.workspace }} @@ -36,7 +36,7 @@ jobs: - name: Set up Go uses: WillAbides/setup-go-faster@v1.14.0 with: - go-version: 1.17 + go-version: 1.21 - name: Lint uses: golangci/golangci-lint-action@v6 diff --git a/controllers/about.go b/controllers/about.go index 197de76d..c3cc5d54 100644 --- a/controllers/about.go +++ b/controllers/about.go @@ -1,7 +1,6 @@ package controllers import ( - "log" "net/http" "github.com/UniversityRadioYork/2016-site/models" @@ -26,8 +25,7 @@ func (aboutC *AboutController) Get(w http.ResponseWriter, r *http.Request) { teamM := models.NewTeamModel(aboutC.session) teams, err := teamM.GetAll() if err != nil { - log.Println(err) - utils.RenderTemplate(w, aboutC.config.PageContext, nil, "404.tmpl") + aboutC.handleError(w, r, err, "TeamModel.GetAll") return } data := struct { @@ -35,9 +33,5 @@ func (aboutC *AboutController) Get(w http.ResponseWriter, r *http.Request) { }{ Teams: teams, } - err = utils.RenderTemplate(w, aboutC.config.PageContext, data, "about.tmpl") - if err != nil { - log.Println(err) - return - } + utils.RenderTemplate(w, aboutC.config.PageContext, data, "about.tmpl") } diff --git a/controllers/controller.go b/controllers/controller.go index 0c4dcc0b..0bfbfc16 100644 --- a/controllers/controller.go +++ b/controllers/controller.go @@ -5,10 +5,18 @@ package controllers */ import ( + "errors" + "fmt" + "log" "net/http" + "runtime" + "strings" "github.com/UniversityRadioYork/2016-site/structs" + "github.com/UniversityRadioYork/2016-site/utils" "github.com/UniversityRadioYork/myradio-go" + "github.com/UniversityRadioYork/myradio-go/api" + "github.com/getsentry/sentry-go" ) // ControllerInterface is the interface to which controllers adhere. @@ -76,3 +84,56 @@ func (c *Controller) Patch(w http.ResponseWriter, r *http.Request) { func (c *Controller) Options(w http.ResponseWriter, r *http.Request) { http.Error(w, "Method Not Allowed", 405) } + +// handleError converts an error into a descriptive error page. It takes care of logging it with the given context. +func (c *Controller) handleError(w http.ResponseWriter, r *http.Request, err error, context string) { + var httpErr utils.HTTPError + if errors.As(err, &httpErr) { + switch httpErr.Status { + case 404, 500: // these we have templates for + w.WriteHeader(httpErr.Status) + utils.RenderTemplate(w, c.config.PageContext, nil, fmt.Sprintf("%d.tmpl", httpErr.Status)) + return + default: + http.Error(w, httpErr.Message, httpErr.Status) + return + } + } + + pc, file, line, ok := runtime.Caller(1) + if ok { + fn := runtime.FuncForPC(pc) + if fn != nil { + context = fmt.Sprintf("%s at %s:%d (%s)", context, file, line, strings.Replace(fn.Name(), "github.com/UniversityRadioYork/2016-site/", "", 1)) + } else { + context = fmt.Sprintf("%s at %s:%d", context, file, line) + } + } + + sentry.WithScope(func(scope *sentry.Scope) { + scope.SetContext("error", map[string]any{ + "context": context, + }) + sentry.CaptureException(err) + }) + + var apiErr api.Error + if errors.As(err, &apiErr) { + switch apiErr.Code { + case http.StatusNotFound: + w.WriteHeader(404) + utils.RenderTemplate(w, c.config.PageContext, nil, "404.tmpl") + return + case http.StatusForbidden: + // 2016-site should never hit this, it's likely a misconfiguration of our API key's permissions + log.Printf("Received 403 from MyRadio API [%s]: %v", context, err) + default: + log.Printf("Unexpected MyRadio API error [%s]: %v", context, err) + } + } else { + log.Printf("Unexpected error [%s]: %v", context, err) + } + + w.WriteHeader(500) + utils.RenderTemplate(w, c.config.PageContext, nil, "500.tmpl") +} diff --git a/controllers/getinvolved.go b/controllers/getinvolved.go index c0a21c7f..1e09be3c 100644 --- a/controllers/getinvolved.go +++ b/controllers/getinvolved.go @@ -1,7 +1,6 @@ package controllers import ( - "log" "net/http" "sort" @@ -54,8 +53,7 @@ func (gic *GetInvolvedController) Get(w http.ResponseWriter, r *http.Request) { colleges, numTeams, listTeamMap, faqs, err := gim.Get() if err != nil { - //@TODO: Do something proper here, render 404 or something - log.Println(err) + gic.handleError(w, r, err, "GetInvolvedModel.Get") return } @@ -74,10 +72,5 @@ func (gic *GetInvolvedController) Get(w http.ResponseWriter, r *http.Request) { FAQs: faqs, } - err = utils.RenderTemplate(w, gic.config.PageContext, data, "getinvolved.tmpl") - if err != nil { - log.Println(err) - return - } - + utils.RenderTemplate(w, gic.config.PageContext, data, "getinvolved.tmpl") } diff --git a/controllers/index.go b/controllers/index.go index 932adb26..6239374b 100644 --- a/controllers/index.go +++ b/controllers/index.go @@ -39,7 +39,7 @@ func (ic *IndexController) Get(w http.ResponseWriter, r *http.Request) { currentAndNext, banners, timeslots, podcasts, showOnAir, err := model.Get() if err != nil { - log.Println(err) + ic.handleError(w, r, err, "IndexModel.Get") return } @@ -67,7 +67,7 @@ func (ic *IndexController) Post(w http.ResponseWriter, r *http.Request) { currentAndNext, banners, timeslots, podcasts, showOnAir, err := model.Get() if err != nil { - log.Println(err) + ic.handleError(w, r, err, "IndexModel.Get") return } @@ -84,6 +84,7 @@ func (ic *IndexController) Post(w http.ResponseWriter, r *http.Request) { msgmodel := models.NewMessageModel(ic.session) err = msgmodel.Put(msg) if err != nil { + log.Printf("Error from MessageModel.Put: %v", err) // Set prompt if send fails data.MsgBoxError = true } @@ -94,9 +95,5 @@ func (ic *IndexController) Post(w http.ResponseWriter, r *http.Request) { func (ic *IndexController) render(w http.ResponseWriter, data RenderData) { // Render page - err := utils.RenderTemplate(w, ic.config.PageContext, data, "index.tmpl", "elements/current_and_next.tmpl", "elements/banner.tmpl", "elements/message_box.tmpl", "elements/index_countdown.tmpl") - if err != nil { - log.Println(err) - return - } + utils.RenderTemplate(w, ic.config.PageContext, data, "index.tmpl", "elements/current_and_next.tmpl", "elements/banner.tmpl", "elements/message_box.tmpl", "elements/index_countdown.tmpl") } diff --git a/controllers/not_found.go b/controllers/not_found.go index fe429d50..129829fe 100644 --- a/controllers/not_found.go +++ b/controllers/not_found.go @@ -2,12 +2,13 @@ package controllers import ( "fmt" - "github.com/UniversityRadioYork/2016-site/models" - "github.com/UniversityRadioYork/myradio-go" "log" "net" "net/http" + "github.com/UniversityRadioYork/2016-site/models" + "github.com/UniversityRadioYork/myradio-go" + "github.com/UniversityRadioYork/2016-site/structs" "github.com/UniversityRadioYork/2016-site/utils" ) @@ -57,9 +58,5 @@ func (sc *NotFoundController) Get(w http.ResponseWriter, r *http.Request) { return } w.WriteHeader(404) - err := utils.RenderTemplate(w, sc.config.PageContext, nil, "404.tmpl") - if err != nil { - log.Println(err) - return - } + utils.RenderTemplate(w, sc.config.PageContext, nil, "404.tmpl") } diff --git a/controllers/on_demand.go b/controllers/on_demand.go index a8cfd1ce..3c234e1d 100644 --- a/controllers/on_demand.go +++ b/controllers/on_demand.go @@ -1,7 +1,6 @@ package controllers import ( - "log" "net/http" "github.com/UniversityRadioYork/2016-site/models" @@ -29,8 +28,7 @@ func (onDemandC *OnDemandController) Get(w http.ResponseWriter, r *http.Request) latestPodcasts, err := PodcastsM.GetAllPodcasts(10, 0) if err != nil { - log.Println(err) - utils.RenderTemplate(w, onDemandC.config.PageContext, err, "404.tmpl") + onDemandC.handleError(w, r, err, "PodcastModel.GetAllPodcasts") return } @@ -39,8 +37,7 @@ func (onDemandC *OnDemandController) Get(w http.ResponseWriter, r *http.Request) latestTimeslots, err := OnDemandM.GetLastMixcloudTimeslots() if err != nil { - log.Println(err) - utils.RenderTemplate(w, onDemandC.config.PageContext, err, "404.tmpl") + onDemandC.handleError(w, r, err, "OnDemandModel.GetLastMixcloudTimeslots") return } @@ -52,10 +49,5 @@ func (onDemandC *OnDemandController) Get(w http.ResponseWriter, r *http.Request) LatestTimeslots: latestTimeslots, } - err = utils.RenderTemplate(w, onDemandC.config.PageContext, data, "on_demand.tmpl") - if err != nil { - log.Println(err) - return - } - + utils.RenderTemplate(w, onDemandC.config.PageContext, data, "on_demand.tmpl") } diff --git a/controllers/people.go b/controllers/people.go index 7e8ef4ee..8bdc536f 100644 --- a/controllers/people.go +++ b/controllers/people.go @@ -1,15 +1,15 @@ package controllers import ( - "log" "net/http" "strconv" + "github.com/gorilla/mux" + "github.com/UniversityRadioYork/2016-site/models" "github.com/UniversityRadioYork/2016-site/structs" "github.com/UniversityRadioYork/2016-site/utils" "github.com/UniversityRadioYork/myradio-go" - "github.com/gorilla/mux" ) // PeopleController is the controller for the user bio page. @@ -33,17 +33,16 @@ func (pc *PeopleController) Get(w http.ResponseWriter, r *http.Request) { id, _ := strconv.Atoi(vars["id"]) user, officerships, credits, currentAndNext, err := pm.Get(id) + if err != nil { + pc.handleError(w, r, err, "PeopleModel.Get") + return + } //404 when the user has no credits. if len(credits) == 0 && len(officerships) == 0 { utils.RenderTemplate(w, pc.config.PageContext, err, "404.tmpl") return } - if err != nil { - log.Println(err) - utils.RenderTemplate(w, pc.config.PageContext, err, "404.tmpl") - return - } data := struct { User *myradio.User @@ -57,10 +56,5 @@ func (pc *PeopleController) Get(w http.ResponseWriter, r *http.Request) { CurrentAndNext: currentAndNext, } - err = utils.RenderTemplate(w, pc.config.PageContext, data, "people.tmpl", "elements/current_and_next.tmpl") - if err != nil { - log.Println(err) - return - } - + utils.RenderTemplate(w, pc.config.PageContext, data, "people.tmpl", "elements/current_and_next.tmpl") } diff --git a/controllers/podcasts.go b/controllers/podcasts.go index 030ec428..01449c51 100644 --- a/controllers/podcasts.go +++ b/controllers/podcasts.go @@ -1,7 +1,6 @@ package controllers import ( - "log" "net/http" "strconv" @@ -10,7 +9,7 @@ import ( "github.com/UniversityRadioYork/2016-site/models" "github.com/UniversityRadioYork/2016-site/structs" "github.com/UniversityRadioYork/2016-site/utils" - myradio "github.com/UniversityRadioYork/myradio-go" + "github.com/UniversityRadioYork/myradio-go" ) // PodcastController is the controller for the URYPlayer Podcast pages. @@ -40,10 +39,14 @@ func (podcastsC *PodcastController) GetAllPodcasts(w http.ResponseWriter, r *htt //podcast page offset is indexed from 0, URL's are from 1. podcasts, err := podcastm.GetAllPodcasts(10, pageNumber-1) - + if err != nil { + podcastsC.handleError(w, r, err, "PodcastModel.GetAllPodcasts") + return + } if podcasts == nil { utils.RenderTemplate(w, podcastsC.config.PageContext, err, "404.tmpl") } + //see if it's possible to load another podcast for a possible next page. nextPodcasts, _ := podcastm.GetAllPodcasts(1, pageNumber) @@ -51,11 +54,6 @@ func (podcastsC *PodcastController) GetAllPodcasts(w http.ResponseWriter, r *htt if nextPodcasts != nil { pageNext = true } - if err != nil { - log.Println(err) - utils.RenderTemplate(w, podcastsC.config.PageContext, err, "404.tmpl") - return - } data := struct { PageNumberPrev int @@ -71,12 +69,7 @@ func (podcastsC *PodcastController) GetAllPodcasts(w http.ResponseWriter, r *htt Podcasts: podcasts, } - err = utils.RenderTemplate(w, podcastsC.config.PageContext, data, "podcasts.tmpl") - if err != nil { - log.Println(err) - return - } - + utils.RenderTemplate(w, podcastsC.config.PageContext, data, "podcasts.tmpl") } // Get handles the HTTP GET request r for a singular podcast page, writing to w. @@ -91,8 +84,7 @@ func (podcastsC *PodcastController) Get(w http.ResponseWriter, r *http.Request) podcast, err := podcastm.Get(id) if err != nil { - log.Println(err) - utils.RenderTemplate(w, podcastsC.config.PageContext, nil, "404.tmpl") + podcastsC.handleError(w, r, err, "PodcastModel.Get") return } @@ -107,13 +99,7 @@ func (podcastsC *PodcastController) Get(w http.ResponseWriter, r *http.Request) Podcast: podcast, } - err = utils.RenderTemplate(w, podcastsC.config.PageContext, data, "podcast.tmpl") - - if err != nil { - log.Println(err) - return - } - + utils.RenderTemplate(w, podcastsC.config.PageContext, data, "podcast.tmpl") } // GetEmbed handles the HTTP GET request r for a singular podcast embed, writing to w. @@ -128,8 +114,7 @@ func (podcastsC *PodcastController) GetEmbed(w http.ResponseWriter, r *http.Requ podcast, err := podcastm.Get(id) if err != nil { - //@TODO: Do something proper here, render 404 or something - log.Println(err) + podcastsC.handleError(w, r, err, "PodcastModel.Get") return } @@ -139,11 +124,5 @@ func (podcastsC *PodcastController) GetEmbed(w http.ResponseWriter, r *http.Requ Podcast: podcast, } - err = utils.RenderTemplate(w, podcastsC.config.PageContext, data, "podcast_player.tmpl") - - if err != nil { - log.Println(err) - return - } - + utils.RenderTemplate(w, podcastsC.config.PageContext, data, "podcast_player.tmpl") } diff --git a/controllers/schedule_week.go b/controllers/schedule_week.go index 6dace15f..3b8ce43a 100644 --- a/controllers/schedule_week.go +++ b/controllers/schedule_week.go @@ -8,22 +8,23 @@ import ( "strconv" "time" + "github.com/gorilla/mux" + "github.com/UniversityRadioYork/2016-site/models" "github.com/UniversityRadioYork/2016-site/structs" "github.com/UniversityRadioYork/2016-site/utils" "github.com/UniversityRadioYork/myradio-go" - "github.com/gorilla/mux" ) // weekFromVars extracts the year, and week strings from vars. func weekFromVars(vars map[string]string) (string, string, error) { y, ok := vars["year"] if !ok { - return "", "", errors.New("no year provided") + return "", "", utils.NewHTTPError(http.StatusBadRequest, "no year provided") } w, ok := vars["week"] if !ok { - return "", "", errors.New("no week provided") + return "", "", utils.NewHTTPError(http.StatusBadRequest, "no week provided") } return y, w, nil @@ -94,7 +95,7 @@ func (sc *ScheduleWeekController) GetThisWeek(w http.ResponseWriter, r *http.Req today := time.Now() year, week := today.ISOWeek() - sc.makeAndRenderWeek(w, year, week) + sc.makeAndRenderWeek(w, r, year, week) } // GetByYearWeek handles the HTTP GET request r for week schedules by year/week date reference, writing to w. @@ -105,46 +106,43 @@ func (sc *ScheduleWeekController) GetByYearWeek(w http.ResponseWriter, r *http.R ystr, wstr, err := weekFromVars(vars) if err != nil { - log.Println(err) + sc.handleError(w, r, err, "weekFromVars") return } year, week, _, err := utils.ParseIsoWeek(ystr, wstr, "1") if err != nil { - log.Println(err) + sc.handleError(w, r, err, "ParseIsoWeek") return } - sc.makeAndRenderWeek(w, year, week) + sc.makeAndRenderWeek(w, r, year, week) } // makeAndRenderWeek makes and renders a week schedule for year and week, writing to w. -func (sc *ScheduleWeekController) makeAndRenderWeek(w http.ResponseWriter, year, week int) { +func (sc *ScheduleWeekController) makeAndRenderWeek(w http.ResponseWriter, r *http.Request, year, week int) { m := models.NewScheduleModel(sc.session) ws, err := m.WeekSchedule(year, week, sc.config.Schedule.Sustainer, sc.timeslotURLBuilder) if err != nil { - //@TODO: Do something proper here, render 404 or something - log.Println(err) + sc.handleError(w, r, err, "ScheduleModel.WeekSchedule") return } purl, curl, nurl, err := sc.getRelatedScheduleURLs(ws) if err != nil { - //@TODO: Do something proper here, render 404 or something - log.Println(err) + sc.handleError(w, r, err, "scheduleController.getRelatedScheduleURLs") return } currentAndNext, err := m.GetCurrentAndNext() if err != nil { - log.Println(err) + log.Printf("Error from ScheduleModel.GetCurrentAndNext: %v", err) // Safe to continue since we can check if CaN is absent in the template } - subtypes, err := sc.session.GetAllShowSubtypes() + subtypes, err := sc.session.GetAllShowSubtypes() // TODO(markspolakovs): strictly this should be on ScheduleModel if err != nil { - //@TODO: Do something proper here, render 404 or something - log.Println(err) + sc.handleError(w, r, err, "Session.GetAllShowSubtypes") return } @@ -169,11 +167,7 @@ func (sc *ScheduleWeekController) makeAndRenderWeek(w http.ResponseWriter, year, Subtypes: subtypes, } - err = utils.RenderTemplate(w, sc.config.PageContext, data, "schedule_week.tmpl", "elements/current_and_next.tmpl") - if err != nil { - log.Println(err) - return - } + utils.RenderTemplate(w, sc.config.PageContext, data, "schedule_week.tmpl", "elements/current_and_next.tmpl") } // getRelatedScheduleURLs gets the URLs for the previous, current, and next schedules relative to ws. diff --git a/controllers/search.go b/controllers/search.go index e2777b21..40bcc861 100644 --- a/controllers/search.go +++ b/controllers/search.go @@ -1,7 +1,6 @@ package controllers import ( - "log" "net/http" "github.com/UniversityRadioYork/2016-site/models" @@ -36,7 +35,7 @@ func (sc *SearchController) Get(w http.ResponseWriter, r *http.Request) { results, err = sm.Get(term) if err != nil { - log.Println(err) + sc.handleError(w, r, err, "SearchModel.Get") return } } @@ -55,9 +54,5 @@ func (sc *SearchController) Get(w http.ResponseWriter, r *http.Request) { Term: term, } - err = utils.RenderTemplate(w, sc.config.PageContext, data, "search.tmpl") - if err != nil { - log.Println(err) - return - } + utils.RenderTemplate(w, sc.config.PageContext, data, "search.tmpl") } diff --git a/controllers/show.go b/controllers/show.go index b4aacf2d..ab9411ad 100644 --- a/controllers/show.go +++ b/controllers/show.go @@ -10,11 +10,12 @@ import ( "strings" "time" + "github.com/gorilla/mux" + "github.com/UniversityRadioYork/2016-site/models" "github.com/UniversityRadioYork/2016-site/structs" "github.com/UniversityRadioYork/2016-site/utils" "github.com/UniversityRadioYork/myradio-go" - "github.com/gorilla/mux" ) // ShowController is the controller for looking up shows. @@ -48,18 +49,16 @@ func (sc *ShowController) GetShow(w http.ResponseWriter, r *http.Request) { id, _ := strconv.Atoi(vars["id"]) showInfo, err := sm.GetShow(id) + if err != nil { + sc.handleError(w, r, err, "ShowModel.GetShow") + return + } // Needed so that credits are grouped by type var scheduledSeasons = make([]myradio.Season, 0) var timeslots = make([]myradio.Timeslot, 0) - if err != nil { - log.Println(err) - utils.RenderTemplate(w, sc.config.PageContext, struct{}{}, "404.tmpl") - return - } - for _, season := range showInfo.Seasons { _, timeslotsSingleSeason, _ := sm.GetSeason(season.SeasonID) if season.FirstTimeRaw != "0" && len(timeslotsSingleSeason) > 0 { @@ -102,11 +101,7 @@ func (sc *ShowController) GetShow(w http.ResponseWriter, r *http.Request) { Podcasts: showInfo.Podcasts, } - err = utils.RenderTemplate(w, sc.config.PageContext, data, "show.tmpl") - if err != nil { - log.Println(err) - return - } + utils.RenderTemplate(w, sc.config.PageContext, data, "show.tmpl") } // GetTimeslot handles the HTTP GET request r for an individual timeslot, writing to w. @@ -118,6 +113,11 @@ func (sc *ShowController) GetTimeslot(w http.ResponseWriter, r *http.Request) { id, _ := strconv.Atoi(vars["id"]) timeslot, tracklist, creditsToUsers, err := sm.GetTimeslot(id) + if err != nil { + sc.handleError(w, r, err, "ShowModel.GetTimeslot") + return + } + odState := 0 endTime := timeslot.StartTime.Add(timeslot.Duration) @@ -149,18 +149,7 @@ func (sc *ShowController) GetTimeslot(w http.ResponseWriter, r *http.Request) { CreditsToUsers: creditsToUsers, } - if err != nil { - log.Println(err) - utils.RenderTemplate(w, sc.config.PageContext, data, "404.tmpl") - return - } - - err = utils.RenderTemplate(w, sc.config.PageContext, data, "timeslot.tmpl") - if err != nil { - log.Println(err) - return - } - + utils.RenderTemplate(w, sc.config.PageContext, data, "timeslot.tmpl") } // GetSeason handles the HTTP GET request r for an individual season, writing to w. @@ -174,8 +163,7 @@ func (sc *ShowController) GetSeason(w http.ResponseWriter, r *http.Request) { season, _, err := sm.GetSeason(id) if err != nil { - utils.RenderTemplate(w, sc.config.PageContext, struct{}{}, "404.tmpl") - log.Println(err) + sc.handleError(w, r, err, "ShowModel.GetSeason") return } @@ -195,8 +183,7 @@ func (sc *ShowController) GetPodcastRssHead(w http.ResponseWriter, r *http.Reque rss, err := sm.GetPodcastRSS(id) if err != nil { - w.WriteHeader(404) - log.Println(err) + sc.handleError(w, r, err, "ShowModel.GetPodcastRSS") return } @@ -223,9 +210,7 @@ func (sc *ShowController) GetPodcastRss(w http.ResponseWriter, r *http.Request) rss, err := sm.GetPodcastRSS(id) if err != nil { - w.WriteHeader(404) - utils.RenderTemplate(w, sc.config.PageContext, struct{}{}, "404.tmpl") - log.Println(err) + sc.handleError(w, r, err, "ShowModel.GetPodcastRSS") return } @@ -244,7 +229,7 @@ func (sc *ShowController) GetPodcastRss(w http.ResponseWriter, r *http.Request) _, err = w.Write(rssBytes) if err != nil { - log.Println(err) + log.Printf("ShowController.GetPodcastRSS: writing feed data: %v", err) } } @@ -265,8 +250,7 @@ func (sc *ShowController) GetUyco(w http.ResponseWriter, r *http.Request) { timeslot, _, _, err := sm.GetTimeslot(value) if err != nil { - log.Println(err) - utils.RenderTemplate(w, sc.config.PageContext, concertData, "404.tmpl") + sc.handleError(w, r, err, "ShowModel.GetTimeslot") return } @@ -281,9 +265,5 @@ func (sc *ShowController) GetUyco(w http.ResponseWriter, r *http.Request) { Concert: concertData, } - err := utils.RenderTemplate(w, sc.config.PageContext, toSend, "uyco.tmpl") - if err != nil { - log.Println(err) - return - } + utils.RenderTemplate(w, sc.config.PageContext, toSend, "uyco.tmpl") } diff --git a/controllers/signup.go b/controllers/signup.go index 079077ff..1e4785e5 100644 --- a/controllers/signup.go +++ b/controllers/signup.go @@ -1,7 +1,6 @@ package controllers import ( - "log" "net/http" "regexp" "strings" @@ -68,9 +67,8 @@ func (gic *SignUpController) Post(w http.ResponseWriter, r *http.Request) { sm := models.NewSignUpModel(gic.session) created, err := sm.Post(formParams) if err != nil { - log.Println(err) - feedback = append(feedback, "Oops. Something went wrong on our end.") - feedback = append(feedback, "Please try again later") + gic.handleError(w, r, err, "SignUpModel.Post") + return } if !created { feedback = append(feedback, "Looks like you already have an account!") @@ -85,10 +83,5 @@ func (gic *SignUpController) Post(w http.ResponseWriter, r *http.Request) { Feedback: feedback, } - err := utils.RenderTemplate(w, gic.config.PageContext, data, "signedup.tmpl") - - if err != nil { - log.Println(err) - return - } + utils.RenderTemplate(w, gic.config.PageContext, data, "signedup.tmpl") } diff --git a/controllers/static.go b/controllers/static.go index 87e374c5..d0396250 100644 --- a/controllers/static.go +++ b/controllers/static.go @@ -1,7 +1,6 @@ package controllers import ( - "log" "net/http" "github.com/UniversityRadioYork/2016-site/structs" @@ -21,35 +20,19 @@ func NewStaticController(c *structs.Config) *StaticController { // GetContact handles the HTTP GET request r for the Contact page, writing to w. func (staticC *StaticController) GetContact(w http.ResponseWriter, r *http.Request) { - err := utils.RenderTemplate(w, staticC.config.PageContext, nil, "contact.tmpl") - if err != nil { - log.Println(err) - return - } + utils.RenderTemplate(w, staticC.config.PageContext, nil, "contact.tmpl") } // GetInvolved handles the HTTP GET request r for the Get Involved page, writing to w. func (staticC *StaticController) GetInvolved(w http.ResponseWriter, r *http.Request) { - err := utils.RenderTemplate(w, staticC.config.PageContext, nil, "getinvolved.tmpl") - if err != nil { - log.Println(err) - return - } + utils.RenderTemplate(w, staticC.config.PageContext, nil, "getinvolved.tmpl") } // GetCompetitions handles the HTTP GET request r for the Get Involved page, writing to w. func (staticC *StaticController) GetCompetitions(w http.ResponseWriter, r *http.Request) { - err := utils.RenderTemplate(w, staticC.config.PageContext, nil, "competitions.tmpl") - if err != nil { - log.Println(err) - return - } + utils.RenderTemplate(w, staticC.config.PageContext, nil, "competitions.tmpl") } func (staticC *StaticController) GetCIN(w http.ResponseWriter, r *http.Request) { - err := utils.RenderTemplate(w, staticC.config.PageContext, nil, "cin.tmpl") - if err != nil { - log.Println(err) - return - } + utils.RenderTemplate(w, staticC.config.PageContext, nil, "cin.tmpl") } diff --git a/controllers/team.go b/controllers/team.go index 81cc1dfd..17874c2d 100644 --- a/controllers/team.go +++ b/controllers/team.go @@ -1,14 +1,14 @@ package controllers import ( - "log" "net/http" + "github.com/gorilla/mux" + "github.com/UniversityRadioYork/2016-site/models" "github.com/UniversityRadioYork/2016-site/structs" "github.com/UniversityRadioYork/2016-site/utils" "github.com/UniversityRadioYork/myradio-go" - "github.com/gorilla/mux" ) // TeamController is the controller for the team information pages. @@ -29,8 +29,7 @@ func (teamC *TeamController) Get(w http.ResponseWriter, r *http.Request) { alias := vars["alias"] team, heads, assistants, officers, err := teamM.Get(alias) if err != nil { - log.Println(err) - utils.RenderTemplate(w, teamC.config.PageContext, nil, "404.tmpl") + teamC.handleError(w, r, err, "TeamModel.Get") return } @@ -45,9 +44,5 @@ func (teamC *TeamController) Get(w http.ResponseWriter, r *http.Request) { Assistants: assistants, Officers: officers, } - err = utils.RenderTemplate(w, teamC.config.PageContext, data, "team.tmpl") - if err != nil { - log.Println(err) - return - } + utils.RenderTemplate(w, teamC.config.PageContext, data, "team.tmpl") } diff --git a/go.mod b/go.mod index 8a5e00bf..b3a43575 100644 --- a/go.mod +++ b/go.mod @@ -1,15 +1,23 @@ module github.com/UniversityRadioYork/2016-site -go 1.16 +go 1.21 require ( - github.com/BurntSushi/toml v0.4.1 + github.com/BurntSushi/toml v1.2.1 github.com/UniversityRadioYork/myradio-go v0.0.0-20210821190257-67bde48f2e7e github.com/codegangsta/negroni v1.0.0 github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 + github.com/getsentry/sentry-go v0.29.0 github.com/gorilla/mux v1.8.0 - github.com/grokify/html-strip-tags-go v0.0.1 - github.com/microcosm-cc/bluemonday v1.0.15 + github.com/microcosm-cc/bluemonday v1.0.23 github.com/stretchr/graceful v1.2.15 - golang.org/x/net v0.7.0 +) + +require ( + github.com/aymerick/douceur v0.2.0 // indirect + github.com/gorilla/css v1.0.0 // indirect + github.com/urfave/negroni v1.0.0 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect ) diff --git a/go.sum b/go.sum index 9981f051..31ee3167 100644 --- a/go.sum +++ b/go.sum @@ -1,52 +1,44 @@ -github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= -github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/UniversityRadioYork/myradio-go v0.0.0-20210821190257-67bde48f2e7e h1:4tEgYVcZraVZAVMaV/lTCPNiW4ZE3OASjV4JKw3KkXc= github.com/UniversityRadioYork/myradio-go v0.0.0-20210821190257-67bde48f2e7e/go.mod h1:iFH6u3KFaQ73MR9bfqTThGd7TFUYUe9cAajxxh9E0Z8= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/codegangsta/negroni v1.0.0 h1:+aYywywx4bnKXWvoWtRfJ91vC59NbEhEY03sZjQhbVY= github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 h1:Uc+IZ7gYqAf/rSGFplbWBSHaGolEQlNLgMgSE3ccnIQ= github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813/go.mod h1:P+oSoE9yhSRvsmYyZsshflcR6ePWYLql6UU1amW13IM= +github.com/getsentry/sentry-go v0.29.0 h1:YtWluuCFg9OfcqnaujpY918N/AhCCwarIDWOYSBAjCA= +github.com/getsentry/sentry-go v0.29.0/go.mod h1:jhPesDAL0Q0W2+2YEuVOvdWmVtdsr1+jtBrlDEVWwLY= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0= -github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= -github.com/microcosm-cc/bluemonday v1.0.15 h1:J4uN+qPng9rvkBZBoBb8YGR+ijuklIMpSOZZLjYpbeY= -github.com/microcosm-cc/bluemonday v1.0.15/go.mod h1:ZLvAzeakRwrGnzQEvstVzVt3ZpqOF2+sdFr0Om+ce30= +github.com/microcosm-cc/bluemonday v1.0.23 h1:SMZe2IGa0NuHvnVNAZ+6B38gsTbi5e4sViiWJyDDqFY= +github.com/microcosm-cc/bluemonday v1.0.23/go.mod h1:mN70sk7UkkF8TUr2IGBpNN0jAgStuPzlK76QuruE/z4= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/graceful v1.2.15 h1:vmXbwPGfe8bI6KkgmHry/P1Pk63bM3TDcfi+5mh+VHg= github.com/stretchr/graceful v1.2.15/go.mod h1:IxdGAOTZueMKoBr3oJIzdeg5CCCXbHXfV44sLhfAXXI= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 144922f8..5592e5c7 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( "github.com/BurntSushi/toml" "github.com/UniversityRadioYork/2016-site/structs" "github.com/UniversityRadioYork/2016-site/utils" + "github.com/getsentry/sentry-go" "github.com/stretchr/graceful" ) @@ -20,6 +21,17 @@ func main() { log.Fatal(err) } + if config.Server.SentryDSN != "" { + err := sentry.Init(sentry.ClientOptions{ + Dsn: config.Server.SentryDSN, + TracesSampleRate: 0.1, + }) + if err != nil { + log.Fatal(fmt.Errorf("sentry.Init: %s", err)) + } + defer sentry.Flush(2 * time.Second) + } + if config.Schedule.StartHour != 0 { utils.StartHour = config.Schedule.StartHour } diff --git a/sass/main.scss b/sass/main.scss index de7514b8..8290b1e4 100644 --- a/sass/main.scss +++ b/sass/main.scss @@ -201,7 +201,7 @@ footer { width: 100%; } -.page404 { +.error-page { text-align: center; margin: 0 auto; @@ -440,4 +440,4 @@ body { // Remove the height of the nav bar roughly 60px padding: calc(5em + 60px) 0 5em; } -} \ No newline at end of file +} diff --git a/server.go b/server.go index 6127f5a4..9ada28e1 100644 --- a/server.go +++ b/server.go @@ -9,6 +9,8 @@ import ( "github.com/UniversityRadioYork/2016-site/structs" "github.com/UniversityRadioYork/myradio-go" "github.com/codegangsta/negroni" + "github.com/getsentry/sentry-go" + sentrynegroni "github.com/getsentry/sentry-go/negroni" "github.com/gorilla/mux" ) @@ -22,6 +24,18 @@ func NewServer(c *structs.Config) (*Server, error) { s := Server{negroni.Classic()} + s.UseHandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + sentry.AddBreadcrumb(&sentry.Breadcrumb{ + Type: "http", + Category: "request", + Message: fmt.Sprintf("%s %s", r.Method, r.URL.Path), + }) + }) + + s.Use(sentrynegroni.New(sentrynegroni.Options{ + Repanic: true, // let negroni return a HTTP page + })) + var session *myradio.Session var err error if c.Server.MyRadioAPI == "" { diff --git a/structs/config.go b/structs/config.go index 9b704d97..5dbdfd83 100644 --- a/structs/config.go +++ b/structs/config.go @@ -19,6 +19,7 @@ type Server struct { Port int `toml:"port"` Timeout int `toml:"timeout"` MyRadioAPI string `toml:"myradio_api"` + SentryDSN string `toml:"sentry_dsn"` } // PageContext is a structure containing static information to provide @@ -40,7 +41,7 @@ type PageContext struct { ODName string `toml:"odName"` Christmas bool `toml:"christmas"` AprilFools bool `toml:"aprilFools"` - Pride bool `toml:"pride"` + Pride bool `toml:"pride"` CIN bool `toml:"cin"` CINLivestreaming bool `toml:"cinLivestreaming"` CINAPI string `toml:"cinAPI"` @@ -94,14 +95,14 @@ type Page struct { } type youtube struct { - APIKey string `toml:"apiKey"` - CINPlaylistID string `toml:"cinPlaylistID"` - ChannelURL string `toml:"channelURL"` + APIKey string `toml:"apiKey"` + CINPlaylistID string `toml:"cinPlaylistID"` + ChannelURL string `toml:"channelURL"` } type osm struct { - Lat float32 `toml:"latitude"` - Lng float32 `toml:"longitude"` + Lat float32 `toml:"latitude"` + Lng float32 `toml:"longitude"` } // ShortURLsConfig is a structure configuring the short-urls subsystem. diff --git a/utils/date.go b/utils/date.go index b892de0c..56f0eda9 100644 --- a/utils/date.go +++ b/utils/date.go @@ -2,6 +2,7 @@ package utils import ( "fmt" + "net/http" "strconv" "time" @@ -61,7 +62,7 @@ func ParseIsoWeek(isoyear, isoweek, isoweekday string) (year int, week int, week return } if year < 0 { - err = fmt.Errorf("Invalid year: %d", year) + err = NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid year: %d", year)) return } @@ -69,7 +70,7 @@ func ParseIsoWeek(isoyear, isoweek, isoweekday string) (year int, week int, week return } if week < 1 || 53 < week { - err = fmt.Errorf("Invalid week: %d", week) + err = NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid week: %d", week)) return } @@ -80,7 +81,7 @@ func ParseIsoWeek(isoyear, isoweek, isoweekday string) (year int, week int, week return } if di < 1 || 7 < di { - err = fmt.Errorf("Invalid day: %d", di) + err = NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid weekday: %d", di)) return } diff --git a/utils/errors.go b/utils/errors.go new file mode 100644 index 00000000..68ab6de7 --- /dev/null +++ b/utils/errors.go @@ -0,0 +1,16 @@ +package utils + +import "fmt" + +type HTTPError struct { + Status int + Message string +} + +func (h HTTPError) Error() string { + return fmt.Sprintf("%d: %s", h.Status, h.Message) +} + +func NewHTTPError(status int, msg string) error { + return HTTPError{status, msg} +} diff --git a/utils/template.go b/utils/template.go index 1c1f853a..804e8111 100644 --- a/utils/template.go +++ b/utils/template.go @@ -3,8 +3,10 @@ package utils import ( "fmt" "html/template" + "log" "net/http" "path/filepath" + "runtime" "strings" "time" @@ -39,8 +41,8 @@ var BaseTemplates = []string{ // template to render. The variadic argument addTmpls names any additional // templates mainTmpl depends on. // -// RenderTemplate returns any error that occurred when rendering the template. -func RenderTemplate(w http.ResponseWriter, context structs.PageContext, data interface{}, mainTmpl string, addTmpls ...string) error { +// RenderTemplate logs any error that occurred when rendering the template. +func RenderTemplate(w http.ResponseWriter, context structs.PageContext, data interface{}, mainTmpl string, addTmpls ...string) { var err error td := structs.Globals{ @@ -133,10 +135,27 @@ func RenderTemplate(w http.ResponseWriter, context structs.PageContext, data int }) t, err = t.ParseFiles(tmpls...) if err != nil { - return err + logTemplateError("Error parsing templates %v: %v", tmpls, err) } - return t.Execute(w, td) + err = t.Execute(w, td) + if err != nil { + logTemplateError("Error executing template %s: %v", mainTmpl, err) + } +} + +func logTemplateError(errMsg string, args ...interface{}) { + msg := fmt.Sprintf(errMsg, args...) + pc, file, line, ok := runtime.Caller(2) + if ok { + fn := runtime.FuncForPC(pc) + if fn != nil { + msg = fmt.Sprintf("%s [at %s:%d (%s)]", msg, file, line, fn.Name()) + } else { + msg = fmt.Sprintf("%s [at %s:%d]", msg, file, line) + } + } + log.Println(msg) } // renderHTML takes some html as a string and returns a template.HTML diff --git a/views/404.tmpl b/views/404.tmpl index 5caeb214..1ce52cbc 100644 --- a/views/404.tmpl +++ b/views/404.tmpl @@ -1,5 +1,5 @@ {{define "content"}} -
+

4
4

diff --git a/views/500.tmpl b/views/500.tmpl new file mode 100644 index 00000000..ff7f5048 --- /dev/null +++ b/views/500.tmpl @@ -0,0 +1,12 @@ +{{define "content"}} +
+
+
+

5

+

We're sorry, something went very wrong!

+

Unfortunately we ran into an error when preparing this page for you. This is our fault, not yours.

+

We've reported the error and our Computing Team will do their best to fix it.

+
+
+
+{{end}}