feat: initialize project
This commit is contained in:
parent
4b6c7d0496
commit
e55167549a
|
|
@ -0,0 +1,2 @@
|
|||
.git
|
||||
.idea
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
* text=auto
|
||||
|
||||
# Force the following filetypes to have unix eols, so Windows does not break them
|
||||
*.* text eol=lf
|
||||
|
||||
# Windows forced line-endings
|
||||
/.idea/* text eol=crlf
|
||||
|
||||
#
|
||||
## These files are binary and should be left untouched
|
||||
#
|
||||
|
||||
# (binary is a macro for -text -diff)
|
||||
*.png binary
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
storage/logs
|
||||
.idea
|
||||
*.log
|
||||
deploy/docker-compose/conf
|
||||
deploy/docker-compose/data
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 Nunu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
.PHONY: init
|
||||
init:
|
||||
go install github.com/google/wire/cmd/wire@latest
|
||||
go install github.com/golang/mock/mockgen@latest
|
||||
go install github.com/swaggo/swag/cmd/swag@latest
|
||||
|
||||
.PHONY: bootstrap
|
||||
bootstrap:
|
||||
cd ./deploy/docker-compose && docker compose up -d && cd ../../
|
||||
go run ./cmd/migration
|
||||
nunu run ./cmd/server
|
||||
|
||||
.PHONY: mock
|
||||
mock:
|
||||
mockgen -source=internal/service/user.go -destination test/mocks/service/user.go
|
||||
mockgen -source=internal/repository/user.go -destination test/mocks/repository/user.go
|
||||
mockgen -source=internal/repository/repository.go -destination test/mocks/repository/repository.go
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -coverpkg=./internal/handler,./internal/service,./internal/repository -coverprofile=./coverage.out ./test/server/...
|
||||
go tool cover -html=./coverage.out -o coverage.html
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
go build -ldflags="-s -w" -o ./bin/server ./cmd/server
|
||||
cd web && npm run build
|
||||
|
||||
.PHONY: docker
|
||||
docker:
|
||||
docker build -f deploy/build/Dockerfile --build-arg APP_RELATIVE_PATH=./cmd/task -t 1.1.1.1:5000/demo-task:v1 .
|
||||
docker run --rm -i 1.1.1.1:5000/demo-task:v1
|
||||
|
||||
.PHONY: swag
|
||||
swag:
|
||||
swag init -g cmd/server/main.go -o ./docs --parseDependency
|
||||
|
|
@ -0,0 +1,228 @@
|
|||
package v1
|
||||
|
||||
type LoginRequest struct {
|
||||
Username string `json:"username" binding:"required" example:"1234@gmail.com"`
|
||||
Password string `json:"password" binding:"required" example:"123456"`
|
||||
}
|
||||
type LoginResponseData struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
}
|
||||
type LoginResponse struct {
|
||||
Response
|
||||
Data LoginResponseData
|
||||
}
|
||||
|
||||
type AdminUserDataItem struct {
|
||||
ID uint `json:"id"`
|
||||
Username string `json:"username" binding:"required" example:"张三"`
|
||||
Nickname string `json:"nickname" binding:"required" example:"小Baby"`
|
||||
Password string `json:"password" binding:"required" example:"123456"`
|
||||
Email string `json:"email" binding:"required,email" example:"1234@gmail.com"`
|
||||
Phone string `form:"phone" binding:"" example:"1858888888"`
|
||||
Roles []string `json:"roles" example:""`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
}
|
||||
type GetAdminUsersRequest struct {
|
||||
Page int `form:"page" binding:"required" example:"1"`
|
||||
PageSize int `form:"pageSize" binding:"required" example:"10"`
|
||||
Username string `json:"username" binding:"" example:"张三"`
|
||||
Nickname string `json:"nickname" binding:"" example:"小Baby"`
|
||||
Phone string `form:"phone" binding:"" example:"1858888888"`
|
||||
Email string `form:"email" binding:"" example:"1234@gmail.com"`
|
||||
}
|
||||
type GetAdminUserResponseData struct {
|
||||
ID uint `json:"id"`
|
||||
Username string `json:"username" example:"张三"`
|
||||
Nickname string `json:"nickname" example:"小Baby"`
|
||||
Password string `json:"password" example:"123456"`
|
||||
Email string `json:"email" example:"1234@gmail.com"`
|
||||
Phone string `form:"phone" example:"1858888888"`
|
||||
Roles []string `json:"roles" example:""`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
}
|
||||
type GetAdminUserResponse struct {
|
||||
Response
|
||||
Data GetAdminUserResponseData
|
||||
}
|
||||
type GetAdminUsersResponseData struct {
|
||||
List []AdminUserDataItem `json:"list"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
type GetAdminUsersResponse struct {
|
||||
Response
|
||||
Data GetAdminUsersResponseData
|
||||
}
|
||||
type AdminUserCreateRequest struct {
|
||||
Username string `json:"username" binding:"required" example:"张三"`
|
||||
Nickname string `json:"nickname" binding:"" example:"小Baby"`
|
||||
Password string `json:"password" binding:"required" example:"123456"`
|
||||
Email string `json:"email" binding:"" example:"1234@gmail.com"`
|
||||
Phone string `form:"phone" binding:"" example:"1858888888"`
|
||||
Roles []string `json:"roles" example:""`
|
||||
}
|
||||
type AdminUserUpdateRequest struct {
|
||||
ID uint `json:"id"`
|
||||
Username string `json:"username" binding:"required" example:"张三"`
|
||||
Nickname string `json:"nickname" binding:"" example:"小Baby"`
|
||||
Password string `json:"password" binding:"" example:"123456"`
|
||||
Email string `json:"email" binding:"" example:"1234@gmail.com"`
|
||||
Phone string `form:"phone" binding:"" example:"1858888888"`
|
||||
Roles []string `json:"roles" example:""`
|
||||
}
|
||||
type AdminUserDeleteRequest struct {
|
||||
ID uint `form:"id" binding:"required" example:"1"`
|
||||
}
|
||||
|
||||
type MenuDataItem struct {
|
||||
ID uint `json:"id,omitempty"` // 唯一id,使用整数表示
|
||||
ParentID uint `json:"parentId,omitempty"` // 父级菜单的id,使用整数表示
|
||||
Weight int `json:"weight"` // 排序权重
|
||||
Path string `json:"path"` // 地址
|
||||
Title string `json:"title"` // 展示名称
|
||||
Name string `json:"name,omitempty"` // 同路由中的name,唯一标识
|
||||
Component string `json:"component,omitempty"` // 绑定的组件
|
||||
Locale string `json:"locale,omitempty"` // 本地化标识
|
||||
Icon string `json:"icon,omitempty"` // 图标,使用字符串表示
|
||||
Redirect string `json:"redirect,omitempty"` // 重定向地址
|
||||
KeepAlive bool `json:"keepAlive,omitempty"` // 是否保活
|
||||
HideInMenu bool `json:"hideInMenu,omitempty"` // 是否保活
|
||||
URL string `json:"url,omitempty"` // iframe模式下的跳转url,不能与path重复
|
||||
UpdatedAt string `json:"updatedAt,omitempty"` // 是否保活
|
||||
}
|
||||
type GetMenuResponseData struct {
|
||||
List []MenuDataItem `json:"list"`
|
||||
}
|
||||
|
||||
type GetMenuResponse struct {
|
||||
Response
|
||||
Data GetMenuResponseData
|
||||
}
|
||||
|
||||
type MenuCreateRequest struct {
|
||||
ParentID uint `json:"parentId,omitempty"` // 父级菜单的id,使用整数表示
|
||||
Weight int `json:"weight"` // 排序权重
|
||||
Path string `json:"path"` // 地址
|
||||
Title string `json:"title"` // 展示名称
|
||||
Name string `json:"name,omitempty"` // 同路由中的name,唯一标识
|
||||
Component string `json:"component,omitempty"` // 绑定的组件
|
||||
Locale string `json:"locale,omitempty"` // 本地化标识
|
||||
Icon string `json:"icon,omitempty"` // 图标,使用字符串表示
|
||||
Redirect string `json:"redirect,omitempty"` // 重定向地址
|
||||
KeepAlive bool `json:"keepAlive,omitempty"` // 是否保活
|
||||
HideInMenu bool `json:"hideInMenu,omitempty"` // 是否保活
|
||||
URL string `json:"url,omitempty"` // iframe模式下的跳转url,不能与path重复
|
||||
|
||||
}
|
||||
type MenuUpdateRequest struct {
|
||||
ID uint `json:"id,omitempty"` // 唯一id,使用整数表示
|
||||
ParentID uint `json:"parentId,omitempty"` // 父级菜单的id,使用整数表示
|
||||
Weight int `json:"weight"` // 排序权重
|
||||
Path string `json:"path"` // 地址
|
||||
Title string `json:"title"` // 展示名称
|
||||
Name string `json:"name,omitempty"` // 同路由中的name,唯一标识
|
||||
Component string `json:"component,omitempty"` // 绑定的组件
|
||||
Locale string `json:"locale,omitempty"` // 本地化标识
|
||||
Icon string `json:"icon,omitempty"` // 图标,使用字符串表示
|
||||
Redirect string `json:"redirect,omitempty"` // 重定向地址
|
||||
KeepAlive bool `json:"keepAlive,omitempty"` // 是否保活
|
||||
HideInMenu bool `json:"hideInMenu,omitempty"` // 是否保活
|
||||
URL string `json:"url,omitempty"` // iframe模式下的跳转url,不能与path重复
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
type MenuDeleteRequest struct {
|
||||
ID uint `form:"id"` // 唯一id,使用整数表示
|
||||
}
|
||||
type GetRoleListRequest struct {
|
||||
Page int `form:"page" binding:"required" example:"1"`
|
||||
PageSize int `form:"pageSize" binding:"required" example:"10"`
|
||||
Sid string `form:"sid" binding:"" example:"1"`
|
||||
Name string `form:"name" binding:"" example:"Admin"`
|
||||
}
|
||||
type RoleDataItem struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Sid string `json:"sid"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
}
|
||||
type GetRolesResponseData struct {
|
||||
List []RoleDataItem `json:"list"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
type GetRolesResponse struct {
|
||||
Response
|
||||
Data GetRolesResponseData
|
||||
}
|
||||
type RoleCreateRequest struct {
|
||||
Sid string `form:"sid" binding:"required" example:"1"`
|
||||
Name string `form:"name" binding:"required" example:"Admin"`
|
||||
}
|
||||
type RoleUpdateRequest struct {
|
||||
ID uint `form:"id" binding:"required" example:"1"`
|
||||
Sid string `form:"sid" binding:"required" example:"1"`
|
||||
Name string `form:"name" binding:"required" example:"Admin"`
|
||||
}
|
||||
type RoleDeleteRequest struct {
|
||||
ID uint `form:"id" binding:"required" example:"1"`
|
||||
}
|
||||
type PermissionCreateRequest struct {
|
||||
Sid string `form:"sid" binding:"required" example:"1"`
|
||||
Name string `form:"name" binding:"required" example:"Admin"`
|
||||
}
|
||||
type GetApisRequest struct {
|
||||
Page int `form:"page" binding:"required" example:"1"`
|
||||
PageSize int `form:"pageSize" binding:"required" example:"10"`
|
||||
Group string `form:"group" binding:"" example:"权限管理"`
|
||||
Name string `form:"name" binding:"" example:"菜单列表"`
|
||||
Path string `form:"path" binding:"" example:"/v1/test"`
|
||||
Method string `form:"method" binding:"" example:"GET"`
|
||||
}
|
||||
type ApiDataItem struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
Method string `json:"method"`
|
||||
Group string `json:"group"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
}
|
||||
type GetApisResponseData struct {
|
||||
List []ApiDataItem `json:"list"`
|
||||
Total int64 `json:"total"`
|
||||
Groups []string `json:"groups"`
|
||||
}
|
||||
type GetApisResponse struct {
|
||||
Response
|
||||
Data GetApisResponseData
|
||||
}
|
||||
type ApiCreateRequest struct {
|
||||
Group string `form:"group" binding:"" example:"权限管理"`
|
||||
Name string `form:"name" binding:"" example:"菜单列表"`
|
||||
Path string `form:"path" binding:"" example:"/v1/test"`
|
||||
Method string `form:"method" binding:"" example:"GET"`
|
||||
}
|
||||
type ApiUpdateRequest struct {
|
||||
ID uint `form:"id" binding:"required" example:"1"`
|
||||
Group string `form:"group" binding:"" example:"权限管理"`
|
||||
Name string `form:"name" binding:"" example:"菜单列表"`
|
||||
Path string `form:"path" binding:"" example:"/v1/test"`
|
||||
Method string `form:"method" binding:"" example:"GET"`
|
||||
}
|
||||
type ApiDeleteRequest struct {
|
||||
ID uint `form:"id" binding:"required" example:"1"`
|
||||
}
|
||||
type GetUserPermissionsData struct {
|
||||
List []string `json:"list"`
|
||||
}
|
||||
type GetRolePermissionsRequest struct {
|
||||
Role string `form:"role" binding:"required" example:"admin"`
|
||||
}
|
||||
type GetRolePermissionsData struct {
|
||||
List []string `json:"list"`
|
||||
}
|
||||
type UpdateRolePermissionRequest struct {
|
||||
Role string `form:"role" binding:"required" example:"admin"`
|
||||
List []string `form:"list" binding:"required" example:""`
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package v1
|
||||
|
||||
var (
|
||||
// common errors
|
||||
ErrSuccess = newError(0, "ok")
|
||||
ErrBadRequest = newError(400, "参数错误")
|
||||
ErrUnauthorized = newError(401, "登录失效,请重新登录~")
|
||||
ErrNotFound = newError(404, "数据不存在")
|
||||
ErrForbidden = newError(403, "权限不足,请联系管理员开通权限~")
|
||||
ErrInternalServerError = newError(500, "服务器错误~")
|
||||
|
||||
// more biz errors
|
||||
ErrUsernameAlreadyUse = newError(1001, "The username is already in use.")
|
||||
)
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package v1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
func HandleSuccess(ctx *gin.Context, data interface{}) {
|
||||
if data == nil {
|
||||
data = map[string]interface{}{}
|
||||
}
|
||||
resp := Response{Code: errorCodeMap[ErrSuccess], Message: ErrSuccess.Error(), Data: data}
|
||||
if _, ok := errorCodeMap[ErrSuccess]; !ok {
|
||||
resp = Response{Code: 0, Message: "", Data: data}
|
||||
}
|
||||
ctx.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func HandleError(ctx *gin.Context, httpCode int, err error, data interface{}) {
|
||||
if data == nil {
|
||||
data = map[string]string{}
|
||||
}
|
||||
resp := Response{Code: errorCodeMap[err], Message: err.Error(), Data: data}
|
||||
if _, ok := errorCodeMap[err]; !ok {
|
||||
resp = Response{Code: 500, Message: "unknown error", Data: data}
|
||||
}
|
||||
ctx.JSON(httpCode, resp)
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
Code int
|
||||
Message string
|
||||
}
|
||||
|
||||
var errorCodeMap = map[error]int{}
|
||||
|
||||
func newError(code int, msg string) error {
|
||||
err := errors.New(msg)
|
||||
errorCodeMap[err] = code
|
||||
return err
|
||||
}
|
||||
func (e Error) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"nunu-layout-admin/cmd/migration/wire"
|
||||
"nunu-layout-admin/pkg/config"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var envConf = flag.String("conf", "config/local.yml", "config path, eg: -conf ./config/local.yml")
|
||||
flag.Parse()
|
||||
conf := config.NewConfig(*envConf)
|
||||
|
||||
logger := log.NewLog(conf)
|
||||
|
||||
app, cleanup, err := wire.NewWire(conf, logger)
|
||||
defer cleanup()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = app.Run(context.Background()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
//go:build wireinject
|
||||
// +build wireinject
|
||||
|
||||
package wire
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
"github.com/spf13/viper"
|
||||
"nunu-layout-admin/internal/repository"
|
||||
"nunu-layout-admin/internal/server"
|
||||
"nunu-layout-admin/pkg/app"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
"nunu-layout-admin/pkg/sid"
|
||||
)
|
||||
|
||||
var repositorySet = wire.NewSet(
|
||||
repository.NewDB,
|
||||
//repository.NewRedis,
|
||||
repository.NewRepository,
|
||||
repository.NewCasbinEnforcer,
|
||||
)
|
||||
var serverSet = wire.NewSet(
|
||||
server.NewMigrateServer,
|
||||
)
|
||||
|
||||
// build App
|
||||
func newApp(
|
||||
migrateServer *server.MigrateServer,
|
||||
) *app.App {
|
||||
return app.NewApp(
|
||||
app.WithServer(migrateServer),
|
||||
app.WithName("demo-migrate"),
|
||||
)
|
||||
}
|
||||
|
||||
func NewWire(*viper.Viper, *log.Logger) (*app.App, func(), error) {
|
||||
panic(wire.Build(
|
||||
repositorySet,
|
||||
serverSet,
|
||||
sid.NewSid,
|
||||
newApp,
|
||||
))
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
// Code generated by Wire. DO NOT EDIT.
|
||||
|
||||
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||
//go:build !wireinject
|
||||
// +build !wireinject
|
||||
|
||||
package wire
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
"github.com/spf13/viper"
|
||||
"nunu-layout-admin/internal/repository"
|
||||
"nunu-layout-admin/internal/server"
|
||||
"nunu-layout-admin/pkg/app"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
"nunu-layout-admin/pkg/sid"
|
||||
)
|
||||
|
||||
// Injectors from wire.go:
|
||||
|
||||
func NewWire(viperViper *viper.Viper, logger *log.Logger) (*app.App, func(), error) {
|
||||
db := repository.NewDB(viperViper, logger)
|
||||
sidSid := sid.NewSid()
|
||||
syncedEnforcer := repository.NewCasbinEnforcer(viperViper, logger, db)
|
||||
migrateServer := server.NewMigrateServer(db, logger, sidSid, syncedEnforcer)
|
||||
appApp := newApp(migrateServer)
|
||||
return appApp, func() {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// wire.go:
|
||||
|
||||
var repositorySet = wire.NewSet(repository.NewDB, repository.NewRepository, repository.NewCasbinEnforcer)
|
||||
|
||||
var serverSet = wire.NewSet(server.NewMigrateServer)
|
||||
|
||||
// build App
|
||||
func newApp(
|
||||
migrateServer *server.MigrateServer,
|
||||
) *app.App {
|
||||
return app.NewApp(app.WithServer(migrateServer), app.WithName("demo-migrate"))
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"nunu-layout-admin/cmd/server/wire"
|
||||
"nunu-layout-admin/pkg/config"
|
||||
)
|
||||
|
||||
// @title Nunu Example API
|
||||
// @version 1.0.0
|
||||
// @description This is a sample server celler server.
|
||||
// @termsOfService http://swagger.io/terms/
|
||||
// @contact.name API Support
|
||||
// @contact.url http://www.swagger.io/support
|
||||
// @contact.email support@swagger.io
|
||||
// @license.name Apache 2.0
|
||||
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
// @host localhost:8000
|
||||
// @securityDefinitions.apiKey Bearer
|
||||
// @in header
|
||||
// @name Authorization
|
||||
// @externalDocs.description OpenAPI
|
||||
// @externalDocs.url https://swagger.io/resources/open-api/
|
||||
func main() {
|
||||
var envConf = flag.String("conf", "config/local.yml", "config path, eg: -conf ./config/local.yml")
|
||||
flag.Parse()
|
||||
conf := config.NewConfig(*envConf)
|
||||
|
||||
logger := log.NewLog(conf)
|
||||
|
||||
app, cleanup, err := wire.NewWire(conf, logger)
|
||||
defer cleanup()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
logger.Info("server start", zap.String("host", fmt.Sprintf("http://%s:%d", conf.GetString("http.host"), conf.GetInt("http.port"))))
|
||||
logger.Info("docs addr", zap.String("addr", fmt.Sprintf("http://%s:%d/swagger/index.html", conf.GetString("http.host"), conf.GetInt("http.port"))))
|
||||
if err = app.Run(context.Background()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
//go:build wireinject
|
||||
// +build wireinject
|
||||
|
||||
package wire
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
"github.com/spf13/viper"
|
||||
"nunu-layout-admin/internal/handler"
|
||||
"nunu-layout-admin/internal/job"
|
||||
"nunu-layout-admin/internal/repository"
|
||||
"nunu-layout-admin/internal/server"
|
||||
"nunu-layout-admin/internal/service"
|
||||
"nunu-layout-admin/pkg/app"
|
||||
"nunu-layout-admin/pkg/jwt"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
"nunu-layout-admin/pkg/server/http"
|
||||
"nunu-layout-admin/pkg/sid"
|
||||
)
|
||||
|
||||
var repositorySet = wire.NewSet(
|
||||
repository.NewDB,
|
||||
//repository.NewRedis,
|
||||
repository.NewRepository,
|
||||
repository.NewTransaction,
|
||||
repository.NewUserRepository,
|
||||
repository.NewCasbinEnforcer,
|
||||
repository.NewAdminRepository,
|
||||
)
|
||||
|
||||
var serviceSet = wire.NewSet(
|
||||
service.NewService,
|
||||
service.NewUserService,
|
||||
service.NewAdminService,
|
||||
)
|
||||
|
||||
var handlerSet = wire.NewSet(
|
||||
handler.NewHandler,
|
||||
handler.NewUserHandler,
|
||||
handler.NewAdminHandler,
|
||||
)
|
||||
|
||||
var jobSet = wire.NewSet(
|
||||
job.NewJob,
|
||||
job.NewUserJob,
|
||||
)
|
||||
var serverSet = wire.NewSet(
|
||||
server.NewHTTPServer,
|
||||
server.NewJobServer,
|
||||
)
|
||||
|
||||
// build App
|
||||
func newApp(
|
||||
httpServer *http.Server,
|
||||
jobServer *server.JobServer,
|
||||
// task *server.Task,
|
||||
) *app.App {
|
||||
return app.NewApp(
|
||||
app.WithServer(httpServer, jobServer),
|
||||
app.WithName("demo-server"),
|
||||
)
|
||||
}
|
||||
|
||||
func NewWire(*viper.Viper, *log.Logger) (*app.App, func(), error) {
|
||||
panic(wire.Build(
|
||||
repositorySet,
|
||||
serviceSet,
|
||||
handlerSet,
|
||||
jobSet,
|
||||
serverSet,
|
||||
sid.NewSid,
|
||||
jwt.NewJwt,
|
||||
newApp,
|
||||
))
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
// Code generated by Wire. DO NOT EDIT.
|
||||
|
||||
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||
//go:build !wireinject
|
||||
// +build !wireinject
|
||||
|
||||
package wire
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
"github.com/spf13/viper"
|
||||
"nunu-layout-admin/internal/handler"
|
||||
"nunu-layout-admin/internal/job"
|
||||
"nunu-layout-admin/internal/repository"
|
||||
"nunu-layout-admin/internal/server"
|
||||
"nunu-layout-admin/internal/service"
|
||||
"nunu-layout-admin/pkg/app"
|
||||
"nunu-layout-admin/pkg/jwt"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
"nunu-layout-admin/pkg/server/http"
|
||||
"nunu-layout-admin/pkg/sid"
|
||||
)
|
||||
|
||||
// Injectors from wire.go:
|
||||
|
||||
func NewWire(viperViper *viper.Viper, logger *log.Logger) (*app.App, func(), error) {
|
||||
jwtJWT := jwt.NewJwt(viperViper)
|
||||
db := repository.NewDB(viperViper, logger)
|
||||
syncedEnforcer := repository.NewCasbinEnforcer(viperViper, logger, db)
|
||||
handlerHandler := handler.NewHandler(logger)
|
||||
repositoryRepository := repository.NewRepository(logger, db, syncedEnforcer)
|
||||
transaction := repository.NewTransaction(repositoryRepository)
|
||||
sidSid := sid.NewSid()
|
||||
serviceService := service.NewService(transaction, logger, sidSid, jwtJWT)
|
||||
adminRepository := repository.NewAdminRepository(repositoryRepository)
|
||||
adminService := service.NewAdminService(serviceService, adminRepository)
|
||||
adminHandler := handler.NewAdminHandler(handlerHandler, adminService)
|
||||
userRepository := repository.NewUserRepository(repositoryRepository)
|
||||
userService := service.NewUserService(serviceService, userRepository)
|
||||
userHandler := handler.NewUserHandler(handlerHandler, userService)
|
||||
httpServer := server.NewHTTPServer(logger, viperViper, jwtJWT, syncedEnforcer, adminHandler, userHandler)
|
||||
jobJob := job.NewJob(transaction, logger, sidSid)
|
||||
userJob := job.NewUserJob(jobJob, userRepository)
|
||||
jobServer := server.NewJobServer(logger, userJob)
|
||||
appApp := newApp(httpServer, jobServer)
|
||||
return appApp, func() {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// wire.go:
|
||||
|
||||
var repositorySet = wire.NewSet(repository.NewDB, repository.NewRepository, repository.NewTransaction, repository.NewUserRepository, repository.NewCasbinEnforcer, repository.NewAdminRepository)
|
||||
|
||||
var serviceSet = wire.NewSet(service.NewService, service.NewUserService, service.NewAdminService)
|
||||
|
||||
var handlerSet = wire.NewSet(handler.NewHandler, handler.NewUserHandler, handler.NewAdminHandler)
|
||||
|
||||
var jobSet = wire.NewSet(job.NewJob, job.NewUserJob)
|
||||
|
||||
var serverSet = wire.NewSet(server.NewHTTPServer, server.NewJobServer)
|
||||
|
||||
// build App
|
||||
func newApp(
|
||||
httpServer *http.Server,
|
||||
jobServer *server.JobServer,
|
||||
|
||||
) *app.App {
|
||||
return app.NewApp(app.WithServer(httpServer, jobServer), app.WithName("demo-server"))
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"nunu-layout-admin/cmd/task/wire"
|
||||
"nunu-layout-admin/pkg/config"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var envConf = flag.String("conf", "config/local.yml", "config path, eg: -conf ./config/local.yml")
|
||||
flag.Parse()
|
||||
conf := config.NewConfig(*envConf)
|
||||
|
||||
logger := log.NewLog(conf)
|
||||
logger.Info("start task")
|
||||
app, cleanup, err := wire.NewWire(conf, logger)
|
||||
defer cleanup()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = app.Run(context.Background()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
//go:build wireinject
|
||||
// +build wireinject
|
||||
|
||||
package wire
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
"github.com/spf13/viper"
|
||||
"nunu-layout-admin/internal/repository"
|
||||
"nunu-layout-admin/internal/server"
|
||||
"nunu-layout-admin/internal/task"
|
||||
"nunu-layout-admin/pkg/app"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
"nunu-layout-admin/pkg/sid"
|
||||
)
|
||||
|
||||
var repositorySet = wire.NewSet(
|
||||
repository.NewDB,
|
||||
//repository.NewRedis,
|
||||
repository.NewRepository,
|
||||
repository.NewTransaction,
|
||||
repository.NewUserRepository,
|
||||
repository.NewCasbinEnforcer,
|
||||
)
|
||||
|
||||
var taskSet = wire.NewSet(
|
||||
task.NewTask,
|
||||
task.NewUserTask,
|
||||
)
|
||||
var serverSet = wire.NewSet(
|
||||
server.NewTaskServer,
|
||||
)
|
||||
|
||||
// build App
|
||||
func newApp(
|
||||
task *server.TaskServer,
|
||||
) *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(
|
||||
repositorySet,
|
||||
taskSet,
|
||||
serverSet,
|
||||
newApp,
|
||||
sid.NewSid,
|
||||
))
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
// Code generated by Wire. DO NOT EDIT.
|
||||
|
||||
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||
//go:build !wireinject
|
||||
// +build !wireinject
|
||||
|
||||
package wire
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
"github.com/spf13/viper"
|
||||
"nunu-layout-admin/internal/repository"
|
||||
"nunu-layout-admin/internal/server"
|
||||
"nunu-layout-admin/internal/task"
|
||||
"nunu-layout-admin/pkg/app"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
"nunu-layout-admin/pkg/sid"
|
||||
)
|
||||
|
||||
// Injectors from wire.go:
|
||||
|
||||
func NewWire(viperViper *viper.Viper, logger *log.Logger) (*app.App, func(), error) {
|
||||
db := repository.NewDB(viperViper, logger)
|
||||
syncedEnforcer := repository.NewCasbinEnforcer(viperViper, logger, db)
|
||||
repositoryRepository := repository.NewRepository(logger, db, syncedEnforcer)
|
||||
transaction := repository.NewTransaction(repositoryRepository)
|
||||
sidSid := sid.NewSid()
|
||||
taskTask := task.NewTask(transaction, logger, sidSid)
|
||||
userRepository := repository.NewUserRepository(repositoryRepository)
|
||||
userTask := task.NewUserTask(taskTask, userRepository)
|
||||
taskServer := server.NewTaskServer(logger, userTask)
|
||||
appApp := newApp(taskServer)
|
||||
return appApp, func() {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// wire.go:
|
||||
|
||||
var repositorySet = wire.NewSet(repository.NewDB, repository.NewRepository, repository.NewTransaction, repository.NewUserRepository, repository.NewCasbinEnforcer)
|
||||
|
||||
var taskSet = wire.NewSet(task.NewTask, task.NewUserTask)
|
||||
|
||||
var serverSet = wire.NewSet(server.NewTaskServer)
|
||||
|
||||
// build App
|
||||
func newApp(task2 *server.TaskServer,
|
||||
) *app.App {
|
||||
return app.NewApp(app.WithServer(task2), app.WithName("demo-task"))
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
env: local
|
||||
http:
|
||||
# host: 0.0.0.0
|
||||
host: 127.0.0.1
|
||||
port: 8000
|
||||
security:
|
||||
api_sign:
|
||||
app_key: 123456
|
||||
app_security: 123456
|
||||
jwt:
|
||||
key: QQYnRFerJTSEcrfB89fw8prOaObmrch8
|
||||
data:
|
||||
db:
|
||||
user:
|
||||
driver: sqlite
|
||||
dsn: storage/nunu-test.db?_busy_timeout=5000
|
||||
# user:
|
||||
# driver: mysql
|
||||
# dsn: root:123456@tcp(127.0.0.1:3380)/user?charset=utf8mb4&parseTime=True&loc=Local
|
||||
# user:
|
||||
# driver: postgres
|
||||
# dsn: host=localhost user=gorm password=gorm dbname=gorm port=9920 sslmode=disable TimeZone=Asia/Shanghai
|
||||
redis:
|
||||
addr: 127.0.0.1:6350
|
||||
password: ""
|
||||
db: 0
|
||||
read_timeout: 0.2s
|
||||
write_timeout: 0.2s
|
||||
|
||||
log:
|
||||
log_level: debug
|
||||
encoding: console # json or console
|
||||
log_file_name: "./storage/logs/server.log"
|
||||
max_backups: 30
|
||||
max_age: 7
|
||||
max_size: 1024
|
||||
compress: true
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
env: prod
|
||||
http:
|
||||
host: 0.0.0.0
|
||||
# host: 127.0.0.1
|
||||
port: 8000
|
||||
security:
|
||||
api_sign:
|
||||
app_key: 123456
|
||||
app_security: 123456
|
||||
jwt:
|
||||
key: QQYnRFerJTSEcrfB89fw8prOaObmrch8
|
||||
data:
|
||||
db:
|
||||
user:
|
||||
driver: sqlite
|
||||
dsn: storage/nunu-test.db?_busy_timeout=5000
|
||||
# user:
|
||||
# driver: mysql
|
||||
# dsn: root:123456@tcp(127.0.0.1:3380)/user?charset=utf8mb4&parseTime=True&loc=Local
|
||||
# user:
|
||||
# driver: postgres
|
||||
# dsn: host=localhost user=gorm password=gorm dbname=gorm port=9920 sslmode=disable TimeZone=Asia/Shanghai
|
||||
redis:
|
||||
addr: 127.0.0.1:6350
|
||||
password: ""
|
||||
db: 0
|
||||
read_timeout: 0.2s
|
||||
write_timeout: 0.2s
|
||||
|
||||
log:
|
||||
log_level: info
|
||||
encoding: json # json or console
|
||||
log_file_name: "./storage/logs/server.log"
|
||||
max_backups: 30
|
||||
max_age: 7
|
||||
max_size: 1024
|
||||
compress: true
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
ARG REGISTRY=docker.io
|
||||
FROM ${REGISTRY}/golang:1.19-alpine AS builder
|
||||
RUN set -eux && sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
|
||||
|
||||
ARG APP_RELATIVE_PATH
|
||||
|
||||
COPY .. /data/app
|
||||
WORKDIR /data/app
|
||||
|
||||
RUN rm -rf /data/app/bin/
|
||||
RUN export GOPROXY=https://goproxy.cn,direct && go mod tidy && go build -ldflags="-s -w" -o ./bin/server ${APP_RELATIVE_PATH}
|
||||
RUN mv config /data/app/bin/
|
||||
|
||||
|
||||
FROM ${REGISTRY}/alpine:3.16
|
||||
RUN set -eux && sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
|
||||
|
||||
|
||||
RUN apk add tzdata && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
|
||||
&& echo "Asia/Shanghai" > /etc/timezone \
|
||||
&& apk del tzdata
|
||||
|
||||
|
||||
ARG APP_ENV
|
||||
ENV APP_ENV=${APP_ENV}
|
||||
|
||||
WORKDIR /data/app
|
||||
COPY --from=builder /data/app/bin /data/app
|
||||
|
||||
EXPOSE 8000
|
||||
ENTRYPOINT [ "./server" ]
|
||||
|
||||
#docker build -t 1.1.1.1:5000/demo-api:v1 --build-arg APP_CONF=config/prod.yml --build-arg APP_RELATIVE_PATH=./cmd/server/... .
|
||||
#docker run -it --rm --entrypoint=ash 1.1.1.1:5000/demo-api:v1
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
version: '3'
|
||||
|
||||
services:
|
||||
user-db:
|
||||
image: mysql:8.0.31-debian
|
||||
hostname: user-db
|
||||
container_name: user-db
|
||||
ports:
|
||||
- 3380:3306
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=123456
|
||||
- MYSQL_ROOT_HOST=%
|
||||
- MYSQL_DATABASE=user
|
||||
# volumes:
|
||||
# - ./data/mysql/user:/var/lib/mysql
|
||||
# - ./conf/mysql/conf.d:/etc/mysql/conf.d
|
||||
cache-redis:
|
||||
image: redis:6-alpine
|
||||
hostname: cache-redis
|
||||
# volumes:
|
||||
# - ./data/redis/cache/:/data
|
||||
# - ./conf/redis/cache/redis.conf:/etc/redis/redis.conf
|
||||
ports:
|
||||
- 6350:6379
|
||||
command: ["redis-server","/etc/redis/redis.conf"]
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,105 @@
|
|||
module nunu-layout-admin
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.24.0
|
||||
|
||||
require (
|
||||
github.com/casbin/casbin/v2 v2.104.0
|
||||
github.com/casbin/gorm-adapter/v3 v3.32.0
|
||||
github.com/duke-git/lancet/v2 v2.3.0
|
||||
github.com/gin-contrib/static v1.1.3
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/go-co-op/gocron v1.28.2
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0
|
||||
github.com/google/wire v0.5.0
|
||||
github.com/redis/go-redis/v9 v9.0.5
|
||||
github.com/sony/sonyflake v1.1.0
|
||||
github.com/spf13/viper v1.16.0
|
||||
github.com/swaggo/files v1.0.1
|
||||
github.com/swaggo/gin-swagger v1.6.0
|
||||
github.com/swaggo/swag v1.16.4
|
||||
go.uber.org/zap v1.26.0
|
||||
golang.org/x/crypto v0.36.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gorm.io/driver/mysql v1.5.7
|
||||
gorm.io/driver/postgres v1.5.9
|
||||
gorm.io/gorm v1.25.12
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect
|
||||
github.com/bytedance/sonic v1.13.2 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
||||
github.com/casbin/govaluate v1.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/gin-contrib/sse v1.0.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.1 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/spec v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.1 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.25.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgx/v5 v5.5.5 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/microsoft/go-mssqldb v1.6.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/spf13/afero v1.9.5 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.4.2 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.15.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a // indirect
|
||||
golang.org/x/net v0.37.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/tools v0.31.0 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gorm.io/driver/sqlserver v1.5.3 // indirect
|
||||
gorm.io/plugin/dbresolver v1.5.3 // indirect
|
||||
modernc.org/libc v1.22.5 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/sqlite v1.23.1 // indirect
|
||||
)
|
||||
|
|
@ -0,0 +1,829 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 h1:/iHxaJhsFr0+xVFfbMr5vxz848jyiWuIEDhYq3y5odY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0 h1:yfJe15aSwEQ6Oo6J+gdfdulPNoZ3TEhmbhLIoxZcA+U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0/go.mod h1:Q28U+75mpCaSCDowNEmhIo/rmgdkqmkmzI7N6TGR4UY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0 h1:T028gtTPiYt/RMUfs8nVsAL7FDQrfLlrm/NnRG/zcC4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0/go.mod h1:cw4zVQgBby0Z5f2v0itn6se2dDP17nTjbZFXW5uPyHA=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0 h1:HCc0+LpPfpCKs6LGGLAhwBARt9632unrVcI6i8s/8os=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao=
|
||||
github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
|
||||
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
|
||||
github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/bytedance/sonic v1.12.6 h1:/isNmCUF2x3Sh8RAp/4mh4ZGkcFAX/hLrzrK3AvpRzk=
|
||||
github.com/bytedance/sonic v1.12.6/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
|
||||
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E=
|
||||
github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
||||
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/casbin/casbin/v2 v2.104.0 h1:qDakyBZ4jUg1VskF1+UzIwkg+uXWcp0u0M9PMm1RsTA=
|
||||
github.com/casbin/casbin/v2 v2.104.0/go.mod h1:Ee33aqGrmES+GNL17L0h9X28wXuo829wnNUnS0edAco=
|
||||
github.com/casbin/gorm-adapter/v3 v3.32.0 h1:Au+IOILBIE9clox5BJhI2nA3p9t7Ep1ePlupdGbGfus=
|
||||
github.com/casbin/gorm-adapter/v3 v3.32.0/go.mod h1:Zre/H8p17mpv5U3EaWgPoxLILLdXO3gHW5aoQQpUDZI=
|
||||
github.com/casbin/govaluate v1.3.0 h1:VA0eSY0M2lA86dYd5kPPuNZMUD9QkWnOCnavGrw9myc=
|
||||
github.com/casbin/govaluate v1.3.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/duke-git/lancet/v2 v2.3.0 h1:Ztie0qOnC4QgGYYqmpmQxbxkPcm54kqFXj1bwhiV8zg=
|
||||
github.com/duke-git/lancet/v2 v2.3.0/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA=
|
||||
github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
|
||||
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
|
||||
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
|
||||
github.com/gin-contrib/static v1.1.3 h1:WLOpkBtMDJ3gATFZgNJyVibFMio/UHonnueqJsQ0w4U=
|
||||
github.com/gin-contrib/static v1.1.3/go.mod h1:zejpJ/YWp8cZj/6EpiL5f/+skv5daQTNwRx1E8Pci30=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
||||
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||
github.com/go-co-op/gocron v1.28.2 h1:H9oHUGH+9HZ5mAorbnzRjzXLf4poP+ctZdbtaKRYagc=
|
||||
github.com/go-co-op/gocron v1.28.2/go.mod h1:39f6KNSGVOU1LO/ZOoZfcSxwlsJDQOKSu8erN0SH48Y=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ=
|
||||
github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA=
|
||||
github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
|
||||
github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
|
||||
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8=
|
||||
github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
|
||||
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
|
||||
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
|
||||
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
|
||||
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
|
||||
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8=
|
||||
github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
|
||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
|
||||
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
|
||||
github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
|
||||
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
|
||||
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
|
||||
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
||||
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.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=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/microsoft/go-mssqldb v1.6.0 h1:mM3gYdVwEPFrlg/Dvr2DNVEgYFG7L42l+dGc67NNNpc=
|
||||
github.com/microsoft/go-mssqldb v1.6.0/go.mod h1:00mDtPbeQCRGC1HwOOR5K/gr30P1NcEG0vx6Kbv2aJU=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
||||
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o=
|
||||
github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/sony/sonyflake v1.1.0 h1:wnrEcL3aOkWmPlhScLEGAXKkLAIslnBteNUq4Bw6MM4=
|
||||
github.com/sony/sonyflake v1.1.0/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y=
|
||||
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
||||
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
|
||||
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc=
|
||||
github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
|
||||
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
|
||||
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
|
||||
github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
|
||||
github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
|
||||
github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04=
|
||||
github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E=
|
||||
github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
|
||||
github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
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.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
||||
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
|
||||
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.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
|
||||
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
|
||||
golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg=
|
||||
golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw=
|
||||
golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw=
|
||||
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
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.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
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=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
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.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
|
||||
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
||||
gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8=
|
||||
gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
|
||||
gorm.io/driver/sqlserver v1.5.3 h1:rjupPS4PVw+rjJkfvr8jn2lJ8BMhT4UW5FwuJY0P3Z0=
|
||||
gorm.io/driver/sqlserver v1.5.3/go.mod h1:B+CZ0/7oFJ6tAlefsKoyxdgDCXJKSgwS2bMOQZT0I00=
|
||||
gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
gorm.io/plugin/dbresolver v1.5.3 h1:wFwINGZZmttuu9h7XpvbDHd8Lf9bb8GNzp/NpAMV2wU=
|
||||
gorm.io/plugin/dbresolver v1.5.3/go.mod h1:TSrVhaUg2DZAWP3PrHlDlITEJmNOkL0tFTjvTEsQ4XE=
|
||||
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=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
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=
|
||||
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
|
||||
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
||||
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
|
@ -0,0 +1,561 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
v1 "nunu-layout-admin/api/v1"
|
||||
"nunu-layout-admin/internal/service"
|
||||
)
|
||||
|
||||
type AdminHandler struct {
|
||||
*Handler
|
||||
adminService service.AdminService
|
||||
}
|
||||
|
||||
func NewAdminHandler(
|
||||
handler *Handler,
|
||||
adminService service.AdminService,
|
||||
) *AdminHandler {
|
||||
return &AdminHandler{
|
||||
Handler: handler,
|
||||
adminService: adminService,
|
||||
}
|
||||
}
|
||||
|
||||
// Login godoc
|
||||
// @Summary 账号登录
|
||||
// @Schemes
|
||||
// @Description
|
||||
// @Tags 用户模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body v1.LoginRequest true "params"
|
||||
// @Success 200 {object} v1.LoginResponse
|
||||
// @Router /v1/login [post]
|
||||
func (h *AdminHandler) Login(ctx *gin.Context) {
|
||||
var req v1.LoginRequest
|
||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
|
||||
token, err := h.adminService.Login(ctx, &req)
|
||||
if err != nil {
|
||||
v1.HandleError(ctx, http.StatusUnauthorized, v1.ErrUnauthorized, nil)
|
||||
return
|
||||
}
|
||||
v1.HandleSuccess(ctx, v1.LoginResponseData{
|
||||
AccessToken: token,
|
||||
})
|
||||
}
|
||||
|
||||
// GetMenus godoc
|
||||
// @Summary 获取用户菜单
|
||||
// @Schemes
|
||||
// @Description 获取当前用户的菜单列表
|
||||
// @Tags 菜单模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Success 200 {object} v1.GetMenuResponse
|
||||
// @Router /v1/menus [get]
|
||||
func (h *AdminHandler) GetMenus(ctx *gin.Context) {
|
||||
data, err := h.adminService.GetMenus(ctx, GetUserIdFromCtx(ctx))
|
||||
if err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
// 过滤权限菜单
|
||||
v1.HandleSuccess(ctx, data)
|
||||
}
|
||||
|
||||
// GetAdminMenus godoc
|
||||
// @Summary 获取管理员菜单
|
||||
// @Schemes
|
||||
// @Description 获取管理员菜单列表
|
||||
// @Tags 菜单模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Success 200 {object} v1.GetMenuResponse
|
||||
// @Router /v1/admin/menus [get]
|
||||
func (h *AdminHandler) GetAdminMenus(ctx *gin.Context) {
|
||||
data, err := h.adminService.GetAdminMenus(ctx)
|
||||
if err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
// 过滤权限菜单
|
||||
v1.HandleSuccess(ctx, data)
|
||||
}
|
||||
|
||||
// GetUserPermissions godoc
|
||||
// @Summary 获取用户权限
|
||||
// @Schemes
|
||||
// @Description 获取当前用户的权限列表
|
||||
// @Tags 权限模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Success 200 {object} v1.GetUserPermissionsData
|
||||
// @Router /v1/admin/user/permissions [get]
|
||||
func (h *AdminHandler) GetUserPermissions(ctx *gin.Context) {
|
||||
data, err := h.adminService.GetUserPermissions(ctx, GetUserIdFromCtx(ctx))
|
||||
if err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
// 过滤权限菜单
|
||||
v1.HandleSuccess(ctx, data)
|
||||
}
|
||||
|
||||
// GetRolePermissions godoc
|
||||
// @Summary 获取角色权限
|
||||
// @Schemes
|
||||
// @Description 获取指定角色的权限列表
|
||||
// @Tags 权限模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param role query string true "角色名称"
|
||||
// @Success 200 {object} v1.GetRolePermissionsData
|
||||
// @Router /v1/admin/role/permissions [get]
|
||||
func (h *AdminHandler) GetRolePermissions(ctx *gin.Context) {
|
||||
var req v1.GetRolePermissionsRequest
|
||||
if err := ctx.ShouldBind(&req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
data, err := h.adminService.GetRolePermissions(ctx, req.Role)
|
||||
if err != nil {
|
||||
v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
v1.HandleSuccess(ctx, data)
|
||||
}
|
||||
|
||||
// UpdateRolePermission godoc
|
||||
// @Summary 更新角色权限
|
||||
// @Schemes
|
||||
// @Description 更新指定角色的权限列表
|
||||
// @Tags 权限模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param request body v1.UpdateRolePermissionRequest true "参数"
|
||||
// @Success 200 {object} v1.Response
|
||||
// @Router /v1/admin/role/permissions [put]
|
||||
func (h *AdminHandler) UpdateRolePermission(ctx *gin.Context) {
|
||||
var req v1.UpdateRolePermissionRequest
|
||||
if err := ctx.ShouldBind(&req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
err := h.adminService.UpdateRolePermission(ctx, &req)
|
||||
if err != nil {
|
||||
v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
v1.HandleSuccess(ctx, nil)
|
||||
}
|
||||
|
||||
// MenuUpdate godoc
|
||||
// @Summary 更新菜单
|
||||
// @Schemes
|
||||
// @Description 更新菜单信息
|
||||
// @Tags 菜单模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param request body v1.MenuUpdateRequest true "参数"
|
||||
// @Success 200 {object} v1.Response
|
||||
// @Router /v1/admin/menu [put]
|
||||
func (h *AdminHandler) MenuUpdate(ctx *gin.Context) {
|
||||
var req v1.MenuUpdateRequest
|
||||
if err := ctx.ShouldBind(&req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
if err := h.adminService.MenuUpdate(ctx, &req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
v1.HandleSuccess(ctx, nil)
|
||||
}
|
||||
|
||||
// MenuCreate godoc
|
||||
// @Summary 创建菜单
|
||||
// @Schemes
|
||||
// @Description 创建新的菜单
|
||||
// @Tags 菜单模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param request body v1.MenuCreateRequest true "参数"
|
||||
// @Success 200 {object} v1.Response
|
||||
// @Router /v1/admin/menu [post]
|
||||
func (h *AdminHandler) MenuCreate(ctx *gin.Context) {
|
||||
var req v1.MenuCreateRequest
|
||||
if err := ctx.ShouldBind(&req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
if err := h.adminService.MenuCreate(ctx, &req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
v1.HandleSuccess(ctx, nil)
|
||||
}
|
||||
|
||||
// MenuDelete godoc
|
||||
// @Summary 删除菜单
|
||||
// @Schemes
|
||||
// @Description 删除指定菜单
|
||||
// @Tags 菜单模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param id query uint true "菜单ID"
|
||||
// @Success 200 {object} v1.Response
|
||||
// @Router /v1/admin/menu [delete]
|
||||
func (h *AdminHandler) MenuDelete(ctx *gin.Context) {
|
||||
var req v1.MenuDeleteRequest
|
||||
if err := ctx.ShouldBind(&req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
if err := h.adminService.MenuDelete(ctx, req.ID); err != nil {
|
||||
v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
|
||||
return
|
||||
|
||||
}
|
||||
v1.HandleSuccess(ctx, nil)
|
||||
}
|
||||
|
||||
// GetRoles godoc
|
||||
// @Summary 获取角色列表
|
||||
// @Schemes
|
||||
// @Description 获取角色列表
|
||||
// @Tags 角色模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param page query int true "页码"
|
||||
// @Param pageSize query int true "每页数量"
|
||||
// @Param sid query string false "角色ID"
|
||||
// @Param name query string false "角色名称"
|
||||
// @Success 200 {object} v1.GetRolesResponse
|
||||
// @Router /v1/admin/roles [get]
|
||||
func (h *AdminHandler) GetRoles(ctx *gin.Context) {
|
||||
var req v1.GetRoleListRequest
|
||||
if err := ctx.ShouldBind(&req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
data, err := h.adminService.GetRoles(ctx, &req)
|
||||
if err != nil {
|
||||
v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
|
||||
v1.HandleSuccess(ctx, data)
|
||||
}
|
||||
|
||||
// RoleCreate godoc
|
||||
// @Summary 创建角色
|
||||
// @Schemes
|
||||
// @Description 创建新的角色
|
||||
// @Tags 角色模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param request body v1.RoleCreateRequest true "参数"
|
||||
// @Success 200 {object} v1.Response
|
||||
// @Router /v1/admin/role [post]
|
||||
func (h *AdminHandler) RoleCreate(ctx *gin.Context) {
|
||||
var req v1.RoleCreateRequest
|
||||
if err := ctx.ShouldBind(&req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
if err := h.adminService.RoleCreate(ctx, &req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
v1.HandleSuccess(ctx, nil)
|
||||
}
|
||||
|
||||
// RoleUpdate godoc
|
||||
// @Summary 更新角色
|
||||
// @Schemes
|
||||
// @Description 更新角色信息
|
||||
// @Tags 角色模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param request body v1.RoleUpdateRequest true "参数"
|
||||
// @Success 200 {object} v1.Response
|
||||
// @Router /v1/admin/role [put]
|
||||
func (h *AdminHandler) RoleUpdate(ctx *gin.Context) {
|
||||
var req v1.RoleUpdateRequest
|
||||
if err := ctx.ShouldBind(&req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
if err := h.adminService.RoleUpdate(ctx, &req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
v1.HandleSuccess(ctx, nil)
|
||||
}
|
||||
|
||||
// RoleDelete godoc
|
||||
// @Summary 删除角色
|
||||
// @Schemes
|
||||
// @Description 删除指定角色
|
||||
// @Tags 角色模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param id query uint true "角色ID"
|
||||
// @Success 200 {object} v1.Response
|
||||
// @Router /v1/admin/role [delete]
|
||||
func (h *AdminHandler) RoleDelete(ctx *gin.Context) {
|
||||
var req v1.RoleDeleteRequest
|
||||
if err := ctx.ShouldBind(&req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
if err := h.adminService.RoleDelete(ctx, req.ID); err != nil {
|
||||
v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
v1.HandleSuccess(ctx, nil)
|
||||
}
|
||||
|
||||
// GetApis godoc
|
||||
// @Summary 获取API列表
|
||||
// @Schemes
|
||||
// @Description 获取API列表
|
||||
// @Tags API模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param page query int true "页码"
|
||||
// @Param pageSize query int true "每页数量"
|
||||
// @Param group query string false "API分组"
|
||||
// @Param name query string false "API名称"
|
||||
// @Param path query string false "API路径"
|
||||
// @Param method query string false "请求方法"
|
||||
// @Success 200 {object} v1.GetApisResponse
|
||||
// @Router /v1/admin/apis [get]
|
||||
func (h *AdminHandler) GetApis(ctx *gin.Context) {
|
||||
var req v1.GetApisRequest
|
||||
if err := ctx.ShouldBind(&req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
data, err := h.adminService.GetApis(ctx, &req)
|
||||
if err != nil {
|
||||
v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
|
||||
v1.HandleSuccess(ctx, data)
|
||||
}
|
||||
|
||||
// ApiCreate godoc
|
||||
// @Summary 创建API
|
||||
// @Schemes
|
||||
// @Description 创建新的API
|
||||
// @Tags API模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param request body v1.ApiCreateRequest true "参数"
|
||||
// @Success 200 {object} v1.Response
|
||||
// @Router /v1/admin/api [post]
|
||||
func (h *AdminHandler) ApiCreate(ctx *gin.Context) {
|
||||
var req v1.ApiCreateRequest
|
||||
if err := ctx.ShouldBind(&req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
if err := h.adminService.ApiCreate(ctx, &req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
v1.HandleSuccess(ctx, nil)
|
||||
}
|
||||
|
||||
// ApiUpdate godoc
|
||||
// @Summary 更新API
|
||||
// @Schemes
|
||||
// @Description 更新API信息
|
||||
// @Tags API模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param request body v1.ApiUpdateRequest true "参数"
|
||||
// @Success 200 {object} v1.Response
|
||||
// @Router /v1/admin/api [put]
|
||||
func (h *AdminHandler) ApiUpdate(ctx *gin.Context) {
|
||||
var req v1.ApiUpdateRequest
|
||||
if err := ctx.ShouldBind(&req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
if err := h.adminService.ApiUpdate(ctx, &req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
v1.HandleSuccess(ctx, nil)
|
||||
}
|
||||
|
||||
// ApiDelete godoc
|
||||
// @Summary 删除API
|
||||
// @Schemes
|
||||
// @Description 删除指定API
|
||||
// @Tags API模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param id query uint true "API ID"
|
||||
// @Success 200 {object} v1.Response
|
||||
// @Router /v1/admin/api [delete]
|
||||
func (h *AdminHandler) ApiDelete(ctx *gin.Context) {
|
||||
var req v1.ApiDeleteRequest
|
||||
if err := ctx.ShouldBind(&req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
if err := h.adminService.ApiDelete(ctx, req.ID); err != nil {
|
||||
v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
v1.HandleSuccess(ctx, nil)
|
||||
}
|
||||
|
||||
// AdminUserUpdate godoc
|
||||
// @Summary 更新管理员用户
|
||||
// @Schemes
|
||||
// @Description 更新管理员用户信息
|
||||
// @Tags 用户模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param request body v1.AdminUserUpdateRequest true "参数"
|
||||
// @Success 200 {object} v1.Response
|
||||
// @Router /v1/admin/user [put]
|
||||
func (h *AdminHandler) AdminUserUpdate(ctx *gin.Context) {
|
||||
var req v1.AdminUserUpdateRequest
|
||||
if err := ctx.ShouldBind(&req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
if err := h.adminService.AdminUserUpdate(ctx, &req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
v1.HandleSuccess(ctx, nil)
|
||||
}
|
||||
|
||||
// AdminUserCreate godoc
|
||||
// @Summary 创建管理员用户
|
||||
// @Schemes
|
||||
// @Description 创建新的管理员用户
|
||||
// @Tags 用户模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param request body v1.AdminUserCreateRequest true "参数"
|
||||
// @Success 200 {object} v1.Response
|
||||
// @Router /v1/admin/user [post]
|
||||
func (h *AdminHandler) AdminUserCreate(ctx *gin.Context) {
|
||||
var req v1.AdminUserCreateRequest
|
||||
if err := ctx.ShouldBind(&req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
if err := h.adminService.AdminUserCreate(ctx, &req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
v1.HandleSuccess(ctx, nil)
|
||||
}
|
||||
|
||||
// AdminUserDelete godoc
|
||||
// @Summary 删除管理员用户
|
||||
// @Schemes
|
||||
// @Description 删除指定管理员用户
|
||||
// @Tags 用户模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param id query uint true "用户ID"
|
||||
// @Success 200 {object} v1.Response
|
||||
// @Router /v1/admin/user [delete]
|
||||
func (h *AdminHandler) AdminUserDelete(ctx *gin.Context) {
|
||||
var req v1.AdminUserDeleteRequest
|
||||
if err := ctx.ShouldBind(&req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
if err := h.adminService.AdminUserDelete(ctx, req.ID); err != nil {
|
||||
v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
|
||||
return
|
||||
|
||||
}
|
||||
v1.HandleSuccess(ctx, nil)
|
||||
}
|
||||
|
||||
// GetAdminUsers godoc
|
||||
// @Summary 获取管理员用户列表
|
||||
// @Schemes
|
||||
// @Description 获取管理员用户列表
|
||||
// @Tags 用户模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param page query int true "页码"
|
||||
// @Param pageSize query int true "每页数量"
|
||||
// @Param username query string false "用户名"
|
||||
// @Param nickname query string false "昵称"
|
||||
// @Param phone query string false "手机号"
|
||||
// @Param email query string false "邮箱"
|
||||
// @Success 200 {object} v1.GetAdminUsersResponse
|
||||
// @Router /v1/admin/users [get]
|
||||
func (h *AdminHandler) GetAdminUsers(ctx *gin.Context) {
|
||||
var req v1.GetAdminUsersRequest
|
||||
if err := ctx.ShouldBind(&req); err != nil {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
return
|
||||
}
|
||||
data, err := h.adminService.GetAdminUsers(ctx, &req)
|
||||
if err != nil {
|
||||
v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
|
||||
v1.HandleSuccess(ctx, data)
|
||||
}
|
||||
|
||||
// GetAdminUser godoc
|
||||
// @Summary 获取管理用户信息
|
||||
// @Schemes
|
||||
// @Description
|
||||
// @Tags 用户模块
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Success 200 {object} v1.GetAdminUserResponse
|
||||
// @Router /v1/admin/user [get]
|
||||
func (h *AdminHandler) GetAdminUser(ctx *gin.Context) {
|
||||
data, err := h.adminService.GetAdminUser(ctx, GetUserIdFromCtx(ctx))
|
||||
if err != nil {
|
||||
v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
|
||||
v1.HandleSuccess(ctx, data)
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"nunu-layout-admin/pkg/jwt"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func NewHandler(
|
||||
logger *log.Logger,
|
||||
) *Handler {
|
||||
return &Handler{
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
func GetUserIdFromCtx(ctx *gin.Context) uint {
|
||||
v, exists := ctx.Get("claims")
|
||||
if !exists {
|
||||
return 0
|
||||
}
|
||||
return v.(*jwt.MyCustomClaims).UserId
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"nunu-layout-admin/internal/service"
|
||||
)
|
||||
|
||||
type UserHandler struct {
|
||||
*Handler
|
||||
userService service.UserService
|
||||
}
|
||||
|
||||
func NewUserHandler(
|
||||
handler *Handler,
|
||||
userService service.UserService,
|
||||
) *UserHandler {
|
||||
return &UserHandler{
|
||||
Handler: handler,
|
||||
userService: userService,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *UserHandler) GetUsers(ctx *gin.Context) {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package job
|
||||
|
||||
import (
|
||||
"nunu-layout-admin/internal/repository"
|
||||
"nunu-layout-admin/pkg/jwt"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
"nunu-layout-admin/pkg/sid"
|
||||
)
|
||||
|
||||
type Job struct {
|
||||
logger *log.Logger
|
||||
sid *sid.Sid
|
||||
jwt *jwt.JWT
|
||||
tm repository.Transaction
|
||||
}
|
||||
|
||||
func NewJob(
|
||||
tm repository.Transaction,
|
||||
logger *log.Logger,
|
||||
sid *sid.Sid,
|
||||
) *Job {
|
||||
return &Job{
|
||||
logger: logger,
|
||||
sid: sid,
|
||||
tm: tm,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package job
|
||||
|
||||
import (
|
||||
"context"
|
||||
"nunu-layout-admin/internal/repository"
|
||||
)
|
||||
|
||||
type UserJob interface {
|
||||
KafkaConsumer(ctx context.Context) error
|
||||
}
|
||||
|
||||
func NewUserJob(
|
||||
job *Job,
|
||||
userRepo repository.UserRepository,
|
||||
) UserJob {
|
||||
return &userJob{
|
||||
userRepo: userRepo,
|
||||
Job: job,
|
||||
}
|
||||
}
|
||||
|
||||
type userJob struct {
|
||||
userRepo repository.UserRepository
|
||||
*Job
|
||||
}
|
||||
|
||||
func (t userJob) KafkaConsumer(ctx context.Context) error {
|
||||
// do something
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func CORSMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
method := c.Request.Method
|
||||
c.Header("Access-Control-Allow-Origin", c.GetHeader("Origin"))
|
||||
c.Header("Access-Control-Allow-Credentials", "true")
|
||||
|
||||
if method == "OPTIONS" {
|
||||
c.Header("Access-Control-Allow-Methods", c.GetHeader("Access-Control-Request-Method"))
|
||||
c.Header("Access-Control-Allow-Headers", c.GetHeader("Access-Control-Request-Headers"))
|
||||
c.Header("Access-Control-Max-Age", "7200")
|
||||
c.AbortWithStatus(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
"net/http"
|
||||
"nunu-layout-admin/api/v1"
|
||||
"nunu-layout-admin/pkg/jwt"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
)
|
||||
|
||||
func StrictAuth(j *jwt.JWT, logger *log.Logger) gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
tokenString := ctx.Request.Header.Get("Authorization")
|
||||
if tokenString == "" {
|
||||
logger.WithContext(ctx).Warn("No token", zap.Any("data", map[string]interface{}{
|
||||
"url": ctx.Request.URL,
|
||||
"params": ctx.Params,
|
||||
}))
|
||||
v1.HandleError(ctx, http.StatusUnauthorized, v1.ErrUnauthorized, nil)
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
claims, err := j.ParseToken(tokenString)
|
||||
if err != nil {
|
||||
logger.WithContext(ctx).Error("token error", zap.Any("data", map[string]interface{}{
|
||||
"url": ctx.Request.URL,
|
||||
"params": ctx.Params,
|
||||
}), zap.Error(err))
|
||||
v1.HandleError(ctx, http.StatusUnauthorized, v1.ErrUnauthorized, nil)
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Set("claims", claims)
|
||||
recoveryLoggerFunc(ctx, logger)
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func NoStrictAuth(j *jwt.JWT, logger *log.Logger) gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
tokenString := ctx.Request.Header.Get("Authorization")
|
||||
if tokenString == "" {
|
||||
tokenString, _ = ctx.Cookie("accessToken")
|
||||
}
|
||||
if tokenString == "" {
|
||||
tokenString = ctx.Query("accessToken")
|
||||
}
|
||||
if tokenString == "" {
|
||||
ctx.Next()
|
||||
return
|
||||
}
|
||||
|
||||
claims, err := j.ParseToken(tokenString)
|
||||
if err != nil {
|
||||
ctx.Next()
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Set("claims", claims)
|
||||
recoveryLoggerFunc(ctx, logger)
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func recoveryLoggerFunc(ctx *gin.Context, logger *log.Logger) {
|
||||
if userInfo, ok := ctx.MustGet("claims").(*jwt.MyCustomClaims); ok {
|
||||
logger.WithValue(ctx, zap.Any("UserId", userInfo.UserId))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/duke-git/lancet/v2/cryptor"
|
||||
"github.com/duke-git/lancet/v2/random"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
"io"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
"time"
|
||||
)
|
||||
|
||||
func RequestLogMiddleware(logger *log.Logger) gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
// The configuration is initialized once per request
|
||||
uuid, err := random.UUIdV4()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
trace := cryptor.Md5String(uuid)
|
||||
logger.WithValue(ctx, zap.String("trace", trace))
|
||||
logger.WithValue(ctx, zap.String("request_method", ctx.Request.Method))
|
||||
logger.WithValue(ctx, zap.Any("request_headers", ctx.Request.Header))
|
||||
logger.WithValue(ctx, zap.String("request_url", ctx.Request.URL.String()))
|
||||
if ctx.Request.Body != nil {
|
||||
bodyBytes, _ := ctx.GetRawData()
|
||||
ctx.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) // 关键点
|
||||
logger.WithValue(ctx, zap.String("request_params", string(bodyBytes)))
|
||||
}
|
||||
logger.WithContext(ctx).Info("Request")
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
func ResponseLogMiddleware(logger *log.Logger) gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
blw := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: ctx.Writer}
|
||||
ctx.Writer = blw
|
||||
startTime := time.Now()
|
||||
ctx.Next()
|
||||
duration := time.Since(startTime).String()
|
||||
logger.WithContext(ctx).Info("Response", zap.Any("response_body", blw.body.String()), zap.Any("time", duration))
|
||||
}
|
||||
}
|
||||
|
||||
type bodyLogWriter struct {
|
||||
gin.ResponseWriter
|
||||
body *bytes.Buffer
|
||||
}
|
||||
|
||||
func (w bodyLogWriter) Write(b []byte) (int, error) {
|
||||
w.body.Write(b)
|
||||
return w.ResponseWriter.Write(b)
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
v1 "nunu-layout-admin/api/v1"
|
||||
"nunu-layout-admin/internal/model"
|
||||
"nunu-layout-admin/pkg/jwt"
|
||||
)
|
||||
|
||||
func AuthMiddleware(e *casbin.SyncedEnforcer) gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
// 从上下文获取用户信息(假设通过 JWT 或其他方式设置)
|
||||
v, exists := ctx.Get("claims")
|
||||
if !exists {
|
||||
v1.HandleError(ctx, http.StatusUnauthorized, v1.ErrUnauthorized, nil)
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
uid := v.(*jwt.MyCustomClaims).UserId
|
||||
if convertor.ToString(uid) == model.AdminUserID {
|
||||
// 防呆设计,超管跳过API权限检查
|
||||
ctx.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// 获取请求的资源和操作
|
||||
sub := convertor.ToString(uid)
|
||||
obj := model.ApiResourcePrefix + ctx.Request.URL.Path
|
||||
act := ctx.Request.Method
|
||||
|
||||
// 检查权限
|
||||
allowed, err := e.Enforce(sub, obj, act)
|
||||
if err != nil {
|
||||
v1.HandleError(ctx, http.StatusForbidden, v1.ErrForbidden, nil)
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
if !allowed {
|
||||
v1.HandleError(ctx, http.StatusForbidden, v1.ErrForbidden, nil)
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/cryptor"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/spf13/viper"
|
||||
"net/http"
|
||||
v1 "nunu-layout-admin/api/v1"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func SignMiddleware(logger *log.Logger, conf *viper.Viper) gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
requiredHeaders := []string{"Timestamp", "Nonce", "Sign", "App-Version"}
|
||||
|
||||
for _, header := range requiredHeaders {
|
||||
value, ok := ctx.Request.Header[header]
|
||||
if !ok || len(value) == 0 {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
data := map[string]string{
|
||||
"AppKey": conf.GetString("security.api_sign.app_key"),
|
||||
"Timestamp": ctx.Request.Header.Get("Timestamp"),
|
||||
"Nonce": ctx.Request.Header.Get("Nonce"),
|
||||
"AppVersion": ctx.Request.Header.Get("App-Version"),
|
||||
}
|
||||
|
||||
var keys []string
|
||||
for k := range data {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool { return strings.ToLower(keys[i]) < strings.ToLower(keys[j]) })
|
||||
|
||||
var str string
|
||||
for _, k := range keys {
|
||||
str += k + data[k]
|
||||
}
|
||||
str += conf.GetString("security.api_sign.app_security")
|
||||
|
||||
if ctx.Request.Header.Get("Sign") != strings.ToUpper(cryptor.Md5String(str)) {
|
||||
v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package model
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
const (
|
||||
AdminRole = "admin"
|
||||
AdminUserID = "1"
|
||||
MenuResourcePrefix = "menu:"
|
||||
ApiResourcePrefix = "api:"
|
||||
PermSep = ","
|
||||
)
|
||||
|
||||
type AdminUser struct {
|
||||
gorm.Model
|
||||
Username string `gorm:"type:varchar(50);not null;uniqueIndex;comment:'用户名'"`
|
||||
Nickname string `gorm:"type:varchar(50);not null;comment:'昵称'"`
|
||||
Password string `gorm:"type:varchar(255);not null;comment:'密码'"`
|
||||
Email string `gorm:"type:varchar(100);not null;comment:'电子邮件'"`
|
||||
Phone string `gorm:"type:varchar(20);not null;comment:'手机号'"`
|
||||
}
|
||||
|
||||
func (m *AdminUser) TableName() string {
|
||||
return "admin_users"
|
||||
}
|
||||
|
||||
type Role struct {
|
||||
gorm.Model
|
||||
Name string `json:"name" gorm:"column:name;type:varchar(100);uniqueIndex;comment:角色名"`
|
||||
Sid string `json:"sid" gorm:"column:sid;type:varchar(100);uniqueIndex;comment:角色标识"`
|
||||
}
|
||||
|
||||
func (m *Role) TableName() string {
|
||||
return "roles"
|
||||
}
|
||||
|
||||
type Api struct {
|
||||
gorm.Model
|
||||
Group string `gorm:"type:varchar(100);not null;comment:'API分组'"`
|
||||
Name string `gorm:"type:varchar(100);not null;comment:'API名称'"`
|
||||
Path string `gorm:"type:varchar(255);not null;comment:'API路径'"`
|
||||
Method string `gorm:"type:varchar(20);not null;comment:'HTTP方法'"`
|
||||
}
|
||||
|
||||
func (m *Api) TableName() string {
|
||||
return "api"
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package model
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type Menu struct {
|
||||
gorm.Model
|
||||
ParentID uint `json:"parentId,omitempty" gorm:"column:parent_id;index;comment:父级菜单的id,使用整数表示"` // 父级菜单的id,使用整数表示
|
||||
Path string `json:"path" gorm:"column:path;type:varchar(255);comment:地址"` // 地址
|
||||
Title string `json:"title" gorm:"column:title;type:varchar(100);comment:标题,使用字符串表示"` // 标题,使用字符串表示
|
||||
Name string `json:"name,omitempty" gorm:"column:name;type:varchar(100);comment:同路由中的name,用于保活"` // 同路由中的name,用于保活
|
||||
Component string `json:"component,omitempty" gorm:"column:component;type:varchar(255);comment:绑定的组件"` // 绑定的组件,默认类型:Iframe、RouteView、ComponentError
|
||||
Locale string `json:"locale,omitempty" gorm:"column:locale;type:varchar(100);comment:本地化标识"` // 本地化标识
|
||||
Icon string `json:"icon,omitempty" gorm:"column:icon;type:varchar(100);comment:图标,使用字符串表示"` // 图标,使用字符串表示
|
||||
Redirect string `json:"redirect,omitempty" gorm:"column:redirect;type:varchar(255);comment:重定向地址"` // 重定向地址
|
||||
URL string `json:"url,omitempty" gorm:"column:url;type:varchar(255);comment:iframe模式下的跳转url"` // iframe模式下的跳转url,不能与path重复
|
||||
KeepAlive bool `json:"keepAlive,omitempty" gorm:"column:keep_alive;default:false;comment:是否保活"` // 是否保活
|
||||
HideInMenu bool `json:"hideInMenu,omitempty" gorm:"column:hide_in_menu;default:false;comment:是否保活"` // 是否保活
|
||||
Target string `json:"target,omitempty" gorm:"column:target;type:varchar(20);comment:全连接跳转模式"` // 全连接跳转模式:'_blank'、'_self'、'_parent'
|
||||
Weight int `json:"weight" gorm:"column:weight;type:int;default:0;comment:排序权重"`
|
||||
}
|
||||
|
||||
func (m *Menu) TableName() string {
|
||||
return "menu"
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package model
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type User struct {
|
||||
gorm.Model
|
||||
}
|
||||
|
||||
func (m *User) TableName() string {
|
||||
return "user"
|
||||
}
|
||||
|
|
@ -0,0 +1,327 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
"go.uber.org/zap"
|
||||
v1 "nunu-layout-admin/api/v1"
|
||||
"nunu-layout-admin/internal/model"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type AdminRepository interface {
|
||||
GetAdminUsers(ctx context.Context, req *v1.GetAdminUsersRequest) ([]model.AdminUser, int64, error)
|
||||
GetAdminUser(ctx context.Context, uid uint) (model.AdminUser, error)
|
||||
GetAdminUserByUsername(ctx context.Context, username string) (model.AdminUser, error)
|
||||
AdminUserUpdate(ctx context.Context, m *model.AdminUser) error
|
||||
AdminUserCreate(ctx context.Context, m *model.AdminUser) error
|
||||
AdminUserDelete(ctx context.Context, id uint) error
|
||||
|
||||
GetUserPermissions(ctx context.Context, uid uint) ([][]string, error)
|
||||
GetUserRoles(ctx context.Context, uid uint) ([]string, error)
|
||||
GetRolePermissions(ctx context.Context, role string) ([][]string, error)
|
||||
UpdateRolePermission(ctx context.Context, role string, permissions map[string]struct{}) error
|
||||
UpdateUserRoles(ctx context.Context, uid uint, roles []string) error
|
||||
DeleteUserRoles(ctx context.Context, uid uint) error
|
||||
|
||||
GetMenuList(ctx context.Context) ([]model.Menu, error)
|
||||
MenuUpdate(ctx context.Context, m *model.Menu) error
|
||||
MenuCreate(ctx context.Context, m *model.Menu) error
|
||||
MenuDelete(ctx context.Context, id uint) error
|
||||
|
||||
GetRoles(ctx context.Context, req *v1.GetRoleListRequest) ([]model.Role, int64, error)
|
||||
RoleUpdate(ctx context.Context, m *model.Role) error
|
||||
RoleCreate(ctx context.Context, m *model.Role) error
|
||||
RoleDelete(ctx context.Context, id uint) error
|
||||
CasbinRoleDelete(ctx context.Context, role string) error
|
||||
GetRole(ctx context.Context, id uint) (model.Role, error)
|
||||
GetRoleBySid(ctx context.Context, sid string) (model.Role, error)
|
||||
|
||||
GetApis(ctx context.Context, req *v1.GetApisRequest) ([]model.Api, int64, error)
|
||||
GetApiGroups(ctx context.Context) ([]string, error)
|
||||
ApiUpdate(ctx context.Context, m *model.Api) error
|
||||
ApiCreate(ctx context.Context, m *model.Api) error
|
||||
ApiDelete(ctx context.Context, id uint) error
|
||||
}
|
||||
|
||||
func NewAdminRepository(
|
||||
repository *Repository,
|
||||
) AdminRepository {
|
||||
return &adminRepository{
|
||||
Repository: repository,
|
||||
}
|
||||
}
|
||||
|
||||
type adminRepository struct {
|
||||
*Repository
|
||||
}
|
||||
|
||||
func (r *adminRepository) CasbinRoleDelete(ctx context.Context, role string) error {
|
||||
_, err := r.e.DeleteRole(role)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *adminRepository) GetRole(ctx context.Context, id uint) (model.Role, error) {
|
||||
m := model.Role{}
|
||||
return m, r.DB(ctx).Where("id = ?", id).First(&m).Error
|
||||
}
|
||||
func (r *adminRepository) GetRoleBySid(ctx context.Context, sid string) (model.Role, error) {
|
||||
m := model.Role{}
|
||||
return m, r.DB(ctx).Where("sid = ?", sid).First(&m).Error
|
||||
}
|
||||
|
||||
func (r *adminRepository) DeleteUserRoles(ctx context.Context, uid uint) error {
|
||||
_, err := r.e.DeleteRolesForUser(convertor.ToString(uid))
|
||||
return err
|
||||
}
|
||||
func (r *adminRepository) UpdateUserRoles(ctx context.Context, uid uint, roles []string) error {
|
||||
if len(roles) == 0 {
|
||||
_, err := r.e.DeleteRolesForUser(convertor.ToString(uid))
|
||||
return err
|
||||
}
|
||||
old, err := r.e.GetRolesForUser(convertor.ToString(uid))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldMap := make(map[string]struct{})
|
||||
newMap := make(map[string]struct{})
|
||||
for _, v := range old {
|
||||
oldMap[v] = struct{}{}
|
||||
}
|
||||
for _, v := range roles {
|
||||
newMap[v] = struct{}{}
|
||||
}
|
||||
addRoles := make([]string, 0)
|
||||
delRoles := make([]string, 0)
|
||||
|
||||
for key, _ := range oldMap {
|
||||
if _, exists := newMap[key]; !exists {
|
||||
delRoles = append(delRoles, key)
|
||||
}
|
||||
}
|
||||
for key, _ := range newMap {
|
||||
if _, exists := oldMap[key]; !exists {
|
||||
addRoles = append(addRoles, key)
|
||||
}
|
||||
}
|
||||
if len(addRoles) == 0 && len(delRoles) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, role := range delRoles {
|
||||
if _, err := r.e.DeleteRoleForUser(convertor.ToString(uid), role); err != nil {
|
||||
r.logger.WithContext(ctx).Error("DeleteRoleForUser error", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = r.e.AddRolesForUser(convertor.ToString(uid), addRoles)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *adminRepository) GetAdminUserByUsername(ctx context.Context, username string) (model.AdminUser, error) {
|
||||
m := model.AdminUser{}
|
||||
return m, r.DB(ctx).Where("username = ?", username).First(&m).Error
|
||||
}
|
||||
|
||||
func (r *adminRepository) GetAdminUsers(ctx context.Context, req *v1.GetAdminUsersRequest) ([]model.AdminUser, int64, error) {
|
||||
var list []model.AdminUser
|
||||
var total int64
|
||||
scope := r.DB(ctx).Model(&model.AdminUser{})
|
||||
if req.Username != "" {
|
||||
scope = scope.Where("username LIKE ?", "%"+req.Username+"%")
|
||||
}
|
||||
if req.Nickname != "" {
|
||||
scope = scope.Where("nickname LIKE ?", "%"+req.Nickname+"%")
|
||||
}
|
||||
if req.Email != "" {
|
||||
scope = scope.Where("email LIKE ?", "%"+req.Email+"%")
|
||||
}
|
||||
if req.Phone != "" {
|
||||
scope = scope.Where("phone LIKE ?", "%"+req.Phone+"%")
|
||||
}
|
||||
if err := scope.Count(&total).Error; err != nil {
|
||||
return nil, total, err
|
||||
}
|
||||
if err := scope.Offset((req.Page - 1) * req.PageSize).Limit(req.PageSize).Order("id DESC").Find(&list).Error; err != nil {
|
||||
return nil, total, err
|
||||
}
|
||||
return list, total, nil
|
||||
}
|
||||
|
||||
func (r *adminRepository) GetAdminUser(ctx context.Context, uid uint) (model.AdminUser, error) {
|
||||
m := model.AdminUser{}
|
||||
return m, r.DB(ctx).Where("id = ?", uid).First(&m).Error
|
||||
}
|
||||
|
||||
func (r *adminRepository) AdminUserUpdate(ctx context.Context, m *model.AdminUser) error {
|
||||
return r.DB(ctx).Where("id = ?", m.ID).Save(m).Error
|
||||
}
|
||||
|
||||
func (r *adminRepository) AdminUserCreate(ctx context.Context, m *model.AdminUser) error {
|
||||
return r.DB(ctx).Create(m).Error
|
||||
}
|
||||
|
||||
func (r *adminRepository) AdminUserDelete(ctx context.Context, id uint) error {
|
||||
return r.DB(ctx).Where("id = ?", id).Delete(&model.AdminUser{}).Error
|
||||
}
|
||||
|
||||
func (r *adminRepository) UpdateRolePermission(ctx context.Context, role string, newPermSet map[string]struct{}) error {
|
||||
if len(newPermSet) == 0 {
|
||||
return nil
|
||||
}
|
||||
// 获取当前角色的所有权限
|
||||
oldPermissions, err := r.e.GetPermissionsForUser(role)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 将旧权限转换为 map 方便查找
|
||||
oldPermSet := make(map[string]struct{})
|
||||
for _, perm := range oldPermissions {
|
||||
if len(perm) == 3 {
|
||||
oldPermSet[strings.Join([]string{perm[1], perm[2]}, model.PermSep)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// 找出需要删除的权限
|
||||
var removePermissions [][]string
|
||||
for key, _ := range oldPermSet {
|
||||
if _, exists := newPermSet[key]; !exists {
|
||||
removePermissions = append(removePermissions, strings.Split(key, model.PermSep))
|
||||
}
|
||||
}
|
||||
|
||||
// 找出需要添加的权限
|
||||
var addPermissions [][]string
|
||||
for key, _ := range newPermSet {
|
||||
if _, exists := oldPermSet[key]; !exists {
|
||||
addPermissions = append(addPermissions, strings.Split(key, model.PermSep))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 先移除多余的权限(使用 DeletePermissionForUser 逐条删除)
|
||||
for _, perm := range removePermissions {
|
||||
_, err := r.e.DeletePermissionForUser(role, perm...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("移除权限失败: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 再添加新的权限
|
||||
if len(addPermissions) > 0 {
|
||||
_, err = r.e.AddPermissionsForUser(role, addPermissions...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("添加新权限失败: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *adminRepository) GetApiGroups(ctx context.Context) ([]string, error) {
|
||||
res := make([]string, 0)
|
||||
if err := r.DB(ctx).Model(&model.Api{}).Group("`group`").Pluck("`group`", &res).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (r *adminRepository) GetApis(ctx context.Context, req *v1.GetApisRequest) ([]model.Api, int64, error) {
|
||||
var list []model.Api
|
||||
var total int64
|
||||
scope := r.DB(ctx).Model(&model.Api{})
|
||||
if req.Name != "" {
|
||||
scope = scope.Where("name LIKE ?", "%"+req.Name+"%")
|
||||
}
|
||||
if req.Group != "" {
|
||||
scope = scope.Where("`group` LIKE ?", "%"+req.Group+"%")
|
||||
}
|
||||
if req.Path != "" {
|
||||
scope = scope.Where("path LIKE ?", "%"+req.Path+"%")
|
||||
}
|
||||
if req.Method != "" {
|
||||
scope = scope.Where("method = ?", req.Method)
|
||||
}
|
||||
if err := scope.Count(&total).Error; err != nil {
|
||||
return nil, total, err
|
||||
}
|
||||
if err := scope.Offset((req.Page - 1) * req.PageSize).Limit(req.PageSize).Order("`group` ASC").Find(&list).Error; err != nil {
|
||||
return nil, total, err
|
||||
}
|
||||
return list, total, nil
|
||||
}
|
||||
|
||||
func (r *adminRepository) ApiUpdate(ctx context.Context, m *model.Api) error {
|
||||
return r.DB(ctx).Where("id = ?", m.ID).Save(m).Error
|
||||
}
|
||||
|
||||
func (r *adminRepository) ApiCreate(ctx context.Context, m *model.Api) error {
|
||||
return r.DB(ctx).Create(m).Error
|
||||
}
|
||||
|
||||
func (r *adminRepository) ApiDelete(ctx context.Context, id uint) error {
|
||||
return r.DB(ctx).Where("id = ?", id).Delete(&model.Api{}).Error
|
||||
}
|
||||
|
||||
func (r *adminRepository) GetUserPermissions(ctx context.Context, uid uint) ([][]string, error) {
|
||||
return r.e.GetImplicitPermissionsForUser(convertor.ToString(uid))
|
||||
|
||||
}
|
||||
func (r *adminRepository) GetRolePermissions(ctx context.Context, role string) ([][]string, error) {
|
||||
return r.e.GetPermissionsForUser(role)
|
||||
}
|
||||
func (r *adminRepository) GetUserRoles(ctx context.Context, uid uint) ([]string, error) {
|
||||
return r.e.GetRolesForUser(convertor.ToString(uid))
|
||||
}
|
||||
func (r *adminRepository) MenuUpdate(ctx context.Context, m *model.Menu) error {
|
||||
return r.DB(ctx).Where("id = ?", m.ID).Save(m).Error
|
||||
}
|
||||
|
||||
func (r *adminRepository) MenuCreate(ctx context.Context, m *model.Menu) error {
|
||||
return r.DB(ctx).Save(m).Error
|
||||
}
|
||||
|
||||
func (r *adminRepository) MenuDelete(ctx context.Context, id uint) error {
|
||||
return r.DB(ctx).Where("id = ?", id).Delete(&model.Menu{}).Error
|
||||
}
|
||||
|
||||
func (r *adminRepository) GetMenuList(ctx context.Context) ([]model.Menu, error) {
|
||||
var menuList []model.Menu
|
||||
if err := r.DB(ctx).Order("weight DESC").Find(&menuList).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return menuList, nil
|
||||
}
|
||||
|
||||
func (r *adminRepository) RoleUpdate(ctx context.Context, m *model.Role) error {
|
||||
return r.DB(ctx).Where("id = ?", m.ID).UpdateColumn("name", m.Name).Error
|
||||
}
|
||||
|
||||
func (r *adminRepository) RoleCreate(ctx context.Context, m *model.Role) error {
|
||||
return r.DB(ctx).Create(m).Error
|
||||
}
|
||||
|
||||
func (r *adminRepository) RoleDelete(ctx context.Context, id uint) error {
|
||||
return r.DB(ctx).Where("id = ?", id).Delete(&model.Role{}).Error
|
||||
}
|
||||
|
||||
func (r *adminRepository) GetRoles(ctx context.Context, req *v1.GetRoleListRequest) ([]model.Role, int64, error) {
|
||||
var list []model.Role
|
||||
var total int64
|
||||
scope := r.DB(ctx).Model(&model.Role{})
|
||||
if req.Name != "" {
|
||||
scope = scope.Where("name LIKE ?", "%"+req.Name+"%")
|
||||
}
|
||||
if req.Sid != "" {
|
||||
scope = scope.Where("sid = ?", req.Sid)
|
||||
}
|
||||
if err := scope.Count(&total).Error; err != nil {
|
||||
return nil, total, err
|
||||
}
|
||||
if err := scope.Offset((req.Page - 1) * req.PageSize).Limit(req.PageSize).Find(&list).Error; err != nil {
|
||||
return nil, total, err
|
||||
}
|
||||
return list, total, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/casbin/casbin/v2/model"
|
||||
gormadapter "github.com/casbin/gorm-adapter/v3"
|
||||
"github.com/glebarez/sqlite"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/spf13/viper"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
"nunu-layout-admin/pkg/zapgorm2"
|
||||
"time"
|
||||
)
|
||||
|
||||
const ctxTxKey = "TxKey"
|
||||
|
||||
type Repository struct {
|
||||
db *gorm.DB
|
||||
e *casbin.SyncedEnforcer
|
||||
//rdb *redis.Client
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func NewRepository(
|
||||
logger *log.Logger,
|
||||
db *gorm.DB,
|
||||
e *casbin.SyncedEnforcer,
|
||||
// rdb *redis.Client,
|
||||
) *Repository {
|
||||
return &Repository{
|
||||
db: db,
|
||||
e: e,
|
||||
//rdb: rdb,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
type Transaction interface {
|
||||
Transaction(ctx context.Context, fn func(ctx context.Context) error) error
|
||||
}
|
||||
|
||||
func NewTransaction(r *Repository) Transaction {
|
||||
return r
|
||||
}
|
||||
|
||||
// DB return tx
|
||||
// If you need to create a Transaction, you must call DB(ctx) and Transaction(ctx,fn)
|
||||
func (r *Repository) DB(ctx context.Context) *gorm.DB {
|
||||
v := ctx.Value(ctxTxKey)
|
||||
if v != nil {
|
||||
if tx, ok := v.(*gorm.DB); ok {
|
||||
return tx
|
||||
}
|
||||
}
|
||||
return r.db.WithContext(ctx)
|
||||
}
|
||||
|
||||
func (r *Repository) Transaction(ctx context.Context, fn func(ctx context.Context) error) error {
|
||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
ctx = context.WithValue(ctx, ctxTxKey, tx)
|
||||
return fn(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func NewDB(conf *viper.Viper, l *log.Logger) *gorm.DB {
|
||||
var (
|
||||
db *gorm.DB
|
||||
err error
|
||||
)
|
||||
|
||||
logger := zapgorm2.New(l.Logger)
|
||||
driver := conf.GetString("data.db.user.driver")
|
||||
dsn := conf.GetString("data.db.user.dsn")
|
||||
|
||||
// GORM doc: https://gorm.io/docs/connecting_to_the_database.html
|
||||
switch driver {
|
||||
case "mysql":
|
||||
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
|
||||
Logger: logger,
|
||||
})
|
||||
case "postgres":
|
||||
db, err = gorm.Open(postgres.New(postgres.Config{
|
||||
DSN: dsn,
|
||||
PreferSimpleProtocol: true, // disables implicit prepared statement usage
|
||||
}), &gorm.Config{})
|
||||
case "sqlite":
|
||||
db, err = gorm.Open(sqlite.Open(dsn), &gorm.Config{})
|
||||
default:
|
||||
panic("unknown db driver")
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
db = db.Debug()
|
||||
|
||||
// Connection Pool config
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
sqlDB.SetMaxIdleConns(10)
|
||||
sqlDB.SetMaxOpenConns(100)
|
||||
sqlDB.SetConnMaxLifetime(time.Hour)
|
||||
return db
|
||||
}
|
||||
func NewCasbinEnforcer(conf *viper.Viper, l *log.Logger, db *gorm.DB) *casbin.SyncedEnforcer {
|
||||
a, _ := gormadapter.NewAdapterByDB(db)
|
||||
m, err := model.NewModelFromString(`
|
||||
[request_definition]
|
||||
r = sub, obj, act
|
||||
|
||||
[policy_definition]
|
||||
p = sub, obj, act
|
||||
|
||||
[role_definition]
|
||||
g = _, _
|
||||
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow))
|
||||
|
||||
[matchers]
|
||||
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
|
||||
`)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
e, _ := casbin.NewSyncedEnforcer(m, a)
|
||||
e.StartAutoLoadPolicy(10 * time.Second) // 每10秒自动加载策略,防止启动多服务进程策略不一致
|
||||
|
||||
// Enable Logger, decide whether to show it in terminal
|
||||
//e.EnableLog(true)
|
||||
|
||||
// Save the policy back to DB.
|
||||
e.EnableAutoSave(true)
|
||||
|
||||
return e
|
||||
}
|
||||
func NewRedis(conf *viper.Viper) *redis.Client {
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: conf.GetString("data.redis.addr"),
|
||||
Password: conf.GetString("data.redis.password"),
|
||||
DB: conf.GetInt("data.redis.db"),
|
||||
})
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
_, err := rdb.Ping(ctx).Result()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("redis error: %s", err.Error()))
|
||||
}
|
||||
|
||||
return rdb
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"nunu-layout-admin/internal/model"
|
||||
)
|
||||
|
||||
type UserRepository interface {
|
||||
GetUser(ctx context.Context, id int64) (*model.User, error)
|
||||
}
|
||||
|
||||
func NewUserRepository(
|
||||
repository *Repository,
|
||||
) UserRepository {
|
||||
return &userRepository{
|
||||
Repository: repository,
|
||||
}
|
||||
}
|
||||
|
||||
type userRepository struct {
|
||||
*Repository
|
||||
}
|
||||
|
||||
func (r *userRepository) GetUser(ctx context.Context, id int64) (*model.User, error) {
|
||||
var user model.User
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/gin-contrib/static"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/spf13/viper"
|
||||
swaggerfiles "github.com/swaggo/files"
|
||||
ginSwagger "github.com/swaggo/gin-swagger"
|
||||
nethttp "net/http"
|
||||
"nunu-layout-admin/docs"
|
||||
"nunu-layout-admin/internal/handler"
|
||||
"nunu-layout-admin/internal/middleware"
|
||||
"nunu-layout-admin/pkg/jwt"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
"nunu-layout-admin/pkg/server/http"
|
||||
"nunu-layout-admin/web"
|
||||
)
|
||||
|
||||
func NewHTTPServer(
|
||||
logger *log.Logger,
|
||||
conf *viper.Viper,
|
||||
jwt *jwt.JWT,
|
||||
e *casbin.SyncedEnforcer,
|
||||
adminHandler *handler.AdminHandler,
|
||||
userHandler *handler.UserHandler,
|
||||
) *http.Server {
|
||||
gin.SetMode(gin.DebugMode)
|
||||
s := http.NewServer(
|
||||
gin.Default(),
|
||||
logger,
|
||||
http.WithServerHost(conf.GetString("http.host")),
|
||||
http.WithServerPort(conf.GetInt("http.port")),
|
||||
)
|
||||
// 设置前端静态资源
|
||||
s.Use(static.Serve("/", static.EmbedFolder(web.Assets(), "dist")))
|
||||
s.NoRoute(func(c *gin.Context) {
|
||||
indexPageData, err := web.Assets().ReadFile("dist/index.html")
|
||||
if err != nil {
|
||||
c.String(nethttp.StatusNotFound, "404 page not found")
|
||||
return
|
||||
}
|
||||
c.Data(nethttp.StatusOK, "text/html; charset=utf-8", indexPageData)
|
||||
})
|
||||
// swagger doc
|
||||
docs.SwaggerInfo.BasePath = "/"
|
||||
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),
|
||||
ginSwagger.PersistAuthorization(true),
|
||||
))
|
||||
|
||||
s.Use(
|
||||
middleware.CORSMiddleware(),
|
||||
middleware.ResponseLogMiddleware(logger),
|
||||
middleware.RequestLogMiddleware(logger),
|
||||
//middleware.SignMiddleware(log),
|
||||
)
|
||||
|
||||
v1 := s.Group("/v1")
|
||||
{
|
||||
// No route group has permission
|
||||
noAuthRouter := v1.Group("/")
|
||||
{
|
||||
noAuthRouter.POST("/login", adminHandler.Login)
|
||||
}
|
||||
|
||||
// Strict permission routing group
|
||||
strictAuthRouter := v1.Group("/").Use(middleware.StrictAuth(jwt, logger), middleware.AuthMiddleware(e))
|
||||
{
|
||||
strictAuthRouter.GET("/users", userHandler.GetUsers)
|
||||
|
||||
strictAuthRouter.GET("/menus", adminHandler.GetMenus)
|
||||
strictAuthRouter.GET("/admin/menus", adminHandler.GetAdminMenus)
|
||||
strictAuthRouter.POST("/admin/menu", adminHandler.MenuCreate)
|
||||
strictAuthRouter.PUT("/admin/menu", adminHandler.MenuUpdate)
|
||||
strictAuthRouter.DELETE("/admin/menu", adminHandler.MenuDelete)
|
||||
|
||||
strictAuthRouter.GET("/admin/users", adminHandler.GetAdminUsers)
|
||||
strictAuthRouter.GET("/admin/user", adminHandler.GetAdminUser)
|
||||
strictAuthRouter.PUT("/admin/user", adminHandler.AdminUserUpdate)
|
||||
strictAuthRouter.POST("/admin/user", adminHandler.AdminUserCreate)
|
||||
strictAuthRouter.DELETE("/admin/user", adminHandler.AdminUserDelete)
|
||||
strictAuthRouter.GET("/admin/user/permissions", adminHandler.GetUserPermissions)
|
||||
strictAuthRouter.GET("/admin/role/permissions", adminHandler.GetRolePermissions)
|
||||
strictAuthRouter.PUT("/admin/role/permission", adminHandler.UpdateRolePermission)
|
||||
strictAuthRouter.GET("/admin/roles", adminHandler.GetRoles)
|
||||
strictAuthRouter.POST("/admin/role", adminHandler.RoleCreate)
|
||||
strictAuthRouter.PUT("/admin/role", adminHandler.RoleUpdate)
|
||||
strictAuthRouter.DELETE("/admin/role", adminHandler.RoleDelete)
|
||||
|
||||
strictAuthRouter.GET("/admin/apis", adminHandler.GetApis)
|
||||
strictAuthRouter.POST("/admin/api", adminHandler.ApiCreate)
|
||||
strictAuthRouter.PUT("/admin/api", adminHandler.ApiUpdate)
|
||||
strictAuthRouter.DELETE("/admin/api", adminHandler.ApiDelete)
|
||||
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"nunu-layout-admin/internal/job"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
)
|
||||
|
||||
type JobServer struct {
|
||||
log *log.Logger
|
||||
userJob job.UserJob
|
||||
}
|
||||
|
||||
func NewJobServer(
|
||||
log *log.Logger,
|
||||
userJob job.UserJob,
|
||||
) *JobServer {
|
||||
return &JobServer{
|
||||
log: log,
|
||||
userJob: userJob,
|
||||
}
|
||||
}
|
||||
|
||||
func (j *JobServer) Start(ctx context.Context) error {
|
||||
// Tips: If you want job to start as a separate process, just refer to the task implementation and adjust the code accordingly.
|
||||
|
||||
// eg: kafka consumer
|
||||
err := j.userJob.KafkaConsumer(ctx)
|
||||
return err
|
||||
}
|
||||
func (j *JobServer) Stop(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,697 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/casbin/casbin/v2"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gorm.io/gorm"
|
||||
"net/http"
|
||||
v1 "nunu-layout-admin/api/v1"
|
||||
"nunu-layout-admin/internal/model"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
"nunu-layout-admin/pkg/sid"
|
||||
"os"
|
||||
)
|
||||
|
||||
type MigrateServer struct {
|
||||
db *gorm.DB
|
||||
log *log.Logger
|
||||
sid *sid.Sid
|
||||
e *casbin.SyncedEnforcer
|
||||
}
|
||||
|
||||
func NewMigrateServer(
|
||||
db *gorm.DB,
|
||||
log *log.Logger,
|
||||
sid *sid.Sid,
|
||||
e *casbin.SyncedEnforcer,
|
||||
) *MigrateServer {
|
||||
return &MigrateServer{
|
||||
e: e,
|
||||
db: db,
|
||||
log: log,
|
||||
sid: sid,
|
||||
}
|
||||
}
|
||||
func (m *MigrateServer) Start(ctx context.Context) error {
|
||||
m.db.Migrator().DropTable(
|
||||
&model.AdminUser{},
|
||||
&model.Menu{},
|
||||
&model.Role{},
|
||||
&model.Api{},
|
||||
)
|
||||
if err := m.db.AutoMigrate(
|
||||
&model.AdminUser{},
|
||||
&model.Menu{},
|
||||
&model.Role{},
|
||||
&model.Api{},
|
||||
); err != nil {
|
||||
m.log.Error("user migrate error", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
err := m.initialAdminUser(ctx)
|
||||
if err != nil {
|
||||
m.log.Error("initialAdminUser error", zap.Error(err))
|
||||
}
|
||||
|
||||
err = m.initialMenuData(ctx)
|
||||
if err != nil {
|
||||
m.log.Error("initialMenuData error", zap.Error(err))
|
||||
}
|
||||
|
||||
err = m.initialApisData(ctx)
|
||||
if err != nil {
|
||||
m.log.Error("initialApisData error", zap.Error(err))
|
||||
}
|
||||
|
||||
err = m.initialRBAC(ctx)
|
||||
if err != nil {
|
||||
m.log.Error("initialRBAC error", zap.Error(err))
|
||||
}
|
||||
|
||||
m.log.Info("AutoMigrate success")
|
||||
os.Exit(0)
|
||||
return nil
|
||||
}
|
||||
func (m *MigrateServer) Stop(ctx context.Context) error {
|
||||
m.log.Info("AutoMigrate stop")
|
||||
return nil
|
||||
}
|
||||
func (m *MigrateServer) initialAdminUser(ctx context.Context) error {
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte("123456"), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = m.db.Create(&model.AdminUser{
|
||||
Model: gorm.Model{ID: 1},
|
||||
Username: "admin",
|
||||
Password: string(hashedPassword),
|
||||
Nickname: "Admin",
|
||||
}).Error
|
||||
return m.db.Create(&model.AdminUser{
|
||||
Model: gorm.Model{ID: 2},
|
||||
Username: "user",
|
||||
Password: string(hashedPassword),
|
||||
Nickname: "运营人员",
|
||||
}).Error
|
||||
|
||||
}
|
||||
func (m *MigrateServer) initialRBAC(ctx context.Context) error {
|
||||
roles := []model.Role{
|
||||
{Sid: model.AdminRole, Name: "超级管理员"},
|
||||
{Sid: "1000", Name: "运营人员"},
|
||||
{Sid: "1001", Name: "访客"},
|
||||
}
|
||||
if err := m.db.Create(&roles).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
m.e.ClearPolicy()
|
||||
err := m.e.SavePolicy()
|
||||
if err != nil {
|
||||
m.log.Error("m.e.SavePolicy error", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
_, err = m.e.AddRoleForUser(model.AdminUserID, model.AdminRole)
|
||||
if err != nil {
|
||||
m.log.Error("m.e.AddRoleForUser error", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
menuList := make([]v1.MenuDataItem, 0)
|
||||
err = json.Unmarshal([]byte(menuData), &menuList)
|
||||
if err != nil {
|
||||
m.log.Error("json.Unmarshal error", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
for _, item := range menuList {
|
||||
m.addPermissionForRole(model.AdminRole, model.MenuResourcePrefix+item.Path, "read")
|
||||
}
|
||||
apiList := make([]model.Api, 0)
|
||||
err = m.db.Find(&apiList).Error
|
||||
if err != nil {
|
||||
m.log.Error("m.db.Find(&apiList).Error error", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
for _, api := range apiList {
|
||||
m.addPermissionForRole(model.AdminRole, model.ApiResourcePrefix+api.Path, api.Method)
|
||||
}
|
||||
|
||||
// 添加运营人员权限
|
||||
_, err = m.e.AddRoleForUser("2", "1000")
|
||||
if err != nil {
|
||||
m.log.Error("m.e.AddRoleForUser error", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
m.addPermissionForRole("1000", model.MenuResourcePrefix+"/profile/basic", "read")
|
||||
m.addPermissionForRole("1000", model.MenuResourcePrefix+"/profile/advanced", "read")
|
||||
m.addPermissionForRole("1000", model.MenuResourcePrefix+"/profile", "read")
|
||||
m.addPermissionForRole("1000", model.MenuResourcePrefix+"/dashboard", "read")
|
||||
m.addPermissionForRole("1000", model.MenuResourcePrefix+"/dashboard/workplace", "read")
|
||||
m.addPermissionForRole("1000", model.MenuResourcePrefix+"/dashboard/analysis", "read")
|
||||
m.addPermissionForRole("1000", model.MenuResourcePrefix+"/account/settings", "read")
|
||||
m.addPermissionForRole("1000", model.MenuResourcePrefix+"/account/center", "read")
|
||||
m.addPermissionForRole("1000", model.MenuResourcePrefix+"/account", "read")
|
||||
m.addPermissionForRole("1000", model.ApiResourcePrefix+"/v1/menus", http.MethodGet)
|
||||
m.addPermissionForRole("1000", model.ApiResourcePrefix+"/v1/admin/user", http.MethodGet)
|
||||
|
||||
return nil
|
||||
}
|
||||
func (m *MigrateServer) addPermissionForRole(role, resource, action string) {
|
||||
_, err := m.e.AddPermissionForUser(role, resource, action)
|
||||
if err != nil {
|
||||
m.log.Sugar().Info("为角色 %s 添加权限 %s:%s 失败: %v", role, resource, action, err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("为角色 %s 添加权限: %s %s\n", role, resource, action)
|
||||
}
|
||||
func (m *MigrateServer) initialApisData(ctx context.Context) error {
|
||||
initialApis := []model.Api{
|
||||
|
||||
{Group: "基础API", Name: "获取用户菜单列表", Path: "/v1/menus", Method: http.MethodGet},
|
||||
{Group: "基础API", Name: "获取管理员信息", Path: "/v1/admin/user", Method: http.MethodGet},
|
||||
|
||||
{Group: "菜单管理", Name: "获取管理菜单", Path: "/v1/admin/menus", Method: http.MethodGet},
|
||||
{Group: "菜单管理", Name: "创建菜单", Path: "/v1/admin/menu", Method: http.MethodPost},
|
||||
{Group: "菜单管理", Name: "更新菜单", Path: "/v1/admin/menu", Method: http.MethodPut},
|
||||
{Group: "菜单管理", Name: "删除菜单", Path: "/v1/admin/menu", Method: http.MethodDelete},
|
||||
|
||||
{Group: "权限模块", Name: "获取用户权限", Path: "/v1/admin/user/permissions", Method: http.MethodGet},
|
||||
{Group: "权限模块", Name: "获取角色权限", Path: "/v1/admin/role/permissions", Method: http.MethodGet},
|
||||
{Group: "权限模块", Name: "更新角色权限", Path: "/v1/admin/role/permission", Method: http.MethodPut},
|
||||
{Group: "权限模块", Name: "获取角色列表", Path: "/v1/admin/roles", Method: http.MethodGet},
|
||||
{Group: "权限模块", Name: "创建角色", Path: "/v1/admin/role", Method: http.MethodPost},
|
||||
{Group: "权限模块", Name: "更新角色", Path: "/v1/admin/role", Method: http.MethodPut},
|
||||
{Group: "权限模块", Name: "删除角色", Path: "/v1/admin/role", Method: http.MethodDelete},
|
||||
|
||||
{Group: "权限模块", Name: "获取管理员列表", Path: "/v1/admin/users", Method: http.MethodGet},
|
||||
{Group: "权限模块", Name: "更新管理员信息", Path: "/v1/admin/user", Method: http.MethodPut},
|
||||
{Group: "权限模块", Name: "创建管理员账号", Path: "/v1/admin/user", Method: http.MethodPost},
|
||||
{Group: "权限模块", Name: "删除管理员", Path: "/v1/admin/user", Method: http.MethodDelete},
|
||||
|
||||
{Group: "权限模块", Name: "获取API列表", Path: "/v1/admin/apis", Method: http.MethodGet},
|
||||
{Group: "权限模块", Name: "创建API", Path: "/v1/admin/api", Method: http.MethodPost},
|
||||
{Group: "权限模块", Name: "更新API", Path: "/v1/admin/api", Method: http.MethodPut},
|
||||
{Group: "权限模块", Name: "删除API", Path: "/v1/admin/api", Method: http.MethodDelete},
|
||||
}
|
||||
|
||||
return m.db.Create(&initialApis).Error
|
||||
}
|
||||
func (m *MigrateServer) initialMenuData(ctx context.Context) error {
|
||||
menuList := make([]v1.MenuDataItem, 0)
|
||||
err := json.Unmarshal([]byte(menuData), &menuList)
|
||||
if err != nil {
|
||||
m.log.Error("json.Unmarshal error", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
menuListDb := make([]model.Menu, 0)
|
||||
for _, item := range menuList {
|
||||
menuListDb = append(menuListDb, model.Menu{
|
||||
Model: gorm.Model{
|
||||
ID: item.ID,
|
||||
},
|
||||
ParentID: item.ParentID,
|
||||
Path: item.Path,
|
||||
Title: item.Title,
|
||||
Name: item.Name,
|
||||
Component: item.Component,
|
||||
Locale: item.Locale,
|
||||
Weight: item.Weight,
|
||||
Icon: item.Icon,
|
||||
Redirect: item.Redirect,
|
||||
URL: item.URL,
|
||||
KeepAlive: item.KeepAlive,
|
||||
HideInMenu: item.HideInMenu,
|
||||
})
|
||||
}
|
||||
return m.db.Create(&menuListDb).Error
|
||||
}
|
||||
|
||||
var menuData = `[
|
||||
{
|
||||
"id": 18,
|
||||
"parentId": 15,
|
||||
"path": "/access/admin",
|
||||
"title": "管理员账号",
|
||||
"name": "accessAdmin",
|
||||
"component": "/access/admin",
|
||||
"locale": "menu.access.admin"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"parentId": 0,
|
||||
"title": "分析页",
|
||||
"icon": "DashboardOutlined",
|
||||
"component": "/dashboard/analysis",
|
||||
"path": "/dashboard/analysis",
|
||||
"name": "DashboardAnalysis",
|
||||
"keepAlive": true,
|
||||
"locale": "menu.dashboard.analysis",
|
||||
"weight": 2
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"parentId": 0,
|
||||
"title": "仪表盘",
|
||||
"icon": "DashboardOutlined",
|
||||
"component": "RouteView",
|
||||
"redirect": "/dashboard/analysis",
|
||||
"path": "/dashboard",
|
||||
"name": "Dashboard",
|
||||
"locale": "menu.dashboard"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"parentId": 0,
|
||||
"title": "表单页",
|
||||
"icon": "FormOutlined",
|
||||
"component": "RouteView",
|
||||
"redirect": "/form/basic",
|
||||
"path": "/form",
|
||||
"name": "Form",
|
||||
"locale": "menu.form"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"parentId": 0,
|
||||
"title": "链接",
|
||||
"icon": "LinkOutlined",
|
||||
"component": "RouteView",
|
||||
"redirect": "/link/iframe",
|
||||
"path": "/link",
|
||||
"name": "Link",
|
||||
"locale": "menu.link"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"parentId": 5,
|
||||
"title": "AntDesign",
|
||||
"url": "https://ant.design/",
|
||||
"component": "Iframe",
|
||||
"path": "/link/iframe",
|
||||
"name": "LinkIframe",
|
||||
"keepAlive": true,
|
||||
"locale": "menu.link.iframe"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"parentId": 5,
|
||||
"title": "AntDesignVue",
|
||||
"url": "https://antdv.com/",
|
||||
"component": "Iframe",
|
||||
"path": "/link/antdv",
|
||||
"name": "LinkAntdv",
|
||||
"keepAlive": true,
|
||||
"locale": "menu.link.antdv"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"parentId": 5,
|
||||
"path": "https://www.baidu.com",
|
||||
"name": "LinkExternal",
|
||||
"title": "跳转百度",
|
||||
"locale": "menu.link.external"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"parentId": 0,
|
||||
"title": "菜单",
|
||||
"icon": "BarsOutlined",
|
||||
"component": "RouteView",
|
||||
"path": "/menu",
|
||||
"redirect": "/menu/menu1",
|
||||
"name": "Menu",
|
||||
"locale": "menu.menu"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"parentId": 9,
|
||||
"title": "菜单1",
|
||||
"component": "/menu/menu1",
|
||||
"path": "/menu/menu1",
|
||||
"name": "MenuMenu11",
|
||||
"keepAlive": true,
|
||||
"locale": "menu.menu.menu1"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"parentId": 9,
|
||||
"title": "菜单2",
|
||||
"component": "/menu/menu2",
|
||||
"path": "/menu/menu2",
|
||||
"keepAlive": true,
|
||||
"locale": "menu.menu.menu2"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"parentId": 9,
|
||||
"path": "/menu/menu3",
|
||||
"redirect": "/menu/menu3/menu1",
|
||||
"title": "菜单1-1",
|
||||
"component": "RouteView",
|
||||
"locale": "menu.menu.menu3"
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"parentId": 12,
|
||||
"path": "/menu/menu3/menu1",
|
||||
"component": "/menu/menu-1-1/menu1",
|
||||
"title": "菜单1-1-1",
|
||||
"keepAlive": true,
|
||||
"locale": "menu.menu3.menu1"
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"parentId": 12,
|
||||
"path": "/menu/menu3/menu2",
|
||||
"component": "/menu/menu-1-1/menu2",
|
||||
"title": "菜单1-1-2",
|
||||
"keepAlive": true,
|
||||
"locale": "menu.menu3.menu2"
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"path": "/access",
|
||||
"component": "RouteView",
|
||||
"redirect": "/access/common",
|
||||
"title": "权限模块",
|
||||
"name": "Access",
|
||||
"parentId": 0,
|
||||
"icon": "ClusterOutlined",
|
||||
"locale": "menu.access",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"id": 51,
|
||||
"parentId": 15,
|
||||
"path": "/access/role",
|
||||
"title": "角色管理",
|
||||
"name": "AccessRoles",
|
||||
"component": "/access/role",
|
||||
"locale": "menu.access.roles"
|
||||
},
|
||||
{
|
||||
"id": 52,
|
||||
"parentId": 15,
|
||||
"path": "/access/menu",
|
||||
"title": "菜单管理",
|
||||
"name": "AccessMenu",
|
||||
"component": "/access/menu",
|
||||
"locale": "menu.access.menus"
|
||||
},
|
||||
{
|
||||
"id": 53,
|
||||
"parentId": 15,
|
||||
"path": "/access/api",
|
||||
"title": "API管理",
|
||||
"name": "AccessAPI",
|
||||
"component": "/access/api",
|
||||
"locale": "menu.access.api"
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"parentId": 0,
|
||||
"title": "异常页",
|
||||
"icon": "WarningOutlined",
|
||||
"component": "RouteView",
|
||||
"redirect": "/exception/403",
|
||||
"path": "/exception",
|
||||
"name": "Exception",
|
||||
"locale": "menu.exception"
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"parentId": 19,
|
||||
"path": "/exception/403",
|
||||
"title": "403",
|
||||
"name": "403",
|
||||
"component": "/exception/403",
|
||||
"locale": "menu.exception.not-permission"
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"parentId": 19,
|
||||
"path": "/exception/404",
|
||||
"title": "404",
|
||||
"name": "404",
|
||||
"component": "/exception/404",
|
||||
"locale": "menu.exception.not-find"
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"parentId": 19,
|
||||
"path": "/exception/500",
|
||||
"title": "500",
|
||||
"name": "500",
|
||||
"component": "/exception/500",
|
||||
"locale": "menu.exception.server-error"
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"parentId": 0,
|
||||
"title": "结果页",
|
||||
"icon": "CheckCircleOutlined",
|
||||
"component": "RouteView",
|
||||
"redirect": "/result/success",
|
||||
"path": "/result",
|
||||
"name": "Result",
|
||||
"locale": "menu.result"
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"parentId": 23,
|
||||
"path": "/result/success",
|
||||
"title": "成功页",
|
||||
"name": "ResultSuccess",
|
||||
"component": "/result/success",
|
||||
"locale": "menu.result.success"
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"parentId": 23,
|
||||
"path": "/result/fail",
|
||||
"title": "失败页",
|
||||
"name": "ResultFail",
|
||||
"component": "/result/fail",
|
||||
"locale": "menu.result.fail"
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"parentId": 0,
|
||||
"title": "列表页",
|
||||
"icon": "TableOutlined",
|
||||
"component": "RouteView",
|
||||
"redirect": "/list/card-list",
|
||||
"path": "/list",
|
||||
"name": "List",
|
||||
"locale": "menu.list"
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
"parentId": 26,
|
||||
"path": "/list/card-list",
|
||||
"title": "卡片列表",
|
||||
"name": "ListCard",
|
||||
"component": "/list/card-list",
|
||||
"locale": "menu.list.card-list"
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
"parentId": 0,
|
||||
"title": "详情页",
|
||||
"icon": "ProfileOutlined",
|
||||
"component": "RouteView",
|
||||
"redirect": "/profile/basic",
|
||||
"path": "/profile",
|
||||
"name": "Profile",
|
||||
"locale": "menu.profile"
|
||||
},
|
||||
{
|
||||
"id": 29,
|
||||
"parentId": 28,
|
||||
"path": "/profile/basic",
|
||||
"title": "基础详情页",
|
||||
"name": "ProfileBasic",
|
||||
"component": "/profile/basic/index",
|
||||
"locale": "menu.profile.basic"
|
||||
},
|
||||
{
|
||||
"id": 30,
|
||||
"parentId": 26,
|
||||
"path": "/list/search-list",
|
||||
"title": "搜索列表",
|
||||
"name": "SearchList",
|
||||
"component": "/list/search-list",
|
||||
"locale": "menu.list.search-list"
|
||||
},
|
||||
{
|
||||
"id": 31,
|
||||
"parentId": 30,
|
||||
"path": "/list/search-list/articles",
|
||||
"title": "搜索列表(文章)",
|
||||
"name": "SearchListArticles",
|
||||
"component": "/list/search-list/articles",
|
||||
"locale": "menu.list.search-list.articles"
|
||||
},
|
||||
{
|
||||
"id": 32,
|
||||
"parentId": 30,
|
||||
"path": "/list/search-list/projects",
|
||||
"title": "搜索列表(项目)",
|
||||
"name": "SearchListProjects",
|
||||
"component": "/list/search-list/projects",
|
||||
"locale": "menu.list.search-list.projects"
|
||||
},
|
||||
{
|
||||
"id": 33,
|
||||
"parentId": 30,
|
||||
"path": "/list/search-list/applications",
|
||||
"title": "搜索列表(应用)",
|
||||
"name": "SearchListApplications",
|
||||
"component": "/list/search-list/applications",
|
||||
"locale": "menu.list.search-list.applications"
|
||||
},
|
||||
{
|
||||
"id": 34,
|
||||
"parentId": 26,
|
||||
"path": "/list/basic-list",
|
||||
"title": "标准列表",
|
||||
"name": "BasicCard",
|
||||
"component": "/list/basic-list",
|
||||
"locale": "menu.list.basic-list"
|
||||
},
|
||||
{
|
||||
"id": 35,
|
||||
"parentId": 28,
|
||||
"path": "/profile/advanced",
|
||||
"title": "高级详细页",
|
||||
"name": "ProfileAdvanced",
|
||||
"component": "/profile/advanced/index",
|
||||
"locale": "menu.profile.advanced"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"parentId": 3,
|
||||
"title": "基础表单",
|
||||
"component": "/form/basic-form/index",
|
||||
"path": "/form/basic-form",
|
||||
"name": "FormBasic",
|
||||
"keepAlive": false,
|
||||
"locale": "menu.form.basic-form"
|
||||
},
|
||||
{
|
||||
"id": 36,
|
||||
"parentId": 0,
|
||||
"title": "个人页",
|
||||
"icon": "UserOutlined",
|
||||
"component": "RouteView",
|
||||
"redirect": "/account/center",
|
||||
"path": "/account",
|
||||
"name": "Account",
|
||||
"locale": "menu.account"
|
||||
},
|
||||
{
|
||||
"id": 37,
|
||||
"parentId": 36,
|
||||
"path": "/account/center",
|
||||
"title": "个人中心",
|
||||
"name": "AccountCenter",
|
||||
"component": "/account/center",
|
||||
"locale": "menu.account.center"
|
||||
},
|
||||
{
|
||||
"id": 38,
|
||||
"parentId": 36,
|
||||
"path": "/account/settings",
|
||||
"title": "个人设置",
|
||||
"name": "AccountSettings",
|
||||
"component": "/account/settings",
|
||||
"locale": "menu.account.settings"
|
||||
},
|
||||
{
|
||||
"id": 39,
|
||||
"parentId": 3,
|
||||
"title": "分步表单",
|
||||
"component": "/form/step-form/index",
|
||||
"path": "/form/step-form",
|
||||
"name": "FormStep",
|
||||
"keepAlive": false,
|
||||
"locale": "menu.form.step-form"
|
||||
},
|
||||
{
|
||||
"id": 40,
|
||||
"parentId": 3,
|
||||
"title": "高级表单",
|
||||
"component": "/form/advanced-form/index",
|
||||
"path": "/form/advanced-form",
|
||||
"name": "FormAdvanced",
|
||||
"keepAlive": false,
|
||||
"locale": "menu.form.advanced-form"
|
||||
},
|
||||
{
|
||||
"id": 41,
|
||||
"parentId": 26,
|
||||
"path": "/list/table-list",
|
||||
"title": "查询表格",
|
||||
"name": "ConsultTable",
|
||||
"component": "/list/table-list",
|
||||
"locale": "menu.list.consult-table"
|
||||
},
|
||||
{
|
||||
"id": 42,
|
||||
"parentId": 1,
|
||||
"title": "监控页",
|
||||
"component": "/dashboard/monitor",
|
||||
"path": "/dashboard/monitor",
|
||||
"name": "DashboardMonitor",
|
||||
"keepAlive": true,
|
||||
"locale": "menu.dashboard.monitor"
|
||||
},
|
||||
{
|
||||
"id": 43,
|
||||
"parentId": 1,
|
||||
"title": "工作台",
|
||||
"component": "/dashboard/workplace",
|
||||
"path": "/dashboard/workplace",
|
||||
"name": "DashboardWorkplace",
|
||||
"keepAlive": true,
|
||||
"locale": "menu.dashboard.workplace"
|
||||
},
|
||||
{
|
||||
"id": 44,
|
||||
"parentId": 26,
|
||||
"path": "/list/crud-table",
|
||||
"title": "增删改查表格",
|
||||
"name": "CrudTable",
|
||||
"component": "/list/crud-table",
|
||||
"locale": "menu.list.crud-table"
|
||||
},
|
||||
{
|
||||
"id": 45,
|
||||
"parentId": 9,
|
||||
"path": "/menu/menu4",
|
||||
"redirect": "/menu/menu4/menu1",
|
||||
"title": "菜单2-1",
|
||||
"component": "RouteView",
|
||||
"locale": "menu.menu.menu4"
|
||||
},
|
||||
{
|
||||
"id": 46,
|
||||
"parentId": 45,
|
||||
"path": "/menu/menu4/menu1",
|
||||
"component": "/menu/menu-2-1/menu1",
|
||||
"title": "菜单2-1-1",
|
||||
"keepAlive": true,
|
||||
"locale": "menu.menu4.menu1"
|
||||
},
|
||||
{
|
||||
"id": 47,
|
||||
"parentId": 45,
|
||||
"path": "/menu/menu4/menu2",
|
||||
"component": "/menu/menu-2-1/menu2",
|
||||
"title": "菜单2-1-2",
|
||||
"keepAlive": true,
|
||||
"locale": "menu.menu4.menu2"
|
||||
}
|
||||
]`
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/go-co-op/gocron"
|
||||
"go.uber.org/zap"
|
||||
"nunu-layout-admin/internal/task"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TaskServer struct {
|
||||
log *log.Logger
|
||||
scheduler *gocron.Scheduler
|
||||
userTask task.UserTask
|
||||
}
|
||||
|
||||
func NewTaskServer(
|
||||
log *log.Logger,
|
||||
userTask task.UserTask,
|
||||
) *TaskServer {
|
||||
return &TaskServer{
|
||||
log: log,
|
||||
userTask: userTask,
|
||||
}
|
||||
}
|
||||
func (t *TaskServer) Start(ctx context.Context) error {
|
||||
gocron.SetPanicHandler(func(jobName string, recoverData interface{}) {
|
||||
t.log.Error("TaskServer Panic", zap.String("job", jobName), zap.Any("recover", recoverData))
|
||||
})
|
||||
|
||||
// eg: crontab task
|
||||
t.scheduler = gocron.NewScheduler(time.UTC)
|
||||
// if you are in China, you will need to change the time zone as follows
|
||||
// t.scheduler = gocron.NewScheduler(time.FixedZone("PRC", 8*60*60))
|
||||
|
||||
//_, err := t.scheduler.Every("3s").Do(func()
|
||||
_, err := t.scheduler.CronWithSeconds("0/3 * * * * *").Do(func() {
|
||||
err := t.userTask.CheckUser(ctx)
|
||||
if err != nil {
|
||||
t.log.Error("CheckUser error", zap.Error(err))
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
t.log.Error("CheckUser error", zap.Error(err))
|
||||
}
|
||||
|
||||
t.scheduler.StartBlocking()
|
||||
return nil
|
||||
}
|
||||
func (t *TaskServer) Stop(ctx context.Context) error {
|
||||
t.scheduler.Stop()
|
||||
t.log.Info("TaskServer stop...")
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,465 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gorm.io/gorm"
|
||||
v1 "nunu-layout-admin/api/v1"
|
||||
"nunu-layout-admin/internal/model"
|
||||
"nunu-layout-admin/internal/repository"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type AdminService interface {
|
||||
Login(ctx context.Context, req *v1.LoginRequest) (string, error)
|
||||
GetAdminUsers(ctx context.Context, req *v1.GetAdminUsersRequest) (*v1.GetAdminUsersResponseData, error)
|
||||
GetAdminUser(ctx context.Context, uid uint) (*v1.GetAdminUserResponseData, error)
|
||||
AdminUserUpdate(ctx context.Context, req *v1.AdminUserUpdateRequest) error
|
||||
AdminUserCreate(ctx context.Context, req *v1.AdminUserCreateRequest) error
|
||||
AdminUserDelete(ctx context.Context, id uint) error
|
||||
|
||||
GetUserPermissions(ctx context.Context, uid uint) (*v1.GetUserPermissionsData, error)
|
||||
GetRolePermissions(ctx context.Context, role string) (*v1.GetRolePermissionsData, error)
|
||||
UpdateRolePermission(ctx context.Context, req *v1.UpdateRolePermissionRequest) error
|
||||
|
||||
GetAdminMenus(ctx context.Context) (*v1.GetMenuResponseData, error)
|
||||
GetMenus(ctx context.Context, uid uint) (*v1.GetMenuResponseData, error)
|
||||
MenuUpdate(ctx context.Context, req *v1.MenuUpdateRequest) error
|
||||
MenuCreate(ctx context.Context, req *v1.MenuCreateRequest) error
|
||||
MenuDelete(ctx context.Context, id uint) error
|
||||
|
||||
GetRoles(ctx context.Context, req *v1.GetRoleListRequest) (*v1.GetRolesResponseData, error)
|
||||
RoleUpdate(ctx context.Context, req *v1.RoleUpdateRequest) error
|
||||
RoleCreate(ctx context.Context, req *v1.RoleCreateRequest) error
|
||||
RoleDelete(ctx context.Context, id uint) error
|
||||
|
||||
GetApis(ctx context.Context, req *v1.GetApisRequest) (*v1.GetApisResponseData, error)
|
||||
ApiUpdate(ctx context.Context, req *v1.ApiUpdateRequest) error
|
||||
ApiCreate(ctx context.Context, req *v1.ApiCreateRequest) error
|
||||
ApiDelete(ctx context.Context, id uint) error
|
||||
}
|
||||
|
||||
func NewAdminService(
|
||||
service *Service,
|
||||
adminRepository repository.AdminRepository,
|
||||
) AdminService {
|
||||
return &adminService{
|
||||
Service: service,
|
||||
adminRepository: adminRepository,
|
||||
}
|
||||
}
|
||||
|
||||
type adminService struct {
|
||||
*Service
|
||||
adminRepository repository.AdminRepository
|
||||
}
|
||||
|
||||
func (s *adminService) GetAdminUser(ctx context.Context, uid uint) (*v1.GetAdminUserResponseData, error) {
|
||||
user, err := s.adminRepository.GetAdminUser(ctx, uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
roles, _ := s.adminRepository.GetUserRoles(ctx, uid)
|
||||
|
||||
return &v1.GetAdminUserResponseData{
|
||||
Email: user.Email,
|
||||
ID: user.ID,
|
||||
Username: user.Username,
|
||||
Nickname: user.Nickname,
|
||||
Phone: user.Phone,
|
||||
Roles: roles,
|
||||
CreatedAt: user.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
UpdatedAt: user.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *adminService) Login(ctx context.Context, req *v1.LoginRequest) (string, error) {
|
||||
user, err := s.adminRepository.GetAdminUserByUsername(ctx, req.Username)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return "", v1.ErrUnauthorized
|
||||
}
|
||||
return "", v1.ErrInternalServerError
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
token, err := s.jwt.GenToken(user.ID, time.Now().Add(time.Hour*24*90))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (s *adminService) GetAdminUsers(ctx context.Context, req *v1.GetAdminUsersRequest) (*v1.GetAdminUsersResponseData, error) {
|
||||
list, total, err := s.adminRepository.GetAdminUsers(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := &v1.GetAdminUsersResponseData{
|
||||
List: make([]v1.AdminUserDataItem, 0),
|
||||
Total: total,
|
||||
}
|
||||
for _, user := range list {
|
||||
roles, err := s.adminRepository.GetUserRoles(ctx, user.ID)
|
||||
if err != nil {
|
||||
s.logger.Error("GetUserRoles error", zap.Error(err))
|
||||
continue
|
||||
}
|
||||
data.List = append(data.List, v1.AdminUserDataItem{
|
||||
Email: user.Email,
|
||||
ID: user.ID,
|
||||
Nickname: user.Nickname,
|
||||
Username: user.Username,
|
||||
Phone: user.Phone,
|
||||
Roles: roles,
|
||||
CreatedAt: user.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
UpdatedAt: user.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (s *adminService) AdminUserUpdate(ctx context.Context, req *v1.AdminUserUpdateRequest) error {
|
||||
old, _ := s.adminRepository.GetAdminUser(ctx, req.ID)
|
||||
if req.Password != "" {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Password = string(hash)
|
||||
} else {
|
||||
req.Password = old.Password
|
||||
}
|
||||
err := s.adminRepository.UpdateUserRoles(ctx, req.ID, req.Roles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.adminRepository.AdminUserUpdate(ctx, &model.AdminUser{
|
||||
Model: gorm.Model{
|
||||
ID: req.ID,
|
||||
},
|
||||
Email: req.Email,
|
||||
Nickname: req.Nickname,
|
||||
Phone: req.Phone,
|
||||
Username: req.Username,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (s *adminService) AdminUserCreate(ctx context.Context, req *v1.AdminUserCreateRequest) error {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Password = string(hash)
|
||||
err = s.adminRepository.AdminUserCreate(ctx, &model.AdminUser{
|
||||
Email: req.Email,
|
||||
Nickname: req.Nickname,
|
||||
Phone: req.Phone,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user, err := s.adminRepository.GetAdminUserByUsername(ctx, req.Username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.adminRepository.UpdateUserRoles(ctx, user.ID, req.Roles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
func (s *adminService) AdminUserDelete(ctx context.Context, id uint) error {
|
||||
// 删除用户角色
|
||||
err := s.adminRepository.DeleteUserRoles(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.adminRepository.AdminUserDelete(ctx, id)
|
||||
}
|
||||
|
||||
func (s *adminService) UpdateRolePermission(ctx context.Context, req *v1.UpdateRolePermissionRequest) error {
|
||||
permissions := map[string]struct{}{}
|
||||
for _, v := range req.List {
|
||||
perm := strings.Split(v, model.PermSep)
|
||||
if len(perm) == 2 {
|
||||
permissions[v] = struct{}{}
|
||||
}
|
||||
|
||||
}
|
||||
return s.adminRepository.UpdateRolePermission(ctx, req.Role, permissions)
|
||||
}
|
||||
|
||||
func (s *adminService) GetApis(ctx context.Context, req *v1.GetApisRequest) (*v1.GetApisResponseData, error) {
|
||||
list, total, err := s.adminRepository.GetApis(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groups, err := s.adminRepository.GetApiGroups(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := &v1.GetApisResponseData{
|
||||
List: make([]v1.ApiDataItem, 0),
|
||||
Total: total,
|
||||
Groups: groups,
|
||||
}
|
||||
for _, api := range list {
|
||||
data.List = append(data.List, v1.ApiDataItem{
|
||||
CreatedAt: api.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
Group: api.Group,
|
||||
ID: api.ID,
|
||||
Method: api.Method,
|
||||
Name: api.Name,
|
||||
Path: api.Path,
|
||||
UpdatedAt: api.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (s *adminService) ApiUpdate(ctx context.Context, req *v1.ApiUpdateRequest) error {
|
||||
return s.adminRepository.ApiUpdate(ctx, &model.Api{
|
||||
Group: req.Group,
|
||||
Method: req.Method,
|
||||
Name: req.Name,
|
||||
Path: req.Path,
|
||||
Model: gorm.Model{
|
||||
ID: req.ID,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *adminService) ApiCreate(ctx context.Context, req *v1.ApiCreateRequest) error {
|
||||
return s.adminRepository.ApiCreate(ctx, &model.Api{
|
||||
Group: req.Group,
|
||||
Method: req.Method,
|
||||
Name: req.Name,
|
||||
Path: req.Path,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *adminService) ApiDelete(ctx context.Context, id uint) error {
|
||||
return s.adminRepository.ApiDelete(ctx, id)
|
||||
}
|
||||
|
||||
func (s *adminService) GetUserPermissions(ctx context.Context, uid uint) (*v1.GetUserPermissionsData, error) {
|
||||
data := &v1.GetUserPermissionsData{
|
||||
List: []string{},
|
||||
}
|
||||
list, err := s.adminRepository.GetUserPermissions(ctx, uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, v := range list {
|
||||
if len(v) == 3 {
|
||||
data.List = append(data.List, strings.Join([]string{v[1], v[2]}, model.PermSep))
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
func (s *adminService) GetRolePermissions(ctx context.Context, role string) (*v1.GetRolePermissionsData, error) {
|
||||
data := &v1.GetRolePermissionsData{
|
||||
List: []string{},
|
||||
}
|
||||
list, err := s.adminRepository.GetRolePermissions(ctx, role)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, v := range list {
|
||||
if len(v) == 3 {
|
||||
data.List = append(data.List, strings.Join([]string{v[1], v[2]}, model.PermSep))
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (s *adminService) MenuUpdate(ctx context.Context, req *v1.MenuUpdateRequest) error {
|
||||
return s.adminRepository.MenuUpdate(ctx, &model.Menu{
|
||||
Component: req.Component,
|
||||
Icon: req.Icon,
|
||||
KeepAlive: req.KeepAlive,
|
||||
HideInMenu: req.HideInMenu,
|
||||
Locale: req.Locale,
|
||||
Weight: req.Weight,
|
||||
Name: req.Name,
|
||||
ParentID: req.ParentID,
|
||||
Path: req.Path,
|
||||
Redirect: req.Redirect,
|
||||
Title: req.Title,
|
||||
URL: req.URL,
|
||||
Model: gorm.Model{
|
||||
ID: req.ID,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *adminService) MenuCreate(ctx context.Context, req *v1.MenuCreateRequest) error {
|
||||
return s.adminRepository.MenuCreate(ctx, &model.Menu{
|
||||
Component: req.Component,
|
||||
Icon: req.Icon,
|
||||
KeepAlive: req.KeepAlive,
|
||||
HideInMenu: req.HideInMenu,
|
||||
Locale: req.Locale,
|
||||
Weight: req.Weight,
|
||||
Name: req.Name,
|
||||
ParentID: req.ParentID,
|
||||
Path: req.Path,
|
||||
Redirect: req.Redirect,
|
||||
Title: req.Title,
|
||||
URL: req.URL,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *adminService) MenuDelete(ctx context.Context, id uint) error {
|
||||
return s.adminRepository.MenuDelete(ctx, id)
|
||||
}
|
||||
|
||||
func (s *adminService) GetMenus(ctx context.Context, uid uint) (*v1.GetMenuResponseData, error) {
|
||||
menuList, err := s.adminRepository.GetMenuList(ctx)
|
||||
if err != nil {
|
||||
s.logger.WithContext(ctx).Error("GetMenuList error", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
data := &v1.GetMenuResponseData{
|
||||
List: make([]v1.MenuDataItem, 0),
|
||||
}
|
||||
// 获取权限的菜单
|
||||
permissions, err := s.adminRepository.GetUserPermissions(ctx, uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
menuPermMap := map[string]struct{}{}
|
||||
for _, permission := range permissions {
|
||||
// 防呆设置,超管可以看到所有菜单
|
||||
if convertor.ToString(uid) == model.AdminUserID {
|
||||
menuPermMap[strings.TrimPrefix(permission[1], model.MenuResourcePrefix)] = struct{}{}
|
||||
} else {
|
||||
if len(permission) == 3 && strings.HasPrefix(permission[1], model.MenuResourcePrefix) {
|
||||
menuPermMap[strings.TrimPrefix(permission[1], model.MenuResourcePrefix)] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, menu := range menuList {
|
||||
if _, ok := menuPermMap[menu.Path]; ok {
|
||||
data.List = append(data.List, v1.MenuDataItem{
|
||||
ID: menu.ID,
|
||||
Name: menu.Name,
|
||||
Title: menu.Title,
|
||||
Path: menu.Path,
|
||||
Component: menu.Component,
|
||||
Redirect: menu.Redirect,
|
||||
KeepAlive: menu.KeepAlive,
|
||||
HideInMenu: menu.HideInMenu,
|
||||
Locale: menu.Locale,
|
||||
Weight: menu.Weight,
|
||||
Icon: menu.Icon,
|
||||
ParentID: menu.ParentID,
|
||||
UpdatedAt: menu.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||||
URL: menu.URL,
|
||||
})
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
func (s *adminService) GetAdminMenus(ctx context.Context) (*v1.GetMenuResponseData, error) {
|
||||
menuList, err := s.adminRepository.GetMenuList(ctx)
|
||||
if err != nil {
|
||||
s.logger.WithContext(ctx).Error("GetMenuList error", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
data := &v1.GetMenuResponseData{
|
||||
List: make([]v1.MenuDataItem, 0),
|
||||
}
|
||||
for _, menu := range menuList {
|
||||
data.List = append(data.List, v1.MenuDataItem{
|
||||
ID: menu.ID,
|
||||
Name: menu.Name,
|
||||
Title: menu.Title,
|
||||
Path: menu.Path,
|
||||
Component: menu.Component,
|
||||
Redirect: menu.Redirect,
|
||||
KeepAlive: menu.KeepAlive,
|
||||
HideInMenu: menu.HideInMenu,
|
||||
Locale: menu.Locale,
|
||||
Weight: menu.Weight,
|
||||
Icon: menu.Icon,
|
||||
ParentID: menu.ParentID,
|
||||
UpdatedAt: menu.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||||
URL: menu.URL,
|
||||
})
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (s *adminService) RoleUpdate(ctx context.Context, req *v1.RoleUpdateRequest) error {
|
||||
return s.adminRepository.RoleUpdate(ctx, &model.Role{
|
||||
Name: req.Name,
|
||||
Sid: req.Sid,
|
||||
Model: gorm.Model{
|
||||
ID: req.ID,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *adminService) RoleCreate(ctx context.Context, req *v1.RoleCreateRequest) error {
|
||||
_, err := s.adminRepository.GetRoleBySid(ctx, req.Sid)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return s.adminRepository.RoleCreate(ctx, &model.Role{
|
||||
Name: req.Name,
|
||||
Sid: req.Sid,
|
||||
})
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *adminService) RoleDelete(ctx context.Context, id uint) error {
|
||||
old, err := s.adminRepository.GetRole(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.adminRepository.CasbinRoleDelete(ctx, old.Sid); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.adminRepository.RoleDelete(ctx, id)
|
||||
}
|
||||
|
||||
func (s *adminService) GetRoles(ctx context.Context, req *v1.GetRoleListRequest) (*v1.GetRolesResponseData, error) {
|
||||
list, total, err := s.adminRepository.GetRoles(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := &v1.GetRolesResponseData{
|
||||
List: make([]v1.RoleDataItem, 0),
|
||||
Total: total,
|
||||
}
|
||||
for _, role := range list {
|
||||
data.List = append(data.List, v1.RoleDataItem{
|
||||
ID: role.ID,
|
||||
Name: role.Name,
|
||||
Sid: role.Sid,
|
||||
UpdatedAt: role.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||||
CreatedAt: role.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"nunu-layout-admin/internal/repository"
|
||||
"nunu-layout-admin/pkg/jwt"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
"nunu-layout-admin/pkg/sid"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
logger *log.Logger
|
||||
sid *sid.Sid
|
||||
jwt *jwt.JWT
|
||||
tm repository.Transaction
|
||||
}
|
||||
|
||||
func NewService(
|
||||
tm repository.Transaction,
|
||||
logger *log.Logger,
|
||||
sid *sid.Sid,
|
||||
jwt *jwt.JWT,
|
||||
) *Service {
|
||||
return &Service{
|
||||
logger: logger,
|
||||
sid: sid,
|
||||
jwt: jwt,
|
||||
tm: tm,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"nunu-layout-admin/internal/model"
|
||||
"nunu-layout-admin/internal/repository"
|
||||
)
|
||||
|
||||
type UserService interface {
|
||||
GetUser(ctx context.Context, id int64) (*model.User, error)
|
||||
}
|
||||
|
||||
func NewUserService(
|
||||
service *Service,
|
||||
userRepository repository.UserRepository,
|
||||
) UserService {
|
||||
return &userService{
|
||||
Service: service,
|
||||
userRepository: userRepository,
|
||||
}
|
||||
}
|
||||
|
||||
type userService struct {
|
||||
*Service
|
||||
userRepository repository.UserRepository
|
||||
}
|
||||
|
||||
func (s *userService) GetUser(ctx context.Context, id int64) (*model.User, error) {
|
||||
return s.userRepository.GetUser(ctx, id)
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"nunu-layout-admin/internal/repository"
|
||||
"nunu-layout-admin/pkg/jwt"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
"nunu-layout-admin/pkg/sid"
|
||||
)
|
||||
|
||||
type Task struct {
|
||||
logger *log.Logger
|
||||
sid *sid.Sid
|
||||
jwt *jwt.JWT
|
||||
tm repository.Transaction
|
||||
}
|
||||
|
||||
func NewTask(
|
||||
tm repository.Transaction,
|
||||
logger *log.Logger,
|
||||
sid *sid.Sid,
|
||||
) *Task {
|
||||
return &Task{
|
||||
logger: logger,
|
||||
sid: sid,
|
||||
tm: tm,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"nunu-layout-admin/internal/repository"
|
||||
)
|
||||
|
||||
type UserTask interface {
|
||||
CheckUser(ctx context.Context) error
|
||||
}
|
||||
|
||||
func NewUserTask(
|
||||
task *Task,
|
||||
userRepo repository.UserRepository,
|
||||
) UserTask {
|
||||
return &userTask{
|
||||
userRepo: userRepo,
|
||||
Task: task,
|
||||
}
|
||||
}
|
||||
|
||||
type userTask struct {
|
||||
userRepo repository.UserRepository
|
||||
*Task
|
||||
}
|
||||
|
||||
func (t userTask) CheckUser(ctx context.Context) error {
|
||||
// do something
|
||||
t.logger.Info("CheckUser")
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"nunu-layout-admin/pkg/server"
|
||||
"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 {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
for _, srv := range a.servers {
|
||||
go func(srv server.Server) {
|
||||
err := srv.Start(ctx)
|
||||
if err != nil {
|
||||
log.Printf("Server start err: %v", err)
|
||||
}
|
||||
}(srv)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-signals:
|
||||
// Received termination signal
|
||||
log.Println("Received termination signal")
|
||||
case <-ctx.Done():
|
||||
// Context canceled
|
||||
log.Println("Context canceled")
|
||||
}
|
||||
|
||||
// Gracefully stop the servers
|
||||
for _, srv := range a.servers {
|
||||
err := srv.Stop(ctx)
|
||||
if err != nil {
|
||||
log.Printf("Server stop err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/spf13/viper"
|
||||
"os"
|
||||
)
|
||||
|
||||
func NewConfig(p string) *viper.Viper {
|
||||
envConf := os.Getenv("APP_CONF")
|
||||
if envConf == "" {
|
||||
envConf = p
|
||||
}
|
||||
fmt.Println("load conf file:", envConf)
|
||||
return getConfig(envConf)
|
||||
}
|
||||
|
||||
func getConfig(path string) *viper.Viper {
|
||||
conf := viper.New()
|
||||
conf.SetConfigFile(path)
|
||||
err := conf.ReadInConfig()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return conf
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package jwt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type JWT struct {
|
||||
key []byte
|
||||
}
|
||||
|
||||
type MyCustomClaims struct {
|
||||
UserId uint
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
func NewJwt(conf *viper.Viper) *JWT {
|
||||
return &JWT{key: []byte(conf.GetString("security.jwt.key"))}
|
||||
}
|
||||
|
||||
func (j *JWT) GenToken(userId uint, expiresAt time.Time) (string, error) {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, MyCustomClaims{
|
||||
UserId: userId,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(expiresAt),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
NotBefore: jwt.NewNumericDate(time.Now()),
|
||||
Issuer: "",
|
||||
Subject: "",
|
||||
ID: "",
|
||||
Audience: []string{},
|
||||
},
|
||||
})
|
||||
|
||||
// Sign and get the complete encoded token as a string using the key
|
||||
tokenString, err := token.SignedString(j.key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return tokenString, nil
|
||||
}
|
||||
|
||||
func (j *JWT) ParseToken(tokenString string) (*MyCustomClaims, error) {
|
||||
tokenString = strings.TrimPrefix(tokenString, "Bearer ")
|
||||
if strings.TrimSpace(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 err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if claims, ok := token.Claims.(*MyCustomClaims); ok && token.Valid {
|
||||
return claims, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/spf13/viper"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
const ctxLoggerKey = "zapLogger"
|
||||
|
||||
type Logger struct {
|
||||
*zap.Logger
|
||||
}
|
||||
|
||||
func NewLog(conf *viper.Viper) *Logger {
|
||||
// log address "out.log" User-defined
|
||||
lp := conf.GetString("log.log_file_name")
|
||||
lv := conf.GetString("log.log_level")
|
||||
var level zapcore.Level
|
||||
//debug<info<warn<error<fatal<panic
|
||||
switch lv {
|
||||
case "debug":
|
||||
level = zap.DebugLevel
|
||||
case "info":
|
||||
level = zap.InfoLevel
|
||||
case "warn":
|
||||
level = zap.WarnLevel
|
||||
case "error":
|
||||
level = zap.ErrorLevel
|
||||
default:
|
||||
level = zap.InfoLevel
|
||||
}
|
||||
hook := lumberjack.Logger{
|
||||
Filename: lp, // Log file path
|
||||
MaxSize: conf.GetInt("log.max_size"), // Maximum size unit for each log file: M
|
||||
MaxBackups: conf.GetInt("log.max_backups"), // The maximum number of backups that can be saved for log files
|
||||
MaxAge: conf.GetInt("log.max_age"), // Maximum number of days the file can be saved
|
||||
Compress: conf.GetBool("log.compress"), // Compression or not
|
||||
}
|
||||
|
||||
var encoder zapcore.Encoder
|
||||
if conf.GetString("log.encoding") == "console" {
|
||||
encoder = zapcore.NewConsoleEncoder(zapcore.EncoderConfig{
|
||||
TimeKey: "ts",
|
||||
LevelKey: "level",
|
||||
NameKey: "Logger",
|
||||
CallerKey: "caller",
|
||||
MessageKey: "msg",
|
||||
StacktraceKey: "stacktrace",
|
||||
LineEnding: zapcore.DefaultLineEnding,
|
||||
EncodeLevel: zapcore.LowercaseColorLevelEncoder,
|
||||
EncodeTime: timeEncoder,
|
||||
EncodeDuration: zapcore.SecondsDurationEncoder,
|
||||
EncodeCaller: zapcore.FullCallerEncoder,
|
||||
})
|
||||
} else {
|
||||
encoder = zapcore.NewJSONEncoder(zapcore.EncoderConfig{
|
||||
TimeKey: "ts",
|
||||
LevelKey: "level",
|
||||
NameKey: "logger",
|
||||
CallerKey: "caller",
|
||||
FunctionKey: zapcore.OmitKey,
|
||||
MessageKey: "msg",
|
||||
StacktraceKey: "stacktrace",
|
||||
LineEnding: zapcore.DefaultLineEnding,
|
||||
EncodeLevel: zapcore.LowercaseLevelEncoder,
|
||||
EncodeTime: zapcore.EpochTimeEncoder,
|
||||
EncodeDuration: zapcore.SecondsDurationEncoder,
|
||||
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||
})
|
||||
}
|
||||
core := zapcore.NewCore(
|
||||
encoder,
|
||||
zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(&hook)), // Print to console and file
|
||||
level,
|
||||
)
|
||||
if conf.GetString("env") != "prod" {
|
||||
return &Logger{zap.New(core, zap.Development(), zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel))}
|
||||
}
|
||||
return &Logger{zap.New(core, zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel))}
|
||||
}
|
||||
|
||||
func timeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
|
||||
//enc.AppendString(t.Format("2006-01-02 15:04:05"))
|
||||
enc.AppendString(t.Format("2006-01-02 15:04:05.000000000"))
|
||||
}
|
||||
|
||||
// WithValue Adds a field to the specified context
|
||||
func (l *Logger) WithValue(ctx context.Context, fields ...zapcore.Field) context.Context {
|
||||
if c, ok := ctx.(*gin.Context); ok {
|
||||
ctx = c.Request.Context()
|
||||
c.Request = c.Request.WithContext(context.WithValue(ctx, ctxLoggerKey, l.WithContext(ctx).With(fields...)))
|
||||
return c
|
||||
}
|
||||
return context.WithValue(ctx, ctxLoggerKey, l.WithContext(ctx).With(fields...))
|
||||
}
|
||||
|
||||
// WithContext Returns a zap instance from the specified context
|
||||
func (l *Logger) WithContext(ctx context.Context) *Logger {
|
||||
if c, ok := ctx.(*gin.Context); ok {
|
||||
ctx = c.Request.Context()
|
||||
}
|
||||
zl := ctx.Value(ctxLoggerKey)
|
||||
ctxLogger, ok := zl.(*zap.Logger)
|
||||
if ok {
|
||||
return &Logger{ctxLogger}
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"google.golang.org/grpc"
|
||||
"net"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
"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
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"nunu-layout-admin/pkg/log"
|
||||
"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 && !errors.Is(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
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package sid
|
||||
|
||||
const (
|
||||
base62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
)
|
||||
|
||||
func IntToBase62(n int) string {
|
||||
if n == 0 {
|
||||
return string(base62[0])
|
||||
}
|
||||
|
||||
var result []byte
|
||||
for n > 0 {
|
||||
result = append(result, base62[n%62])
|
||||
n /= 62
|
||||
}
|
||||
|
||||
// 反转字符串
|
||||
for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 {
|
||||
result[i], result[j] = result[j], result[i]
|
||||
}
|
||||
|
||||
return string(result)
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package sid
|
||||
|
||||
import (
|
||||
"github.com/sony/sonyflake"
|
||||
)
|
||||
|
||||
type Sid struct {
|
||||
sf *sonyflake.Sonyflake
|
||||
}
|
||||
|
||||
func NewSid() *Sid {
|
||||
sf := sonyflake.NewSonyflake(sonyflake.Settings{})
|
||||
if sf == nil {
|
||||
panic("sonyflake not created")
|
||||
}
|
||||
return &Sid{sf}
|
||||
}
|
||||
func (s Sid) GenString() (string, error) {
|
||||
id, err := s.sf.NextID()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return IntToBase62(int(id)), nil
|
||||
}
|
||||
func (s Sid) GenUint64() (uint64, error) {
|
||||
return s.sf.NextID()
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
package zapgorm2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
gormlogger "gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
const ctxLoggerKey = "zapLogger"
|
||||
|
||||
type Logger struct {
|
||||
ZapLogger *zap.Logger
|
||||
SlowThreshold time.Duration
|
||||
Colorful bool
|
||||
IgnoreRecordNotFoundError bool
|
||||
ParameterizedQueries bool
|
||||
LogLevel gormlogger.LogLevel
|
||||
}
|
||||
|
||||
func New(zapLogger *zap.Logger) gormlogger.Interface {
|
||||
return &Logger{
|
||||
ZapLogger: zapLogger,
|
||||
LogLevel: gormlogger.Warn,
|
||||
SlowThreshold: 100 * time.Millisecond,
|
||||
Colorful: false,
|
||||
IgnoreRecordNotFoundError: false,
|
||||
ParameterizedQueries: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) LogMode(level gormlogger.LogLevel) gormlogger.Interface {
|
||||
newlogger := *l
|
||||
newlogger.LogLevel = level
|
||||
return &newlogger
|
||||
}
|
||||
|
||||
// Info print info
|
||||
func (l Logger) Info(ctx context.Context, msg string, data ...interface{}) {
|
||||
if l.LogLevel >= gormlogger.Info {
|
||||
l.logger(ctx).Sugar().Infof(msg, data...)
|
||||
}
|
||||
}
|
||||
|
||||
// Warn print warn messages
|
||||
func (l Logger) Warn(ctx context.Context, msg string, data ...interface{}) {
|
||||
if l.LogLevel >= gormlogger.Warn {
|
||||
l.logger(ctx).Sugar().Warnf(msg, data...)
|
||||
}
|
||||
}
|
||||
|
||||
// Error print error messages
|
||||
func (l Logger) Error(ctx context.Context, msg string, data ...interface{}) {
|
||||
if l.LogLevel >= gormlogger.Error {
|
||||
l.logger(ctx).Sugar().Errorf(msg, data...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l Logger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
|
||||
if l.LogLevel <= gormlogger.Silent {
|
||||
return
|
||||
}
|
||||
|
||||
elapsed := time.Since(begin)
|
||||
elapsedStr := fmt.Sprintf("%.3fms", float64(elapsed.Nanoseconds())/1e6)
|
||||
logger := l.logger(ctx)
|
||||
switch {
|
||||
case err != nil && l.LogLevel >= gormlogger.Error && (!errors.Is(err, gormlogger.ErrRecordNotFound) || !l.IgnoreRecordNotFoundError):
|
||||
sql, rows := fc()
|
||||
if rows == -1 {
|
||||
logger.Error("trace", zap.Error(err), zap.String("elapsed", elapsedStr), zap.Int64("rows", rows), zap.String("sql", sql))
|
||||
} else {
|
||||
logger.Error("trace", zap.Error(err), zap.String("elapsed", elapsedStr), zap.Int64("rows", rows), zap.String("sql", sql))
|
||||
}
|
||||
case elapsed > l.SlowThreshold && l.SlowThreshold != 0 && l.LogLevel >= gormlogger.Warn:
|
||||
sql, rows := fc()
|
||||
slowLog := fmt.Sprintf("SLOW SQL >= %v", l.SlowThreshold)
|
||||
if rows == -1 {
|
||||
logger.Warn("trace", zap.String("slow", slowLog), zap.String("elapsed", elapsedStr), zap.Int64("rows", rows), zap.String("sql", sql))
|
||||
} else {
|
||||
logger.Warn("trace", zap.String("slow", slowLog), zap.String("elapsed", elapsedStr), zap.Int64("rows", rows), zap.String("sql", sql))
|
||||
}
|
||||
case l.LogLevel == gormlogger.Info:
|
||||
sql, rows := fc()
|
||||
if rows == -1 {
|
||||
logger.Info("trace", zap.String("elapsed", elapsedStr), zap.Int64("rows", rows), zap.String("sql", sql))
|
||||
} else {
|
||||
logger.Info("trace", zap.String("elapsed", elapsedStr), zap.Int64("rows", rows), zap.String("sql", sql))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
gormPackage = filepath.Join("gorm.io", "gorm")
|
||||
)
|
||||
|
||||
func (l Logger) logger(ctx context.Context) *zap.Logger {
|
||||
logger := l.ZapLogger
|
||||
if ctx != nil {
|
||||
if c, ok := ctx.(*gin.Context); ok {
|
||||
ctx = c.Request.Context()
|
||||
}
|
||||
zl := ctx.Value(ctxLoggerKey)
|
||||
ctxLogger, ok := zl.(*zap.Logger)
|
||||
if ok {
|
||||
logger = ctxLogger
|
||||
}
|
||||
}
|
||||
|
||||
for i := 2; i < 15; i++ {
|
||||
_, file, _, ok := runtime.Caller(i)
|
||||
switch {
|
||||
case !ok:
|
||||
case strings.HasSuffix(file, "_test.go"):
|
||||
case strings.Contains(file, gormPackage):
|
||||
default:
|
||||
return logger.WithOptions(zap.AddCallerSkip(i - 1))
|
||||
}
|
||||
}
|
||||
return logger
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# `/scripts`
|
||||
|
||||
Scripts to perform various build, install, analysis, etc operations.
|
||||
|
||||
These scripts keep the root level Makefile small and simple.
|
||||
|
||||
Examples:
|
||||
|
||||
* https://github.com/kubernetes/helm/tree/master/scripts
|
||||
* https://github.com/cockroachdb/cockroach/tree/master/scripts
|
||||
* https://github.com/hashicorp/terraform/tree/master/scripts
|
||||
Binary file not shown.
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"extends": ["@commitlint/config-conventional"],
|
||||
"rules": {
|
||||
"type-enum": [
|
||||
2,
|
||||
"always",
|
||||
[
|
||||
"init",
|
||||
"build",
|
||||
"ci",
|
||||
"chore",
|
||||
"docs",
|
||||
"feat",
|
||||
"fix",
|
||||
"perf",
|
||||
"refactor",
|
||||
"revert",
|
||||
"style",
|
||||
"test"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
VITE_APP_NAME=N-Admin
|
||||
VITE_APP_BASE=/
|
||||
VITE_APP_BASE_API=/api
|
||||
VITE_APP_LOAD_ROUTE_WAY=BACKEND
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
VITE_APP_BASE_API=http://127.0.0.1:8000
|
||||
VITE_APP_BASE_URL=http://127.0.0.1:8000
|
||||
|
||||
# https://docs.antdv-pro.com/guide/server.html
|
||||
# The following api is requested when the request parameter includes customDev
|
||||
VITE_APP_BASE_API_DEV=/dev-api
|
||||
VITE_APP_BASE_URL_DEV=http://127.0.0.1:6678/api
|
||||
|
||||
VITE_APP_LOAD_ROUTE_WAY=BACKEND
|
||||
|
||||
# The title of your application (string)
|
||||
VITE_GLOB_APP_TITLE="N-Admin"
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
VITE_APP_BASE_API=http://127.0.0.1:8000
|
||||
VITE_APP_BASE_URL=http://127.0.0.1:8000
|
||||
# The title of your application (string)
|
||||
VITE_GLOB_APP_TITLE="N-admin"
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
# Nuxt dev/build outputs
|
||||
.output
|
||||
.data
|
||||
.nuxt
|
||||
.nitro
|
||||
.cache
|
||||
|
||||
# Node dependencies
|
||||
node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.fleet
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
#dist
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
public-hoist-pattern[]=@vue/runtime-core
|
||||
public-hoist-pattern[]=eslint-*
|
||||
public-hoist-pattern[]=@typescript-eslint*
|
||||
package-manager-strict=false
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,8 @@
|
|||
FROM nginx
|
||||
|
||||
RUN rm /etc/nginx/conf.d/default.conf
|
||||
|
||||
ADD default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY dist/ /usr/share/nginx/html/
|
||||
RUN chmod 775 -R /usr/share/nginx/html
|
||||
EXPOSE 80/tcp
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) [2023] [Antdv Pro]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
<div align="center"> <a href="https://github.com/antdv-pro/antdv-pro"> <img alt="VbenAdmin Logo" width="200" height="200" src="./public/logo.svg"> </a> <br> <br>
|
||||
|
||||
|
||||
<h1>Antdv Pro</h1>
|
||||
|
||||
</div>
|
||||
|
||||

|
||||

|
||||
|
||||
**English** | [简体中文](./README.zh-CN.md)
|
||||
|
||||
|
||||
## Introduction
|
||||
|
||||
AntdvPro is a complete set of enterprise-level mid-backend front-end/design solutions based on Vue3, Vite4, ant-design-vue4, Pinia, UnoCSS and Typescript. It refers to the design pattern of Ali react version antd-pro, using the latest and most popular The front-end technology stack has built-in dynamic routing, multi-theme, multi-layout and other functions, which can help you quickly build enterprise-level mid-background product prototypes.
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
* pnpm: Using the latest pnpm as a package management tool, it can greatly reduce the size of node_modules, speed up the installation speed of packages, and can also share dependencies to reduce disk usage.
|
||||
* vite: vite as a front-end development tool, it can greatly speed up the start-up speed of the project, and also supports hot updates, which can greatly improve development efficiency.
|
||||
* vue3: vue3.3.x as the front-end framework, the basic code is written in script-setup, with less code and low maintenance cost.
|
||||
* nitro mock: Use nitro as the server's mock data, decoupled from the project, and more flexible and easy to use.
|
||||
* ant-design-vue4: ant-design-vue4 as the UI framework, the author of admin-pro is also a core member of ant-design-vue, which can provide long-term maintenance support.
|
||||
* pinia: pinia as a state management tool, it can greatly improve the readability and maintainability of the code, and also supports Typescript.
|
||||
* UnoCSS: Atomic CSS framework, reduce the troubles caused by thinking about some common class names, and improve our development efficiency.
|
||||
* Code specification: We have encapsulated a set of eslint-based code specification configuration files, which can be used out of the box to unify the problems brought by different teams.
|
||||
* Theme: The design specifications of antd-pro of the react version are used, and a set of theme modes based on vue are developed. On this basis, some new functions are added to meet various different needs as much as possible.
|
||||
* Request function: Based on axios, a set of request functions with complete types and some basic interceptor encapsulations are encapsulated. You only need to make corresponding implementation adjustments according to the requirements to meet the different needs of various projects.
|
||||
* Mobile compatibility: We have tried our best to make the basic framework compatible with the mobile terminal mode, but because our main goal is the enterprise-level mid-background product, we have not made too much adaptation to the mobile terminal. If your project needs to adapt to the mobile terminal, you can refer to our code for corresponding adjustments.
|
||||
|
||||
|
||||
## Preview
|
||||
|
||||
[antdv-pro](https://antdv-pro.com) - Test Account: admin/admin
|
||||
|
||||
[antdv-pro-docs](https://docs.antdv-pro.com) - Documentation
|
||||
|
||||
## Community
|
||||
|
||||
QQ Group: apply wechat group
|
||||
|
||||
Wechat: aibayanyu2022
|
||||
|
||||
Discord: [discord](https://discord.gg/tPb4G6gXmm)
|
||||
|
||||
WeChatGroup: apply wechat group to add author wechat
|
||||
|
||||
|
||||
|
||||
|
||||
## Useage
|
||||
|
||||
```bash
|
||||
|
||||
# Install degit
|
||||
npm i -g degit
|
||||
|
||||
# Pull the code
|
||||
degit antdv-pro/antdv-pro [your project name]
|
||||
|
||||
# Switch to the project directory
|
||||
cd [your project name]
|
||||
|
||||
# Install
|
||||
pnpm install
|
||||
|
||||
# Development
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
## Contribute
|
||||
|
||||
We are very welcome to have you participate in our open source project.
|
||||
|
||||
|
||||
**Pull Request:**
|
||||
|
||||
1. Fork code!
|
||||
2. Create your own branch: `git checkout -b feat-xxxx`
|
||||
3. Submit your changes: `git commit -am 'feat(function): add xxxxx'`
|
||||
4. Push your branch: `git push origin feat-xxxx`
|
||||
5. submit`pull request`
|
||||
|
||||
Thank you to all the people who already contributed to antdv-pro!
|
||||
|
||||
<a href="https://github.com/antdv-pro/antdv-pro/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=antdv-pro/antdv-pro&max=100&columns=15" />
|
||||
</a>
|
||||
|
||||
## Support
|
||||
|
||||
If you like our project, you can support us by clicking the "Star" button in the upper right corner. Your support is my motivation. Thank you ~
|
||||
|
||||
Thanks to the open source project license provided by [Jetbrains](https://www.jetbrains.com/?from=antdv-pro).
|
||||
|
||||
## Sponsor
|
||||
|
||||
If you like our project, you can sponsor us to help us maintain the project better.
|
||||
|
||||
[Alipay/Wechat](https://docs.antdv-pro.com/other/sponsor.html)
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
<div align="center"> <a href="https://github.com/antdv-pro/antdv-pro"> <img alt="VbenAdmin Logo" width="200" height="200" src="./public/logo.svg"> </a> <br> <br>
|
||||
|
||||
|
||||
<h1>Antdv Pro</h1>
|
||||
|
||||
</div>
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
[English](./README.md) | **简体中文**
|
||||
|
||||
|
||||
## 介绍
|
||||
|
||||
AntdvPro是一个基于Vue3、Vite4、ant-design-vue4、Pinia、UnoCSS和Typescript的一整套企业级中后台前端/设计解决方案,它参考了阿里react版本antd-pro的设计模式,使用了最新最流行的前端技术栈,内置了动态路由、多主题、多布局等功能,可以帮助你快速搭建企业级中后台产品原型。
|
||||
|
||||
|
||||
## 特性
|
||||
|
||||
* pnpm:使用了最新的pnpm作为包管理工具,它可以大大减少node_modules的体积,加快包的安装速度,同时还可以共享依赖,减少磁盘占用。
|
||||
* vite:vite作为前端开发工具,它可以大大加快项目的启动速度,同时还支持热更新,可以大大提高开发效率。
|
||||
* vue3:vue3.3.x作为前端框架,基础代码全部使用script-setup的写法,代码量少维护成本低。
|
||||
* nitro mock:采用nitro作为服务端的mock数据,从工程中解耦处理,更加灵活易用。
|
||||
* ant-design-vue4:ant-design-vue4作为UI框架,admin-pro的作者也是ant-design-vue的核心成员,可提供长期的维护支持。
|
||||
* pinia:pinia作为状态管理工具,它可以大大提高代码的可读性和可维护性,同时还支持Typescript。
|
||||
* UnoCSS:原子化的CSS框架,减少我们去想一些通用类名带来的烦恼,提升我们的开发效率。
|
||||
* 代码规范:我们封装了一套基于eslint的代码规范配置文件,开箱即用,统一不同团队所带来的问题。
|
||||
* 主题:延用了react版本的antd-pro的设计规范,开发了一套基于vue的主题模式,在此基础上增加了一些新的功能,尽可能的满足各种不同的需求。
|
||||
* 请求函数:基于axios封装了一套具有完善类型的请求函数,以及一些基础的拦截器的封装,只需要按照需求做对应的实现调整就能满足各种项目带来的不一样的需求。
|
||||
* 移动端兼容:基础框架部分我们尽可能的对移动端的模式进行了兼容处理,但是由于我们的主要目标是企业级中后台产品,所以我们并没有对移动端做过多的适配,如果你的项目需要移动端的适配,可以参考我们的代码进行相应的调整。
|
||||
|
||||
|
||||
## 演示
|
||||
|
||||
[antdv-pro](https://antdv-pro.com) - 测试账号:admin/admin
|
||||
|
||||
[antdv-pro-docs](https://docs.antdv-pro.com) - 在线文档地址
|
||||
|
||||
|
||||
## 社区
|
||||
|
||||
QQ群: 申请微信群
|
||||
|
||||
微信: [aibayanyu2022](https://u.wechat.com/MASIsAa8353Hi4e59-aBPaA)
|
||||
|
||||
Discord: [discord](https://discord.gg/tPb4G6gXmm)
|
||||
|
||||
微信群: 申请微信群加作者微信
|
||||
|
||||
|
||||
## 使用
|
||||
|
||||
```bash
|
||||
|
||||
# 安装degit
|
||||
npm i -g degit
|
||||
|
||||
# 拉取代码
|
||||
degit antdv-pro/antdv-pro [your project name]
|
||||
|
||||
# 切换到项目目录
|
||||
cd [your project name]
|
||||
|
||||
# 安装依赖
|
||||
|
||||
pnpm install
|
||||
|
||||
# 启动项目
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
## 贡献
|
||||
|
||||
非常欢迎您参与到我们的开源项目中来~
|
||||
|
||||
**PR流程:**
|
||||
|
||||
1. Fork 代码!
|
||||
2. 创建自己的分支: `git checkout -b feat-xxxx`
|
||||
3. 提交你的修改: `git commit -am 'feat(function): add xxxxx'`
|
||||
4. 推送您的分支: `git push origin feat-xxxx`
|
||||
5. 提交`pull request`
|
||||
|
||||
感谢所有为`antdv-pro`做出贡献的小伙伴儿们!
|
||||
|
||||
<a href="https://github.com/antdv-pro/antdv-pro/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=antdv-pro/antdv-pro&max=100&columns=15" />
|
||||
</a>
|
||||
|
||||
|
||||
## 支持
|
||||
|
||||
如果你觉得这个项目对你有帮助,你可以点右上角 "Star" 支持一下,你的支持就是我的动力,谢谢~
|
||||
|
||||
感谢[Jetbrains](https://www.jetbrains.com/?from=antdv-pro).提供的开源项目许可证支持
|
||||
|
||||
## 赞助
|
||||
|
||||
如果你觉得这个项目对你有帮助,你可以点击下方链接对我进行赞助,谢谢~
|
||||
|
||||
[赞助](https://docs.antdv-pro.com/other/sponsor.html)
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
# gzip config
|
||||
gzip on;
|
||||
gzip_min_length 1k;
|
||||
gzip_comp_level 9;
|
||||
gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml;
|
||||
gzip_vary on;
|
||||
gzip_disable "MSIE [1-6]\.";
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
include /etc/nginx/mime.types;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location /api {
|
||||
# rewrite ^/api(.*)$ $1 break;
|
||||
proxy_pass https://api.antdv-pro.com;
|
||||
client_max_body_size 100M;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
window.__PRODUCTION__N__ADMIN__CONF__={"VITE_GLOB_APP_TITLE":"N-admin"};Object.freeze(window.__PRODUCTION__N__ADMIN__CONF__);Object.defineProperty(window, "__PRODUCTION__N__ADMIN__CONF__", {configurable: false,writable: false,});
|
||||
|
|
@ -0,0 +1 @@
|
|||
import{a9 as n,aa as e,a2 as c,k as u,G as _,ac as p}from"./vue-Dl1fzmsf.js";import{a6 as l,H as i}from"./antd-vtmm7CAy.js";const k={__name:"401",setup(m){const a=p();function o(){a.replace({path:"/login"})}return(f,t)=>{const s=i,r=l;return c(),n(r,{status:"404",title:"401","sub-title":"登录已过期,请重新登陆"},{extra:e(()=>[u(s,{type:"primary",onClick:o},{default:e(()=>t[0]||(t[0]=[_(" 跳转登录 ")])),_:1})]),_:1})}}};export{k as default};
|
||||
|
|
@ -0,0 +1 @@
|
|||
import{ac as c,a2 as n,a9 as u,aa as a,k as p,G as _}from"./vue-Dl1fzmsf.js";import{H as i,a6 as l}from"./antd-vtmm7CAy.js";const x={__name:"403",setup(m){const e=c();function o(){e.replace({path:"/"})}return(f,t)=>{const s=i,r=l;return n(),u(r,{status:"403",title:"403","sub-title":"Sorry, you don't have access to this page."},{extra:a(()=>[p(s,{type:"primary",onClick:o},{default:a(()=>t[0]||(t[0]=[_(" Back to home ")])),_:1})]),_:1})}}};export{x as default};
|
||||
|
|
@ -0,0 +1 @@
|
|||
import{ac as n,a2 as c,a9 as u,aa as e,k as p,G as _}from"./vue-Dl1fzmsf.js";import{H as i,a6 as l}from"./antd-vtmm7CAy.js";const x={__name:"404",setup(m){const a=n();function o(){a.replace({path:"/"})}return(d,t)=>{const s=i,r=l;return c(),u(r,{status:"404",title:"404","sub-title":"Sorry, the page you visited does not exist."},{extra:e(()=>[p(s,{type:"primary",onClick:o},{default:e(()=>t[0]||(t[0]=[_(" Back Home ")])),_:1})]),_:1})}}};export{x as default};
|
||||
|
|
@ -0,0 +1 @@
|
|||
import{ac as n,a2 as c,a9 as u,aa as e,k as p,G as _}from"./vue-Dl1fzmsf.js";import{H as i,a6 as l}from"./antd-vtmm7CAy.js";const d={__name:"500",setup(m){const a=n();function r(){a.replace({path:"/"})}return(f,t)=>{const o=i,s=l;return c(),u(s,{status:"500",title:"500","sub-title":"Sorry, the server is reporting an error."},{extra:e(()=>[p(o,{type:"primary",onClick:r},{default:e(()=>t[0]||(t[0]=[_(" Back Home ")])),_:1})]),_:1})}}};export{d as default};
|
||||
|
|
@ -0,0 +1 @@
|
|||
.activeChart[data-v-c858f221]{position:relative}.activeChartGrid p[data-v-c858f221]{position:absolute;top:80px}.activeChartGrid p[data-v-c858f221]:last-child{top:115px}.activeChartLegend[data-v-c858f221]{position:relative;height:20px;margin-top:8px;font-size:0;line-height:20px}.activeChartLegend span[data-v-c858f221]{display:inline-block;width:33.33%;font-size:12px;text-align:center}.activeChartLegend span[data-v-c858f221]:first-child{text-align:left}.activeChartLegend span[data-v-c858f221]:last-child{text-align:right}.dashedLine[data-v-c858f221]{position:relative;top:-70px;left:-3px;height:1px}.dashedLine .line[data-v-c858f221]{position:absolute;top:0;left:0;width:100%;height:100%;background-image:linear-gradient(to right,transparent 50%,#e9e9e9 50%);background-size:6px}.dashedLine[data-v-c858f221]:last-child{top:-36px}
|
||||
|
|
@ -0,0 +1 @@
|
|||
import{T as y}from"./index-sUMRYBhU.js";import{_ as D}from"./index-C-JhWVfG.js";import{aD as M}from"./antd-vtmm7CAy.js";import{f as u,o as C,j as A,a2 as T,a3 as k,k as B,a4 as t,ad as r,u as n}from"./vue-Dl1fzmsf.js";import"./vec2-4Cx-bOHg.js";const w={class:"activeChart"},F={style:{marginTop:"32px"}},L={class:"activeChartGrid"},q={class:"activeChartLegend"},N={__name:"active-chart",setup(S){const e=u([]),i=u([]);let o,l,c;function g(a){return a<10?`0${a}`:a}function d(){e.value=[],i.value=[];for(let a=0;a<24;a+=1)e.value.push({x:`${g(a)}:00`,y:Math.floor(Math.random()*200)+a*50}),i.value.push(Math.floor(Math.random()*200)+a*50);o==null||o.changeData(i.value)}function f(){l=requestAnimationFrame(()=>{c=window.setTimeout(()=>{d(),f()},1e3)})}const m=u();return C(()=>{o=new y(m.value,{height:84,data:i.value,smooth:!0,autoFit:!0}),o.render(),f(),d()}),A(()=>{clearTimeout(c),l&&cancelAnimationFrame(l),o==null||o.destroy(),o=void 0}),(a,s)=>{var v,p,h,_;const x=M;return T(),k("div",w,[B(x,{title:"目标评估",value:"有望达到预期"}),t("div",F,[t("div",{ref_key:"tinyAreaContainer",ref:m},null,512)]),t("div",null,[t("div",L,[t("p",null,r(((v=[...n(e)].sort()[n(e).length-1])==null?void 0:v.y)+200)+" 亿元",1),t("p",null,r((p=[...n(e)].sort()[Math.floor(n(e).length/2)])==null?void 0:p.y)+" 亿元",1)]),s[0]||(s[0]=t("div",{class:"dashedLine"},[t("div",{class:"line"})],-1)),s[1]||(s[1]=t("div",{class:"dashedLine"},[t("div",{class:"line"})],-1))]),t("div",q,[s[2]||(s[2]=t("span",null,"00:00",-1)),t("span",null,r((h=n(e)[Math.floor(n(e).length/2)])==null?void 0:h.x),1),t("span",null,r((_=n(e)[n(e).length-1])==null?void 0:_.x),1)])])}}},I=D(N,[["__scopeId","data-v-c858f221"]]);export{I as default};
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1 @@
|
|||
import{D as i,j as t,E as n,F as r}from"./index-C-JhWVfG.js";function s(e){return i("/v1/admin/roles",e)}function o(e){return t("/v1/admin/role",e)}function u(e){return n("/v1/admin/role",e)}function p(e){return r("/v1/admin/role",e)}function d(e){return i("/v1/admin/role/permissions",e)}function m(e){return n("/v1/admin/role/permission",e)}function A(e){return i("/v1/admin/apis",e)}function l(e){return t("/v1/admin/api",e)}function c(e){return n("/v1/admin/api",e)}function f(e){return r("/v1/admin/api",e)}export{A as a,d as b,l as c,f as d,p as e,u as f,s as g,o as h,m as i,c as u};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1 @@
|
|||
.scrollbar[data-v-24f2c992]::-webkit-scrollbar{width:5px;height:10px}.scrollbar[data-v-24f2c992]::-webkit-scrollbar-thumb{border-radius:5px;-webkit-box-shadow:inset 0 0 5px rgba(0,0,0,.2);background:#bebebe33}.scrollbar[data-v-24f2c992]::-webkit-scrollbar-track{-webkit-box-shadow:inset 0 0 5px rgba(227,227,227,.2);border-radius:0;background:#0000001a}.list-container[data-v-24f2c992]{height:350px;width:100%;background-color:var(--bg-color)}.list-container .scroller-container[data-v-24f2c992]{position:relative;width:100%;height:100%;overflow:auto;--webkit-overflow-scrolling: touch}.list-container .scroller-container .pillar[data-v-24f2c992]{position:absolute;left:0;top:0;right:0;z-index:-1}.list-container .scroller-container .list[data-v-24f2c992]{position:absolute;top:0;left:0;right:0}.list-container .scroller-container .list .item[data-v-24f2c992]{box-sizing:border-box;display:flex;align-items:center;width:100%;height:50px;padding:0 20px;border-bottom:1px solid var(--bg-color-container)}.list-container .scroller-container .list .item[data-v-24f2c992] .ant-list-item{display:flex;justify-content:space-between;align-items:center;width:100%}.list-container .scroller-container .list .item[data-v-24f2c992] .ant-list-item .ant-list-item-action{margin-top:18px}.list-container .scroller-container .list .item[data-v-24f2c992] .ant-list-item>div:nth-child(1){flex:1}.list-container .scroller-container .list .item[data-v-24f2c992] .ant-list-item-meta{display:flex}.list-container .scroller-container .list .item[data-v-24f2c992] .ant-list-item-meta .ant-list-item-meta-avatar{margin-right:15px}.a-extra{display:flex;align-items:center;justify-content:end}
|
||||
|
|
@ -0,0 +1 @@
|
|||
import{_ as g}from"./index-1DQ9lz7_.js";import{_ as f}from"./index-C-JhWVfG.js";import{H as h,a9 as v,ab as w,aa as k}from"./antd-vtmm7CAy.js";import{f as x,a2 as e,a9 as i,aa as s,a4 as t,k as a,G as y,a3 as b,F as z,aj as j,u as V,ad as r}from"./vue-Dl1fzmsf.js";import"./context-Dawj80bg.js";const E={class:"mt-2"},A={class:"flex h-27"},B={class:"w-10 h-10 bg-gray-300 rounded-full"},C=["src"],F={class:"ml"},K={style:{"font-size":"18px","font-weight":"500"}},I="在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,这些类似的组件会被抽离成一套标准规范。",L={__name:"card-list",setup(N){const c=x([{title:"Aipay",link:"https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png"},{title:"Ant Design Vue",link:"https://www.antdv.com/assets/logo.1ef800a8.svg"},{title:"Vue",link:"https://gw.alipayobjects.com/zos/rmsportal/ComBAopevLwENQdKWiIn.png"},{title:"Vite",link:"https://cn.vitejs.dev/logo.svg"},{title:"React",link:"https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png"},{title:"Antdv Pro",link:"/logo.svg"},{title:"Webpack",link:"https://gw.alipayobjects.com/zos/rmsportal/nxkuOJlFJuAUhzlMTCEe.png"},{title:"Angular",link:"https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png"}]);return(O,o)=>{const p=h,n=v,_=w,d=k,m=g;return e(),i(m,null,{default:s(()=>[t("div",E,[a(d,{gutter:16},{default:s(()=>[a(n,{xs:16,sm:8,md:6,lg:6,xl:6,class:"mb-4"},{default:s(()=>[a(p,{class:"w-1/1 h-204px",type:"dashed"},{default:s(()=>o[0]||(o[0]=[y(" +新增产品 ")])),_:1})]),_:1}),(e(!0),b(z,null,j(V(c),(l,u)=>(e(),i(n,{key:u,xs:16,sm:8,md:6,lg:6,xl:6,class:"mb-4"},{default:s(()=>[a(_,{bordered:!1,style:{borderRadius:"0"},class:"cursor-pointer hover:shadow-[0_4px_20px_-5px_rgba(0,0,0,0.35)] transition duration-300"},{default:s(()=>[t("div",A,[t("div",B,[t("img",{class:"w-10 h-10 rounded-full",src:l.link},null,8,C)]),t("div",F,[t("div",K,r(l.title),1),t("div",{class:"h-17 overflow-hidden overflow"},r(I))])])]),actions:s(()=>o[1]||(o[1]=[t("li",null,"操作一",-1),t("li",null,"操作二",-1)])),_:2},1024)]),_:2},1024))),128))]),_:1})])]),_:1})}}},G=f(L,[["__scopeId","data-v-9ea2ae90"]]);export{G as default};
|
||||
|
|
@ -0,0 +1 @@
|
|||
.overflow[data-v-9ea2ae90]{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow:hidden}
|
||||
|
|
@ -0,0 +1 @@
|
|||
.category-other-item .ant-form-item{margin-bottom:0}
|
||||
|
|
@ -0,0 +1 @@
|
|||
import{ad as x,a1 as w,F as C,v as B,bh as F,V as L,ab as V}from"./antd-vtmm7CAy.js";import{s as _,f as N,a2 as r,a9 as p,aa as t,k as a,a4 as d,a3 as $,F as S,aj as T,u as c,G as j,ad as D}from"./vue-Dl1fzmsf.js";const E={class:"flex flex-wrap gap-2"},G={class:"flex gap-4 category-other-item"},I={__name:"category",setup(R){const u=_([{name:"全部",key:"all"},...["一","二","三","四","五","六","七","八","九","十","十一","十二"].map((n,l)=>({name:`类目${n}`,key:`category${l+1}`}))]),e=N([]);function f(n){if(n.key==="all"){if(e.value.includes("all")){e.value=[];return}else e.value=u.value.map(l=>l.key);return}e.value.includes(n.key)?(e.value=e.value.filter(l=>l!==n.key),e.value.includes("all")&&(e.value=e.value.filter(l=>l!=="all"))):(e.value=[...e.value,n.key],e.value.length===u.value.length-1&&(e.value=[...e.value,"all"]))}const m=_([{label:"付晓晓",value:"付晓晓"},{label:"周毛毛",value:"周毛毛"}]),v=_([{label:"优秀",value:1},{label:"普通",value:2}]);return(n,l)=>{const k=x,o=w,h=C,i=B,y=F,g=L,b=V;return r(),p(b,{bordered:!1},{default:t(()=>[a(g,null,{default:t(()=>[a(o,{label:"所属类目"},{default:t(()=>[d("div",E,[(r(!0),$(S,null,T(c(u),s=>(r(),p(k,{key:s.key,"cursor-pointer":"",color:c(e).includes(s.key)?"#108ee9":"",onClick:z=>f(s)},{default:t(()=>[j(D(s.name),1)]),_:2},1032,["color","onClick"]))),128))])]),_:1}),a(h,{dashed:""}),a(o,{label:"其他选项"},{default:t(()=>[a(y,null,{default:t(()=>[d("div",G,[a(o,{label:"作者"},{default:t(()=>[a(i,{placeholder:"不限",style:{width:"100px"},options:c(m)},null,8,["options"])]),_:1}),a(o,{label:"好评度"},{default:t(()=>[a(i,{placeholder:"不限",style:{width:"100px"},options:c(v)},null,8,["options"])]),_:1})])]),_:1})]),_:1})]),_:1})]),_:1})}}};export{I as _};
|
||||
|
|
@ -0,0 +1 @@
|
|||
[data-v-3eca9ca5] .ant-list-item{flex-direction:column!important;align-items:normal!important}[data-v-3eca9ca5] .ant-btn{padding-left:0}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1 @@
|
|||
import{w as S,x as o}from"./index-C-JhWVfG.js";import{u as s,S as g,ae as _,n as k,a2 as c,a3 as v,a4 as N,G as n,ad as I,k as a,aa as t,a9 as u,m as D}from"./vue-Dl1fzmsf.js";import{G as M,H as B,S as E}from"./antd-vtmm7CAy.js";const V={__name:"index",props:{access:{type:[String,Number,Array],required:!0}},setup(d){const{hasAccess:l}=S();return(m,x)=>s(l)(d.access)?g(m.$slots,"default",{key:0}):_("",!0)}},w={class:"flex flex-col gap-2"},R={class:"c-primary"},G={__name:"common",setup(d){const{hasAccess:l,roles:m}=S();return(x,e)=>{var y;const p=M,r=B,f=V,i=E,A=k("access");return c(),v("div",w,[N("div",null,[e[0]||(e[0]=n(" 当前用户拥有权限列表 ")),N("a",R,I((y=s(m))==null?void 0:y.join(",")),1)]),e[7]||(e[7]=n(" 所有用户均可查看 细粒度控制到按钮级别 ")),a(p,{message:"使用Access组件"}),a(i,null,{default:t(()=>[a(f,{access:[s(o).USER,s(o).ADMIN]},{default:t(()=>[a(r,null,{default:t(()=>e[1]||(e[1]=[n("普通用户")])),_:1})]),_:1},8,["access"]),a(f,{access:s(o).ADMIN},{default:t(()=>[a(r,{type:"primary"},{default:t(()=>e[2]||(e[2]=[n(" 管理员 ")])),_:1})]),_:1},8,["access"])]),_:1}),a(p,{message:"使用useAccess组合式Api"}),a(i,null,{default:t(()=>[s(l)([s(o).USER,s(o).ADMIN])?(c(),u(r,{key:0},{default:t(()=>e[3]||(e[3]=[n(" 普通用户 ")])),_:1})):_("",!0),s(l)(s(o).ADMIN)?(c(),u(r,{key:1,type:"primary"},{default:t(()=>e[4]||(e[4]=[n(" 管理员 ")])),_:1})):_("",!0)]),_:1}),a(p,{message:"使用v-access指令"}),a(i,null,{default:t(()=>[D((c(),u(r,null,{default:t(()=>e[5]||(e[5]=[n(" 普通用户 ")])),_:1})),[[A,[s(o).USER,s(o).ADMIN]]]),D((c(),u(r,{type:"primary"},{default:t(()=>e[6]||(e[6]=[n(" 管理员 ")])),_:1})),[[A,s(o).ADMIN]])]),_:1})])}}};export{G as default};
|
||||
|
|
@ -0,0 +1 @@
|
|||
import{_ as o}from"./index-C-JhWVfG.js";import{a6 as r}from"./antd-vtmm7CAy.js";import{a2 as e,a9 as s}from"./vue-Dl1fzmsf.js";const c={};function a(n,_){const t=r;return e(),s(t,{status:"404",title:"页面配置错误","sub-title":"动态配置页面不存在,请检查配置项"})}const i=o(c,[["render",a]]);export{i as default};
|
||||
|
|
@ -0,0 +1 @@
|
|||
import"./index-C-JhWVfG.js";import{aw as J,s as b,c as t,r as x,w as m}from"./vue-Dl1fzmsf.js";function Z(e){return{type:String,default:e}}function _(e){return{type:Number,default:e}}function $(e){return{type:Boolean,default:e}}function p(e){return{type:Array,default:e}}function ee(){return{type:[Function,Array]}}function K(e,...u){if(typeof e=="function")return e(...u);if(Array.isArray(e))return e.map(f=>f(...u))}function Q(e,u={}){const f=b(!1),H=t(()=>e.logo),W=t(()=>e.title),v=t(()=>e.layout),w=t(()=>e.collapsedWidth),C=t(()=>e.siderWidth),o=t(()=>e.menuData),i=t(()=>e.splitMenus),T=t(()=>e.fixedHeader),A=t(()=>e.fixedSider),D=t(()=>e.collapsed),L=t(()=>e.theme),j=t(()=>e.headerHeight),k=t(()=>e.contentWidth),E=t(()=>e.copyright),S=t(()=>e.isMobile),y=b(!1),F=()=>{y.value=!y.value},P=t(()=>e.header),U=t(()=>e.menu),B=t(()=>e.footer),I=t(()=>e.menuHeader),N=t(()=>e.leftCollapsed),d=x(new Map),l=x({selectedKeys:[]});m(o,()=>{var n;d.clear(),(n=o.value)==null||n.forEach(a=>{d.set(a.path,a)})},{immediate:!0});const O=t(()=>{var a,s;if(S.value||v.value!=="mix"||!i.value)return o.value;const n=(a=l.selectedKeys)==null?void 0:a[0];return n?((s=d.get(n))==null?void 0:s.children)??[]:[]}),R=n=>{l.selectedKeys=n},g=t(()=>e.openKeys),r=t(()=>e.selectedKeys),q=n=>{K(e["onUpdate:openKeys"],n)},z=n=>{K(e["onUpdate:selectedKeys"],n)},G=n=>{K(e.onMenuSelect,n)},h=(n,a,s)=>{for(const c of a??[]){if(c.path===n)return s??c;if(c.children&&c.children.length){const M=h(n,c.children,s??c);if(M)return M}}};return m(r,()=>{var n;if(i.value){const a=(n=r.value)==null?void 0:n[0];if(a){const s=h(a,o.value??[]);s&&(l.selectedKeys=[s.path])}}},{immediate:!0}),m(i,()=>{var n,a;if(!i.value)l.selectedKeys=[];else{const s=((n=r.value)==null?void 0:n[0])??((a=g.value)==null?void 0:a[0])??"",c=h(s,o.value??[]);c?l.selectedKeys=[c==null?void 0:c.path]:l.selectedKeys=[]}}),{logo:H,title:W,layout:v,collapsed:D,leftCollapsed:N,collapsedWidth:w,menuData:o,siderWidth:C,fixedHeader:T,fixedSider:A,headerHeight:j,theme:L,isMobile:S,mobileCollapsed:y,contentWidth:k,copyright:E,hasPageContainer:f,splitMenus:i,splitState:l,menuDataMap:d,selectedMenus:O,handleMobileCollapsed:F,header:P,menu:U,footer:B,openKeys:g,handleOpenKeys:q,selectedKeys:r,handleSelectedKeys:z,handleMenuSelect:G,handleSplitSelectedKeys:R,menuHeader:I,...u}}const[te,V]=J(Q),ne=()=>V();export{p as a,$ as b,te as c,ee as e,_ as n,Z as s,ne as u};
|
||||
|
|
@ -0,0 +1 @@
|
|||
.system-crud-wrapper .ant-form-item[data-v-00d2ba9b]{margin:0}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue