fix: extract html rendering

This commit is contained in:
2024-12-04 21:10:25 +01:00
parent 01ceb9eb33
commit 9e8e595258
6 changed files with 138 additions and 145 deletions

View File

@@ -2,7 +2,6 @@ package handler
import ( import (
"me-fit/service" "me-fit/service"
"me-fit/template"
"me-fit/template/auth" "me-fit/template/auth"
"me-fit/types" "me-fit/types"
"me-fit/utils" "me-fit/utils"
@@ -19,31 +18,36 @@ type HandlerAuth interface {
type HandlerAuthImpl struct { type HandlerAuthImpl struct {
service service.AuthService service service.AuthService
serverSettings *types.ServerSettings render *Render
} }
func NewHandlerAuth(service service.AuthService, serverSettings *types.ServerSettings) HandlerAuth { func NewHandlerAuth(service service.AuthService, render *Render) HandlerAuth {
return HandlerAuthImpl{ return HandlerAuthImpl{
service: service, service: service,
serverSettings: serverSettings, render: render,
} }
} }
func (handler HandlerAuthImpl) Handle(router *http.ServeMux) { 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 // 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/signup", handler.handleSignUpPage())
router.Handle("/auth/verify", handler.handleSignUpVerifyPage()) // Hint for the user to verify their email router.Handle("/auth/verify", handler.handleSignUpVerifyPage())
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("/api/auth/verify-resend", handler.HandleVerifyResendComp()) 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("/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", handler.HandleForgotPasswordComp())
router.Handle("/api/auth/reset-password-actual", handler.HandleForgotPasswordResponseComp()) router.Handle("/api/auth/reset-password-actual", handler.HandleForgotPasswordResponseComp())
} }
@@ -54,24 +58,19 @@ var (
func (handler HandlerAuthImpl) handleSignInPage() http.HandlerFunc { func (handler HandlerAuthImpl) handleSignInPage() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
user, err := handler.service.GetUserFromSessionId(utils.GetSessionID(r)) user, _ := handler.service.GetUserFromSessionId(utils.GetSessionID(r))
if err != nil { if user != 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)
}
return
}
if !user.EmailVerified { if !user.EmailVerified {
utils.DoRedirect(w, r, "/auth/verify") utils.DoRedirect(w, r, "/auth/verify")
} else { } else {
utils.DoRedirect(w, r, "/") utils.DoRedirect(w, r, "/")
} }
return
}
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 { func (handler HandlerAuthImpl) handleSignUpPage() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
user, err := handler.service.GetUserFromSessionId(utils.GetSessionID(r)) user, _ := 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)
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 { if !user.EmailVerified {
utils.DoRedirect(w, r, "/auth/verify") utils.DoRedirect(w, r, "/auth/verify")
} else { } else {
utils.DoRedirect(w, r, "/") utils.DoRedirect(w, r, "/")
} }
return
}
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 { func (handler HandlerAuthImpl) handleSignUpVerifyPage() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
user, err := handler.service.GetUserFromSessionId(utils.GetSessionID(r)) user, _ := handler.service.GetUserFromSessionId(utils.GetSessionID(r))
if err != nil { if user == nil {
utils.DoRedirect(w, r, "/auth/signin") utils.DoRedirect(w, r, "/auth/signin")
return
} }
if user.EmailVerified { if user.EmailVerified {
utils.DoRedirect(w, r, "/") utils.DoRedirect(w, r, "/")
} else { return
userComp := service.UserInfoComp(user) }
signIn := auth.VerifyComp() signIn := auth.VerifyComp()
err := template.Layout(signIn, userComp, handler.serverSettings.Environment).Render(r.Context(), w) handler.render.RenderLayout(r, w, signIn, user)
if err != nil {
utils.LogError("Failed to render verify page", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}
} }
} }
@@ -225,13 +217,8 @@ func (handler HandlerAuthImpl) handleDeleteAccountPage() http.HandlerFunc {
utils.DoRedirect(w, r, "/auth/signin") utils.DoRedirect(w, r, "/auth/signin")
} }
userComp := service.UserInfoComp(user)
comp := auth.DeleteAccountComp() comp := auth.DeleteAccountComp()
err = template.Layout(comp, userComp, handler.serverSettings.Environment).Render(r.Context(), w) handler.render.RenderLayout(r, w, comp, user)
if err != nil {
utils.LogError("Failed to render delete account page", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
} }
} }
@@ -244,15 +231,11 @@ func (handler HandlerAuthImpl) handleChangePasswordPage() http.HandlerFunc {
if user == nil && !isPasswordReset { if user == nil && !isPasswordReset {
utils.DoRedirect(w, r, "/auth/signin") utils.DoRedirect(w, r, "/auth/signin")
} else { return
userComp := service.UserInfoComp(user) }
comp := auth.ChangePasswordComp(isPasswordReset) comp := auth.ChangePasswordComp(isPasswordReset)
err := template.Layout(comp, userComp, handler.serverSettings.Environment).Render(r.Context(), w) handler.render.RenderLayout(r, w, comp, user)
if err != nil {
utils.LogError("Failed to render change password page", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}
} }
} }
@@ -262,15 +245,11 @@ func (handler HandlerAuthImpl) handleResetPasswordPage() http.HandlerFunc {
user, err := handler.service.GetUserFromSessionId(utils.GetSessionID(r)) user, err := handler.service.GetUserFromSessionId(utils.GetSessionID(r))
if err != nil { if err != nil {
utils.DoRedirect(w, r, "/auth/signin") utils.DoRedirect(w, r, "/auth/signin")
return
} }
userComp := service.UserInfoComp(user)
comp := auth.ResetPasswordComp() comp := auth.ResetPasswordComp()
err = template.Layout(comp, userComp, handler.serverSettings.Environment).Render(r.Context(), w) handler.render.RenderLayout(r, w, comp, user)
if err != nil {
utils.LogError("Failed to render change password page", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
} }
} }
@@ -283,13 +262,8 @@ func (handler HandlerAuthImpl) HandleResetPasswordPage() http.HandlerFunc {
return return
} }
userComp := service.UserInfoComp(user)
comp := auth.ResetPasswordComp() comp := auth.ResetPasswordComp()
err = template.Layout(comp, userComp, handler.serverSettings.Environment).Render(r.Context(), w) handler.render.RenderLayout(r, w, comp, user)
if err != nil {
utils.LogError("Failed to render change password page", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
} }
} }
@@ -382,7 +356,7 @@ func (handler HandlerAuthImpl) HandleForgotPasswordComp() http.HandlerFunc {
return return
} }
err := handler.service.ForgotPassword(email) err := handler.service.SendForgotPasswordMail(email)
if err != nil { if err != nil {
utils.TriggerToast(w, r, "error", "Internal Server Error") utils.TriggerToast(w, r, "error", "Internal Server Error")
} else { } else {
@@ -409,7 +383,7 @@ func (handler HandlerAuthImpl) HandleForgotPasswordResponseComp() http.HandlerFu
newPass := r.FormValue("new-password") newPass := r.FormValue("new-password")
err = handler.service.ForgotPasswordResponse(token, newPass) err = handler.service.ForgotPassword(token, newPass)
if err != nil { if err != nil {
utils.TriggerToast(w, r, "error", err.Error()) utils.TriggerToast(w, r, "error", err.Error())
} else { } else {

View File

@@ -3,7 +3,6 @@ package handler
import ( import (
"me-fit/service" "me-fit/service"
"me-fit/template" "me-fit/template"
"me-fit/types"
"me-fit/utils" "me-fit/utils"
"net/http" "net/http"
@@ -17,13 +16,13 @@ type IndexHandler interface {
type IndexHandlerImpl struct { type IndexHandlerImpl struct {
service service.AuthService service service.AuthService
serverSettings *types.ServerSettings render *Render
} }
func NewIndexHandler(service service.AuthService, serverSettings *types.ServerSettings) IndexHandler { func NewIndexHandler(service service.AuthService, render *Render) IndexHandler {
return IndexHandlerImpl{ return IndexHandlerImpl{
service: service, service: service,
serverSettings: serverSettings, render: render,
} }
} }
@@ -35,20 +34,15 @@ func (handler IndexHandlerImpl) handleIndexAnd404() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
user, _ := handler.service.GetUserFromSessionId(utils.GetSessionID(r)) user, _ := handler.service.GetUserFromSessionId(utils.GetSessionID(r))
var comp templ.Component = nil var comp templ.Component
userComp := service.UserInfoComp(user)
if r.URL.Path != "/" { if r.URL.Path != "/" {
comp = template.Layout(template.NotFound(), userComp, handler.serverSettings.Environment) comp = template.NotFound()
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
} else { } else {
comp = template.Layout(template.Index(), userComp, handler.serverSettings.Environment) comp = template.Index()
} }
err := comp.Render(r.Context(), w) handler.render.RenderLayout(r, w, comp, user)
if err != nil {
utils.LogError("Failed to render index", err)
http.Error(w, "Failed to render index", http.StatusInternalServerError)
}
} }
} }

47
handler/render.go Normal file
View File

@@ -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("")
}
}

View File

@@ -2,9 +2,7 @@ package handler
import ( import (
"me-fit/service" "me-fit/service"
"me-fit/template"
"me-fit/template/workout" "me-fit/template/workout"
"me-fit/types"
"me-fit/utils" "me-fit/utils"
"log/slog" "log/slog"
@@ -20,14 +18,14 @@ type WorkoutHandler interface {
type WorkoutHandlerImpl struct { type WorkoutHandlerImpl struct {
service service.WorkoutService service service.WorkoutService
auth service.AuthService auth service.AuthService
serverSettings *types.ServerSettings 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{ return WorkoutHandlerImpl{
service: service, service: service,
auth: auth, auth: auth,
serverSettings: serverSettings, render: render,
} }
} }
@@ -47,13 +45,8 @@ func (handler WorkoutHandlerImpl) handleWorkoutPage() http.HandlerFunc {
} }
currentDate := time.Now().Format("2006-01-02") currentDate := time.Now().Format("2006-01-02")
inner := workout.WorkoutComp(currentDate) comp := workout.WorkoutComp(currentDate)
userComp := service.UserInfoComp(user) handler.render.RenderLayout(r, w, comp, 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)
}
} }
} }
@@ -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} 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) comp := workout.WorkoutItemComp(wor, true)
if err != nil { handler.render.Render(r, w, comp)
utils.LogError("Could not render workoutitem", err)
utils.TriggerToast(w, r, "error", "Internal Server Error")
http.Error(w, err.Error(), http.StatusInternalServerError)
}
} }
} }
@@ -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}) 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) comp := workout.WorkoutListComp(wos)
if err != nil { handler.render.Render(r, w, comp)
utils.LogError("Could not render workoutlist", err)
utils.TriggerToast(w, r, "error", "Internal Server Error")
http.Error(w, err.Error(), http.StatusInternalServerError)
}
} }
} }

