Skip to content

Commit

Permalink
Merge branch 'main' into feat/183/verify-cookie-route
Browse files Browse the repository at this point in the history
Signed-off-by: Jc <[email protected]>
  • Loading branch information
juancwu authored Dec 23, 2024
2 parents 2b559bb + 971716b commit 5fb916d
Show file tree
Hide file tree
Showing 18 changed files with 1,017 additions and 305 deletions.
10 changes: 10 additions & 0 deletions backend/.sqlc/queries/email_tokens.sql
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,13 @@ WHERE id = $1;

-- name: RemoveVerifyEmailTokenByID :exec
DELETE FROM verify_email_tokens WHERE id = $1;

-- name: NewVerifyEmailToken :one
INSERT INTO verify_email_tokens (user_id, expires_at)
VALUES ($1, $2) RETURNING id;

-- name: ExistsVerifyEmailTokenByUserID :one
SELECT EXISTS(SELECT 1 FROM verify_email_tokens WHERE user_id = $1);

-- name: RemoveVerifyEmailTokenByUserID :exec
DELETE FROM verify_email_tokens WHERE user_id = $1;
37 changes: 37 additions & 0 deletions backend/db/email_tokens.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 11 additions & 10 deletions backend/db/users.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion backend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ require (
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions backend/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
124 changes: 70 additions & 54 deletions backend/internal/server/error_handler.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package server

import (
"KonferCA/SPUR/internal/v1/v1_common"
"encoding/json"
"fmt"
"net/http"
"strings"

"github.com/go-playground/validator/v10"
"github.com/labstack/echo/v4"
Expand All @@ -26,80 +30,92 @@ Example:
})
*/
func errorHandler(err error, c echo.Context) {
req := c.Request()
internalErr, ok := c.Get("internal_error").(error)
if !ok {
internalErr = nil
}
requestID := req.Header.Get(echo.HeaderXRequestID)
var (
code = http.StatusInternalServerError
message = "internal server error"
errType = v1_common.ErrorTypeInternal
details string
)

// default error response
status := http.StatusInternalServerError
message := "internal server error"
var validationErrors []string
internalErr, _ := c.Get("internal_error").(error)

// handle different error types
switch e := err.(type) {
case *echo.HTTPError:
status = e.Code
// since the echo.HTTPError allows type any for the
// message field, we should make sure that it is an
// actual string that was passed before using it.
// problems can arise if an struct was passed but
// not meant to be exposed to the public or
// is just straight up unreadable.
code = e.Code
errType = v1_common.DetermineErrorType(code)
if msg, ok := e.Message.(string); ok {
message = msg
} else {
message = http.StatusText(e.Code)
message = http.StatusText(code)
}

case validator.ValidationErrors:
// handle validation errors specially
status = http.StatusBadRequest
code = http.StatusBadRequest
errType = v1_common.ErrorTypeValidation
message = "validation failed"
validationErrors = make([]string, len(e))
for i, err := range e {
validationErrors[i] = err.Error()
fieldErrors := make(map[string]map[string]interface{})

for _, err := range e {
fieldName := strings.ToLower(err.Field())
fieldErrors[fieldName] = map[string]interface{}{
"tag": err.Tag(),
"value": err.Value(),
"condition": err.Param(),
}
}

case error:
// assign the returned error from handlers as the internal error.
// this is probably an internal error when trying to respond.
// this ensures that no internal error message gets leaks to the public.
if internalErr == nil {
internalErr = err
detailsBytes, err := json.Marshal(fieldErrors)
if err != nil {
details = "error formatting validation details"
log.Error().Err(err).Msg("failed to format validation details")
} else {
details = string(detailsBytes)
}
case *v1_common.APIError:
code = e.Code
errType = e.Type
message = e.Message
details = e.Details
default:
if internalErr != nil {
log.Error().Err(internalErr).Msg("internal error occurred")
} else {
log.Error().Err(err).Msg("unexpected error occurred")
}
details = "an unexpected error occurred. please try again later"
}

requestID := c.Request().Header.Get(echo.HeaderXRequestID)

// log with more context
log.
Error().
AnErr("internal_error", internalErr).
AnErr("request_error", err).
logContext := log.With().
Str("request_error", fmt.Sprintf("code=%d, message=%s", code, message)).
Str("request_id", requestID).
Str("method", req.Method).
Str("path", req.URL.Path).
Int("status", status).
Str("user_agent", req.UserAgent()).
Msg("request error")
Str("method", c.Request().Method).
Str("path", c.Request().URL.Path).
Int("status", code).
Str("user_agent", c.Request().UserAgent())

// return json response
if !c.Response().Committed {
response := ErrorResponse{
Status: status,
Message: message,
RequestID: requestID,
}
if len(validationErrors) > 0 {
response.Errors = validationErrors
}
if internalErr != nil {
logContext = logContext.Str("error", internalErr.Error())
} else if err != nil && code == http.StatusInternalServerError {
logContext = logContext.Str("error", err.Error())
}

logger := logContext.Logger()
logger.Error().Msg("request error")

if err := c.JSON(status, response); err != nil {
log.Error().
Err(err).
Str("request_id", requestID).
Msg("failed to send error response")
apiError := &v1_common.APIError{
Type: errType,
Message: message,
Details: details,
RequestID: requestID,
Code: code,
}

if !c.Response().Committed {
if err := c.JSON(code, apiError); err != nil {
log.Error().Err(err).Msg("failed to send error response")
}
}
}
Loading

0 comments on commit 5fb916d

Please sign in to comment.