feat: Adjust directory structure and add multi server support

This commit is contained in:
chris
2023-10-13 00:05:02 +08:00
parent aa00079f15
commit 9e052d7853
46 changed files with 772 additions and 741 deletions
-185
View File
@@ -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.
-216
View File
@@ -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
<p align="left"><img src="https://github.com/go-nunu/nunu/blob/main/.github/assets/qrcode.jpg" width="200"></p>
## 简洁分层架构
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)文件。
@@ -1,4 +1,4 @@
package response
package v1
var (
// common errors
@@ -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
}
@@ -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 {
-19
View File
@@ -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,
))
}
-26
View File
@@ -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)
+6 -3
View File
@@ -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)
}
}
+15 -5
View File
@@ -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,
))
}
+13 -6
View File
@@ -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"))
}
+7 -10
View File
@@ -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)
}
}
+25 -9
View File
@@ -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,
))
}
+18 -8
View File
@@ -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"))
}
+8 -6
View File
@@ -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)
}
}
+29
View File
@@ -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,
))
}
+33
View File
@@ -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"))
}
+1
View File
@@ -1,5 +1,6 @@
env: local
http:
host: 0.0.0.0
port: 8000
security:
api_sign:
+2 -1
View File
@@ -1,5 +1,6 @@
env: local
env: prod
http:
host: 0.0.0.0
port: 8000
security:
api_sign:
+56 -9
View File
@@ -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.",
+55 -9
View File
@@ -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": {
+38 -9
View File
@@ -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: 获取用户信息
+4 -3
View File
@@ -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
)
+18
View File
@@ -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=
+24 -24
View File
@@ -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)
}
-35
View File
@@ -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()
}
@@ -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
}
@@ -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))
@@ -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
}
+7 -3
View File
@@ -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 {
+2 -2
View File
@@ -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")
}
+29 -22
View File
@@ -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
}
+25
View File
@@ -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
}
@@ -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
}
-17
View File
@@ -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,
}
}
+46
View File
@@ -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
}
+17 -13
View File
@@ -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")
+70
View File
@@ -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
}
-48
View File
@@ -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")
}
+4 -1
View File
@@ -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 {
+61
View File
@@ -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
}
+67
View File
@@ -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
}
+16
View File
@@ -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)
}
+6 -7
View File
@@ -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)
+19 -16
View File
@@ -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(), &params).Return(token, nil)
mockUserService.EXPECT().Login(gomock.Any(), &params).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
}
+7 -8
View File
@@ -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",
}