View File

@@ -112,12 +112,14 @@ func createHandler(d *sql.DB, serverSettings *types.ServerSettings) http.Handler
randomService := service.NewRandomServiceImpl() randomService := service.NewRandomServiceImpl()
clockService := service.NewClockServiceImpl() clockService := service.NewClockServiceImpl()
mailService := service.NewMailServiceImpl(serverSettings) mailService := service.NewMailServiceImpl(serverSettings)
authService := service.NewAuthServiceImpl(authDb, randomService, clockService, mailService, serverSettings) authService := service.NewAuthServiceImpl(authDb, randomService, clockService, mailService, serverSettings)
workoutService := service.NewWorkoutServiceImpl(workoutDb, randomService, clockService, mailService, serverSettings) workoutService := service.NewWorkoutServiceImpl(workoutDb, randomService, clockService, mailService, serverSettings)
indexHandler := handler.NewIndexHandler(authService, serverSettings) render := handler.NewRender(serverSettings)
authHandler := handler.NewHandlerAuth(authService, serverSettings) indexHandler := handler.NewIndexHandler(authService, render)
workoutHandler := handler.NewWorkoutHandler(workoutService, authService, serverSettings) authHandler := handler.NewHandlerAuth(authService, render)
workoutHandler := handler.NewWorkoutHandler(workoutService, authService, render)
indexHandler.Handle(router) indexHandler.Handle(router)

