diff --git a/README.md b/README.md index de6536f..98a38bf 100644 --- a/README.md +++ b/README.md @@ -13,191 +13,6 @@ Nunu is a scaffolding tool for building Go applications. Its name comes from a g * [Unit Testing](https://github.com/go-nunu/nunu/blob/main/docs/en/unit_testing.md) -## Features -- **Gin**: https://github.com/gin-gonic/gin -- **Gorm**: https://github.com/go-gorm/gorm -- **Wire**: https://github.com/google/wire -- **Viper**: https://github.com/spf13/viper -- **Zap**: https://github.com/uber-go/zap -- **Golang-jwt**: https://github.com/golang-jwt/jwt -- **Go-redis**: https://github.com/go-redis/redis -- **Testify**: https://github.com/stretchr/testify -- **Sonyflake**: https://github.com/sony/sonyflake -- **Gocron**: https://github.com/go-co-op/gocron -- **Go-sqlmock**: https://github.com/DATA-DOG/go-sqlmock -- **Gomock**: https://github.com/golang/mock -- **Swaggo**: https://github.com/swaggo/swag -- More... - -## Key Features -* **Low Learning Curve and Customization**: Nunu encapsulates popular libraries that Gophers are familiar with, allowing you to easily customize the application to meet specific requirements. -* **High Performance and Scalability**: Nunu aims to be high-performance and scalable. It uses the latest technologies and best practices to ensure that your application can handle high traffic and large amounts of data. -* **Security and Reliability**: Nunu uses stable and reliable third-party libraries to ensure the security and reliability of your application. -* **Modular and Extensible**: Nunu is designed to be modular and extensible. You can easily add new features and functionality by using third-party libraries or writing your own modules. -* **Complete Documentation and Testing**: Nunu has comprehensive documentation and testing. It provides extensive documentation and examples to help you get started quickly. It also includes a test suite to ensure that your application works as expected. - -## Concise Layered Architecture -Nunu adopts a classic layered architecture. In order to achieve modularity and decoupling, it uses the dependency injection framework `Wire`. - -![Nunu Layout](https://github.com/go-nunu/nunu/blob/main/.github/assets/layout.png) - -## Nunu CLI - -![Nunu](https://github.com/go-nunu/nunu/blob/main/.github/assets/screenshot.jpg) - - -## Directory Structure -``` -. -├── cmd -│   └── server -│   ├── wire -│   │   ├── wire.go -│   │   └── wire_gen.go -│   └── main.go -├── config -│   ├── local.yml -│   └── prod.yml -├── deploy -├── internal -│   ├── handler -│   │   ├── handler.go -│   │   └── user.go -│   ├── job -│   │   └── job.go -│   ├── model -│   │   └── user.go -│   ├── pkg -│   ├── repository -│   │   ├── repository.go -│   │   └── user.go -│   ├── server -│   │   ├── http.go -│   │   └── server.go -│   └── service -│   ├── service.go -│   └── user.go -├── pkg -├── scripts -├── storage -├── test -├── web -├── Makefile -├── go.mod -└── go.sum - -``` - -The project architecture follows a typical layered structure, consisting of the following modules: - -* `cmd`: This module contains the entry points of the application, which perform different operations based on different commands, such as starting the server or executing database migrations. Each sub-module has a `main.go` file as the entry file, as well as `wire.go` and `wire_gen.go` files for dependency injection. -* `config`: This module contains the configuration files for the application, providing different configurations for different environments, such as development and production. -* `deploy`: This module is used for deploying the application and includes deployment scripts and configuration files. -* `internal`: This module is the core module of the application and contains the implementation of various business logic. - - - `handler`: This sub-module contains the handlers for handling HTTP requests, responsible for receiving requests and invoking the corresponding services for processing. - - - `job`: This sub-module contains the logic for background tasks. - - - `model`: This sub-module contains the definition of data models. - - - `repository`: This sub-module contains the implementation of the data access layer, responsible for interacting with the database. - - - `server`: This sub-module contains the implementation of the HTTP server. - - - `service`: This sub-module contains the implementation of the business logic, responsible for handling specific business operations. - -* `pkg`: This module contains some common utilities and functions. - -* `scripts`: This module contains some script files used for project build, test, and deployment operations. - -* `storage`: This module is used for storing files or other static resources. - -* `test`: This module contains the unit tests for various modules, organized into sub-directories based on modules. - -* `web`: This module contains the frontend-related files, such as HTML, CSS, and JavaScript. - -In addition, there are some other files and directories, such as license files, build files, and README. Overall, the project architecture is clear, with clear responsibilities for each module, making it easy to understand and maintain. - -## Requirements -To use Nunu, you need to have the following software installed on your system: - -* Go 1.19 or higher -* Git -* Docker (optional) -* MySQL 5.7 or higher (optional) -* Redis (optional) - -### Installation - -You can install Nunu with the following command: - -```bash -go install github.com/go-nunu/nunu@latest -``` - -> Tips: If `go install` succeeds but the `nunu` command is not recognized, it is because the environment variable is not configured. You can add the GOBIN directory to the environment variable. - -### Create a New Project - -You can create a new Go project with the following command: - -```bash -nunu new projectName -``` - -By default, it pulls from the GitHub source, but you can also use an accelerated repository in China: - -``` -// Use the basic template -nunu new projectName -r https://gitee.com/go-nunu/nunu-layout-basic.git -// Use the advanced template -nunu new projectName -r https://gitee.com/go-nunu/nunu-layout-advanced.git -``` - -This command will create a directory named `projectName` and generate an elegant Go project structure within it. - -### Create Components - -You can create handlers, services, repositories, and models for your project using the following commands: - -```bash -nunu create handler user -nunu create service user -nunu create repository user -nunu create model user -``` -or -``` -nunu create all user -``` - -These commands will create components named `UserHandler`, `UserService`, `UserRepository`, and `UserModel`, respectively, and place them in the correct directories. - -### Run the Project - -You can quickly run the project with the following command: - -```bash -nunu run -``` - -This command will start your Go project and support hot-reloading when files are updated. - -### Compile wire.go - -You can quickly compile `wire.go` with the following command: - -```bash -nunu wire -``` - -This command will compile your `wire.go` file and generate the required dependencies. - -## Contribution - -If you find any issues or have any improvement suggestions, please feel free to raise an issue or submit a pull request. Your contributions are highly appreciated! - ## License Nunu is released under the MIT License. For more information, see the [LICENSE](LICENSE) file. \ No newline at end of file diff --git a/README_zh.md b/README_zh.md index 792d6f2..3d40b17 100644 --- a/README_zh.md +++ b/README_zh.md @@ -13,222 +13,6 @@ Nunu是一个基于Golang的应用脚手架,它的名字来自于英雄联盟 * [上手教程](https://github.com/go-nunu/nunu/blob/main/docs/zh/tutorial.md) * [高效编写单元测试](https://github.com/go-nunu/nunu/blob/main/docs/zh/unit_testing.md) -## 功能 -- **Gin**: https://github.com/gin-gonic/gin -- **Gorm**: https://github.com/go-gorm/gorm -- **Wire**: https://github.com/google/wire -- **Viper**: https://github.com/spf13/viper -- **Zap**: https://github.com/uber-go/zap -- **Golang-jwt**: https://github.com/golang-jwt/jwt -- **Go-redis**: https://github.com/go-redis/redis -- **Testify**: https://github.com/stretchr/testify -- **Sonyflake**: https://github.com/sony/sonyflake -- **Gocron**: https://github.com/go-co-op/gocron -- **Go-sqlmock**: https://github.com/DATA-DOG/go-sqlmock -- **Gomock**: https://github.com/golang/mock -- **Swaggo**: https://github.com/swaggo/swag -- More... -## 特性 -* **超低学习成本和定制**:Nunu封装了Gopher最熟悉的一些流行库。您可以轻松定制应用程序以满足特定需求。 -* **高性能和可扩展性**:Nunu旨在具有高性能和可扩展性。它使用最新的技术和最佳实践,确保您的应用程序可以处理高流量和大量数据。 -* **安全可靠**:Nunu使用了稳定可靠的第三方库,确保您的应用程序安全可靠。 -* **模块化和可扩展**:Nunu旨在具有模块化和可扩展性。您可以通过使用第三方库或编写自己的模块轻松添加新功能和功能。 -* **文档完善和测试完备**:Nunu文档完善,测试完备。它提供了全面的文档和示例,帮助您快速入门。它还包括一套测试套件,确保您的应用程序按预期工作。 - -## 交流群组 - -微信入群,请备注Nunu - -

- -## 简洁分层架构 -Nunu采用了经典的分层架构。同时,为了更好地实现模块化和解耦,采用了依赖注入框架`Wire`。 - -![Nunu Layout](https://github.com/go-nunu/nunu/blob/main/.github/assets/layout.png) - -## Nunu CLI - -![Nunu](https://github.com/go-nunu/nunu/blob/main/.github/assets/screenshot.jpg) - - - -## 目录结构 -``` -. -├── cmd -│   └── server -│   ├── wire -│   │   ├── wire.go -│   │   └── wire_gen.go -│   └── main.go -├── config -│   ├── local.yml -│   └── prod.yml -├── deploy -├── internal -│   ├── handler -│   │   ├── handler.go -│   │   └── user.go -│   ├── job -│   │   └── job.go -│   ├── model -│   │   └── user.go -│   ├── pkg -│   ├── repository -│   │   ├── repository.go -│   │   └── user.go -│   ├── server -│   │   ├── http.go -│   │   └── server.go -│   └── service -│   ├── service.go -│   └── user.go -├── pkg -├── scripts -├── storage -├── test -├── web -├── Makefile -├── go.mod -└── go.sum - -``` - -该项目的架构采用了典型的分层架构,主要包括以下几个模块: - -* `cmd`:该模块包含了应用的入口点,根据不同的命令进行不同的操作,例如启动服务器、执行数据库迁移等。每个子模块都有一个`main.go`文件作为入口文件,以及`wire.go`和`wire_gen.go`文件用于依赖注入。 -* `config`:该模块包含了应用的配置文件,根据不同的环境(如开发环境和生产环境)提供不同的配置。 -* `deploy`:该模块用于部署应用,包含了一些部署脚本和配置文件。 -* `internal`:该模块是应用的核心模块,包含了各种业务逻辑的实现。 - - - `handler`:该子模块包含了处理HTTP请求的处理器,负责接收请求并调用相应的服务进行处理。 - - - `job`:该子模块包含了后台任务的逻辑实现。 - - - `model`:该子模块包含了数据模型的定义。 - - - `repository`:该子模块包含了数据访问层的实现,负责与数据库进行交互。 - - - `server`:该子模块包含了HTTP服务器的实现。 - - - `service`:该子模块包含了业务逻辑的实现,负责处理具体的业务操作。 - -* `pkg`:该模块包含了一些通用的功能和工具。 - -* `scripts`:该模块包含了一些脚本文件,用于项目的构建、测试和部署等操作。 - -* `storage`:该模块用于存储文件或其他静态资源。 - -* `test`:该模块包含了各个模块的单元测试,按照模块划分子目录。 - -* `web`:该模块包含了前端相关的文件,如HTML、CSS和JavaScript等。 - -此外,还包含了一些其他的文件和目录,如授权文件、构建文件、README等。整体上,该项目的架构清晰,各个模块之间的职责明确,便于理解和维护。 - -## 要求 -要使用Nunu,您需要在系统上安装以下软件: - -* Golang 1.19或更高版本 -* Git -* Docker (可选) -* MySQL5.7或更高版本(可选) -* Redis(可选) - - - -### 安装 - -您可以通过以下命令安装Nunu: - -```bash -go install github.com/go-nunu/nunu@latest -``` - -国内用户可以使用`GOPROXY`加速`go install` - -``` -$ go env -w GO111MODULE=on -$ go env -w GOPROXY=https://goproxy.cn,direct -``` - -> tips: 如果`go install`成功,却提示找不到nunu命令,这是因为环境变量没有配置,可以把 GOBIN 目录配置到环境变量中即可 - - -### 创建新项目 - -您可以使用以下命令创建一个新的Golang项目: - -```bash -// 推荐新用户选择Advanced Layout -nunu new projectName -``` - -`nunu new`默认拉取github源,你也可以使用国内加速仓库 -``` -// 使用高级模板(推荐) -nunu new projectName -r https://gitee.com/go-nunu/nunu-layout-advanced.git - -// 使用基础模板 -nunu new projectName -r https://gitee.com/go-nunu/nunu-layout-basic.git - -``` - - -> Nunu内置了两种类型的Layout: - -* **基础模板(Basic Layout)** - -Basic Layout 包含一个非常精简的架构目录结构,适合非常熟悉Nunu项目的开发者使用。 - -* **高级模板(Advanced Layout)** - -**建议:我们推荐新手优先选择使用Advanced Layout。** - - -Advanced Layout 包含了很多Nunu的用法示例( db、redis、 jwt、 cron、 migration等),适合开发者快速学习了解Nunu的架构思想。 - -此命令将创建一个名为`projectName`的目录,并在其中生成一个优雅的Golang项目结构。 - -### 创建组件 - -您可以使用以下命令为项目创建handler、service、repository和model等组件: - -```bash -nunu create handler user -nunu create service user -nunu create repository user -nunu create model user -``` -或 -``` -nunu create all user -``` -这些命令将分别创建一个名为`UserHandler`、`UserService`、`UserRepository`和`UserModel`的组件,并将它们放置在正确的目录中。 - -### 启动项目 - -您可以使用以下命令快速启动项目: - -```bash -nunu run -``` - -此命令将启动您的Golang项目,并支持文件更新热重启。 - -### 编译wire.go - -您可以使用以下命令快速编译`wire.go`: - -```bash -nunu wire -``` - -此命令将编译您的`wire.go`文件,并生成所需的依赖项。 - -## 贡献 - -如果您发现任何问题或有任何改进意见,请随时提出问题或提交拉取请求。我们非常欢迎您的贡献! - ## 许可证 Nunu是根据MIT许可证发布的。有关更多信息,请参见[LICENSE](LICENSE)文件。 diff --git a/internal/pkg/response/errors.go b/api/v1/errors.go similarity index 95% rename from internal/pkg/response/errors.go rename to api/v1/errors.go index 0889b09..942c520 100644 --- a/internal/pkg/response/errors.go +++ b/api/v1/errors.go @@ -1,4 +1,4 @@ -package response +package v1 var ( // common errors diff --git a/internal/pkg/request/user.go b/api/v1/user.go similarity index 63% rename from internal/pkg/request/user.go rename to api/v1/user.go index 4cb0a8a..355a822 100644 --- a/internal/pkg/request/user.go +++ b/api/v1/user.go @@ -1,4 +1,4 @@ -package request +package v1 type RegisterRequest struct { Username string `json:"username" binding:"required" example:"alan"` @@ -10,9 +10,25 @@ type LoginRequest struct { Username string `json:"username" binding:"required" example:"alan"` Password string `json:"password" binding:"required" example:"123456"` } +type LoginResponseData struct { + AccessToken string `json:"accessToken"` +} +type LoginResponse struct { + Response + Data LoginResponseData +} type UpdateProfileRequest struct { Nickname string `json:"nickname" example:"alan"` Email string `json:"email" binding:"required,email" example:"1234@gmail.com"` Avatar string `json:"avatar" example:"xxxx"` } +type GetProfileResponseData struct { + UserId string `json:"userId"` + Nickname string `json:"nickname"` + Username string `json:"username"` +} +type GetProfileResponse struct { + Response + Data GetProfileResponseData +} diff --git a/internal/pkg/response/response.go b/api/v1/v1.go similarity index 72% rename from internal/pkg/response/response.go rename to api/v1/v1.go index 040a4d4..652180a 100644 --- a/internal/pkg/response/response.go +++ b/api/v1/v1.go @@ -1,8 +1,8 @@ -package response +package v1 import ( - "errors" "github.com/gin-gonic/gin" + "github.com/pkg/errors" "net/http" ) @@ -14,9 +14,9 @@ type Response struct { func HandleSuccess(ctx *gin.Context, data interface{}) { if data == nil { - data = map[string]string{} + data = map[string]interface{}{} } - resp := Response{Code: errorCodeMap[ErrSuccess], Message: ErrSuccess.Error(), Data: data} + resp := Response{Code: ErrorCodeMap[ErrSuccess], Message: ErrSuccess.Error(), Data: data} ctx.JSON(http.StatusOK, resp) } @@ -24,7 +24,7 @@ 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} + resp := Response{Code: ErrorCodeMap[err], Message: err.Error(), Data: data} ctx.JSON(httpCode, resp) } @@ -33,11 +33,11 @@ type Error struct { Message string } -var errorCodeMap = map[error]int{} +var ErrorCodeMap = map[error]int{} func newError(code int, msg string) error { err := errors.New(msg) - errorCodeMap[err] = code + ErrorCodeMap[err] = code return err } func (e Error) Error() string { diff --git a/cmd/job/wire/wire.go b/cmd/job/wire/wire.go deleted file mode 100644 index 172e0cc..0000000 --- a/cmd/job/wire/wire.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build wireinject -// +build wireinject - -package wire - -import ( - "github.com/go-nunu/nunu-layout-advanced/internal/job" - "github.com/go-nunu/nunu-layout-advanced/pkg/log" - "github.com/google/wire" - "github.com/spf13/viper" -) - -var JobSet = wire.NewSet(job.NewJob) - -func NewApp(*viper.Viper, *log.Logger) (*job.Job, func(), error) { - panic(wire.Build( - JobSet, - )) -} diff --git a/cmd/job/wire/wire_gen.go b/cmd/job/wire/wire_gen.go deleted file mode 100644 index 8dabde0..0000000 --- a/cmd/job/wire/wire_gen.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by Wire. DO NOT EDIT. - -//go:generate go run github.com/google/wire/cmd/wire -//go:build !wireinject -// +build !wireinject - -package wire - -import ( - "github.com/go-nunu/nunu-layout-advanced/internal/job" - "github.com/go-nunu/nunu-layout-advanced/pkg/log" - "github.com/google/wire" - "github.com/spf13/viper" -) - -// Injectors from wire.go: - -func NewApp(viperViper *viper.Viper, logger *log.Logger) (*job.Job, func(), error) { - jobJob := job.NewJob(logger) - return jobJob, func() { - }, nil -} - -// wire.go: - -var JobSet = wire.NewSet(job.NewJob) diff --git a/cmd/migration/main.go b/cmd/migration/main.go index b2d474e..a1bcf81 100644 --- a/cmd/migration/main.go +++ b/cmd/migration/main.go @@ -1,6 +1,7 @@ package main import ( + "context" "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" @@ -10,10 +11,12 @@ func main() { conf := config.NewConfig() logger := log.NewLog(conf) - app, cleanup, err := wire.NewApp(conf, logger) + app, cleanup, err := wire.NewWire(conf, logger) + defer cleanup() if err != nil { panic(err) } - app.Run() - defer cleanup() + if err = app.Run(context.Background()); err != nil { + panic(err) + } } diff --git a/cmd/migration/wire/wire.go b/cmd/migration/wire/wire.go index e5d92fe..9c5d291 100644 --- a/cmd/migration/wire/wire.go +++ b/cmd/migration/wire/wire.go @@ -4,23 +4,33 @@ 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/internal/server" + "github.com/go-nunu/nunu-layout-advanced/pkg/app" "github.com/go-nunu/nunu-layout-advanced/pkg/log" "github.com/google/wire" "github.com/spf13/viper" ) -var RepositorySet = wire.NewSet( +var repositorySet = wire.NewSet( repository.NewDB, repository.NewRedis, repository.NewRepository, repository.NewUserRepository, ) -func NewApp(*viper.Viper, *log.Logger) (*internal.Migrate, func(), error) { +// build App +func newApp(migrate *server.Migrate) *app.App { + return app.NewApp( + app.WithServer(migrate), + app.WithName("demo-migrate"), + ) +} + +func NewWire(*viper.Viper, *log.Logger) (*app.App, func(), error) { panic(wire.Build( - RepositorySet, - internal.NewMigrate, + repositorySet, + server.NewMigrate, + newApp, )) } diff --git a/cmd/migration/wire/wire_gen.go b/cmd/migration/wire/wire_gen.go index 068e5dc..b54893b 100644 --- a/cmd/migration/wire/wire_gen.go +++ b/cmd/migration/wire/wire_gen.go @@ -7,8 +7,9 @@ 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/internal/server" + "github.com/go-nunu/nunu-layout-advanced/pkg/app" "github.com/go-nunu/nunu-layout-advanced/pkg/log" "github.com/google/wire" "github.com/spf13/viper" @@ -16,13 +17,19 @@ import ( // Injectors from wire.go: -func NewApp(viperViper *viper.Viper, logger *log.Logger) (*internal.Migrate, func(), error) { - db := repository.NewDB(viperViper) - migrate := internal.NewMigrate(db, logger) - return migrate, func() { +func NewWire(viperViper *viper.Viper, logger *log.Logger) (*app.App, func(), error) { + db := repository.NewDB(viperViper, logger) + migrate := server.NewMigrate(db, logger) + appApp := newApp(migrate) + return appApp, func() { }, nil } // wire.go: -var RepositorySet = wire.NewSet(repository.NewDB, repository.NewRedis, repository.NewRepository, repository.NewUserRepository) +var repositorySet = wire.NewSet(repository.NewDB, repository.NewRedis, repository.NewRepository, repository.NewUserRepository) + +// build App +func newApp(migrate *server.Migrate) *app.App { + return app.NewApp(app.WithServer(migrate), app.WithName("demo-migrate")) +} diff --git a/cmd/server/main.go b/cmd/server/main.go index 69252ec..2bdd77a 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -1,10 +1,10 @@ package main import ( + "context" "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" "go.uber.org/zap" ) @@ -19,7 +19,6 @@ import ( // @license.name Apache 2.0 // @license.url http://www.apache.org/licenses/LICENSE-2.0.html // @host localhost:8000 -// @BasePath / // @securityDefinitions.apiKey Bearer // @in header // @name Authorization @@ -29,16 +28,14 @@ func main() { conf := config.NewConfig() logger := log.NewLog(conf) - servers, cleanup, err := wire.NewApp(conf, logger) + app, cleanup, err := wire.NewWire(conf, logger) + defer cleanup() if err != nil { panic(err) } - - logger.Info("server start", zap.String("host", "http://localhost:"+conf.GetString("http.port"))) + logger.Info("server start", zap.String("host", "http://127.0.0.1:"+conf.GetString("http.port"))) logger.Info("docs addr", zap.String("addr", fmt.Sprintf("http://127.0.0.1:%d/swagger/index.html", conf.GetInt("http.port")))) - - //go grpc.Run(servers.ServerGRPC, conf) - http.Run(servers.ServerHTTP, fmt.Sprintf(":%d", conf.GetInt("http.port"))) - defer cleanup() - + if err = app.Run(context.Background()); err != nil { + panic(err) + } } diff --git a/cmd/server/wire/wire.go b/cmd/server/wire/wire.go index c62b58e..7afc108 100644 --- a/cmd/server/wire/wire.go +++ b/cmd/server/wire/wire.go @@ -8,38 +8,54 @@ import ( "github.com/go-nunu/nunu-layout-advanced/internal/repository" "github.com/go-nunu/nunu-layout-advanced/internal/server" "github.com/go-nunu/nunu-layout-advanced/internal/service" + "github.com/go-nunu/nunu-layout-advanced/pkg/app" "github.com/go-nunu/nunu-layout-advanced/pkg/helper/sid" "github.com/go-nunu/nunu-layout-advanced/pkg/jwt" "github.com/go-nunu/nunu-layout-advanced/pkg/log" + "github.com/go-nunu/nunu-layout-advanced/pkg/server/http" "github.com/google/wire" "github.com/spf13/viper" ) -var HandlerSet = wire.NewSet( +var handlerSet = wire.NewSet( handler.NewHandler, handler.NewUserHandler, ) -var ServiceSet = wire.NewSet( +var serviceSet = wire.NewSet( service.NewService, service.NewUserService, ) -var RepositorySet = wire.NewSet( +var repositorySet = wire.NewSet( repository.NewDB, repository.NewRedis, repository.NewRepository, repository.NewUserRepository, ) +var serverSet = wire.NewSet( + server.NewHTTPServer, + server.NewJob, + server.NewTask, +) + +// build App +func newApp(httpServer *http.Server, job *server.Job) *app.App { + return app.NewApp( + app.WithServer(httpServer, job), + app.WithName("demo-server"), + ) +} + +func NewWire(*viper.Viper, *log.Logger) (*app.App, func(), error) { -func NewApp(*viper.Viper, *log.Logger) (*server.Server, func(), error) { panic(wire.Build( - RepositorySet, - ServiceSet, - HandlerSet, - server.NewServer, - server.NewServerHTTP, + repositorySet, + serviceSet, + handlerSet, + serverSet, sid.NewSid, jwt.NewJwt, + newApp, )) } diff --git a/cmd/server/wire/wire_gen.go b/cmd/server/wire/wire_gen.go index 9ee2041..8391b45 100644 --- a/cmd/server/wire/wire_gen.go +++ b/cmd/server/wire/wire_gen.go @@ -11,36 +11,46 @@ import ( "github.com/go-nunu/nunu-layout-advanced/internal/repository" "github.com/go-nunu/nunu-layout-advanced/internal/server" "github.com/go-nunu/nunu-layout-advanced/internal/service" + "github.com/go-nunu/nunu-layout-advanced/pkg/app" "github.com/go-nunu/nunu-layout-advanced/pkg/helper/sid" "github.com/go-nunu/nunu-layout-advanced/pkg/jwt" "github.com/go-nunu/nunu-layout-advanced/pkg/log" + "github.com/go-nunu/nunu-layout-advanced/pkg/server/http" "github.com/google/wire" "github.com/spf13/viper" ) // Injectors from wire.go: -func NewApp(viperViper *viper.Viper, logger *log.Logger) (*server.Server, func(), error) { +func NewWire(viperViper *viper.Viper, logger *log.Logger) (*app.App, func(), error) { jwtJWT := jwt.NewJwt(viperViper) handlerHandler := handler.NewHandler(logger) sidSid := sid.NewSid() serviceService := service.NewService(logger, sidSid, jwtJWT) - db := repository.NewDB(viperViper) + db := repository.NewDB(viperViper, logger) client := repository.NewRedis(viperViper) repositoryRepository := repository.NewRepository(db, client, logger) userRepository := repository.NewUserRepository(repositoryRepository) userService := service.NewUserService(serviceService, userRepository) userHandler := handler.NewUserHandler(handlerHandler, userService) - engine := server.NewServerHTTP(logger, jwtJWT, userHandler) - serverServer := server.NewServer(engine) - return serverServer, func() { + httpServer := server.NewHTTPServer(logger, viperViper, jwtJWT, userHandler) + job := server.NewJob(logger) + appApp := newApp(httpServer, job) + return appApp, func() { }, nil } // wire.go: -var HandlerSet = wire.NewSet(handler.NewHandler, handler.NewUserHandler) +var handlerSet = wire.NewSet(handler.NewHandler, handler.NewUserHandler) -var ServiceSet = wire.NewSet(service.NewService, service.NewUserService) +var serviceSet = wire.NewSet(service.NewService, service.NewUserService) -var RepositorySet = wire.NewSet(repository.NewDB, repository.NewRedis, repository.NewRepository, repository.NewUserRepository) +var repositorySet = wire.NewSet(repository.NewDB, repository.NewRedis, repository.NewRepository, repository.NewUserRepository) + +var serverSet = wire.NewSet(server.NewHTTPServer, server.NewJob, server.NewTask) + +// build App +func newApp(httpServer *http.Server, job *server.Job) *app.App { + return app.NewApp(app.WithServer(httpServer, job), app.WithName("demo-server")) +} diff --git a/cmd/job/main.go b/cmd/task/main.go similarity index 54% rename from cmd/job/main.go rename to cmd/task/main.go index 3677609..f020947 100644 --- a/cmd/job/main.go +++ b/cmd/task/main.go @@ -1,7 +1,8 @@ package main import ( - "github.com/go-nunu/nunu-layout-advanced/cmd/job/wire" + "context" + "github.com/go-nunu/nunu-layout-advanced/cmd/task/wire" "github.com/go-nunu/nunu-layout-advanced/pkg/config" "github.com/go-nunu/nunu-layout-advanced/pkg/log" ) @@ -9,13 +10,14 @@ import ( func main() { conf := config.NewConfig() logger := log.NewLog(conf) - logger.Info("start") - - app, cleanup, err := wire.NewApp(conf, logger) + logger.Info("start task") + app, cleanup, err := wire.NewWire(conf, logger) + defer cleanup() if err != nil { panic(err) } - app.Run() - defer cleanup() + if err = app.Run(context.Background()); err != nil { + panic(err) + } } diff --git a/cmd/task/wire/wire.go b/cmd/task/wire/wire.go new file mode 100644 index 0000000..15a191d --- /dev/null +++ b/cmd/task/wire/wire.go @@ -0,0 +1,29 @@ +//go:build wireinject +// +build wireinject + +package wire + +import ( + "github.com/go-nunu/nunu-layout-advanced/internal/server" + "github.com/go-nunu/nunu-layout-advanced/pkg/app" + "github.com/go-nunu/nunu-layout-advanced/pkg/log" + "github.com/google/wire" + "github.com/spf13/viper" +) + +var taskSet = wire.NewSet(server.NewTask) + +// build App +func newApp(task *server.Task) *app.App { + return app.NewApp( + app.WithServer(task), + app.WithName("demo-task"), + ) +} + +func NewWire(*viper.Viper, *log.Logger) (*app.App, func(), error) { + panic(wire.Build( + taskSet, + newApp, + )) +} diff --git a/cmd/task/wire/wire_gen.go b/cmd/task/wire/wire_gen.go new file mode 100644 index 0000000..4616d2a --- /dev/null +++ b/cmd/task/wire/wire_gen.go @@ -0,0 +1,33 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package wire + +import ( + "github.com/go-nunu/nunu-layout-advanced/internal/server" + "github.com/go-nunu/nunu-layout-advanced/pkg/app" + "github.com/go-nunu/nunu-layout-advanced/pkg/log" + "github.com/google/wire" + "github.com/spf13/viper" +) + +// Injectors from wire.go: + +func NewWire(viperViper *viper.Viper, logger *log.Logger) (*app.App, func(), error) { + task := server.NewTask(logger) + appApp := newApp(task) + return appApp, func() { + }, nil +} + +// wire.go: + +var taskSet = wire.NewSet(server.NewTask) + +// build App +func newApp(task *server.Task) *app.App { + return app.NewApp(app.WithServer(task), app.WithName("demo-task")) +} diff --git a/config/local.yml b/config/local.yml index 12afd97..f1d92a7 100644 --- a/config/local.yml +++ b/config/local.yml @@ -1,5 +1,6 @@ env: local http: + host: 0.0.0.0 port: 8000 security: api_sign: diff --git a/config/prod.yml b/config/prod.yml index 1bb9d3a..12fdfe2 100644 --- a/config/prod.yml +++ b/config/prod.yml @@ -1,5 +1,6 @@ -env: local +env: prod http: + host: 0.0.0.0 port: 8000 security: api_sign: diff --git a/docs/docs.go b/docs/docs.go index e196d1a..3e13ec4 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -43,7 +43,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_internal_pkg_request.LoginRequest" + "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.LoginRequest" } } ], @@ -51,7 +51,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_internal_pkg_response.Response" + "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.LoginResponse" } } } @@ -77,7 +77,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_internal_pkg_request.RegisterRequest" + "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.RegisterRequest" } } ], @@ -85,7 +85,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_internal_pkg_response.Response" + "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.Response" } } } @@ -112,7 +112,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_internal_pkg_response.Response" + "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.GetProfileResponse" } } } @@ -120,7 +120,32 @@ const docTemplate = `{ } }, "definitions": { - "github_com_go-nunu_nunu-layout-advanced_internal_pkg_request.LoginRequest": { + "github_com_go-nunu_nunu-layout-advanced_api_v1.GetProfileResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "data": { + "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.GetProfileResponseData" + }, + "message": { + "type": "string" + } + } + }, + "github_com_go-nunu_nunu-layout-advanced_api_v1.GetProfileResponseData": { + "type": "object", + "properties": { + "nickname": { + "type": "string" + }, + "userId": { + "type": "string" + } + } + }, + "github_com_go-nunu_nunu-layout-advanced_api_v1.LoginRequest": { "type": "object", "required": [ "password", @@ -137,7 +162,29 @@ const docTemplate = `{ } } }, - "github_com_go-nunu_nunu-layout-advanced_internal_pkg_request.RegisterRequest": { + "github_com_go-nunu_nunu-layout-advanced_api_v1.LoginResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "data": { + "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.LoginResponseData" + }, + "message": { + "type": "string" + } + } + }, + "github_com_go-nunu_nunu-layout-advanced_api_v1.LoginResponseData": { + "type": "object", + "properties": { + "accessToken": { + "type": "string" + } + } + }, + "github_com_go-nunu_nunu-layout-advanced_api_v1.RegisterRequest": { "type": "object", "required": [ "email", @@ -159,7 +206,7 @@ const docTemplate = `{ } } }, - "github_com_go-nunu_nunu-layout-advanced_internal_pkg_response.Response": { + "github_com_go-nunu_nunu-layout-advanced_api_v1.Response": { "type": "object", "properties": { "code": { @@ -185,7 +232,7 @@ const docTemplate = `{ var SwaggerInfo = &swag.Spec{ Version: "1.0.0", Host: "localhost:8000", - BasePath: "/", + BasePath: "", Schemes: []string{}, Title: "Nunu Example API", Description: "This is a sample server celler server.", diff --git a/docs/swagger.json b/docs/swagger.json index 69f9fc6..3c3385f 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -16,7 +16,6 @@ "version": "1.0.0" }, "host": "localhost:8000", - "basePath": "/", "paths": { "/login": { "post": { @@ -37,7 +36,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_internal_pkg_request.LoginRequest" + "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.LoginRequest" } } ], @@ -45,7 +44,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_internal_pkg_response.Response" + "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.LoginResponse" } } } @@ -71,7 +70,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_internal_pkg_request.RegisterRequest" + "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.RegisterRequest" } } ], @@ -79,7 +78,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_internal_pkg_response.Response" + "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.Response" } } } @@ -106,7 +105,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_internal_pkg_response.Response" + "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.GetProfileResponse" } } } @@ -114,7 +113,32 @@ } }, "definitions": { - "github_com_go-nunu_nunu-layout-advanced_internal_pkg_request.LoginRequest": { + "github_com_go-nunu_nunu-layout-advanced_api_v1.GetProfileResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "data": { + "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.GetProfileResponseData" + }, + "message": { + "type": "string" + } + } + }, + "github_com_go-nunu_nunu-layout-advanced_api_v1.GetProfileResponseData": { + "type": "object", + "properties": { + "nickname": { + "type": "string" + }, + "userId": { + "type": "string" + } + } + }, + "github_com_go-nunu_nunu-layout-advanced_api_v1.LoginRequest": { "type": "object", "required": [ "password", @@ -131,7 +155,29 @@ } } }, - "github_com_go-nunu_nunu-layout-advanced_internal_pkg_request.RegisterRequest": { + "github_com_go-nunu_nunu-layout-advanced_api_v1.LoginResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "data": { + "$ref": "#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.LoginResponseData" + }, + "message": { + "type": "string" + } + } + }, + "github_com_go-nunu_nunu-layout-advanced_api_v1.LoginResponseData": { + "type": "object", + "properties": { + "accessToken": { + "type": "string" + } + } + }, + "github_com_go-nunu_nunu-layout-advanced_api_v1.RegisterRequest": { "type": "object", "required": [ "email", @@ -153,7 +199,7 @@ } } }, - "github_com_go-nunu_nunu-layout-advanced_internal_pkg_response.Response": { + "github_com_go-nunu_nunu-layout-advanced_api_v1.Response": { "type": "object", "properties": { "code": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index ce71095..166f623 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,6 +1,21 @@ -basePath: / definitions: - github_com_go-nunu_nunu-layout-advanced_internal_pkg_request.LoginRequest: + github_com_go-nunu_nunu-layout-advanced_api_v1.GetProfileResponse: + properties: + code: + type: integer + data: + $ref: '#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.GetProfileResponseData' + message: + type: string + type: object + github_com_go-nunu_nunu-layout-advanced_api_v1.GetProfileResponseData: + properties: + nickname: + type: string + userId: + type: string + type: object + github_com_go-nunu_nunu-layout-advanced_api_v1.LoginRequest: properties: password: example: "123456" @@ -12,7 +27,21 @@ definitions: - password - username type: object - github_com_go-nunu_nunu-layout-advanced_internal_pkg_request.RegisterRequest: + github_com_go-nunu_nunu-layout-advanced_api_v1.LoginResponse: + properties: + code: + type: integer + data: + $ref: '#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.LoginResponseData' + message: + type: string + type: object + github_com_go-nunu_nunu-layout-advanced_api_v1.LoginResponseData: + properties: + accessToken: + type: string + type: object + github_com_go-nunu_nunu-layout-advanced_api_v1.RegisterRequest: properties: email: example: 1234@gmail.com @@ -28,7 +57,7 @@ definitions: - password - username type: object - github_com_go-nunu_nunu-layout-advanced_internal_pkg_response.Response: + github_com_go-nunu_nunu-layout-advanced_api_v1.Response: properties: code: type: integer @@ -60,14 +89,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github_com_go-nunu_nunu-layout-advanced_internal_pkg_request.LoginRequest' + $ref: '#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.LoginRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/github_com_go-nunu_nunu-layout-advanced_internal_pkg_response.Response' + $ref: '#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.LoginResponse' summary: 账号登录 tags: - 用户模块 @@ -82,14 +111,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github_com_go-nunu_nunu-layout-advanced_internal_pkg_request.RegisterRequest' + $ref: '#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.RegisterRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/github_com_go-nunu_nunu-layout-advanced_internal_pkg_response.Response' + $ref: '#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.Response' summary: 用户注册 tags: - 用户模块 @@ -103,7 +132,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github_com_go-nunu_nunu-layout-advanced_internal_pkg_response.Response' + $ref: '#/definitions/github_com_go-nunu_nunu-layout-advanced_api_v1.GetProfileResponse' security: - Bearer: [] summary: 获取用户信息 diff --git a/go.mod b/go.mod index b3c8f50..eee4dd4 100644 --- a/go.mod +++ b/go.mod @@ -19,11 +19,11 @@ require ( github.com/swaggo/files v1.0.1 github.com/swaggo/gin-swagger v1.6.0 github.com/swaggo/swag v1.16.2 - go.uber.org/zap v1.24.0 + go.uber.org/zap v1.26.0 golang.org/x/crypto v0.12.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gorm.io/driver/mysql v1.5.1 - gorm.io/gorm v1.25.1 + gorm.io/gorm v1.25.5 ) require ( @@ -69,7 +69,7 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect go.uber.org/atomic v1.11.0 // indirect - go.uber.org/multierr v1.8.0 // indirect + go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/net v0.14.0 // indirect golang.org/x/sys v0.12.0 // indirect @@ -78,4 +78,5 @@ require ( google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + moul.io/zapgorm2 v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 02a439d..291bb4f 100644 --- a/go.sum +++ b/go.sum @@ -43,6 +43,7 @@ github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= @@ -191,6 +192,7 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -237,6 +239,7 @@ github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= @@ -308,10 +311,18 @@ go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= @@ -530,6 +541,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= @@ -640,6 +652,7 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -649,8 +662,11 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw= gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o= +gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gorm.io/gorm v1.25.1 h1:nsSALe5Pr+cM3V1qwwQ7rOkw+6UeLrX5O4v3llhHa64= gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls= +gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -658,6 +674,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +moul.io/zapgorm2 v1.3.0 h1:+CzUTMIcnafd0d/BvBce8T4uPn6DQnpIrz64cyixlkk= +moul.io/zapgorm2 v1.3.0/go.mod h1:nPVy6U9goFKHR4s+zfSo1xVFaoU7Qgd5DoCdOfzoCqs= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= diff --git a/internal/handler/user.go b/internal/handler/user.go index 26c91b3..f8bbdc9 100644 --- a/internal/handler/user.go +++ b/internal/handler/user.go @@ -2,9 +2,9 @@ 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" + v1 "github.com/go-nunu/nunu-layout-advanced/api/v1" "github.com/go-nunu/nunu-layout-advanced/internal/service" + "go.uber.org/zap" "net/http" ) @@ -34,22 +34,23 @@ type userHandler struct { // @Tags 用户模块 // @Accept json // @Produce json -// @Param request body request.RegisterRequest true "params" -// @Success 200 {object} response.Response +// @Param request body v1.RegisterRequest true "params" +// @Success 200 {object} v1.Response // @Router /register [post] func (h *userHandler) Register(ctx *gin.Context) { - req := new(request.RegisterRequest) + req := new(v1.RegisterRequest) if err := ctx.ShouldBindJSON(req); err != nil { - response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil) + v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil) return } if err := h.userService.Register(ctx, req); err != nil { - response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil) + h.logger.WithContext(ctx).Error("userService.Register error", zap.Error(err)) + v1.HandleError(ctx, http.StatusInternalServerError, err, nil) return } - response.HandleSuccess(ctx, nil) + v1.HandleSuccess(ctx, nil) } // Login godoc @@ -59,24 +60,23 @@ func (h *userHandler) Register(ctx *gin.Context) { // @Tags 用户模块 // @Accept json // @Produce json -// @Param request body request.LoginRequest true "params" -// @Success 200 {object} response.Response +// @Param request body v1.LoginRequest true "params" +// @Success 200 {object} v1.LoginResponse // @Router /login [post] func (h *userHandler) Login(ctx *gin.Context) { - var req request.LoginRequest + var req v1.LoginRequest if err := ctx.ShouldBindJSON(&req); err != nil { - response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil) + v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil) return } token, err := h.userService.Login(ctx, &req) if err != nil { - response.HandleError(ctx, http.StatusUnauthorized, response.ErrUnauthorized, nil) + v1.HandleError(ctx, http.StatusUnauthorized, v1.ErrUnauthorized, nil) return } - - response.HandleSuccess(ctx, gin.H{ - "accessToken": token, + v1.HandleSuccess(ctx, v1.LoginResponseData{ + AccessToken: token, }) } @@ -88,37 +88,37 @@ func (h *userHandler) Login(ctx *gin.Context) { // @Accept json // @Produce json // @Security Bearer -// @Success 200 {object} response.Response +// @Success 200 {object} v1.GetProfileResponse // @Router /user [get] func (h *userHandler) GetProfile(ctx *gin.Context) { userId := GetUserIdFromCtx(ctx) if userId == "" { - response.HandleError(ctx, http.StatusUnauthorized, response.ErrUnauthorized, nil) + v1.HandleError(ctx, http.StatusUnauthorized, v1.ErrUnauthorized, nil) return } user, err := h.userService.GetProfile(ctx, userId) if err != nil { - response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil) + v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil) return } - response.HandleSuccess(ctx, user) + v1.HandleSuccess(ctx, user) } func (h *userHandler) UpdateProfile(ctx *gin.Context) { userId := GetUserIdFromCtx(ctx) - var req request.UpdateProfileRequest + var req v1.UpdateProfileRequest if err := ctx.ShouldBindJSON(&req); err != nil { - response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil) + v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil) return } if err := h.userService.UpdateProfile(ctx, userId, &req); err != nil { - response.HandleError(ctx, http.StatusInternalServerError, response.ErrInternalServerError, nil) + v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil) return } - response.HandleSuccess(ctx, nil) + v1.HandleSuccess(ctx, nil) } diff --git a/internal/job/job.go b/internal/job/job.go deleted file mode 100644 index 9815f0a..0000000 --- a/internal/job/job.go +++ /dev/null @@ -1,35 +0,0 @@ -package job - -import ( - "fmt" - "github.com/go-co-op/gocron" - "github.com/go-nunu/nunu-layout-advanced/pkg/log" - "time" -) - -type Job struct { - log *log.Logger -} - -func NewJob(log *log.Logger) *Job { - return &Job{ - log: log, - } -} -func (j *Job) Run() { - s := gocron.NewScheduler(time.UTC) - _, err := s.CronWithSeconds("0/3 * * * * *").Do(func() { - j.log.Info("I'm a Task1.") - }) - if err != nil { - fmt.Println(err) - } - _, err = s.Every("3s").Do(func() { - j.log.Info("I'm a Task2.") - }) - if err != nil { - fmt.Println(err) - } - - s.StartBlocking() -} diff --git a/internal/pkg/middleware/cors.go b/internal/middleware/cors.go similarity index 100% rename from internal/pkg/middleware/cors.go rename to internal/middleware/cors.go diff --git a/internal/pkg/middleware/jwt.go b/internal/middleware/jwt.go similarity index 86% rename from internal/pkg/middleware/jwt.go rename to internal/middleware/jwt.go index 9a33ecc..433e30f 100644 --- a/internal/pkg/middleware/jwt.go +++ b/internal/middleware/jwt.go @@ -2,7 +2,7 @@ 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/api/v1" "github.com/go-nunu/nunu-layout-advanced/pkg/jwt" "github.com/go-nunu/nunu-layout-advanced/pkg/log" "go.uber.org/zap" @@ -17,7 +17,7 @@ func StrictAuth(j *jwt.JWT, logger *log.Logger) gin.HandlerFunc { "url": ctx.Request.URL, "params": ctx.Params, })) - response.HandleError(ctx, http.StatusUnauthorized, response.ErrUnauthorized, nil) + v1.HandleError(ctx, http.StatusUnauthorized, v1.ErrUnauthorized, nil) ctx.Abort() return } @@ -27,8 +27,8 @@ func StrictAuth(j *jwt.JWT, logger *log.Logger) gin.HandlerFunc { logger.WithContext(ctx).Error("token error", zap.Any("data", map[string]interface{}{ "url": ctx.Request.URL, "params": ctx.Params, - })) - response.HandleError(ctx, http.StatusUnauthorized, response.ErrUnauthorized, nil) + }), zap.Error(err)) + v1.HandleError(ctx, http.StatusUnauthorized, v1.ErrUnauthorized, nil) ctx.Abort() return } diff --git a/internal/pkg/middleware/log.go b/internal/middleware/log.go similarity index 99% rename from internal/pkg/middleware/log.go rename to internal/middleware/log.go index 5aa76b0..b6d71e7 100644 --- a/internal/pkg/middleware/log.go +++ b/internal/middleware/log.go @@ -13,7 +13,6 @@ import ( func RequestLogMiddleware(logger *log.Logger) gin.HandlerFunc { return func(ctx *gin.Context) { - // The configuration is initialized once per request trace := md5.Md5(uuid.GenUUID()) logger.NewContext(ctx, zap.String("trace", trace)) diff --git a/internal/pkg/middleware/sign.go b/internal/middleware/sign.go similarity index 84% rename from internal/pkg/middleware/sign.go rename to internal/middleware/sign.go index ce2dc9f..624061a 100644 --- a/internal/pkg/middleware/sign.go +++ b/internal/middleware/sign.go @@ -2,7 +2,7 @@ package middleware import ( "github.com/gin-gonic/gin" - "github.com/go-nunu/nunu-layout-advanced/internal/pkg/response" + v1 "github.com/go-nunu/nunu-layout-advanced/api/v1" "github.com/go-nunu/nunu-layout-advanced/pkg/helper/md5" "github.com/go-nunu/nunu-layout-advanced/pkg/log" "github.com/spf13/viper" @@ -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 { - response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil) + v1.HandleError(ctx, http.StatusBadRequest, v1.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)) { - response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil) + v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil) ctx.Abort() return } diff --git a/internal/repository/repository.go b/internal/repository/repository.go index 968a94f..92a3667 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -8,6 +8,7 @@ import ( "github.com/spf13/viper" "gorm.io/driver/mysql" "gorm.io/gorm" + "moul.io/zapgorm2" "time" ) @@ -25,11 +26,14 @@ 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{}) +func NewDB(conf *viper.Viper, l *log.Logger) *gorm.DB { + logger := zapgorm2.New(l.Logger) + logger.SetAsDefault() + db, err := gorm.Open(mysql.Open(conf.GetString("data.mysql.user")), &gorm.Config{Logger: logger}) if err != nil { - panic(fmt.Sprintf("mysql error: %s", err.Error())) + panic(err) } + db = db.Debug() return db } func NewRedis(conf *viper.Viper) *redis.Client { diff --git a/internal/repository/user.go b/internal/repository/user.go index 91ea2db..660a6ba 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -2,8 +2,8 @@ package repository import ( "context" + v1 "github.com/go-nunu/nunu-layout-advanced/api/v1" "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" ) @@ -44,7 +44,7 @@ func (r *userRepository) GetByID(ctx context.Context, userId string) (*model.Use 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, v1.ErrNotFound } return nil, errors.Wrap(err, "failed to get user by ID") } diff --git a/internal/server/http.go b/internal/server/http.go index b256b27..ebd995f 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -2,64 +2,71 @@ package server import ( "github.com/gin-gonic/gin" + apiV1 "github.com/go-nunu/nunu-layout-advanced/api/v1" "github.com/go-nunu/nunu-layout-advanced/docs" "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/internal/pkg/response" + "github.com/go-nunu/nunu-layout-advanced/internal/middleware" "github.com/go-nunu/nunu-layout-advanced/pkg/jwt" "github.com/go-nunu/nunu-layout-advanced/pkg/log" + "github.com/go-nunu/nunu-layout-advanced/pkg/server/http" + "github.com/spf13/viper" swaggerfiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" ) -func NewServerHTTP( +func NewHTTPServer( logger *log.Logger, + conf *viper.Viper, jwt *jwt.JWT, userHandler handler.UserHandler, -) *gin.Engine { - gin.SetMode(gin.ReleaseMode) - r := gin.Default() +) *http.Server { + gin.SetMode(gin.DebugMode) + s := http.NewServer( + gin.Default(), + logger, + http.WithServerHost(conf.GetString("http.host")), + http.WithServerPort(conf.GetInt("http.port")), + ) // swagger doc - docs.SwaggerInfo.BasePath = "/" - r.GET("/swagger/*any", ginSwagger.WrapHandler( + docs.SwaggerInfo.BasePath = "/v1" + s.GET("/swagger/*any", ginSwagger.WrapHandler( swaggerfiles.Handler, //ginSwagger.URL(fmt.Sprintf("http://localhost:%d/swagger/doc.json", conf.GetInt("app.http.port"))), ginSwagger.DefaultModelsExpandDepth(-1), )) - r.Use( + s.Use( middleware.CORSMiddleware(), middleware.ResponseLogMiddleware(logger), middleware.RequestLogMiddleware(logger), //middleware.SignMiddleware(log), ) - - // No route group has permission - noAuthRouter := r.Group("/") - { - - noAuthRouter.GET("/", func(ctx *gin.Context) { - logger.WithContext(ctx).Info("hello") - response.HandleSuccess(ctx, map[string]interface{}{ - ":)": "Thank you for using nunu!", - }) + s.GET("/", func(ctx *gin.Context) { + logger.WithContext(ctx).Info("hello") + apiV1.HandleSuccess(ctx, map[string]interface{}{ + ":)": "Thank you for using nunu!", }) + }) + v1 := s.Group("/v1") + // No route group has permission + noAuthRouter := v1 + { noAuthRouter.POST("/register", userHandler.Register) noAuthRouter.POST("/login", userHandler.Login) } // Non-strict permission routing group - noStrictAuthRouter := r.Group("/").Use(middleware.NoStrictAuth(jwt, logger)) + noStrictAuthRouter := v1.Use(middleware.NoStrictAuth(jwt, logger)) { noStrictAuthRouter.GET("/user", userHandler.GetProfile) } // Strict permission routing group - strictAuthRouter := r.Group("/").Use(middleware.StrictAuth(jwt, logger)) + strictAuthRouter := v1.Use(middleware.StrictAuth(jwt, logger)) { strictAuthRouter.PUT("/user", userHandler.UpdateProfile) } - return r + return s } diff --git a/internal/server/job.go b/internal/server/job.go new file mode 100644 index 0000000..ebee819 --- /dev/null +++ b/internal/server/job.go @@ -0,0 +1,25 @@ +package server + +import ( + "context" + "github.com/go-nunu/nunu-layout-advanced/pkg/log" +) + +type Job struct { + log *log.Logger +} + +func NewJob( + log *log.Logger, +) *Job { + return &Job{ + log: log, + } +} +func (j *Job) Start(ctx context.Context) error { + // eg: kafka consumer + return nil +} +func (j *Job) Stop(ctx context.Context) error { + return nil +} diff --git a/cmd/migration/internal/migration.go b/internal/server/migration.go similarity index 62% rename from cmd/migration/internal/migration.go rename to internal/server/migration.go index b5f2be1..6724a12 100644 --- a/cmd/migration/internal/migration.go +++ b/internal/server/migration.go @@ -1,10 +1,12 @@ -package internal +package server import ( + "context" "github.com/go-nunu/nunu-layout-advanced/internal/model" "github.com/go-nunu/nunu-layout-advanced/pkg/log" "go.uber.org/zap" "gorm.io/gorm" + "os" ) type Migrate struct { @@ -18,10 +20,16 @@ func NewMigrate(db *gorm.DB, log *log.Logger) *Migrate { log: log, } } -func (m *Migrate) Run() { +func (m *Migrate) Start(ctx context.Context) error { if err := m.db.AutoMigrate(&model.User{}); err != nil { m.log.Error("user migrate error", zap.Error(err)) - return + return err } - m.log.Info("AutoMigrate end") + m.log.Info("AutoMigrate success") + os.Exit(0) + return nil +} +func (m *Migrate) Stop(ctx context.Context) error { + m.log.Info("AutoMigrate stop") + return nil } diff --git a/internal/server/server.go b/internal/server/server.go deleted file mode 100644 index f515b59..0000000 --- a/internal/server/server.go +++ /dev/null @@ -1,17 +0,0 @@ -package server - -import ( - "github.com/gin-gonic/gin" -) - -type Server struct { - ServerHTTP *gin.Engine - //ServerGRPC *grpc.Server - //ServerWS *ws.Server -} - -func NewServer(serverHTTP *gin.Engine) *Server { - return &Server{ - ServerHTTP: serverHTTP, - } -} diff --git a/internal/server/task.go b/internal/server/task.go new file mode 100644 index 0000000..4f90117 --- /dev/null +++ b/internal/server/task.go @@ -0,0 +1,46 @@ +package server + +import ( + "context" + "github.com/go-co-op/gocron" + "github.com/go-nunu/nunu-layout-advanced/pkg/log" + "go.uber.org/zap" + "time" +) + +type Task struct { + log *log.Logger + scheduler *gocron.Scheduler +} + +func NewTask(log *log.Logger) *Task { + return &Task{ + log: log, + } +} +func (t *Task) Start(ctx context.Context) error { + // eg: crontab task + t.scheduler = gocron.NewScheduler(time.UTC) + + _, err := t.scheduler.CronWithSeconds("0/3 * * * * *").Do(func() { + t.log.Info("I'm a Task1.") + }) + if err != nil { + t.log.Error("Task1 error", zap.Error(err)) + } + + _, err = t.scheduler.Every("3s").Do(func() { + t.log.Info("I'm a Task2.") + }) + if err != nil { + t.log.Error("Task2 error", zap.Error(err)) + } + + t.scheduler.StartBlocking() + return nil +} +func (t *Task) Stop(ctx context.Context) error { + t.scheduler.Stop() + t.log.Info("Task stop...") + return nil +} diff --git a/internal/service/user.go b/internal/service/user.go index 6ce93fc..8f36229 100644 --- a/internal/service/user.go +++ b/internal/service/user.go @@ -2,9 +2,8 @@ package service import ( "context" + v1 "github.com/go-nunu/nunu-layout-advanced/api/v1" "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" @@ -12,10 +11,10 @@ import ( ) type UserService interface { - Register(ctx context.Context, req *request.RegisterRequest) error - Login(ctx context.Context, req *request.LoginRequest) (string, error) - GetProfile(ctx context.Context, userId string) (*model.User, error) - UpdateProfile(ctx context.Context, userId string, req *request.UpdateProfileRequest) error + Register(ctx context.Context, req *v1.RegisterRequest) error + Login(ctx context.Context, req *v1.LoginRequest) (string, error) + GetProfile(ctx context.Context, userId string) (*v1.GetProfileResponseData, error) + UpdateProfile(ctx context.Context, userId string, req *v1.UpdateProfileRequest) error } func NewUserService(service *Service, userRepo repository.UserRepository) UserService { @@ -30,10 +29,10 @@ type userService struct { *Service } -func (s *userService) Register(ctx context.Context, req *request.RegisterRequest) error { +func (s *userService) Register(ctx context.Context, req *v1.RegisterRequest) error { // check username if user, err := s.userRepo.GetByUsername(ctx, req.Username); err == nil && user != nil { - return response.ErrUsernameAlreadyUse + return v1.ErrUsernameAlreadyUse } hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) @@ -49,6 +48,7 @@ func (s *userService) Register(ctx context.Context, req *request.RegisterRequest user := &model.User{ UserId: userId, Username: req.Username, + Nickname: req.Username, Password: string(hashedPassword), Email: req.Email, } @@ -59,10 +59,10 @@ func (s *userService) Register(ctx context.Context, req *request.RegisterRequest return nil } -func (s *userService) Login(ctx context.Context, req *request.LoginRequest) (string, error) { +func (s *userService) Login(ctx context.Context, req *v1.LoginRequest) (string, error) { user, err := s.userRepo.GetByUsername(ctx, req.Username) if err != nil || user == nil { - return "", response.ErrUnauthorized + return "", v1.ErrUnauthorized } err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password)) @@ -77,16 +77,20 @@ func (s *userService) Login(ctx context.Context, req *request.LoginRequest) (str return token, nil } -func (s *userService) GetProfile(ctx context.Context, userId string) (*model.User, error) { +func (s *userService) GetProfile(ctx context.Context, userId string) (*v1.GetProfileResponseData, error) { user, err := s.userRepo.GetByID(ctx, userId) if err != nil { return nil, errors.Wrap(err, "failed to get user by ID") } - return user, nil + return &v1.GetProfileResponseData{ + UserId: user.UserId, + Nickname: user.Nickname, + Username: user.Username, + }, nil } -func (s *userService) UpdateProfile(ctx context.Context, userId string, req *request.UpdateProfileRequest) error { +func (s *userService) UpdateProfile(ctx context.Context, userId string, req *v1.UpdateProfileRequest) error { user, err := s.userRepo.GetByID(ctx, userId) if err != nil { return errors.Wrap(err, "failed to get user by ID") diff --git a/pkg/app/app.go b/pkg/app/app.go new file mode 100644 index 0000000..b19613c --- /dev/null +++ b/pkg/app/app.go @@ -0,0 +1,70 @@ +package app + +import ( + "context" + "github.com/go-nunu/nunu-layout-advanced/pkg/server" + "log" + "os" + "os/signal" + "syscall" +) + +type App struct { + name string + servers []server.Server +} + +type Option func(a *App) + +func NewApp(opts ...Option) *App { + a := &App{} + for _, opt := range opts { + opt(a) + } + return a +} + +func WithServer(servers ...server.Server) Option { + return func(a *App) { + a.servers = servers + } +} +func WithName(name string) Option { + return func(a *App) { + a.name = name + } +} + +func (a *App) Run(ctx context.Context) error { + ctx, cancel := context.WithCancel(ctx) + signals := make(chan os.Signal, 1) + signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) + endpoints := make([]string, 0) + for _, srv := range a.servers { + if s, ok := srv.(server.Endpointer); ok { + e, err := s.Endpoint() + if err != nil { + return err + } + endpoints = append(endpoints, e.String()) + } + go func(srv server.Server) { + err := srv.Start(ctx) + if err != nil { + cancel() + log.Fatal("app start err:", err) + } + }(srv) + } + + <-signals + cancel() + for _, srv := range a.servers { + err := srv.Stop(ctx) + if err != nil { + log.Fatal("app stop err:", err) + } + } + + return nil +} diff --git a/pkg/http/http.go b/pkg/http/http.go deleted file mode 100644 index 82a8a0d..0000000 --- a/pkg/http/http.go +++ /dev/null @@ -1,48 +0,0 @@ -package http - -import ( - "context" - "github.com/gin-gonic/gin" - "log" - "net/http" - "os" - "os/signal" - "syscall" - "time" -) - -func Run(r *gin.Engine, addr string) { - - srv := &http.Server{ - Addr: addr, - Handler: r, - } - - // Initializing the server in a goroutine so that - // it won't block the graceful shutdown handling below - go func() { - if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { - log.Fatalf("listen: %s\n", err) - } - }() - - // Wait for interrupt signal to gracefully shutdown the server with - // a timeout of 5 seconds. - quit := make(chan os.Signal, 1) - // kill (no param) default send syscall.SIGTERM - // kill -2 is syscall.SIGINT - // kill -9 is syscall.SIGKILL but can't be catch, so don't need add it - signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) - <-quit - log.Println("Shutting down server...") - - // The context is used to inform the server it has 5 seconds to finish - // the request it is currently handling - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - if err := srv.Shutdown(ctx); err != nil { - log.Fatal("Server forced to shutdown: ", err) - } - - log.Println("Server exiting") -} diff --git a/pkg/jwt/jwt.go b/pkg/jwt/jwt.go index b0e70a6..7fd00e8 100644 --- a/pkg/jwt/jwt.go +++ b/pkg/jwt/jwt.go @@ -1,6 +1,7 @@ package jwt import ( + "github.com/pkg/errors" "regexp" "time" @@ -46,10 +47,12 @@ func (j *JWT) GenToken(userId string, expiresAt time.Time) (string, error) { func (j *JWT) ParseToken(tokenString string) (*MyCustomClaims, error) { re := regexp.MustCompile(`(?i)Bearer `) tokenString = re.ReplaceAllString(tokenString, "") + if tokenString == "" { + return nil, errors.New("token is empty") + } token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, func(token *jwt.Token) (interface{}, error) { return j.key, nil }) - if claims, ok := token.Claims.(*MyCustomClaims); ok && token.Valid { return claims, nil } else { diff --git a/pkg/server/grpc/grpc.go b/pkg/server/grpc/grpc.go new file mode 100644 index 0000000..5d5bbde --- /dev/null +++ b/pkg/server/grpc/grpc.go @@ -0,0 +1,61 @@ +package grpc + +import ( + "context" + "fmt" + "github.com/go-nunu/nunu-layout-advanced/pkg/log" + "google.golang.org/grpc" + "net" + "time" +) + +type Server struct { + *grpc.Server + host string + port int + logger *log.Logger +} + +type Option func(s *Server) + +func NewServer(logger *log.Logger, opts ...Option) *Server { + s := &Server{ + Server: grpc.NewServer(), + logger: logger, + } + for _, opt := range opts { + opt(s) + } + return s +} +func WithServerHost(host string) Option { + return func(s *Server) { + s.host = host + } +} +func WithServerPort(port int) Option { + return func(s *Server) { + s.port = port + } +} + +func (s *Server) Start(ctx context.Context) error { + lis, err := net.Listen("tcp", fmt.Sprintf("%s:%d", s.host, s.port)) + if err != nil { + s.logger.Sugar().Fatalf("Failed to listen: %v", err) + } + if err = s.Server.Serve(lis); err != nil { + s.logger.Sugar().Fatalf("Failed to serve: %v", err) + } + return nil + +} +func (s *Server) Stop(ctx context.Context) error { + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + s.Server.GracefulStop() + + s.logger.Info("Server exiting") + + return nil +} diff --git a/pkg/server/http/http.go b/pkg/server/http/http.go new file mode 100644 index 0000000..93007fd --- /dev/null +++ b/pkg/server/http/http.go @@ -0,0 +1,67 @@ +package http + +import ( + "context" + "fmt" + "github.com/gin-gonic/gin" + "github.com/go-nunu/nunu-layout-advanced/pkg/log" + "net/http" + "time" +) + +type Server struct { + *gin.Engine + httpSrv *http.Server + host string + port int + logger *log.Logger +} +type Option func(s *Server) + +func NewServer(engine *gin.Engine, logger *log.Logger, opts ...Option) *Server { + s := &Server{ + Engine: engine, + logger: logger, + } + for _, opt := range opts { + opt(s) + } + return s +} +func WithServerHost(host string) Option { + return func(s *Server) { + s.host = host + } +} +func WithServerPort(port int) Option { + return func(s *Server) { + s.port = port + } +} + +func (s *Server) Start(ctx context.Context) error { + s.httpSrv = &http.Server{ + Addr: fmt.Sprintf("%s:%d", s.host, s.port), + Handler: s, + } + + if err := s.httpSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + s.logger.Sugar().Fatalf("listen: %s\n", err) + } + + return nil +} +func (s *Server) Stop(ctx context.Context) error { + s.logger.Sugar().Info("Shutting down server...") + + // The context is used to inform the server it has 5 seconds to finish + // the request it is currently handling + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := s.httpSrv.Shutdown(ctx); err != nil { + s.logger.Sugar().Fatal("Server forced to shutdown: ", err) + } + + s.logger.Sugar().Info("Server exiting") + return nil +} diff --git a/pkg/server/server.go b/pkg/server/server.go new file mode 100644 index 0000000..7de9b8d --- /dev/null +++ b/pkg/server/server.go @@ -0,0 +1,16 @@ +package server + +import ( + "context" + "net/url" +) + +type Server interface { + Start(context.Context) error + Stop(context.Context) error +} + +// Endpointer is registry endpoint. +type Endpointer interface { + Endpoint() (*url.URL, error) +} diff --git a/test/mocks/service/user.go b/test/mocks/service/user.go index db3f8cd..ce27f6f 100644 --- a/test/mocks/service/user.go +++ b/test/mocks/service/user.go @@ -8,8 +8,7 @@ import ( context "context" reflect "reflect" - model "github.com/go-nunu/nunu-layout-advanced/internal/model" - request "github.com/go-nunu/nunu-layout-advanced/internal/pkg/request" + v1 "github.com/go-nunu/nunu-layout-advanced/api/v1" gomock "github.com/golang/mock/gomock" ) @@ -37,10 +36,10 @@ func (m *MockUserService) EXPECT() *MockUserServiceMockRecorder { } // GetProfile mocks base method. -func (m *MockUserService) GetProfile(ctx context.Context, userId string) (*model.User, error) { +func (m *MockUserService) GetProfile(ctx context.Context, userId string) (*v1.GetProfileResponseData, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetProfile", ctx, userId) - ret0, _ := ret[0].(*model.User) + ret0, _ := ret[0].(*v1.GetProfileResponseData) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -52,7 +51,7 @@ func (mr *MockUserServiceMockRecorder) GetProfile(ctx, userId interface{}) *gomo } // Login mocks base method. -func (m *MockUserService) Login(ctx context.Context, req *request.LoginRequest) (string, error) { +func (m *MockUserService) Login(ctx context.Context, req *v1.LoginRequest) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Login", ctx, req) ret0, _ := ret[0].(string) @@ -67,7 +66,7 @@ func (mr *MockUserServiceMockRecorder) Login(ctx, req interface{}) *gomock.Call } // Register mocks base method. -func (m *MockUserService) Register(ctx context.Context, req *request.RegisterRequest) error { +func (m *MockUserService) Register(ctx context.Context, req *v1.RegisterRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Register", ctx, req) ret0, _ := ret[0].(error) @@ -81,7 +80,7 @@ func (mr *MockUserServiceMockRecorder) Register(ctx, req interface{}) *gomock.Ca } // UpdateProfile mocks base method. -func (m *MockUserService) UpdateProfile(ctx context.Context, userId string, req *request.UpdateProfileRequest) error { +func (m *MockUserService) UpdateProfile(ctx context.Context, userId string, req *v1.UpdateProfileRequest) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateProfile", ctx, userId, req) ret0, _ := ret[0].(error) diff --git a/test/server/handler/user_test.go b/test/server/handler/user_test.go index c80c50b..e24ebd7 100644 --- a/test/server/handler/user_test.go +++ b/test/server/handler/user_test.go @@ -4,11 +4,12 @@ import ( "bytes" "encoding/json" "fmt" + v1 "github.com/go-nunu/nunu-layout-advanced/api/v1" "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/internal/pkg/request" + "github.com/go-nunu/nunu-layout-advanced/internal/middleware" jwt2 "github.com/go-nunu/nunu-layout-advanced/pkg/jwt" "github.com/go-nunu/nunu-layout-advanced/test/mocks/service" + "time" "net/http" "net/http/httptest" @@ -16,7 +17,6 @@ import ( "testing" "github.com/gin-gonic/gin" - "github.com/go-nunu/nunu-layout-advanced/internal/model" "github.com/go-nunu/nunu-layout-advanced/pkg/config" "github.com/go-nunu/nunu-layout-advanced/pkg/log" "github.com/golang/mock/gomock" @@ -24,9 +24,7 @@ import ( ) var ( - userId = "yhs6HesfgF" - - token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOiJ5aHM2SGVzZmdGIiwiZXhwIjoxNjkzOTE0ODgwLCJuYmYiOjE2ODYxMzg4ODAsImlhdCI6MTY4NjEzODg4MH0.NnFrZFgc_333a9PXqaoongmIDksNvQoHzgM_IhJM4MQ" + userId = "xxx" ) var logger *log.Logger var hdl *handler.Handler @@ -64,7 +62,7 @@ func TestUserHandler_Register(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - params := request.RegisterRequest{ + params := v1.RegisterRequest{ Username: "xxx", Password: "123456", Email: "xxx@gmail.com", @@ -88,13 +86,13 @@ func TestUserHandler_Login(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - params := request.LoginRequest{ + params := v1.LoginRequest{ Username: "xxx", Password: "123456", } mockUserService := mock_service.NewMockUserService(ctrl) - mockUserService.EXPECT().Login(gomock.Any(), ¶ms).Return(token, nil) + mockUserService.EXPECT().Login(gomock.Any(), ¶ms).Return("", nil) userHandler := handler.NewUserHandler(hdl, mockUserService) router.POST("/login", userHandler.Login) @@ -111,20 +109,17 @@ func TestUserHandler_GetProfile(t *testing.T) { defer ctrl.Finish() mockUserService := mock_service.NewMockUserService(ctrl) - mockUserService.EXPECT().GetProfile(gomock.Any(), userId).Return(&model.User{ - Id: 1, + mockUserService.EXPECT().GetProfile(gomock.Any(), userId).Return(&v1.GetProfileResponseData{ UserId: userId, Username: "xxxxx", Nickname: "xxxxx", - Password: "xxxxx", - Email: "xxxxx@gmail.com", }, nil) userHandler := handler.NewUserHandler(hdl, mockUserService) router.Use(middleware.NoStrictAuth(jwt, logger)) router.GET("/user", userHandler.GetProfile) req, _ := http.NewRequest("GET", "/user", nil) - req.Header.Set("Authorization", "Bearer "+token) + req.Header.Set("Authorization", "Bearer "+genToken(t)) resp := httptest.NewRecorder() @@ -137,7 +132,7 @@ func TestUserHandler_UpdateProfile(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - params := request.UpdateProfileRequest{ + params := v1.UpdateProfileRequest{ Nickname: "alan", Email: "alan@gmail.com", Avatar: "xxx", @@ -152,7 +147,7 @@ func TestUserHandler_UpdateProfile(t *testing.T) { paramsJson, _ := json.Marshal(params) req, _ := http.NewRequest("PUT", "/user", bytes.NewBuffer(paramsJson)) - req.Header.Set("Authorization", "Bearer "+token) + req.Header.Set("Authorization", "Bearer "+genToken(t)) req.Header.Set("Content-Type", "application/json") resp := httptest.NewRecorder() @@ -168,3 +163,11 @@ func performRequest(r http.Handler, method, path string, body *bytes.Buffer) *ht r.ServeHTTP(resp, req) return resp } +func genToken(t *testing.T) string { + token, err := jwt.GenToken(userId, time.Now().Add(time.Hour*24*90)) + if err != nil { + t.Error(err) + return token + } + return token +} diff --git a/test/server/service/user_test.go b/test/server/service/user_test.go index 3cf256c..e66acf1 100644 --- a/test/server/service/user_test.go +++ b/test/server/service/user_test.go @@ -4,7 +4,7 @@ import ( "context" "errors" "fmt" - "github.com/go-nunu/nunu-layout-advanced/internal/pkg/request" + v1 "github.com/go-nunu/nunu-layout-advanced/api/v1" "github.com/go-nunu/nunu-layout-advanced/pkg/jwt" "github.com/go-nunu/nunu-layout-advanced/test/mocks/repository" "os" @@ -54,7 +54,7 @@ func TestUserService_Register(t *testing.T) { userService := service.NewUserService(srv, mockUserRepo) ctx := context.Background() - req := &request.RegisterRequest{ + req := &v1.RegisterRequest{ Username: "testuser", Password: "password", Email: "test@example.com", @@ -77,7 +77,7 @@ func TestUserService_Register_UsernameExists(t *testing.T) { userService := service.NewUserService(srv, mockUserRepo) ctx := context.Background() - req := &request.RegisterRequest{ + req := &v1.RegisterRequest{ Username: "testuser", Password: "password", Email: "test@example.com", @@ -99,7 +99,7 @@ func TestUserService_Login(t *testing.T) { userService := service.NewUserService(srv, mockUserRepo) ctx := context.Background() - req := &request.LoginRequest{ + req := &v1.LoginRequest{ Username: "testuser", Password: "password", } @@ -127,7 +127,7 @@ func TestUserService_Login_UserNotFound(t *testing.T) { userService := service.NewUserService(srv, mockUserRepo) ctx := context.Background() - req := &request.LoginRequest{ + req := &v1.LoginRequest{ Username: "testuser", Password: "password", } @@ -161,7 +161,6 @@ func TestUserService_GetProfile(t *testing.T) { assert.NoError(t, err) assert.Equal(t, userId, user.UserId) assert.Equal(t, "testuser", user.Username) - assert.Equal(t, "test@example.com", user.Email) } func TestUserService_UpdateProfile(t *testing.T) { @@ -174,7 +173,7 @@ func TestUserService_UpdateProfile(t *testing.T) { ctx := context.Background() userId := "123" - req := &request.UpdateProfileRequest{ + req := &v1.UpdateProfileRequest{ Nickname: "testuser", Email: "test@example.com", } @@ -201,7 +200,7 @@ func TestUserService_UpdateProfile_UserNotFound(t *testing.T) { ctx := context.Background() userId := "123" - req := &request.UpdateProfileRequest{ + req := &v1.UpdateProfileRequest{ Nickname: "testuser", Email: "test@example.com", }