feat: add response errors

This commit is contained in:
chris
2023-08-26 12:18:01 +08:00
parent be929a2632
commit 1d049bfee6
25 changed files with 139 additions and 80 deletions

4
.gitignore vendored
View File

@@ -1,5 +1,5 @@
storage
.idea
*.log
scripts/docker-compose/conf
scripts/docker-compose/data
deploy/docker-compose/conf
deploy/docker-compose/data

View File

@@ -3,6 +3,12 @@ init:
go install github.com/google/wire/cmd/wire@latest
go install github.com/golang/mock/mockgen@latest
.PHONY: bootstrap
bootstrap:
cd ./deploy/docker-compose && docker compose up -d && cd ../../
go run ./cmd/migration
nunu run ./cmd/server
.PHONY: mock
mock:
mockgen -source=internal/service/user.go -destination test/mocks/service/user.go
@@ -15,8 +21,9 @@ test:
.PHONY: build
build:
go build -ldflags="-s -w" -o ./bin/server ./cmd/server/...
go build -ldflags="-s -w" -o ./bin/server ./cmd/server
.PHONY: docker
docker:
docker build -f deploy/build/Dockerfile --build-arg APP_RELATIVE_PATH=./cmd/job/... -t 1.1.1.1:5000/demo-api:v1 .
docker build -f deploy/build/Dockerfile --build-arg APP_RELATIVE_PATH=./cmd/job -t 1.1.1.1:5000/demo-job:v1 .
docker run --rm -i 1.1.1.1:5000/demo-job:v1

View File