View File

@@ -9,12 +9,10 @@ import (
"time" "time"
"me-fit/db" "me-fit/db"
"me-fit/template/auth"
mailTemplate "me-fit/template/mail" mailTemplate "me-fit/template/mail"
"me-fit/types" "me-fit/types"
"me-fit/utils" "me-fit/utils"
"github.com/a-h/templ"
"github.com/google/uuid" "github.com/google/uuid"
"golang.org/x/crypto/argon2" "golang.org/x/crypto/argon2"
) )
@@ -56,15 +54,19 @@ func NewSession(session *db.Session, user *User) *Session {
} }
type AuthService interface { type AuthService interface {
SignIn(email string, password string) (*Session, error)
SignUp(email string, password string) (*User, error) SignUp(email string, password string) (*User, error)
SendVerificationMail(userId uuid.UUID, email string) SendVerificationMail(userId uuid.UUID, email string)
VerifyUserEmail(token string) error VerifyUserEmail(token string) error
SignIn(email string, password string) (*Session, error)
SignOut(sessionId string) error SignOut(sessionId string) error
DeleteAccount(user *User) error DeleteAccount(user *User) error
ChangePassword(user *User, currPass, newPass string) 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) GetUserFromSessionId(sessionId string) (*User, error)
} }
@@ -271,17 +273,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 { func (service AuthServiceImpl) DeleteAccount(user *User) error {
err := service.dbAuth.DeleteUser(user.Id) err := service.dbAuth.DeleteUser(user.Id)
@@ -326,7 +317,7 @@ func (service AuthServiceImpl) ChangePassword(user *User, currPass, newPass stri
return nil return nil
} }
func (service AuthServiceImpl) ForgotPassword(email string) error { func (service AuthServiceImpl) SendForgotPasswordMail(email string) error {
tokenStr, err := service.randomGenerator.String(32) tokenStr, err := service.randomGenerator.String(32)
if err != nil { if err != nil {
@@ -360,7 +351,7 @@ func (service AuthServiceImpl) ForgotPassword(email string) error {
return nil return nil
} }
func (service AuthServiceImpl) ForgotPasswordResponse(tokenStr string, newPass string) error { func (service AuthServiceImpl) ForgotPassword(tokenStr string, newPass string) error {
if !isPasswordValid(newPass) { if !isPasswordValid(newPass) {
return ErrInvalidPassword return ErrInvalidPassword