diff --git a/handlers/account.go b/handlers/account.go index dac0a3b..f9d60fc 100644 --- a/handlers/account.go +++ b/handlers/account.go @@ -3,6 +3,7 @@ package handlers import ( "context" "net/http" + "time" "projectreshoot/contexts" "projectreshoot/cookies" @@ -47,35 +48,41 @@ func HandleChangeUsername( ) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { - WithTransaction(w, r, logger, conn, - func(ctx context.Context, tx *db.SafeTX, w http.ResponseWriter, r *http.Request) { - r.ParseForm() - newUsername := r.FormValue("username") - unique, err := db.CheckUsernameUnique(ctx, tx, newUsername) - if err != nil { - tx.Rollback() - logger.Error().Err(err).Msg("Error updating username") - w.WriteHeader(http.StatusInternalServerError) - return - } - if !unique { - tx.Rollback() - account.ChangeUsername("Username is taken", newUsername). - Render(r.Context(), w) - return - } - user := contexts.GetUser(r.Context()) - err = user.ChangeUsername(ctx, tx, newUsername) - if err != nil { - tx.Rollback() - logger.Error().Err(err).Msg("Error updating username") - w.WriteHeader(http.StatusInternalServerError) - return - } - tx.Commit() - w.Header().Set("HX-Refresh", "true") - }, - ) + ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) + defer cancel() + + // Start the transaction + tx, err := conn.Begin(ctx) + if err != nil { + logger.Warn().Err(err).Msg("Error updating username") + w.WriteHeader(http.StatusServiceUnavailable) + return + } + r.ParseForm() + newUsername := r.FormValue("username") + unique, err := db.CheckUsernameUnique(ctx, tx, newUsername) + if err != nil { + tx.Rollback() + logger.Error().Err(err).Msg("Error updating username") + w.WriteHeader(http.StatusInternalServerError) + return + } + if !unique { + tx.Rollback() + account.ChangeUsername("Username is taken", newUsername). + Render(r.Context(), w) + return + } + user := contexts.GetUser(r.Context()) + err = user.ChangeUsername(ctx, tx, newUsername) + if err != nil { + tx.Rollback() + logger.Error().Err(err).Msg("Error updating username") + w.WriteHeader(http.StatusInternalServerError) + return + } + tx.Commit() + w.Header().Set("HX-Refresh", "true") }, ) } @@ -87,29 +94,35 @@ func HandleChangeBio( ) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { - WithTransaction(w, r, logger, conn, - func(ctx context.Context, tx *db.SafeTX, w http.ResponseWriter, r *http.Request) { - r.ParseForm() - newBio := r.FormValue("bio") - leng := len([]rune(newBio)) - if leng > 128 { - tx.Rollback() - account.ChangeBio("Bio limited to 128 characters", newBio). - Render(r.Context(), w) - return - } - user := contexts.GetUser(r.Context()) - err := user.ChangeBio(ctx, tx, newBio) - if err != nil { - tx.Rollback() - logger.Error().Err(err).Msg("Error updating bio") - w.WriteHeader(http.StatusInternalServerError) - return - } - tx.Commit() - w.Header().Set("HX-Refresh", "true") - }, - ) + ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) + defer cancel() + + // Start the transaction + tx, err := conn.Begin(ctx) + if err != nil { + logger.Warn().Err(err).Msg("Error updating bio") + w.WriteHeader(http.StatusServiceUnavailable) + return + } + r.ParseForm() + newBio := r.FormValue("bio") + leng := len([]rune(newBio)) + if leng > 128 { + tx.Rollback() + account.ChangeBio("Bio limited to 128 characters", newBio). + Render(r.Context(), w) + return + } + user := contexts.GetUser(r.Context()) + err = user.ChangeBio(ctx, tx, newBio) + if err != nil { + tx.Rollback() + logger.Error().Err(err).Msg("Error updating bio") + w.WriteHeader(http.StatusInternalServerError) + return + } + tx.Commit() + w.Header().Set("HX-Refresh", "true") }, ) } @@ -137,26 +150,32 @@ func HandleChangePassword( ) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { - WithTransaction(w, r, logger, conn, - func(ctx context.Context, tx *db.SafeTX, w http.ResponseWriter, r *http.Request) { - newPass, err := validateChangePassword(ctx, tx, r) - if err != nil { - tx.Rollback() - account.ChangePassword(err.Error()).Render(r.Context(), w) - return - } - user := contexts.GetUser(r.Context()) - err = user.SetPassword(ctx, tx, newPass) - if err != nil { - tx.Rollback() - logger.Error().Err(err).Msg("Error updating password") - w.WriteHeader(http.StatusInternalServerError) - return - } - tx.Commit() - w.Header().Set("HX-Refresh", "true") - }, - ) + ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) + defer cancel() + + // Start the transaction + tx, err := conn.Begin(ctx) + if err != nil { + logger.Warn().Err(err).Msg("Error updating password") + w.WriteHeader(http.StatusServiceUnavailable) + return + } + newPass, err := validateChangePassword(ctx, tx, r) + if err != nil { + tx.Rollback() + account.ChangePassword(err.Error()).Render(r.Context(), w) + return + } + user := contexts.GetUser(r.Context()) + err = user.SetPassword(ctx, tx, newPass) + if err != nil { + tx.Rollback() + logger.Error().Err(err).Msg("Error updating password") + w.WriteHeader(http.StatusInternalServerError) + return + } + tx.Commit() + w.Header().Set("HX-Refresh", "true") }, ) } diff --git a/handlers/login.go b/handlers/login.go index 8788b01..b646e7e 100644 --- a/handlers/login.go +++ b/handlers/login.go @@ -3,6 +3,7 @@ package handlers import ( "context" "net/http" + "time" "projectreshoot/config" "projectreshoot/cookies" @@ -55,34 +56,41 @@ func HandleLoginRequest( ) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { - WithTransaction(w, r, logger, conn, - func(ctx context.Context, tx *db.SafeTX, w http.ResponseWriter, r *http.Request) { - r.ParseForm() - user, err := validateLogin(ctx, tx, r) - if err != nil { - tx.Rollback() - if err.Error() != "Username or password incorrect" { - logger.Warn().Caller().Err(err).Msg("Login request failed") - w.WriteHeader(http.StatusInternalServerError) - } else { - form.LoginForm(err.Error()).Render(r.Context(), w) - } - return - } + ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) + defer cancel() - rememberMe := checkRememberMe(r) - err = cookies.SetTokenCookies(w, r, config, user, true, rememberMe) - if err != nil { - tx.Rollback() - w.WriteHeader(http.StatusInternalServerError) - logger.Warn().Caller().Err(err).Msg("Failed to set token cookies") - return - } + // Start the transaction + tx, err := conn.Begin(ctx) + if err != nil { + logger.Warn().Err(err).Msg("Failed to set token cookies") + w.WriteHeader(http.StatusServiceUnavailable) + return + } + r.ParseForm() + user, err := validateLogin(ctx, tx, r) + if err != nil { + tx.Rollback() + if err.Error() != "Username or password incorrect" { + logger.Warn().Caller().Err(err).Msg("Login request failed") + w.WriteHeader(http.StatusInternalServerError) + } else { + form.LoginForm(err.Error()).Render(r.Context(), w) + } + return + } - tx.Commit() - pageFrom := cookies.CheckPageFrom(w, r) - w.Header().Set("HX-Redirect", pageFrom) - }) + rememberMe := checkRememberMe(r) + err = cookies.SetTokenCookies(w, r, config, user, true, rememberMe) + if err != nil { + tx.Rollback() + w.WriteHeader(http.StatusInternalServerError) + logger.Warn().Caller().Err(err).Msg("Failed to set token cookies") + return + } + + tx.Commit() + pageFrom := cookies.CheckPageFrom(w, r) + w.Header().Set("HX-Redirect", pageFrom) }, ) } diff --git a/handlers/logout.go b/handlers/logout.go index da78925..b93db43 100644 --- a/handlers/logout.go +++ b/handlers/logout.go @@ -4,6 +4,7 @@ import ( "context" "net/http" "strings" + "time" "projectreshoot/config" "projectreshoot/cookies" @@ -86,20 +87,27 @@ func HandleLogout( ) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { - WithTransaction(w, r, logger, conn, - func(ctx context.Context, tx *db.SafeTX, w http.ResponseWriter, r *http.Request) { - err := revokeTokens(config, ctx, tx, r) - if err != nil { - tx.Rollback() - logger.Error().Err(err).Msg("Error occured on user logout") - w.WriteHeader(http.StatusInternalServerError) - return - } - tx.Commit() - cookies.DeleteCookie(w, "access", "/") - cookies.DeleteCookie(w, "refresh", "/") - w.Header().Set("HX-Redirect", "/login") - }) + ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) + defer cancel() + + // Start the transaction + tx, err := conn.Begin(ctx) + if err != nil { + logger.Warn().Err(err).Msg("Error occured on user logout") + w.WriteHeader(http.StatusServiceUnavailable) + return + } + err = revokeTokens(config, ctx, tx, r) + if err != nil { + tx.Rollback() + logger.Error().Err(err).Msg("Error occured on user logout") + w.WriteHeader(http.StatusInternalServerError) + return + } + tx.Commit() + cookies.DeleteCookie(w, "access", "/") + cookies.DeleteCookie(w, "refresh", "/") + w.Header().Set("HX-Redirect", "/login") }, ) } diff --git a/handlers/reauthenticatate.go b/handlers/reauthenticatate.go index 87a0928..6adb3f2 100644 --- a/handlers/reauthenticatate.go +++ b/handlers/reauthenticatate.go @@ -3,6 +3,7 @@ package handlers import ( "context" "net/http" + "time" "projectreshoot/config" "projectreshoot/contexts" @@ -105,25 +106,32 @@ func HandleReauthenticate( ) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { - WithTransaction(w, r, logger, conn, - func(ctx context.Context, tx *db.SafeTX, w http.ResponseWriter, r *http.Request) { - err := validatePassword(r) - if err != nil { - tx.Rollback() - w.WriteHeader(445) - form.ConfirmPassword("Incorrect password").Render(r.Context(), w) - return - } - err = refreshTokens(config, ctx, tx, w, r) - if err != nil { - tx.Rollback() - logger.Error().Err(err).Msg("Failed to refresh user tokens") - w.WriteHeader(http.StatusInternalServerError) - return - } - tx.Commit() - w.WriteHeader(http.StatusOK) - }) + ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) + defer cancel() + + // Start the transaction + tx, err := conn.Begin(ctx) + if err != nil { + logger.Warn().Err(err).Msg("Failed to refresh user tokens") + w.WriteHeader(http.StatusServiceUnavailable) + return + } + err = validatePassword(r) + if err != nil { + tx.Rollback() + w.WriteHeader(445) + form.ConfirmPassword("Incorrect password").Render(r.Context(), w) + return + } + err = refreshTokens(config, ctx, tx, w, r) + if err != nil { + tx.Rollback() + logger.Error().Err(err).Msg("Failed to refresh user tokens") + w.WriteHeader(http.StatusInternalServerError) + return + } + tx.Commit() + w.WriteHeader(http.StatusOK) }, ) } diff --git a/handlers/register.go b/handlers/register.go index 605b02c..dc4e856 100644 --- a/handlers/register.go +++ b/handlers/register.go @@ -3,6 +3,7 @@ package handlers import ( "context" "net/http" + "time" "projectreshoot/config" "projectreshoot/cookies" @@ -50,36 +51,42 @@ func HandleRegisterRequest( ) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { - WithTransaction(w, r, logger, conn, - func(ctx context.Context, tx *db.SafeTX, w http.ResponseWriter, r *http.Request) { - r.ParseForm() - user, err := validateRegistration(ctx, tx, r) - if err != nil { - tx.Rollback() - if err.Error() != "Username is taken" && - err.Error() != "Passwords do not match" && - err.Error() != "Password exceeds maximum length of 72 bytes" { - logger.Warn().Caller().Err(err).Msg("Registration request failed") - w.WriteHeader(http.StatusInternalServerError) - } else { - form.RegisterForm(err.Error()).Render(r.Context(), w) - } - return - } + ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) + defer cancel() - rememberMe := checkRememberMe(r) - err = cookies.SetTokenCookies(w, r, config, user, true, rememberMe) - if err != nil { - tx.Rollback() - w.WriteHeader(http.StatusInternalServerError) - logger.Warn().Caller().Err(err).Msg("Failed to set token cookies") - return - } - tx.Commit() - pageFrom := cookies.CheckPageFrom(w, r) - w.Header().Set("HX-Redirect", pageFrom) - }, - ) + // Start the transaction + tx, err := conn.Begin(ctx) + if err != nil { + logger.Warn().Err(err).Msg("Failed to set token cookies") + w.WriteHeader(http.StatusServiceUnavailable) + return + } + r.ParseForm() + user, err := validateRegistration(ctx, tx, r) + if err != nil { + tx.Rollback() + if err.Error() != "Username is taken" && + err.Error() != "Passwords do not match" && + err.Error() != "Password exceeds maximum length of 72 bytes" { + logger.Warn().Caller().Err(err).Msg("Registration request failed") + w.WriteHeader(http.StatusInternalServerError) + } else { + form.RegisterForm(err.Error()).Render(r.Context(), w) + } + return + } + + rememberMe := checkRememberMe(r) + err = cookies.SetTokenCookies(w, r, config, user, true, rememberMe) + if err != nil { + tx.Rollback() + w.WriteHeader(http.StatusInternalServerError) + logger.Warn().Caller().Err(err).Msg("Failed to set token cookies") + return + } + tx.Commit() + pageFrom := cookies.CheckPageFrom(w, r) + w.Header().Set("HX-Redirect", pageFrom) }, ) } diff --git a/handlers/withtransaction.go b/handlers/withtransaction.go index a097719..c5e23d0 100644 --- a/handlers/withtransaction.go +++ b/handlers/withtransaction.go @@ -6,12 +6,11 @@ import ( "time" "projectreshoot/db" - "projectreshoot/view/page" "github.com/rs/zerolog" ) -func WithTransaction( +func removeme( w http.ResponseWriter, r *http.Request, logger *zerolog.Logger, @@ -22,6 +21,7 @@ func WithTransaction( w http.ResponseWriter, r *http.Request, ), + onfail func(err error), ) { ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) defer cancel() @@ -29,13 +29,7 @@ func WithTransaction( // Start the transaction tx, err := conn.Begin(ctx) if err != nil { - logger.Warn().Err(err).Msg("Request failed to start a transaction") - w.WriteHeader(http.StatusServiceUnavailable) - page.Error( - "503", - http.StatusText(503), - "This service is currently unavailable. It could be down for maintenance"). - Render(r.Context(), w) + onfail(err) return } diff --git a/view/component/popup/errorPopup.templ b/view/component/popup/error500Popup.templ similarity index 95% rename from view/component/popup/errorPopup.templ rename to view/component/popup/error500Popup.templ index f809230..45573e0 100644 --- a/view/component/popup/errorPopup.templ +++ b/view/component/popup/error500Popup.templ @@ -1,9 +1,9 @@ package popup -templ ErrorPopup() { +templ Error500Popup() {
+ The service is currently available. It could be down for maintenance. + Please try again later. +
+