@@ -1,6 +1,7 @@
package main
import (
"github.com/go-nunu/nunu-layout-advanced/cmd/job/wire"
"github.com/go-nunu/nunu-layout-advanced/pkg/config"
"github.com/go-nunu/nunu-layout-advanced/pkg/log"
)
@@ -10,7 +11,7 @@ func main() {
logger := log.NewLog(conf)
logger.Info("start")
app, cleanup, err := newApp(conf, logger)
app, cleanup, err := wire.NewApp(conf, logger)
if err != nil {
panic(err)
}

View File

@@ -1,7 +1,7 @@
//go:build wireinject
// +build wireinject
package main
package wire
import (
"github.com/go-nunu/nunu-layout-advanced/internal/job"
@@ -12,7 +12,7 @@ import (
var JobSet = wire.NewSet(job.NewJob)
func newApp(*viper.Viper, *log.Logger) (*job.Job, func(), error) {
func NewApp(*viper.Viper, *log.Logger) (*job.Job, func(), error) {
panic(wire.Build(
JobSet,
))

View File

@@ -4,7 +4,7 @@
//go:build !wireinject
// +build !wireinject
package main
package wire
import (
"github.com/go-nunu/nunu-layout-advanced/internal/job"
@@ -15,7 +15,7 @@ import (
// Injectors from wire.go:
func newApp(viperViper *viper.Viper, logger *log.Logger) (*job.Job, func(), error) {
func NewApp(viperViper *viper.Viper, logger *log.Logger) (*job.Job, func(), error) {
jobJob := job.NewJob(logger)
return jobJob, func() {
}, nil

View File

@@ -1,4 +1,4 @@
package main
package internal
import (
"github.com/go-nunu/nunu-layout-advanced/internal/model"

View File

@@ -1,6 +1,7 @@
package main
import (
"github.com/go-nunu/nunu-layout-advanced/cmd/migration/wire"
"github.com/go-nunu/nunu-layout-advanced/pkg/config"
"github.com/go-nunu/nunu-layout-advanced/pkg/log"
)
@@ -9,7 +10,7 @@ func main() {
conf := config.NewConfig()
logger := log.NewLog(conf)
app, cleanup, err := newApp(conf, logger)
app, cleanup, err := wire.NewApp(conf, logger)
if err != nil {
panic(err)
}

View File

@@ -1,9 +1,10 @@
//go:build wireinject
// +build wireinject
package main
package wire
import (
"github.com/go-nunu/nunu-layout-advanced/cmd/migration/internal"
"github.com/go-nunu/nunu-layout-advanced/internal/repository"
"github.com/go-nunu/nunu-layout-advanced/pkg/log"
"github.com/google/wire"
@@ -17,9 +18,9 @@ var RepositorySet = wire.NewSet(
repository.NewUserRepository,
)
func newApp(*viper.Viper, *log.Logger) (*Migrate, func(), error) {
func NewApp(*viper.Viper, *log.Logger) (*internal.Migrate, func(), error) {
panic(wire.Build(
RepositorySet,
NewMigrate,
internal.NewMigrate,
))
}

View File

@@ -4,9 +4,10 @@
//go:build !wireinject
// +build !wireinject
package main
package wire
import (
"github.com/go-nunu/nunu-layout-advanced/cmd/migration/internal"
"github.com/go-nunu/nunu-layout-advanced/internal/repository"
"github.com/go-nunu/nunu-layout-advanced/pkg/log"
"github.com/google/wire"
@@ -15,9 +16,9 @@ import (
// Injectors from wire.go:
func newApp(viperViper *viper.Viper, logger *log.Logger) (*Migrate, func(), error) {
func NewApp(viperViper *viper.Viper, logger *log.Logger) (*internal.Migrate, func(), error) {
db := repository.NewDB(viperViper)
migrate := NewMigrate(db, logger)
migrate := internal.NewMigrate(db, logger)
return migrate, func() {
}, nil
}

View File

@@ -2,6 +2,7 @@ package main
import (
"fmt"
"github.com/go-nunu/nunu-layout-advanced/cmd/server/wire"
"github.com/go-nunu/nunu-layout-advanced/pkg/config"
"github.com/go-nunu/nunu-layout-advanced/pkg/http"
"github.com/go-nunu/nunu-layout-advanced/pkg/log"
@@ -12,7 +13,7 @@ func main() {
conf := config.NewConfig()
logger := log.NewLog(conf)
servers, cleanup, err := newApp(conf, logger)
servers, cleanup, err := wire.NewApp(conf, logger)
if err != nil {
panic(err)
}

View File

@@ -1,7 +1,7 @@
//go:build wireinject
// +build wireinject
package main
package wire
import (
"github.com/go-nunu/nunu-layout-advanced/internal/handler"
@@ -32,7 +32,7 @@ var RepositorySet = wire.NewSet(
repository.NewUserRepository,
)
func newApp(*viper.Viper, *log.Logger) (*server.Server, func(), error) {
func NewApp(*viper.Viper, *log.Logger) (*server.Server, func(), error) {
panic(wire.Build(
RepositorySet,
ServiceSet,

View File

@@ -4,7 +4,7 @@
//go:build !wireinject
// +build !wireinject
package main
package wire
import (
"github.com/go-nunu/nunu-layout-advanced/internal/handler"
@@ -20,7 +20,7 @@ import (
// Injectors from wire.go:
func newApp(viperViper *viper.Viper, logger *log.Logger) (*server.Server, func(), error) {
func NewApp(viperViper *viper.Viper, logger *log.Logger) (*server.Server, func(), error) {
jwtJWT := jwt.NewJwt(viperViper)
handlerHandler := handler.NewHandler(logger)
sidSid := sid.NewSid()

View File

@@ -3,7 +3,7 @@ RUN set -eux && sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk
ARG APP_RELATIVE_PATH
COPY ../../deploy /data/app
COPY .. /data/app
WORKDIR /data/app
RUN rm -rf /data/app/bin/

View File

@@ -3,9 +3,8 @@ package handler
import (
"github.com/gin-gonic/gin"
"github.com/go-nunu/nunu-layout-advanced/internal/pkg/request"
"github.com/go-nunu/nunu-layout-advanced/internal/pkg/response"
"github.com/go-nunu/nunu-layout-advanced/internal/service"
"github.com/go-nunu/nunu-layout-advanced/pkg/helper/resp"
"github.com/pkg/errors"
"net/http"
)
@@ -40,32 +39,32 @@ type userHandler struct {
func (h *userHandler) Register(ctx *gin.Context) {
req := new(request.RegisterRequest)
if err := ctx.ShouldBindJSON(req); err != nil {
resp.HandleError(ctx, http.StatusBadRequest, 1, errors.Wrap(err, "invalid request").Error(), nil)
response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil)
return
}
if err := h.userService.Register(ctx, req); err != nil {
resp.HandleError(ctx, http.StatusBadRequest, 1, errors.Wrap(err, "invalid request").Error(), nil)
response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil)
return
}
resp.HandleSuccess(ctx, nil)
response.HandleSuccess(ctx, nil)
}
func (h *userHandler) Login(ctx *gin.Context) {
var req request.LoginRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
resp.HandleError(ctx, http.StatusBadRequest, 1, errors.Wrap(err, "invalid request").Error(), nil)
response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil)
return
}
token, err := h.userService.Login(ctx, &req)
if err != nil {
resp.HandleError(ctx, http.StatusUnauthorized, 1, err.Error(), nil)
response.HandleError(ctx, http.StatusUnauthorized, response.ErrUnauthorized, nil)
return
}
resp.HandleSuccess(ctx, gin.H{
response.HandleSuccess(ctx, gin.H{
"accessToken": token,
})
}
@@ -73,17 +72,17 @@ func (h *userHandler) Login(ctx *gin.Context) {
func (h *userHandler) GetProfile(ctx *gin.Context) {
userId := GetUserIdFromCtx(ctx)
if userId == "" {
resp.HandleError(ctx, http.StatusUnauthorized, 1, "unauthorized", nil)
response.HandleError(ctx, http.StatusUnauthorized, response.ErrUnauthorized, nil)
return
}
user, err := h.userService.GetProfile(ctx, userId)
if err != nil {
resp.HandleError(ctx, http.StatusBadRequest, 1, err.Error(), nil)
response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil)
return
}
resp.HandleSuccess(ctx, user)
response.HandleSuccess(ctx, user)
}
func (h *userHandler) UpdateProfile(ctx *gin.Context) {
@@ -91,14 +90,14 @@ func (h *userHandler) UpdateProfile(ctx *gin.Context) {
var req request.UpdateProfileRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
resp.HandleError(ctx, http.StatusBadRequest, 1, errors.Wrap(err, "invalid request").Error(), nil)
response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil)
return
}
if err := h.userService.UpdateProfile(ctx, userId, &req); err != nil {
resp.HandleError(ctx, http.StatusBadRequest, 1, err.Error(), nil)
response.HandleError(ctx, http.StatusInternalServerError, response.ErrInternalServerError, nil)
return
}
resp.HandleSuccess(ctx, nil)
response.HandleSuccess(ctx, nil)
}

View File

@@ -2,7 +2,7 @@ package middleware
import (
"github.com/gin-gonic/gin"
"github.com/go-nunu/nunu-layout-advanced/pkg/helper/resp"
"github.com/go-nunu/nunu-layout-advanced/internal/pkg/response"
"github.com/go-nunu/nunu-layout-advanced/pkg/jwt"
"github.com/go-nunu/nunu-layout-advanced/pkg/log"
"go.uber.org/zap"
@@ -13,11 +13,11 @@ func StrictAuth(j *jwt.JWT, logger *log.Logger) gin.HandlerFunc {
return func(ctx *gin.Context) {
tokenString := ctx.Request.Header.Get("Authorization")
if tokenString == "" {
logger.WithContext(ctx).Warn("请求未携带token无权限访问", zap.Any("data", map[string]interface{}{
logger.WithContext(ctx).Warn("No token", zap.Any("data", map[string]interface{}{
"url": ctx.Request.URL,
"params": ctx.Params,
}))
resp.HandleError(ctx, http.StatusUnauthorized, 1, "no token", nil)
response.HandleError(ctx, http.StatusUnauthorized, response.ErrUnauthorized, nil)
ctx.Abort()
return
}
@@ -28,7 +28,7 @@ func StrictAuth(j *jwt.JWT, logger *log.Logger) gin.HandlerFunc {
"url": ctx.Request.URL,
"params": ctx.Params,
}))
resp.HandleError(ctx, http.StatusUnauthorized, 1, err.Error(), nil)
response.HandleError(ctx, http.StatusUnauthorized, response.ErrUnauthorized, nil)
ctx.Abort()
return
}

View File

@@ -2,8 +2,8 @@ package middleware
import (
"github.com/gin-gonic/gin"
"github.com/go-nunu/nunu-layout-advanced/internal/pkg/response"
"github.com/go-nunu/nunu-layout-advanced/pkg/helper/md5"
"github.com/go-nunu/nunu-layout-advanced/pkg/helper/resp"
"github.com/go-nunu/nunu-layout-advanced/pkg/log"
"github.com/spf13/viper"
"net/http"
@@ -18,7 +18,7 @@ func SignMiddleware(logger *log.Logger, conf *viper.Viper) gin.HandlerFunc {
for _, header := range requiredHeaders {
value, ok := ctx.Request.Header[header]
if !ok || len(value) == 0 {
resp.HandleError(ctx, http.StatusBadRequest, 1, "sign error.", nil)
response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil)
ctx.Abort()
return
}
@@ -44,7 +44,7 @@ func SignMiddleware(logger *log.Logger, conf *viper.Viper) gin.HandlerFunc {
str += conf.GetString("security.api_sign.app_security")
if ctx.Request.Header.Get("Sign") != strings.ToUpper(md5.Md5(str)) {
resp.HandleError(ctx, http.StatusBadRequest, 1, "sign error.", nil)
response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil)
ctx.Abort()
return
}

View File

@@ -0,0 +1,13 @@
package response
var (
// common errors
ErrSuccess = newError(0, "ok")
ErrBadRequest = newError(400, "Bad Request")
ErrUnauthorized = newError(401, "Unauthorized")
ErrNotFound = newError(404, "Not Found")
ErrInternalServerError = newError(500, "Internal Server Error")
// more biz errors
ErrUsernameAlreadyUse = newError(1001, "The username is already in use.")
)

View File

@@ -0,0 +1,48 @@
package response
import (
"errors"
"github.com/gin-gonic/gin"
"net/http"
)
type response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
func HandleSuccess(ctx *gin.Context, data interface{}) {
if data == nil {
data = map[string]string{}
}
resp := response{Code: ErrSuccess.Code, Message: ErrSuccess.Message, Data: data}
ctx.JSON(http.StatusOK, resp)
}
func HandleError(ctx *gin.Context, httpCode int, err error, data interface{}) {
if data == nil {
data = map[string]string{}
}
resp := response{Code: errorCodeMap[err], Message: err.Error(), Data: data}
ctx.JSON(httpCode, resp)
}
type Error struct {
Code int
Message string
}
var errorCodeMap = map[error]int{}
func newError(code int, msg string) *Error {
err := errors.New(msg)
errorCodeMap[err] = code
return &Error{
Code: code,
Message: msg,
}
}
func (e Error) Error() string {
return e.Message
}

View File

@@ -28,7 +28,7 @@ func NewRepository(db *gorm.DB, rdb *redis.Client, logger *log.Logger) *Reposito
func NewDB(conf *viper.Viper) *gorm.DB {
db, err := gorm.Open(mysql.Open(conf.GetString("data.mysql.user")), &gorm.Config{})
if err != nil {
panic(err)
panic(fmt.Sprintf("mysql error: %s", err.Error()))
}
return db
}

View File

@@ -3,6 +3,7 @@ package repository
import (
"context"
"github.com/go-nunu/nunu-layout-advanced/internal/model"
"github.com/go-nunu/nunu-layout-advanced/internal/pkg/response"
"github.com/pkg/errors"
"gorm.io/gorm"
)
@@ -42,7 +43,9 @@ func (r *userRepository) Update(ctx context.Context, user *model.User) error {
func (r *userRepository) GetByID(ctx context.Context, userId string) (*model.User, error) {
var user model.User
if err := r.db.Where("user_id = ?", userId).First(&user).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, response.ErrNotFound
}
return nil, errors.Wrap(err, "failed to get user by ID")
}
@@ -52,7 +55,7 @@ func (r *userRepository) GetByID(ctx context.Context, userId string) (*model.Use
func (r *userRepository) GetByUsername(ctx context.Context, username string) (*model.User, error) {
var user model.User
if err := r.db.Where("username = ?", username).First(&user).Error; err != nil {
if err == gorm.ErrRecordNotFound {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, errors.Wrap(err, "failed to get user by username")

View File

@@ -4,7 +4,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/go-nunu/nunu-layout-advanced/internal/handler"
"github.com/go-nunu/nunu-layout-advanced/internal/pkg/middleware"
"github.com/go-nunu/nunu-layout-advanced/pkg/helper/resp"
"github.com/go-nunu/nunu-layout-advanced/internal/pkg/response"
"github.com/go-nunu/nunu-layout-advanced/pkg/jwt"
"github.com/go-nunu/nunu-layout-advanced/pkg/log"
)
@@ -30,8 +30,8 @@ func NewServerHTTP(
noAuthRouter.GET("/", func(ctx *gin.Context) {
logger.WithContext(ctx).Info("hello")
resp.HandleSuccess(ctx, map[string]interface{}{
"say": "Hi Nunu!",
response.HandleSuccess(ctx, map[string]interface{}{
":)": "Thank you for using nunu!",
})
})

View File

@@ -4,6 +4,7 @@ import (
"context"
"github.com/go-nunu/nunu-layout-advanced/internal/model"
"github.com/go-nunu/nunu-layout-advanced/internal/pkg/request"
"github.com/go-nunu/nunu-layout-advanced/internal/pkg/response"
"github.com/go-nunu/nunu-layout-advanced/internal/repository"
"github.com/pkg/errors"
"golang.org/x/crypto/bcrypt"
@@ -30,9 +31,9 @@ type userService struct {
}
func (s *userService) Register(ctx context.Context, req *request.RegisterRequest) error {
// 检查用户名是否已存在
// check username
if user, err := s.userRepo.GetByUsername(ctx, req.Username); err == nil && user != nil {
return errors.New("username already exists")
return response.ErrUsernameAlreadyUse
}
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)

View File

@@ -1,28 +0,0 @@
package resp
import (
"github.com/gin-gonic/gin"
"net/http"
)
type response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
func HandleSuccess(ctx *gin.Context, data interface{}) {
if data == nil {
data = map[string]string{}
}
resp := response{Code: 0, Message: "success", Data: data}
ctx.JSON(http.StatusOK, resp)
}
func HandleError(ctx *gin.Context, httpCode, code int, message string, data interface{}) {
if data == nil {
data = map[string]string{}
}
resp := response{Code: code, Message: message, Data: data}
ctx.JSON(httpCode, resp)
}

11
scripts/README.md Normal file
View File

@@ -0,0 +1,11 @@
# `/scripts`
Scripts to perform various build, install, analysis, etc operations.
These scripts keep the root level Makefile small and simple.
Examples:
* https://github.com/kubernetes/helm/tree/master/scripts
* https://github.com/cockroachdb/cockroach/tree/master/scripts
* https://github.com/hashicorp/terraform/tree/master/scripts