From 54f2a66be7d67531928aa24fa1e6744ff09256d4 Mon Sep 17 00:00:00 2001 From: Tim Wundenberg Date: Wed, 4 Dec 2024 21:10:25 +0100 Subject: [PATCH] fix: extract html rendering --- handler/auth.go | 136 ++++++++++++++++----------------------- handler/index_and_404.go | 24 +++---- handler/render.go | 47 ++++++++++++++ handler/workout.go | 41 ++++-------- main.go | 8 ++- service/auth.go | 25 +++---- 6 files changed, 138 insertions(+), 143 deletions(-) create mode 100644 handler/render.go diff --git a/handler/auth.go b/handler/auth.go index 42f07c0..ecf9b7b 100644 --- a/handler/auth.go +++ b/handler/auth.go @@ -2,7 +2,6 @@ package handler import ( "me-fit/service" - "me-fit/template" "me-fit/template/auth" "me-fit/types" "me-fit/utils" @@ -18,32 +17,37 @@ type HandlerAuth interface { } type HandlerAuthImpl struct { - service service.AuthService - serverSettings *types.ServerSettings + service service.AuthService + render *Render } -func NewHandlerAuth(service service.AuthService, serverSettings *types.ServerSettings) HandlerAuth { +func NewHandlerAuth(service service.AuthService, render *Render) HandlerAuth { return HandlerAuthImpl{ - service: service, - serverSettings: serverSettings, + service: service, + render: render, } } func (handler HandlerAuthImpl) Handle(router *http.ServeMux) { // Don't use auth middleware for these routes, as it makes redirecting very difficult, if the mail is not yet verified - router.Handle("/auth/signin", handler.handleSignInPage()) router.Handle("/auth/signup", handler.handleSignUpPage()) - router.Handle("/auth/verify", handler.handleSignUpVerifyPage()) // Hint for the user to verify their email - router.Handle("/auth/delete-account", handler.handleDeleteAccountPage()) - router.Handle("/auth/verify-email", handler.HandleSignUpVerifyResponsePage()) // The link contained in the email - router.Handle("/auth/change-password", handler.handleChangePasswordPage()) - router.Handle("/auth/reset-password", handler.handleResetPasswordPage()) - router.Handle("/api/auth/signup", handler.handleSignUp()) - router.Handle("/api/auth/signin", handler.handleSignIn()) - router.Handle("/api/auth/signout", handler.handleSignOut()) - router.Handle("/api/auth/delete-account", handler.HandleDeleteAccountComp()) + router.Handle("/auth/verify", handler.handleSignUpVerifyPage()) router.Handle("/api/auth/verify-resend", handler.HandleVerifyResendComp()) + router.Handle("/auth/verify-email", handler.HandleSignUpVerifyResponsePage()) + router.Handle("/api/auth/signup", handler.handleSignUp()) + + router.Handle("/auth/signin", handler.handleSignInPage()) + router.Handle("/api/auth/signin", handler.handleSignIn()) + + router.Handle("/api/auth/signout", handler.handleSignOut()) + + router.Handle("/auth/delete-account", handler.handleDeleteAccountPage()) + router.Handle("/api/auth/delete-account", handler.HandleDeleteAccountComp()) + + router.Handle("/auth/change-password", handler.handleChangePasswordPage()) router.Handle("/api/auth/change-password", handler.HandleChangePasswordComp()) + + router.Handle("/auth/reset-password", handler.handleResetPasswordPage()) router.Handle("/api/auth/reset-password", handler.HandleForgotPasswordComp()) router.Handle("/api/auth/reset-password-actual", handler.HandleForgotPasswordResponseComp()) } @@ -54,24 +58,19 @@ var ( func (handler HandlerAuthImpl) handleSignInPage() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - user, err := handler.service.GetUserFromSessionId(utils.GetSessionID(r)) - if err != nil { - userComp := service.UserInfoComp(nil) - signIn := auth.SignInOrUpComp(true) - err := template.Layout(signIn, userComp, handler.serverSettings.Environment).Render(r.Context(), w) - - if err != nil { - utils.LogError("Failed to render sign in page", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) + user, _ := handler.service.GetUserFromSessionId(utils.GetSessionID(r)) + if user != nil { + if !user.EmailVerified { + utils.DoRedirect(w, r, "/auth/verify") + } else { + utils.DoRedirect(w, r, "/") } return } - if !user.EmailVerified { - utils.DoRedirect(w, r, "/auth/verify") - } else { - utils.DoRedirect(w, r, "/") - } + comp := auth.SignInOrUpComp(true) + + handler.render.RenderLayout(r, w, comp, nil) } } @@ -122,23 +121,19 @@ func (handler HandlerAuthImpl) handleSignIn() http.HandlerFunc { func (handler HandlerAuthImpl) handleSignUpPage() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - user, err := handler.service.GetUserFromSessionId(utils.GetSessionID(r)) - if err != nil { - userComp := service.UserInfoComp(nil) - signUpComp := auth.SignInOrUpComp(false) - err := template.Layout(signUpComp, userComp, handler.serverSettings.Environment).Render(r.Context(), w) + user, _ := handler.service.GetUserFromSessionId(utils.GetSessionID(r)) - if err != nil { - utils.LogError("Failed to render sign up page", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) + if user != nil { + if !user.EmailVerified { + utils.DoRedirect(w, r, "/auth/verify") + } else { + utils.DoRedirect(w, r, "/") } + return } - if !user.EmailVerified { - utils.DoRedirect(w, r, "/auth/verify") - } else { - utils.DoRedirect(w, r, "/") - } + signUpComp := auth.SignInOrUpComp(false) + handler.render.RenderLayout(r, w, signUpComp, nil) } } @@ -198,22 +193,19 @@ func (handler HandlerAuthImpl) handleSignOut() http.HandlerFunc { func (handler HandlerAuthImpl) handleSignUpVerifyPage() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - user, err := handler.service.GetUserFromSessionId(utils.GetSessionID(r)) - if err != nil { + user, _ := handler.service.GetUserFromSessionId(utils.GetSessionID(r)) + if user == nil { utils.DoRedirect(w, r, "/auth/signin") + return } if user.EmailVerified { utils.DoRedirect(w, r, "/") - } else { - userComp := service.UserInfoComp(user) - signIn := auth.VerifyComp() - err := template.Layout(signIn, userComp, handler.serverSettings.Environment).Render(r.Context(), w) - if err != nil { - utils.LogError("Failed to render verify page", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - } + return } + + signIn := auth.VerifyComp() + handler.render.RenderLayout(r, w, signIn, user) } } @@ -225,13 +217,8 @@ func (handler HandlerAuthImpl) handleDeleteAccountPage() http.HandlerFunc { utils.DoRedirect(w, r, "/auth/signin") } - userComp := service.UserInfoComp(user) comp := auth.DeleteAccountComp() - err = template.Layout(comp, userComp, handler.serverSettings.Environment).Render(r.Context(), w) - if err != nil { - utils.LogError("Failed to render delete account page", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - } + handler.render.RenderLayout(r, w, comp, user) } } @@ -244,15 +231,11 @@ func (handler HandlerAuthImpl) handleChangePasswordPage() http.HandlerFunc { if user == nil && !isPasswordReset { utils.DoRedirect(w, r, "/auth/signin") - } else { - userComp := service.UserInfoComp(user) - comp := auth.ChangePasswordComp(isPasswordReset) - err := template.Layout(comp, userComp, handler.serverSettings.Environment).Render(r.Context(), w) - if err != nil { - utils.LogError("Failed to render change password page", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - } + return } + + comp := auth.ChangePasswordComp(isPasswordReset) + handler.render.RenderLayout(r, w, comp, user) } } @@ -262,15 +245,11 @@ func (handler HandlerAuthImpl) handleResetPasswordPage() http.HandlerFunc { user, err := handler.service.GetUserFromSessionId(utils.GetSessionID(r)) if err != nil { utils.DoRedirect(w, r, "/auth/signin") + return } - userComp := service.UserInfoComp(user) comp := auth.ResetPasswordComp() - err = template.Layout(comp, userComp, handler.serverSettings.Environment).Render(r.Context(), w) - if err != nil { - utils.LogError("Failed to render change password page", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - } + handler.render.RenderLayout(r, w, comp, user) } } @@ -283,13 +262,8 @@ func (handler HandlerAuthImpl) HandleResetPasswordPage() http.HandlerFunc { return } - userComp := service.UserInfoComp(user) comp := auth.ResetPasswordComp() - err = template.Layout(comp, userComp, handler.serverSettings.Environment).Render(r.Context(), w) - if err != nil { - utils.LogError("Failed to render change password page", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - } + handler.render.RenderLayout(r, w, comp, user) } } @@ -382,7 +356,7 @@ func (handler HandlerAuthImpl) HandleForgotPasswordComp() http.HandlerFunc { return } - err := handler.service.ForgotPassword(email) + err := handler.service.SendForgotPasswordMail(email) if err != nil { utils.TriggerToast(w, r, "error", "Internal Server Error") } else { @@ -409,7 +383,7 @@ func (handler HandlerAuthImpl) HandleForgotPasswordResponseComp() http.HandlerFu newPass := r.FormValue("new-password") - err = handler.service.ForgotPasswordResponse(token, newPass) + err = handler.service.ForgotPassword(token, newPass) if err != nil { utils.TriggerToast(w, r, "error", err.Error()) } else { diff --git a/handler/index_and_404.go b/handler/index_and_404.go index 5654336..577cb84 100644 --- a/handler/index_and_404.go +++ b/handler/index_and_404.go @@ -3,7 +3,6 @@ package handler import ( "me-fit/service" "me-fit/template" - "me-fit/types" "me-fit/utils" "net/http" @@ -16,14 +15,14 @@ type IndexHandler interface { } type IndexHandlerImpl struct { - service service.AuthService - serverSettings *types.ServerSettings + service service.AuthService + render *Render } -func NewIndexHandler(service service.AuthService, serverSettings *types.ServerSettings) IndexHandler { +func NewIndexHandler(service service.AuthService, render *Render) IndexHandler { return IndexHandlerImpl{ - service: service, - serverSettings: serverSettings, + service: service, + render: render, } } @@ -35,20 +34,15 @@ func (handler IndexHandlerImpl) handleIndexAnd404() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { user, _ := handler.service.GetUserFromSessionId(utils.GetSessionID(r)) - var comp templ.Component = nil - userComp := service.UserInfoComp(user) + var comp templ.Component if r.URL.Path != "/" { - comp = template.Layout(template.NotFound(), userComp, handler.serverSettings.Environment) + comp = template.NotFound() w.WriteHeader(http.StatusNotFound) } else { - comp = template.Layout(template.Index(), userComp, handler.serverSettings.Environment) + comp = template.Index() } - err := comp.Render(r.Context(), w) - if err != nil { - utils.LogError("Failed to render index", err) - http.Error(w, "Failed to render index", http.StatusInternalServerError) - } + handler.render.RenderLayout(r, w, comp, user) } } diff --git a/handler/render.go b/handler/render.go new file mode 100644 index 0000000..56d2227 --- /dev/null +++ b/handler/render.go @@ -0,0 +1,47 @@ +package handler + +import ( + "me-fit/service" + "me-fit/template" + "me-fit/template/auth" + "me-fit/types" + "me-fit/utils" + + "net/http" + + "github.com/a-h/templ" +) + +type Render struct { + serverSettings *types.ServerSettings +} + +func NewRender(serverSettings *types.ServerSettings) *Render { + return &Render{ + serverSettings: serverSettings, + } +} + +func (render *Render) Render(r *http.Request, w http.ResponseWriter, comp templ.Component) { + err := comp.Render(r.Context(), w) + if err != nil { + utils.LogError("Failed to render layout", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + } +} + +func (render *Render) RenderLayout(r *http.Request, w http.ResponseWriter, slot templ.Component, user *service.User) { + userComp := render.getUserComp(user) + layout := template.Layout(slot, userComp, render.serverSettings.Environment) + + render.Render(r, w, layout) +} + +func (render *Render) getUserComp(user *service.User) templ.Component { + + if user != nil { + return auth.UserComp(user.Email) + } else { + return auth.UserComp("") + } +} diff --git a/handler/workout.go b/handler/workout.go index 4c36157..6e8815a 100644 --- a/handler/workout.go +++ b/handler/workout.go @@ -2,9 +2,7 @@ package handler import ( "me-fit/service" - "me-fit/template" "me-fit/template/workout" - "me-fit/types" "me-fit/utils" "log/slog" @@ -18,16 +16,16 @@ type WorkoutHandler interface { } type WorkoutHandlerImpl struct { - service service.WorkoutService - auth service.AuthService - serverSettings *types.ServerSettings + service service.WorkoutService + auth service.AuthService + render *Render } -func NewWorkoutHandler(service service.WorkoutService, auth service.AuthService, serverSettings *types.ServerSettings) WorkoutHandler { +func NewWorkoutHandler(service service.WorkoutService, auth service.AuthService, render *Render) WorkoutHandler { return WorkoutHandlerImpl{ - service: service, - auth: auth, - serverSettings: serverSettings, + service: service, + auth: auth, + render: render, } } @@ -47,13 +45,8 @@ func (handler WorkoutHandlerImpl) handleWorkoutPage() http.HandlerFunc { } currentDate := time.Now().Format("2006-01-02") - inner := workout.WorkoutComp(currentDate) - userComp := service.UserInfoComp(user) - err = template.Layout(inner, userComp, handler.serverSettings.Environment).Render(r.Context(), w) - if err != nil { - utils.LogError("Failed to render workout page", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - } + comp := workout.WorkoutComp(currentDate) + handler.render.RenderLayout(r, w, comp, user) } } @@ -79,12 +72,8 @@ func (handler WorkoutHandlerImpl) handleAddWorkout() http.HandlerFunc { } wor := workout.Workout{Id: wo.RowId, Date: wo.Date, Type: wo.Type, Sets: wo.Sets, Reps: wo.Reps} - err = workout.WorkoutItemComp(wor, true).Render(r.Context(), w) - if err != nil { - utils.LogError("Could not render workoutitem", err) - utils.TriggerToast(w, r, "error", "Internal Server Error") - http.Error(w, err.Error(), http.StatusInternalServerError) - } + comp := workout.WorkoutItemComp(wor, true) + handler.render.Render(r, w, comp) } } @@ -106,12 +95,8 @@ func (handler WorkoutHandlerImpl) handleGetWorkout() http.HandlerFunc { wos = append(wos, workout.Workout{Id: wo.RowId, Date: wo.Date, Type: wo.Type, Sets: wo.Sets, Reps: wo.Reps}) } - err = workout.WorkoutListComp(wos).Render(r.Context(), w) - if err != nil { - utils.LogError("Could not render workoutlist", err) - utils.TriggerToast(w, r, "error", "Internal Server Error") - http.Error(w, err.Error(), http.StatusInternalServerError) - } + comp := workout.WorkoutListComp(wos) + handler.render.Render(r, w, comp) } } diff --git a/main.go b/main.go index 06ee9ea..805c700 100644 --- a/main.go +++ b/main.go @@ -112,12 +112,14 @@ func createHandler(d *sql.DB, serverSettings *types.ServerSettings) http.Handler randomService := service.NewRandomServiceImpl() clockService := service.NewClockServiceImpl() mailService := service.NewMailServiceImpl(serverSettings) + authService := service.NewAuthServiceImpl(authDb, randomService, clockService, mailService, serverSettings) workoutService := service.NewWorkoutServiceImpl(workoutDb, randomService, clockService, mailService, serverSettings) - indexHandler := handler.NewIndexHandler(authService, serverSettings) - authHandler := handler.NewHandlerAuth(authService, serverSettings) - workoutHandler := handler.NewWorkoutHandler(workoutService, authService, serverSettings) + render := handler.NewRender(serverSettings) + indexHandler := handler.NewIndexHandler(authService, render) + authHandler := handler.NewHandlerAuth(authService, render) + workoutHandler := handler.NewWorkoutHandler(workoutService, authService, render) indexHandler.Handle(router) diff --git a/service/auth.go b/service/auth.go index a67c60b..38ef248 100644 --- a/service/auth.go +++ b/service/auth.go @@ -9,12 +9,10 @@ import ( "time" "me-fit/db" - "me-fit/template/auth" mailTemplate "me-fit/template/mail" "me-fit/types" "me-fit/utils" - "github.com/a-h/templ" "github.com/google/uuid" "golang.org/x/crypto/argon2" ) @@ -56,15 +54,19 @@ func NewSession(session *db.Session, user *User) *Session { } type AuthService interface { - SignIn(email string, password string) (*Session, error) SignUp(email string, password string) (*User, error) SendVerificationMail(userId uuid.UUID, email string) VerifyUserEmail(token string) error + + SignIn(email string, password string) (*Session, error) SignOut(sessionId string) error + DeleteAccount(user *User) error + ChangePassword(user *User, currPass, newPass string) error - ForgotPassword(email string) error - ForgotPasswordResponse(token string, newPass string) error + + SendForgotPasswordMail(email string) error + ForgotPassword(token string, newPass string) error GetUserFromSessionId(sessionId string) (*User, error) } @@ -273,15 +275,6 @@ func (service AuthServiceImpl) GetUserFromSessionId(sessionId string) (*User, er // TODO -func UserInfoComp(user *User) templ.Component { - - if user != nil { - return auth.UserComp(user.Email) - } else { - return auth.UserComp("") - } -} - func (service AuthServiceImpl) DeleteAccount(user *User) error { err := service.dbAuth.DeleteUser(user.Id) @@ -326,7 +319,7 @@ func (service AuthServiceImpl) ChangePassword(user *User, currPass, newPass stri return nil } -func (service AuthServiceImpl) ForgotPassword(email string) error { +func (service AuthServiceImpl) SendForgotPasswordMail(email string) error { tokenStr, err := service.randomGenerator.String(32) if err != nil { @@ -360,7 +353,7 @@ func (service AuthServiceImpl) ForgotPassword(email string) error { return nil } -func (service AuthServiceImpl) ForgotPasswordResponse(tokenStr string, newPass string) error { +func (service AuthServiceImpl) ForgotPassword(tokenStr string, newPass string) error { if !isPasswordValid(newPass) { return ErrInvalidPassword