diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..0f94601 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +build +config.yaml \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3764eb4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/build +/config.yaml +.idea +.vscode \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e638415 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +# docker build -t registry.leafdev.top/leaf/rag-new:v0.0.1-fix . +FROM golang:latest as builder + +COPY . /app + +RUN go env -w GO111MODULE=on && go env -w GOPROXY=https://goproxy.cn,direct && go mod download +RUN CGO_ENABLED=0 go build -ldflags "-w -s" -gcflags "-N -l" -o main . + +# RUN +FROM alpine:latest + +WORKDIR /app + +COPY --from=builder /app/main /app/main +ENTRYPOINT ["/app/main"] \ No newline at end of file diff --git a/Dockerfile2 b/Dockerfile2 new file mode 100644 index 0000000..db40d19 --- /dev/null +++ b/Dockerfile2 @@ -0,0 +1,8 @@ +# docker build -t registry.leafdev.top/leaf/rag-new:v0.0.1-fix . -f Dockerfile2 +# RUN +FROM alpine:latest + +WORKDIR /app + +COPY ./main /app/main +ENTRYPOINT ["/app/main"] \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..95004c4 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +.PHONY: install-deps generate + +install-deps: + go install github.com/google/wire/cmd/wire@latest + go install github.com/swaggo/swag/cmd/swag@latest + go install github.com/pressly/goose/v3/cmd/goose@latest + +generate: + go generate ./... + +swag: + swag init -g main.go --parseDependency diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 0000000..e1e5b6e --- /dev/null +++ b/buf.gen.yaml @@ -0,0 +1,70 @@ +http: + port: 8080 + host: 0.0.0.0 + # the production url + url: http://127.0.0.1:8080 + +grpc: + address: 0.0.0.0:9090 + +debug: + enabled: false + +database: + host: 127.0.0.1 + port: 3306 + user: root + password: root + name: db_name + +redis: + host: 127.0.0.1 + port: 6379 + password: "" + db: 0 + +jwks: + url: "" + +metrics: + enabled: true + port: 8081 + host: 0.0.0.0 + +s3: + endpoint: 127.0.0.1:9000 + external_endpoint: 127.0.0.1:9000 + access_key: minio + secret_key: minio123 + bucket: amber + use_ssl: false + region: + +milvus: + host: 127.0.0.1 + port: 19530 + db_name: library + # 由于 Milvus 不支持新增列, 如果更换了 Embedding Model,建议新建一个 Collection + # 或者可以扩展张量 + document_collection: documents + user: + password: + + +kafka: + bootstrap_servers: + - 127.0.0.1:9092 + topic: "amber" + group_id: "" + # Plain + username: "" + password: "" + +openai: + api_key: "" + internal_base_url: http://192.168.81.108:9997/v1 + embedding_dim: 768 + embedding_model: jina-v2-zh + +third_party: + openai_api_key: "" \ No newline at end of file diff --git a/cmd/document.go b/cmd/document.go new file mode 100644 index 0000000..d22f2bd --- /dev/null +++ b/cmd/document.go @@ -0,0 +1,55 @@ +package cmd + +import ( + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" + "github.com/spf13/cobra" + "go-template/pkg/protos/documentService" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" + "net" +) + +func init() { + RootCmd.AddCommand(documentServiceCommand) +} + +var documentServiceCommand = &cobra.Command{ + Use: "document", + Short: "Start document service", + Run: func(cmd *cobra.Command, args []string) { + app, err := CreateApp() + if err != nil { + panic(err) + return + } + + app.Logger.Sugar.Info("Start document service") + + lis, err := net.Listen("tcp", app.Config.Grpc.Address) + if err != nil { + app.Logger.Sugar.Fatal(err) + } + var opts = []grpc.ServerOption{ + grpc.ChainUnaryInterceptor( + logging.UnaryServerInterceptor(app.Handler.GRPC.Interceptor.Logger.ZapLogInterceptor()), + + auth.UnaryServerInterceptor(app.Handler.GRPC.Interceptor.Auth.JwtAuth), + ), + grpc.ChainStreamInterceptor( + logging.StreamServerInterceptor(app.Handler.GRPC.Interceptor.Logger.ZapLogInterceptor()), + auth.StreamServerInterceptor(app.Handler.GRPC.Interceptor.Auth.JwtAuth), + ), + } + grpcServer := grpc.NewServer(opts...) + + documentService.RegisterDocumentServiceServer(grpcServer, app.Handler.GRPC.DocumentService) + reflection.Register(grpcServer) + + app.Logger.Sugar.Info("Document Service listing on " + app.Config.Grpc.Address) + + if err := grpcServer.Serve(lis); err != nil { + app.Logger.Sugar.Fatal(err) + } + }, +} diff --git a/cmd/http.go b/cmd/http.go new file mode 100644 index 0000000..b913445 --- /dev/null +++ b/cmd/http.go @@ -0,0 +1,100 @@ +package cmd + +import ( + "context" + "errors" + "github.com/spf13/cobra" + "net/http" + "os" + "os/signal" + "strconv" + "syscall" + "time" +) + +func init() { + RootCmd.AddCommand(httpCmd) +} + +var httpCmd = &cobra.Command{ + Use: "http", + Run: func(cmd *cobra.Command, args []string) { + initHttpServer() + }, +} + +func initHttpServer() { + app, err := CreateApp() + if err != nil { + panic(err) + return + } + + if app.Config.Http.Host == "" { + app.Config.Http.Host = "0.0.0.0" + } + + if app.Config.Http.Port == 0 { + app.Config.Http.Port = 8000 + } + + var bizServer *http.Server + var metricServer *http.Server + + bizServer = &http.Server{ + Addr: ":8080", + } + + bizServer.Addr = app.Config.Http.Host + ":" + strconv.Itoa(app.Config.Http.Port) + bizServer.Handler = app.HttpServer.BizRouter() + + // 启动 http + go func() { + // refresh + app.Service.Jwks.SetupAuthRefresh() + + app.Logger.Sugar.Info("Listening and serving HTTP on ", bizServer.Addr) + err = bizServer.ListenAndServe() + if err != nil && !errors.Is(http.ErrServerClosed, err) { + panic(err) + return + } + }() + + // 启动 metrics + if app.Config.Metrics.Enabled { + metricServer = &http.Server{ + Addr: ":8080", + } + metricServer.Addr = app.Config.Metrics.Host + ":" + strconv.Itoa(app.Config.Metrics.Port) + go func() { + app.Logger.Sugar.Info("Metrics and serving HTTP on ", metricServer.Addr) + + metricServer.Handler = app.HttpServer.MetricRouter() + + err = metricServer.ListenAndServe() + if err != nil && !errors.Is(http.ErrServerClosed, err) { + panic(err) + return + } + }() + } + + // 等待一个 INT 或 TERM 信号 + quit := make(chan os.Signal) + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + <-quit + app.Logger.Sugar.Info("Shutdown Server ...") + // 创建超时上下文,Shutdown 可以让未处理的连接在这个时间内关闭 + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // 停止HTTP服务器 + if err := bizServer.Shutdown(ctx); err != nil { + app.Logger.Sugar.Fatal("Biz Server Shutdown Error:", err) + } + + if err := metricServer.Shutdown(ctx); err != nil { + app.Logger.Sugar.Fatal("Metric Server Shutdown Error:", err) + } +} diff --git a/cmd/migrate.go b/cmd/migrate.go new file mode 100644 index 0000000..2773388 --- /dev/null +++ b/cmd/migrate.go @@ -0,0 +1,162 @@ +package cmd + +import ( + "context" + "fmt" + "go-template/internal/migrations" + "log" + "os" + "strings" + "time" + + "github.com/pressly/goose/v3" + "github.com/spf13/cobra" +) + +func init() { + RootCmd.AddCommand(migrateCommand, createGoMigrateCommand) +} + +var migrateCommand = &cobra.Command{ + Use: "goose [command]", + Short: "goose 迁移,用法 ", + Long: "适用于生产环境的数据库迁移", + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + _ = cmd.Help() + return + } + RunMigrate(args) + }, +} + +var createGoMigrateCommand = &cobra.Command{ + Use: "create-migrate", + Short: "新建 go 迁移", + Long: "新建 goose 的 go 迁移。", + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + _ = cmd.Help() + return + } + + name := args[0] + + createGooseMigration(name) + }, +} + +// RunMigrate 为数据库函数 +func RunMigrate(args []string) { + app, err := CreateApp() + if err != nil { + panic(err) + } + + // setup config + migrations.Config = app.Config + + // dic + goose.SetBaseFS(migrations.MigrationFS) + + // dialect tidb + err = goose.SetDialect("tidb") + if err != nil { + return + } + + db, err := app.GORM.DB() + if err != nil { + log.Fatalf("goose: %v\n", err) + } + + command := args[0] + + var arguments []string + if len(args) > 3 { + arguments = append(arguments, args[3:]...) + } + + err = goose.RunContext(context.Background(), command, db, ".", arguments...) + + if err != nil { + log.Fatalf("goose: %v\n", err) + } + + defer func() { + if err := db.Close(); err != nil { + log.Fatalf("goose: failed to close DB: %v\n", err) + } + }() + +} + +func createGooseMigration(name string) { + // 在 internal/migrations 目录下新建一个迁移文件 + // 文件名为 yyyy-mm-dd-hh-mm-ss-.go + month := int(time.Now().Month()) + monthString := fmt.Sprintf("%d", month) + if month < 10 { + // 转 string + monthString = "0" + monthString + } + + day := time.Now().Day() + dayString := fmt.Sprintf("%d", day) + if day < 10 { + dayString = "0" + dayString + } + + hour := time.Now().Hour() + hourString := fmt.Sprintf("%d", hour) + if hour < 10 { + hourString = "0" + hourString + } + + minute := time.Now().Minute() + minuteString := fmt.Sprintf("%d", minute) + if minute < 10 { + minuteString = "0" + minuteString + } + + // 秒 + second := time.Now().Second() + secondString := fmt.Sprintf("%d", second) + if second < 10 { + secondString = "0" + secondString + } + + funcName := fmt.Sprintf("%d%s%s%s%s%s", time.Now().Year(), monthString, dayString, hourString, minuteString, secondString) + fileName := fmt.Sprintf("%s_%s.go", funcName, name) + + // 模板内容 + var template = `package migrations + +import ( + "context" + "database/sql" + "github.com/pressly/goose/v3" +) + +func init() { + goose.AddMigrationContext(Up, Down) +} + +func Up(ctx context.Context, tx *sql.Tx) error { + _, err := tx.ExecContext(ctx, "UPDATE users SET username='admin' WHERE username='root';") + return err +} + +func Down(ctx context.Context, tx *sql.Tx) error { + _, err := tx.ExecContext(ctx, "UPDATE users SET username='root' WHERE username='admin';") + return err +} +` + + template = strings.ReplaceAll(template, "", funcName+name) + err := os.WriteFile("internal/migrations/"+fileName, []byte(template), 0644) + if err != nil { + log.Fatalf("failed creating migration file: %v", err) + } + +} diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..b29f678 --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,14 @@ +package cmd + +import "github.com/spf13/cobra" + +var RootCmd = &cobra.Command{ + Use: "rag", + Run: func(cmd *cobra.Command, args []string) { + err := cmd.Help() + if err != nil { + panic(err) + return + } + }, +} diff --git a/cmd/schedule.go b/cmd/schedule.go new file mode 100644 index 0000000..01f0589 --- /dev/null +++ b/cmd/schedule.go @@ -0,0 +1,39 @@ +package cmd + +import ( + "go-template/internal/base" + + "github.com/spf13/cobra" +) + +func init() { + RootCmd.AddCommand(scheduleCmd) +} + +var scheduleCmd = &cobra.Command{ + Use: "schedule", + Short: "Schedule commands", + Long: `Schedule commands`, + Run: func(cmd *cobra.Command, args []string) { + app, err := CreateApp() + if err != nil { + panic(err) + } + + runSchedule(app) + }, +} + +func runSchedule(app *base.Application) { + // var wg sync.WaitGroup + + // var ctx = context.Background() + + // wg.Add(1) + // // 启动一个定时器 + // go func() { + + // }() + + // wg.Wait() +} diff --git a/cmd/wire.go b/cmd/wire.go new file mode 100644 index 0000000..1c8bb87 --- /dev/null +++ b/cmd/wire.go @@ -0,0 +1,42 @@ +//go:build wireinject +// +build wireinject + +package cmd + +import ( + "go-template/internal/base" + "go-template/internal/base/conf" + "go-template/internal/base/logger" + "go-template/internal/base/orm" + "go-template/internal/base/redis" + "go-template/internal/base/s3" + "go-template/internal/base/server" + "go-template/internal/batch" + "go-template/internal/dao" + "go-template/internal/handler" + "go-template/internal/router" + "go-template/internal/service" + + "github.com/google/wire" +) + +var ProviderSet = wire.NewSet( + conf.ProviderConfig, + logger.NewZapLogger, + orm.NewGORM, + dao.NewQuery, + redis.NewRedis, + s3.NewS3, + batch.NewBatch, + service.Provider, + handler.ProviderSet, + router.ProviderSetRouter, + server.NewHTTPServer, + base.NewApplication, +) + +func CreateApp() (*base.Application, error) { + wire.Build(ProviderSet) + + return nil, nil +} diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go new file mode 100644 index 0000000..cae8df3 --- /dev/null +++ b/cmd/wire_gen.go @@ -0,0 +1,63 @@ +// 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 cmd + +import ( + "github.com/google/wire" + "go-template/internal/base" + "go-template/internal/base/conf" + "go-template/internal/base/logger" + "go-template/internal/base/orm" + "go-template/internal/base/redis" + "go-template/internal/base/s3" + "go-template/internal/base/server" + "go-template/internal/batch" + "go-template/internal/dao" + "go-template/internal/handler" + "go-template/internal/handler/grpc" + "go-template/internal/handler/grpc/documents" + "go-template/internal/handler/http" + "go-template/internal/handler/http/controller/v1" + "go-template/internal/handler/http/middleware" + "go-template/internal/router" + "go-template/internal/service" + "go-template/internal/service/auth" + "go-template/internal/service/jwks" +) + +// Injectors from wire.go: + +func CreateApp() (*base.Application, error) { + loggerLogger := logger.NewZapLogger() + config := conf.ProviderConfig(loggerLogger) + jwksJWKS := jwks.NewJWKS(config, loggerLogger) + authService := auth.NewAuthService(config, jwksJWKS, loggerLogger) + userController := v1.NewUserController(authService) + handlers := http.NewHandler(userController) + api := router.NewApiRoute(handlers) + swaggerRouter := router.NewSwaggerRoute() + ginLoggerMiddleware := middleware.NewGinLoggerMiddleware(loggerLogger) + authMiddleware := middleware.NewAuthMiddleware(authService) + jsonResponseMiddleware := middleware.NewJSONResponseMiddleware() + httpMiddleware := http.NewMiddleware(ginLoggerMiddleware, authMiddleware, jsonResponseMiddleware) + httpServer := server.NewHTTPServer(config, api, swaggerRouter, httpMiddleware) + db := orm.NewGORM(config, loggerLogger) + query := dao.NewQuery(db) + documentService := documents.NewDocumentService(query) + grpcHandlers := grpc.NewHandler(documentService) + handlerHandler := handler.NewHandler(grpcHandlers, handlers) + serviceService := service.NewService(loggerLogger, jwksJWKS, authService) + redisRedis := redis.NewRedis(config) + batchBatch := batch.NewBatch(loggerLogger) + s3S3 := s3.NewS3(config) + application := base.NewApplication(config, httpServer, handlerHandler, loggerLogger, serviceService, httpMiddleware, redisRedis, batchBatch, s3S3, db, query) + return application, nil +} + +// wire.go: + +var ProviderSet = wire.NewSet(conf.ProviderConfig, logger.NewZapLogger, orm.NewGORM, dao.NewQuery, redis.NewRedis, s3.NewS3, batch.NewBatch, service.Provider, handler.ProviderSet, router.ProviderSetRouter, server.NewHTTPServer, base.NewApplication) diff --git a/configs/config.go b/configs/config.go new file mode 100644 index 0000000..bc2ad64 --- /dev/null +++ b/configs/config.go @@ -0,0 +1,6 @@ +package configs + +import _ "embed" + +//go:embed config.yaml +var Config []byte diff --git a/configs/config.yaml b/configs/config.yaml new file mode 100644 index 0000000..e1e5b6e --- /dev/null +++ b/configs/config.yaml @@ -0,0 +1,70 @@ +http: + port: 8080 + host: 0.0.0.0 + # the production url + url: http://127.0.0.1:8080 + +grpc: + address: 0.0.0.0:9090 + +debug: + enabled: false + +database: + host: 127.0.0.1 + port: 3306 + user: root + password: root + name: db_name + +redis: + host: 127.0.0.1 + port: 6379 + password: "" + db: 0 + +jwks: + url: "" + +metrics: + enabled: true + port: 8081 + host: 0.0.0.0 + +s3: + endpoint: 127.0.0.1:9000 + external_endpoint: 127.0.0.1:9000 + access_key: minio + secret_key: minio123 + bucket: amber + use_ssl: false + region: + +milvus: + host: 127.0.0.1 + port: 19530 + db_name: library + # 由于 Milvus 不支持新增列, 如果更换了 Embedding Model,建议新建一个 Collection + # 或者可以扩展张量 + document_collection: documents + user: + password: + + +kafka: + bootstrap_servers: + - 127.0.0.1:9092 + topic: "amber" + group_id: "" + # Plain + username: "" + password: "" + +openai: + api_key: "" + internal_base_url: http://192.168.81.108:9997/v1 + embedding_dim: 768 + embedding_model: jina-v2-zh + +third_party: + openai_api_key: "" \ No newline at end of file diff --git a/docs/docs.go b/docs/docs.go new file mode 100644 index 0000000..95a3e4b --- /dev/null +++ b/docs/docs.go @@ -0,0 +1,128 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": {}, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/api/v1/ping": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "测试接口,将会返回当前用户的信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ping" + ], + "summary": "Greet", + "deprecated": true, + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/schema.ResponseBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/schema.CurrentUserResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/schema.ResponseBody" + } + } + } + } + } + }, + "definitions": { + "schema.CurrentUserResponse": { + "type": "object", + "properties": { + "ip": { + "type": "string" + }, + "userEmail": { + "type": "string" + }, + "userId": { + "type": "string" + }, + "userName": { + "type": "string" + }, + "valid": { + "type": "boolean" + } + } + }, + "schema.ResponseBody": { + "type": "object", + "properties": { + "data": {}, + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + } + }, + "securityDefinitions": { + "ApiKeyAuth": { + "type": "apiKey", + "name": "Authorization", + "in": "header" + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0", + Host: "", + BasePath: "", + Schemes: []string{}, + Title: "API Docs", + Description: "", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/docs/swagger.json b/docs/swagger.json new file mode 100644 index 0000000..b58d392 --- /dev/null +++ b/docs/swagger.json @@ -0,0 +1,101 @@ +{ + "swagger": "2.0", + "info": { + "title": "API Docs", + "contact": {}, + "version": "1.0" + }, + "paths": { + "/api/v1/ping": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "测试接口,将会返回当前用户的信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ping" + ], + "summary": "Greet", + "deprecated": true, + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/schema.ResponseBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/schema.CurrentUserResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/schema.ResponseBody" + } + } + } + } + } + }, + "definitions": { + "schema.CurrentUserResponse": { + "type": "object", + "properties": { + "ip": { + "type": "string" + }, + "userEmail": { + "type": "string" + }, + "userId": { + "type": "string" + }, + "userName": { + "type": "string" + }, + "valid": { + "type": "boolean" + } + } + }, + "schema.ResponseBody": { + "type": "object", + "properties": { + "data": {}, + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + } + }, + "securityDefinitions": { + "ApiKeyAuth": { + "type": "apiKey", + "name": "Authorization", + "in": "header" + } + } +} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml new file mode 100644 index 0000000..d3d551a --- /dev/null +++ b/docs/swagger.yaml @@ -0,0 +1,62 @@ +definitions: + schema.CurrentUserResponse: + properties: + ip: + type: string + userEmail: + type: string + userId: + type: string + userName: + type: string + valid: + type: boolean + type: object + schema.ResponseBody: + properties: + data: {} + error: + type: string + message: + type: string + success: + type: boolean + type: object +info: + contact: {} + title: API Docs + version: "1.0" +paths: + /api/v1/ping: + get: + consumes: + - application/json + deprecated: true + description: 测试接口,将会返回当前用户的信息 + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/schema.ResponseBody' + - properties: + data: + $ref: '#/definitions/schema.CurrentUserResponse' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/schema.ResponseBody' + security: + - ApiKeyAuth: [] + summary: Greet + tags: + - ping +securityDefinitions: + ApiKeyAuth: + in: header + name: Authorization + type: apiKey +swagger: "2.0" diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c407775 --- /dev/null +++ b/go.mod @@ -0,0 +1,111 @@ +module go-template + +go 1.23.2 + +require ( + github.com/MicahParks/keyfunc/v3 v3.3.5 + github.com/bsm/redislock v0.9.4 + github.com/gin-contrib/cors v1.7.2 + github.com/gin-gonic/gin v1.10.0 + github.com/golang-jwt/jwt/v5 v5.2.1 + github.com/google/wire v0.6.0 + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 + github.com/minio/minio-go/v7 v7.0.78 + github.com/mitchellh/mapstructure v1.5.0 + github.com/pressly/goose/v3 v3.22.1 + github.com/prometheus/client_golang v1.20.5 + github.com/redis/go-redis/v9 v9.6.2 + github.com/spf13/cobra v1.8.1 + github.com/spf13/viper v1.19.0 + github.com/swaggo/files v1.0.1 + github.com/swaggo/gin-swagger v1.6.0 + github.com/swaggo/swag v1.16.3 + go.uber.org/zap v1.27.0 + google.golang.org/grpc v1.64.1 + google.golang.org/protobuf v1.35.1 + gorm.io/driver/mysql v1.5.7 + gorm.io/gen v0.3.26 + gorm.io/gorm v1.25.12 + gorm.io/plugin/dbresolver v1.5.3 + moul.io/zapgorm2 v1.3.0 +) + +require ( + filippo.io/edwards25519 v1.1.0 // indirect + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/MicahParks/jwkset v0.5.20 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bytedance/sonic v1.12.3 // indirect + github.com/bytedance/sonic/loader v0.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cloudwego/base64x v0.1.4 // 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.7.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.6 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-ini/ini v1.67.0 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // 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.0 // 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.22.1 // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect + github.com/goccy/go-json v0.10.3 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // 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/compress v1.17.11 // indirect + github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-sqlite3 v1.14.17 // indirect + github.com/mfridman/interpolate v0.0.2 // indirect + github.com/minio/md5-simd v1.1.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.60.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/rs/xid v1.6.0 // indirect + github.com/sagikazarmark/locafero v0.6.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sethvargo/go-retry v0.3.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.7.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/arch v0.11.0 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect + golang.org/x/mod v0.21.0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/time v0.7.0 // indirect + golang.org/x/tools v0.26.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gorm.io/datatypes v1.2.3 // indirect + gorm.io/hints v1.1.2 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..3bea6f0 --- /dev/null +++ b/go.sum @@ -0,0 +1,391 @@ +cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= +cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU= +cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/MicahParks/jwkset v0.5.20 h1:gTIKx9AofTqQJ0srd8AL7ty9NeadP5WUXSPOZadTpOI= +github.com/MicahParks/jwkset v0.5.20/go.mod h1:q8ptTGn/Z9c4MwbcfeCDssADeVQb3Pk7PnVxrvi+2QY= +github.com/MicahParks/keyfunc/v3 v3.3.5 h1:7ceAJLUAldnoueHDNzF8Bx06oVcQ5CfJnYwNt1U3YYo= +github.com/MicahParks/keyfunc/v3 v3.3.5/go.mod h1:SdCCyMJn/bYqWDvARspC6nCT8Sk74MjuAY22C7dCST8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bsm/redislock v0.9.4 h1:X/Wse1DPpiQgHbVYRE9zv6m070UcKoOGekgvpNhiSvw= +github.com/bsm/redislock v0.9.4/go.mod h1:Epf7AJLiSFwLCiZcfi6pWFO/8eAYrYpQXFxEDPoDeAk= +github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU= +github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= +github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/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/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/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc= +github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc= +github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw= +github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E= +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-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +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.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= +github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +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.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= +github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= +github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/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/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +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/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI= +github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +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-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs= +github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +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/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= +github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +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/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +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/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= +github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= +github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= +github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA= +github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA= +github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= +github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= +github.com/minio/minio-go/v7 v7.0.78 h1:LqW2zy52fxnI4gg8C2oZviTaKHcBV36scS+RzJnxUFs= +github.com/minio/minio-go/v7 v7.0.78/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0= +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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +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/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pressly/goose/v3 v3.22.1 h1:2zICEfr1O3yTP9BRZMGPj7qFxQ+ik6yeo+z1LMuioLc= +github.com/pressly/goose/v3 v3.22.1/go.mod h1:xtMpbstWyCpyH+0cxLTMCENWBG+0CSxvTsXhW95d5eo= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= +github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/redis/go-redis/v9 v9.6.2 h1:w0uvkRbc9KpgD98zcvo5IrVUsn0lXpRMuhNgiHDJzdk= +github.com/redis/go-redis/v9 v9.6.2/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= +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/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/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= +github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= +github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +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.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +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/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +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.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg= +github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk= +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.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4= +golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +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-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.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/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.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/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-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.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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/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.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.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.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +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-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= +google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +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/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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/datatypes v1.2.3 h1:95ucr9ip9dZMPhB3Tc9zbcoAi62hxYAgHicu7SLjK4g= +gorm.io/datatypes v1.2.3/go.mod h1:f4BsLcFAX67szSv8svwLRjklArSHAvHLeE3pXAS5DZI= +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.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U= +gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A= +gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c= +gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I= +gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0= +gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig= +gorm.io/gen v0.3.26 h1:sFf1j7vNStimPRRAtH4zz5NiHM+1dr6eA9aaRdplyhY= +gorm.io/gen v0.3.26/go.mod h1:a5lq5y3w4g5LMxBcw0wnO6tYUCdNutWODq5LrIt75LE= +gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +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/hints v1.1.2 h1:b5j0kwk5p4+3BtDtYqqfY+ATSxjj+6ptPgVveuynn9o= +gorm.io/hints v1.1.2/go.mod h1:/ARdpUHAtyEMCh5NNi3tI7FsGh+Cj/MIUlvNxCNCFWg= +gorm.io/plugin/dbresolver v1.5.3 h1:wFwINGZZmttuu9h7XpvbDHd8Lf9bb8GNzp/NpAMV2wU= +gorm.io/plugin/dbresolver v1.5.3/go.mod h1:TSrVhaUg2DZAWP3PrHlDlITEJmNOkL0tFTjvTEsQ4XE= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= +modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/sqlite v1.33.0 h1:WWkA/T2G17okiLGgKAj4/RMIvgyMT19yQ038160IeYk= +modernc.org/sqlite v1.33.0/go.mod h1:9uQ9hF/pCZoYZK73D/ud5Z7cIRIILSZI8NdIemVMTX8= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +moul.io/zapgorm2 v1.3.0 h1:+CzUTMIcnafd0d/BvBce8T4uPn6DQnpIrz64cyixlkk= +moul.io/zapgorm2 v1.3.0/go.mod h1:nPVy6U9goFKHR4s+zfSo1xVFaoU7Qgd5DoCdOfzoCqs= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= diff --git a/hack/gorm-gen/.gitignore b/hack/gorm-gen/.gitignore new file mode 100644 index 0000000..653f39f --- /dev/null +++ b/hack/gorm-gen/.gitignore @@ -0,0 +1 @@ +dao \ No newline at end of file diff --git a/hack/gorm-gen/gorm.go b/hack/gorm-gen/gorm.go new file mode 100644 index 0000000..18498b0 --- /dev/null +++ b/hack/gorm-gen/gorm.go @@ -0,0 +1,35 @@ +package main + +import ( + "go-template/internal/entity" + + "gorm.io/gen" +) + +// Dynamic SQL +//type Querier interface { +// // SELECT * FROM @@table WHERE name = @name{{if role !=""}} AND role = @role{{end}} +// FilterWithNameAndRole(name, role string) ([]gen.T, error) +//} + +func main() { + //app, err := cmd.CreateApp() + //if err != nil { + // panic(err) + //} + g := gen.NewGenerator(gen.Config{ + OutPath: "../../internal/dao", + Mode: gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface, // generate mode + }) + + //g.UseDB(app.GORM) + + g.ApplyBasic( + entity.User{}, + ) + + // Generate Type Safe API with Dynamic SQL defined on Querier interface for `model.User` and `model.Company` + //g.ApplyInterface(func(Querier) {}, model.User{}, model.Company{}) + + g.Execute() +} diff --git a/hack/rename/rename.go b/hack/rename/rename.go new file mode 100644 index 0000000..9cf213f --- /dev/null +++ b/hack/rename/rename.go @@ -0,0 +1,135 @@ +package main + +import ( + "bufio" + "fmt" + "io/fs" + "os" + "os/exec" + "path/filepath" + "strings" +) + +const frameworkModuleName = "leafdev.top/ecosystem/billing" + +func main() { + // 输入新的 go.mod module + var newModName string + fmt.Printf("Enter new module name(eg: github.com//): ") + _, err := fmt.Scanln(&newModName) + if err != nil { + fmt.Printf("Unable get new module name: %v\n", err) + os.Exit(1) + return + } + + fmt.Printf("Do you want to setup the project to %s? (y/n)", newModName) + var answer string + _, err = fmt.Scanln(&answer) + if err != nil { + fmt.Printf("Error reading user input: %v\n", err) + os.Exit(1) + } + if answer != "y" { + fmt.Printf("Aborting setup.\n") + } + + // 修改 go.mod 文件中的 module 名称 + err = replaceInFile("../../go.mod", frameworkModuleName, newModName) + if err != nil { + fmt.Printf("Error replacing module name in go.mod: %v\n", err) + os.Exit(1) + } + + // 读取 go.mod 中的 module 名称 + modName, err := getModuleName("../../go.mod") + if err != nil { + fmt.Printf("Error reading go.mod: %v\n", err) + os.Exit(1) + } + + if modName == frameworkModuleName { + fmt.Printf("Please update go.mod module to a different name.\n") + os.Exit(1) + } + + fmt.Printf("Module name found: %s\n", modName) + // 遍历当前文件夹(排除 vendor、setup.go 和版本控制文件夹) + err = filepath.Walk("../../", func(path string, info fs.FileInfo, err error) error { + if err != nil { + return err + } + + // 条件排除 + if info.IsDir() && (info.Name() == "vendor" || info.Name() == ".git") { + return filepath.SkipDir + } + if !info.IsDir() && info.Name() == "setup.go" { + return nil + } + + // 处理文件 + if !info.IsDir() { + err := replaceInFile(path, `"`+frameworkModuleName, fmt.Sprintf(`"%s`, modName)) + if err != nil { + fmt.Printf("Error replacing in file %s: %v\n", path, err) + } + } + + return nil + }) + if err != nil { + fmt.Printf("Error walking the path: %v\n", err) + } + + // run go mod tidy + fmt.Println("Running go mod tidy...") + var aCmd = exec.Command("go", "mod", "tidy") + if err := aCmd.Run(); err != nil { + fmt.Printf("Error running go mod tidy: %v\n", err) + } + +} + +// 读取 go.mod 文件中的 module 名称 +func getModuleName(modFilePath string) (string, error) { + file, err := os.Open(modFilePath) + if err != nil { + return "", err + } + defer func(file *os.File) { + err := file.Close() + if err != nil { + fmt.Printf("Error closing file: %v\n", err) + } + }(file) + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "module ") { + return strings.TrimSpace(strings.TrimPrefix(line, "module ")), nil + } + } + + if err := scanner.Err(); err != nil { + return "", err + } + + return "", fmt.Errorf("module name not found in go.mod") +} + +// 在文件中替换指定的字符串 +func replaceInFile(filePath string, old string, new string) error { + input, err := os.ReadFile(filePath) + if err != nil { + return err + } + + output := strings.ReplaceAll(string(input), old, new) + if err = os.WriteFile(filePath, []byte(output), 0666); err != nil { + return err + } + + return nil +} diff --git a/internal/base/app.go b/internal/base/app.go new file mode 100644 index 0000000..8793bf7 --- /dev/null +++ b/internal/base/app.go @@ -0,0 +1,57 @@ +package base + +import ( + "go-template/internal/base/conf" + "go-template/internal/base/logger" + "go-template/internal/base/redis" + "go-template/internal/base/s3" + "go-template/internal/base/server" + "go-template/internal/batch" + "go-template/internal/dao" + "go-template/internal/handler" + "go-template/internal/handler/http" + "go-template/internal/service" + "gorm.io/gorm" +) + +type Application struct { + Config *conf.Config + Logger *logger.Logger + Handler *handler.Handler + Middleware *http.Middleware + HttpServer *server.HttpServer + GORM *gorm.DB + DAO *dao.Query + Service *service.Service + Redis *redis.Redis + Batch *batch.Batch + S3 *s3.S3 +} + +func NewApplication( + config *conf.Config, + httpServer *server.HttpServer, + handler *handler.Handler, + logger *logger.Logger, + services *service.Service, + middleware *http.Middleware, + redis *redis.Redis, + batch *batch.Batch, + S3 *s3.S3, + GORM *gorm.DB, + DAO *dao.Query, +) *Application { + return &Application{ + Config: config, + HttpServer: httpServer, + Handler: handler, + Logger: logger, + Service: services, + Middleware: middleware, + Redis: redis, + Batch: batch, + S3: S3, + GORM: GORM, + DAO: DAO, + } +} diff --git a/internal/base/conf/conf.go b/internal/base/conf/conf.go new file mode 100644 index 0000000..557f3b5 --- /dev/null +++ b/internal/base/conf/conf.go @@ -0,0 +1,107 @@ +package conf + +// Config 配置文件不能有下划线或横线,否则不能解析 +type Config struct { + Http *Http `yaml:"http"` + + Grpc *Grpc `yaml:"grpc"` + + Debug *Debug `yaml:"debug"` + + Database *Database `yaml:"database"` + + Redis *Redis `yaml:"redis"` + + JWKS *JWKS `yaml:"jwks"` + + Metrics *Metrics `yaml:"metrics"` + + S3 *S3 `yaml:"s3"` + + Kafka *Kafka `yaml:"kafka"` + + OpenAI *OpenAI `yaml:"openai"` + + Milvus *Milvus `yaml:"milvus"` + + //ThirdParty *ThirdParty `yaml:"third_party" mapstructure:"third_party"` +} + +type ThirdParty struct { + OpenAIApiKey string `yaml:"openai_api_key" mapstructure:"openai_api_key"` +} + +type Http struct { + Host string `yaml:"host"` + Port int `yaml:"port"` + Url string `yaml:"url"` +} + +type Debug struct { + Enabled bool `yaml:"enabled"` +} + +type Database struct { + Host string `yaml:"host"` + Port int `yaml:"port"` + User string `yaml:"user"` + Password string `yaml:"password"` + Name string `yaml:"name"` +} + +type Redis struct { + Host string `yaml:"host"` + Port int `yaml:"port"` + Password string `yaml:"password"` + DB int `yaml:"db"` +} + +type JWKS struct { + Url string `yaml:"url"` +} + +type Metrics struct { + Enabled bool `yaml:"enabled"` + Port int `yaml:"port"` + Host string `yaml:"host"` +} + +type S3 struct { + Endpoint string `yaml:"endpoint" mapstructure:"endpoint"` + ExternalEndpoint string `yaml:"external_endpoint" mapstructure:"external_endpoint"` + AccessKey string `yaml:"access_key" mapstructure:"access_key"` + SecretKey string `yaml:"secret_key" mapstructure:"secret_key"` + Bucket string `yaml:"bucket" mapstructure:"bucket"` + UseSSL bool `yaml:"use_ssl" mapstructure:"use_ssl"` + Region string `yaml:"region" mapstructure:"region"` +} + +type Kafka struct { + BootstrapServers []string `yaml:"bootstrap_servers" mapstructure:"bootstrap_servers"` + Topic string `yaml:"topic" mapstructure:"topic"` + GroupId string `yaml:"group_id" mapstructure:"group_id"` + Username string `yaml:"username" mapstructure:"username"` + Password string `yaml:"password" mapstructure:"password"` +} + +type Grpc struct { + Address string `yaml:"address" mapstructure:"address"` +} + +type OpenAI struct { + ApiKey string `yaml:"api_key" mapstructure:"api_key"` + InternalBaseUrl string `yaml:"internal_base_url" mapstructure:"internal_base_url"` + LongContextModel string `yaml:"long_context_model" mapstructure:"long_context_model"` + EmbeddingModel string `yaml:"embedding_model" mapstructure:"embedding_model"` + EmbeddingDim int `yaml:"embedding_dim" mapstructure:"embedding_dim"` + EmbeddingMaxToken int `yaml:"embedding_max_token" mapstructure:"embedding_max_token"` +} + +type Milvus struct { + Host string `yaml:"host" mapstructure:"host"` + Port int `yaml:"port" mapstructure:"port"` + DBName string `yaml:"db_name" mapstructure:"db_name"` + DocumentCollection string `yaml:"document_collection" mapstructure:"document_collection"` + User string `yaml:"user" mapstructure:"user"` + Password string `yaml:"password" mapstructure:"password"` +} diff --git a/internal/base/conf/helper.go b/internal/base/conf/helper.go new file mode 100644 index 0000000..877d87e --- /dev/null +++ b/internal/base/conf/helper.go @@ -0,0 +1,35 @@ +package conf + +import ( + "go-template/configs" + "os" +) + +func createConfigIfNotExists(path string) { + if path != "" { + return + } + + // create if not exists + var configName = "config.yaml" + + if _, err := os.Stat(configName); os.IsNotExist(err) { + f, err := os.Create(configName) + if err != nil { + panic(err) + } + + // write default from embed + _, err = f.Write(configs.Config) + if err != nil { + panic(err) + } + + defer func(f *os.File) { + err := f.Close() + if err != nil { + panic(err) + } + }(f) + } +} diff --git a/internal/base/conf/provider.go b/internal/base/conf/provider.go new file mode 100644 index 0000000..2874511 --- /dev/null +++ b/internal/base/conf/provider.go @@ -0,0 +1,71 @@ +package conf + +import ( + "go-template/internal/base/logger" + "os" + "path/filepath" + "strings" + + "github.com/spf13/viper" +) + +// depth 是配置文件的搜索深度 +var depth = 8 + +func getConfigPath() string { + var path string + if os.Getenv("AMBER_CONFIG") != "" { + path = os.Getenv("AMBER_CONFIG") + return path + } + var pathOptions []string + for i := 0; i <= depth; i++ { + pathOptions = append(pathOptions, strings.Repeat("../", i)+"config.yaml") + } + for _, p := range pathOptions { + if _, err := os.Stat(p); err == nil { + path = p + break + } + } + + if path != "" { + // 假设 workDir 是当前工作目录的路径 + workDir, err := os.Getwd() + if err != nil { + panic(err) + } + + // 将相对路径转换为绝对路径 + path, err = filepath.Abs(filepath.Join(workDir, path)) + if err != nil { + panic(err) + } + } + + return path +} + +func ProviderConfig(logger *logger.Logger) *Config { + var path = getConfigPath() + createConfigIfNotExists(path) + + if path == "" { + logger.Sugar.Fatal("config file not found, created.") + } else { + logger.Sugar.Infof("config file found at %s", path) + } + + c := &Config{} + v := viper.New() + v.SetConfigType("yaml") + v.SetConfigFile(path) + if err := v.ReadInConfig(); err != nil { + logger.Sugar.Fatal(err) + } + if err := v.Unmarshal(c); err != nil { + panic(err) + } + + return c +} diff --git a/internal/base/logger/provider.go b/internal/base/logger/provider.go new file mode 100644 index 0000000..06842ba --- /dev/null +++ b/internal/base/logger/provider.go @@ -0,0 +1,25 @@ +package logger + +import "go.uber.org/zap" + +type Logger struct { + Sugar *zap.SugaredLogger + Logger *zap.Logger +} + +func NewZapLogger() *Logger { + logger, err := zap.NewProduction() + if err != nil { + panic(err) + return nil + } + + //defer func(logger *zap.Logger) { + // err := logger.Sync() + // if err != nil { + // panic(err) + // } + //}(logger) + + return &Logger{Sugar: logger.Sugar(), Logger: logger} +} diff --git a/internal/base/milvus/provide.go b/internal/base/milvus/provide.go new file mode 100644 index 0000000..2d62e96 --- /dev/null +++ b/internal/base/milvus/provide.go @@ -0,0 +1,29 @@ +package milvus + +import ( + "context" + "github.com/milvus-io/milvus-sdk-go/v2/client" + "leafdev.top/Leaf/leaf-library/internal/base/conf" + "leafdev.top/Leaf/leaf-library/internal/base/logger" + + "strconv" +) + +func NewMilvus(config *conf.Config, logger *logger.Logger) client.Client { + var address = config.Milvus.Host + ":" + strconv.Itoa(config.Milvus.Port) + + logger.Sugar.Infof("Waiting for milvus, address=%s, dbname=%s", address, config.Milvus.DBName) + + c, err := client.NewClient(context.Background(), client.Config{ + Address: address, + DBName: config.Milvus.DBName, + }) + + logger.Sugar.Infof("Connected to milvus, address=%s, dbname=%s", address, config.Milvus.DBName) + + if err != nil { + panic(err) + } + + return c +} diff --git a/internal/base/orm/User.go b/internal/base/orm/User.go new file mode 100644 index 0000000..40d72c0 --- /dev/null +++ b/internal/base/orm/User.go @@ -0,0 +1,6 @@ +package orm + +type User struct { + Username string + Password string +} diff --git a/internal/base/orm/provide.go b/internal/base/orm/provide.go new file mode 100644 index 0000000..eadf965 --- /dev/null +++ b/internal/base/orm/provide.go @@ -0,0 +1,46 @@ +package orm + +import ( + "fmt" + "go-template/internal/base/conf" + "go-template/internal/base/logger" + + "gorm.io/driver/mysql" + "gorm.io/gorm" + "moul.io/zapgorm2" +) + +// +//func ProviderOrm() *Orm { +// return NewOrm() +//} + +func NewGORM( + config *conf.Config, + logger *logger.Logger, +) *gorm.DB { + dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", config.Database.User, config.Database.Password, config.Database.Host, config.Database.Port, config.Database.Name) + + if config.Debug.Enabled { + db, err := gorm.Open(mysql.Open(dsn)) + + if err != nil { + panic(err) + } + + return db + } + + zapGormLogger := zapgorm2.New(logger.Logger) + zapGormLogger.SetAsDefault() + db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ + Logger: zapGormLogger, + }) + + if err != nil { + panic(err) + } + + return db + +} diff --git a/internal/base/redis/provide.go b/internal/base/redis/provide.go new file mode 100644 index 0000000..516f5bd --- /dev/null +++ b/internal/base/redis/provide.go @@ -0,0 +1,38 @@ +package redis + +import ( + "context" + "fmt" + "go-template/internal/base/conf" + + "github.com/bsm/redislock" + "github.com/redis/go-redis/v9" +) + +type Redis struct { + Client *redis.Client + Locker *redislock.Client +} + +func NewRedis(c *conf.Config) *Redis { + var client = redis.NewClient(&redis.Options{ + Addr: fmt.Sprintf("%s:%d", c.Redis.Host, c.Redis.Port), + Password: c.Redis.Password, + DB: c.Redis.DB, + }) + + status := client.Ping(context.Background()) + if status.Err() != nil { + panic(status.Err()) + } + + // Create a new lock client. + locker := redislock.New(client) + + var r = &Redis{ + Client: client, + Locker: locker, + } + + return r +} diff --git a/internal/base/s3/provide.go b/internal/base/s3/provide.go new file mode 100644 index 0000000..91a5a8b --- /dev/null +++ b/internal/base/s3/provide.go @@ -0,0 +1,29 @@ +package s3 + +import ( + "go-template/internal/base/conf" + + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" +) + +type S3 struct { + Client *minio.Client + Bucket string +} + +func NewS3(config *conf.Config) *S3 { + minioClient, err := minio.New(config.S3.Endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(config.S3.AccessKey, config.S3.SecretKey, ""), + Secure: config.S3.UseSSL, + }) + + if err != nil { + panic(err) + } + + return &S3{ + Client: minioClient, + Bucket: config.S3.Bucket, + } +} diff --git a/internal/base/server/gin.go b/internal/base/server/gin.go new file mode 100644 index 0000000..97e55de --- /dev/null +++ b/internal/base/server/gin.go @@ -0,0 +1,105 @@ +package server + +import ( + "go-template/internal/base/conf" + httpHandler "go-template/internal/handler/http" + "go-template/internal/router" + "go-template/internal/schema" + "go-template/pkg/consts" + "net/http" + "time" + + "github.com/gin-contrib/cors" + "github.com/gin-gonic/gin" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +func promHandler(handler http.Handler) gin.HandlerFunc { + return func(c *gin.Context) { + handler.ServeHTTP(c.Writer, c.Request) + } +} + +type HttpServer struct { + Gin *gin.Engine + apiRouter *router.Api + swaggerRouter *router.SwaggerRouter + middleware *httpHandler.Middleware +} + +// NewHTTPServer new http server. +func NewHTTPServer( + config *conf.Config, + apiRouter *router.Api, + swaggerRouter *router.SwaggerRouter, + middleware *httpHandler.Middleware, +) *HttpServer { + if config.Debug.Enabled { + gin.SetMode(gin.DebugMode) + } else { + gin.SetMode(gin.ReleaseMode) + } + + r := gin.New() + r.Use(gin.Recovery()) + r.Use(middleware.GinLogger.GinLogger) + + return &HttpServer{ + Gin: r, + apiRouter: apiRouter, + swaggerRouter: swaggerRouter, + middleware: middleware, + } +} + +func (hs *HttpServer) AllowAllCors() { + var config = cors.DefaultConfig() + config.AllowAllOrigins = true + config.AllowCredentials = true + config.AllowMethods = []string{"GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS"} + config.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type", "Cookie", "X-Requested-With", "X-Auth-Token", "Authorization"} + config.MaxAge = 12 * time.Hour + + hs.Gin.Use(cors.New(config)) +} + +func (hs *HttpServer) BizRouter() *gin.Engine { + hs.AllowAllCors() + + rootGroup := hs.Gin.Group("") + + // swagger + hs.swaggerRouter.Register(rootGroup) + + apiV1 := rootGroup.Group("/api/v1") + { + //apiV1.Use(corsMiddleWare) + apiV1.Use(hs.middleware.JSONResponse.ContentTypeJSON) + apiV1.Use(hs.middleware.Auth.RequireJWTIDToken) + hs.apiRouter.InitApiRouter(apiV1) + } + + apiV1NoAuth := rootGroup.Group("/api/v1") + { + //apiV1.Use(corsMiddleWare) + hs.apiRouter.InitNoAuthApiRouter(apiV1NoAuth) + } + + hs.Gin.NoRoute(func(ctx *gin.Context) { + schema.NewResponse(ctx).Status(http.StatusNotFound).Error(consts.ErrPageNotFound).Send() + }) + + return hs.Gin +} + +func (hs *HttpServer) MetricRouter() *gin.Engine { + r := gin.New() + r.Use(gin.Recovery()) + + metricGroup := r.Group("") + // prometheus + metricGroup.GET("/metrics", promHandler(promhttp.Handler())) + metricGroup.GET("/healthz", func(ctx *gin.Context) { ctx.String(200, "OK") }) + + return r +} diff --git a/internal/batch/provider.go b/internal/batch/provider.go new file mode 100644 index 0000000..4525cb5 --- /dev/null +++ b/internal/batch/provider.go @@ -0,0 +1,16 @@ +package batch + +import ( + "go-template/internal/base/logger" +) + +type Batch struct { + logger *logger.Logger +} + +func NewBatch( + logger *logger.Logger, +) *Batch { + //base.NewApplication() + return &Batch{logger} +} diff --git a/internal/dao/gen.go b/internal/dao/gen.go new file mode 100644 index 0000000..13df407 --- /dev/null +++ b/internal/dao/gen.go @@ -0,0 +1,103 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package dao + +import ( + "context" + "database/sql" + + "gorm.io/gorm" + + "gorm.io/gen" + + "gorm.io/plugin/dbresolver" +) + +var ( + Q = new(Query) + User *user +) + +func SetDefault(db *gorm.DB, opts ...gen.DOOption) { + *Q = *Use(db, opts...) + User = &Q.User +} + +func Use(db *gorm.DB, opts ...gen.DOOption) *Query { + return &Query{ + db: db, + User: newUser(db, opts...), + } +} + +type Query struct { + db *gorm.DB + + User user +} + +func (q *Query) Available() bool { return q.db != nil } + +func (q *Query) clone(db *gorm.DB) *Query { + return &Query{ + db: db, + User: q.User.clone(db), + } +} + +func (q *Query) ReadDB() *Query { + return q.ReplaceDB(q.db.Clauses(dbresolver.Read)) +} + +func (q *Query) WriteDB() *Query { + return q.ReplaceDB(q.db.Clauses(dbresolver.Write)) +} + +func (q *Query) ReplaceDB(db *gorm.DB) *Query { + return &Query{ + db: db, + User: q.User.replaceDB(db), + } +} + +type queryCtx struct { + User IUserDo +} + +func (q *Query) WithContext(ctx context.Context) *queryCtx { + return &queryCtx{ + User: q.User.WithContext(ctx), + } +} + +func (q *Query) Transaction(fc func(tx *Query) error, opts ...*sql.TxOptions) error { + return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.clone(tx)) }, opts...) +} + +func (q *Query) Begin(opts ...*sql.TxOptions) *QueryTx { + tx := q.db.Begin(opts...) + return &QueryTx{Query: q.clone(tx), Error: tx.Error} +} + +type QueryTx struct { + *Query + Error error +} + +func (q *QueryTx) Commit() error { + return q.db.Commit().Error +} + +func (q *QueryTx) Rollback() error { + return q.db.Rollback().Error +} + +func (q *QueryTx) SavePoint(name string) error { + return q.db.SavePoint(name).Error +} + +func (q *QueryTx) RollbackTo(name string) error { + return q.db.RollbackTo(name).Error +} diff --git a/internal/dao/provider.go b/internal/dao/provider.go new file mode 100644 index 0000000..0fadf4b --- /dev/null +++ b/internal/dao/provider.go @@ -0,0 +1,7 @@ +package dao + +import "gorm.io/gorm" + +func NewQuery(db *gorm.DB) *Query { + return Use(db) +} diff --git a/internal/dao/users.gen.go b/internal/dao/users.gen.go new file mode 100644 index 0000000..342b46c --- /dev/null +++ b/internal/dao/users.gen.go @@ -0,0 +1,392 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package dao + +import ( + "context" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" + + "gorm.io/gen" + "gorm.io/gen/field" + + "gorm.io/plugin/dbresolver" + + "go-template/internal/entity" +) + +func newUser(db *gorm.DB, opts ...gen.DOOption) user { + _user := user{} + + _user.userDo.UseDB(db, opts...) + _user.userDo.UseModel(&entity.User{}) + + tableName := _user.userDo.TableName() + _user.ALL = field.NewAsterisk(tableName) + _user.Id = field.NewUint(tableName, "id") + _user.CreatedAt = field.NewTime(tableName, "created_at") + _user.UpdatedAt = field.NewTime(tableName, "updated_at") + _user.Name = field.NewString(tableName, "name") + + _user.fillFieldMap() + + return _user +} + +type user struct { + userDo + + ALL field.Asterisk + Id field.Uint + CreatedAt field.Time + UpdatedAt field.Time + Name field.String + + fieldMap map[string]field.Expr +} + +func (u user) Table(newTableName string) *user { + u.userDo.UseTable(newTableName) + return u.updateTableName(newTableName) +} + +func (u user) As(alias string) *user { + u.userDo.DO = *(u.userDo.As(alias).(*gen.DO)) + return u.updateTableName(alias) +} + +func (u *user) updateTableName(table string) *user { + u.ALL = field.NewAsterisk(table) + u.Id = field.NewUint(table, "id") + u.CreatedAt = field.NewTime(table, "created_at") + u.UpdatedAt = field.NewTime(table, "updated_at") + u.Name = field.NewString(table, "name") + + u.fillFieldMap() + + return u +} + +func (u *user) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := u.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (u *user) fillFieldMap() { + u.fieldMap = make(map[string]field.Expr, 4) + u.fieldMap["id"] = u.Id + u.fieldMap["created_at"] = u.CreatedAt + u.fieldMap["updated_at"] = u.UpdatedAt + u.fieldMap["name"] = u.Name +} + +func (u user) clone(db *gorm.DB) user { + u.userDo.ReplaceConnPool(db.Statement.ConnPool) + return u +} + +func (u user) replaceDB(db *gorm.DB) user { + u.userDo.ReplaceDB(db) + return u +} + +type userDo struct{ gen.DO } + +type IUserDo interface { + gen.SubQuery + Debug() IUserDo + WithContext(ctx context.Context) IUserDo + WithResult(fc func(tx gen.Dao)) gen.ResultInfo + ReplaceDB(db *gorm.DB) + ReadDB() IUserDo + WriteDB() IUserDo + As(alias string) gen.Dao + Session(config *gorm.Session) IUserDo + Columns(cols ...field.Expr) gen.Columns + Clauses(conds ...clause.Expression) IUserDo + Not(conds ...gen.Condition) IUserDo + Or(conds ...gen.Condition) IUserDo + Select(conds ...field.Expr) IUserDo + Where(conds ...gen.Condition) IUserDo + Order(conds ...field.Expr) IUserDo + Distinct(cols ...field.Expr) IUserDo + Omit(cols ...field.Expr) IUserDo + Join(table schema.Tabler, on ...field.Expr) IUserDo + LeftJoin(table schema.Tabler, on ...field.Expr) IUserDo + RightJoin(table schema.Tabler, on ...field.Expr) IUserDo + Group(cols ...field.Expr) IUserDo + Having(conds ...gen.Condition) IUserDo + Limit(limit int) IUserDo + Offset(offset int) IUserDo + Count() (count int64, err error) + Scopes(funcs ...func(gen.Dao) gen.Dao) IUserDo + Unscoped() IUserDo + Create(values ...*entity.User) error + CreateInBatches(values []*entity.User, batchSize int) error + Save(values ...*entity.User) error + First() (*entity.User, error) + Take() (*entity.User, error) + Last() (*entity.User, error) + Find() ([]*entity.User, error) + FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*entity.User, err error) + FindInBatches(result *[]*entity.User, batchSize int, fc func(tx gen.Dao, batch int) error) error + Pluck(column field.Expr, dest interface{}) error + Delete(...*entity.User) (info gen.ResultInfo, err error) + Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error) + UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error) + Updates(value interface{}) (info gen.ResultInfo, err error) + UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error) + UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error) + UpdateColumns(value interface{}) (info gen.ResultInfo, err error) + UpdateFrom(q gen.SubQuery) gen.Dao + Attrs(attrs ...field.AssignExpr) IUserDo + Assign(attrs ...field.AssignExpr) IUserDo + Joins(fields ...field.RelationField) IUserDo + Preload(fields ...field.RelationField) IUserDo + FirstOrInit() (*entity.User, error) + FirstOrCreate() (*entity.User, error) + FindByPage(offset int, limit int) (result []*entity.User, count int64, err error) + ScanByPage(result interface{}, offset int, limit int) (count int64, err error) + Scan(result interface{}) (err error) + Returning(value interface{}, columns ...string) IUserDo + UnderlyingDB() *gorm.DB + schema.Tabler +} + +func (u userDo) Debug() IUserDo { + return u.withDO(u.DO.Debug()) +} + +func (u userDo) WithContext(ctx context.Context) IUserDo { + return u.withDO(u.DO.WithContext(ctx)) +} + +func (u userDo) ReadDB() IUserDo { + return u.Clauses(dbresolver.Read) +} + +func (u userDo) WriteDB() IUserDo { + return u.Clauses(dbresolver.Write) +} + +func (u userDo) Session(config *gorm.Session) IUserDo { + return u.withDO(u.DO.Session(config)) +} + +func (u userDo) Clauses(conds ...clause.Expression) IUserDo { + return u.withDO(u.DO.Clauses(conds...)) +} + +func (u userDo) Returning(value interface{}, columns ...string) IUserDo { + return u.withDO(u.DO.Returning(value, columns...)) +} + +func (u userDo) Not(conds ...gen.Condition) IUserDo { + return u.withDO(u.DO.Not(conds...)) +} + +func (u userDo) Or(conds ...gen.Condition) IUserDo { + return u.withDO(u.DO.Or(conds...)) +} + +func (u userDo) Select(conds ...field.Expr) IUserDo { + return u.withDO(u.DO.Select(conds...)) +} + +func (u userDo) Where(conds ...gen.Condition) IUserDo { + return u.withDO(u.DO.Where(conds...)) +} + +func (u userDo) Order(conds ...field.Expr) IUserDo { + return u.withDO(u.DO.Order(conds...)) +} + +func (u userDo) Distinct(cols ...field.Expr) IUserDo { + return u.withDO(u.DO.Distinct(cols...)) +} + +func (u userDo) Omit(cols ...field.Expr) IUserDo { + return u.withDO(u.DO.Omit(cols...)) +} + +func (u userDo) Join(table schema.Tabler, on ...field.Expr) IUserDo { + return u.withDO(u.DO.Join(table, on...)) +} + +func (u userDo) LeftJoin(table schema.Tabler, on ...field.Expr) IUserDo { + return u.withDO(u.DO.LeftJoin(table, on...)) +} + +func (u userDo) RightJoin(table schema.Tabler, on ...field.Expr) IUserDo { + return u.withDO(u.DO.RightJoin(table, on...)) +} + +func (u userDo) Group(cols ...field.Expr) IUserDo { + return u.withDO(u.DO.Group(cols...)) +} + +func (u userDo) Having(conds ...gen.Condition) IUserDo { + return u.withDO(u.DO.Having(conds...)) +} + +func (u userDo) Limit(limit int) IUserDo { + return u.withDO(u.DO.Limit(limit)) +} + +func (u userDo) Offset(offset int) IUserDo { + return u.withDO(u.DO.Offset(offset)) +} + +func (u userDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IUserDo { + return u.withDO(u.DO.Scopes(funcs...)) +} + +func (u userDo) Unscoped() IUserDo { + return u.withDO(u.DO.Unscoped()) +} + +func (u userDo) Create(values ...*entity.User) error { + if len(values) == 0 { + return nil + } + return u.DO.Create(values) +} + +func (u userDo) CreateInBatches(values []*entity.User, batchSize int) error { + return u.DO.CreateInBatches(values, batchSize) +} + +// Save : !!! underlying implementation is different with GORM +// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values) +func (u userDo) Save(values ...*entity.User) error { + if len(values) == 0 { + return nil + } + return u.DO.Save(values) +} + +func (u userDo) First() (*entity.User, error) { + if result, err := u.DO.First(); err != nil { + return nil, err + } else { + return result.(*entity.User), nil + } +} + +func (u userDo) Take() (*entity.User, error) { + if result, err := u.DO.Take(); err != nil { + return nil, err + } else { + return result.(*entity.User), nil + } +} + +func (u userDo) Last() (*entity.User, error) { + if result, err := u.DO.Last(); err != nil { + return nil, err + } else { + return result.(*entity.User), nil + } +} + +func (u userDo) Find() ([]*entity.User, error) { + result, err := u.DO.Find() + return result.([]*entity.User), err +} + +func (u userDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*entity.User, err error) { + buf := make([]*entity.User, 0, batchSize) + err = u.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error { + defer func() { results = append(results, buf...) }() + return fc(tx, batch) + }) + return results, err +} + +func (u userDo) FindInBatches(result *[]*entity.User, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return u.DO.FindInBatches(result, batchSize, fc) +} + +func (u userDo) Attrs(attrs ...field.AssignExpr) IUserDo { + return u.withDO(u.DO.Attrs(attrs...)) +} + +func (u userDo) Assign(attrs ...field.AssignExpr) IUserDo { + return u.withDO(u.DO.Assign(attrs...)) +} + +func (u userDo) Joins(fields ...field.RelationField) IUserDo { + for _, _f := range fields { + u = *u.withDO(u.DO.Joins(_f)) + } + return &u +} + +func (u userDo) Preload(fields ...field.RelationField) IUserDo { + for _, _f := range fields { + u = *u.withDO(u.DO.Preload(_f)) + } + return &u +} + +func (u userDo) FirstOrInit() (*entity.User, error) { + if result, err := u.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*entity.User), nil + } +} + +func (u userDo) FirstOrCreate() (*entity.User, error) { + if result, err := u.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*entity.User), nil + } +} + +func (u userDo) FindByPage(offset int, limit int) (result []*entity.User, count int64, err error) { + result, err = u.Offset(offset).Limit(limit).Find() + if err != nil { + return + } + + if size := len(result); 0 < limit && 0 < size && size < limit { + count = int64(size + offset) + return + } + + count, err = u.Offset(-1).Limit(-1).Count() + return +} + +func (u userDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = u.Count() + if err != nil { + return + } + + err = u.Offset(offset).Limit(limit).Scan(result) + return +} + +func (u userDo) Scan(result interface{}) (err error) { + return u.DO.Scan(result) +} + +func (u userDo) Delete(models ...*entity.User) (result gen.ResultInfo, err error) { + return u.DO.Delete(models) +} + +func (u *userDo) withDO(do gen.Dao) *userDo { + u.DO = *do.(*gen.DO) + return u +} diff --git a/internal/entity/Model.go b/internal/entity/Model.go new file mode 100644 index 0000000..aa89f82 --- /dev/null +++ b/internal/entity/Model.go @@ -0,0 +1,14 @@ +package entity + +import ( + "go-template/internal/schema" + "time" +) + +// Model 是所有 entity 的基类,后期要将所有的 Base 改成这种形式 +type Model struct { + Id schema.EntityId `gorm:"primarykey" json:"id"` + CreatedAt time.Time `gorm:"autoUpdateTime:milli" json:"created_at"` + UpdatedAt time.Time `gorm:"autoUpdateTime:milli" json:"updated_at"` + //DeletedAt gorm.DeletedAt `gorm:"index"` +} diff --git a/internal/entity/User.go b/internal/entity/User.go new file mode 100644 index 0000000..c7c65ae --- /dev/null +++ b/internal/entity/User.go @@ -0,0 +1,10 @@ +package entity + +type User struct { + Model + Name string `json:"name"` +} + +func (u *User) TableName() string { + return "user" +} diff --git a/internal/handler/grpc/documents/document.go b/internal/handler/grpc/documents/document.go new file mode 100644 index 0000000..ddf32c7 --- /dev/null +++ b/internal/handler/grpc/documents/document.go @@ -0,0 +1,11 @@ +package documents + +import ( + "context" + "go-template/pkg/protos/documentService" +) + +func (d *DocumentService) ListDocuments(ctx context.Context, req *documentService.ListDocumentsRequest) (*documentService.ListDocumentsResponse, error) { + + return &documentService.ListDocumentsResponse{}, nil +} diff --git a/internal/handler/grpc/documents/provider.go b/internal/handler/grpc/documents/provider.go new file mode 100644 index 0000000..3b020b6 --- /dev/null +++ b/internal/handler/grpc/documents/provider.go @@ -0,0 +1,18 @@ +package documents + +import ( + "go-template/internal/dao" + "go-template/pkg/protos/documentService" +) + +type DocumentService struct { + documentService.UnimplementedDocumentServiceServer + dao *dao.Query +} + +func NewDocumentService(dao *dao.Query) *DocumentService { + return &DocumentService{ + documentService.UnimplementedDocumentServiceServer{}, + dao, + } +} diff --git a/internal/handler/grpc/interceptor/auth.go b/internal/handler/grpc/interceptor/auth.go new file mode 100644 index 0000000..bb3d407 --- /dev/null +++ b/internal/handler/grpc/interceptor/auth.go @@ -0,0 +1,40 @@ +package interceptor + +import ( + "context" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" + "go-template/internal/schema" + auth2 "go-template/internal/service/auth" + "go-template/pkg/consts" +) + +type Auth struct { + authService *auth2.Service +} + +func NewAuth(authService *auth2.Service) *Auth { + return &Auth{ + authService: authService, + } +} + +func (a *Auth) JwtAuth(ctx context.Context) (context.Context, error) { + tokenString, err := auth.AuthFromMD(ctx, "bearer") + if err != nil { + return nil, err + } + + token, err := a.authService.AuthFromToken(schema.JWTIDToken, tokenString) + if err != nil { + return nil, err + } + + if !token.Valid { + return nil, consts.ErrNotValidToken + } + + ctx = logging.InjectFields(ctx, logging.Fields{"auth.sub", token.Token.Sub}) + + return a.authService.SetUser(ctx, token), nil +} diff --git a/internal/handler/grpc/interceptor/zap_logger.go b/internal/handler/grpc/interceptor/zap_logger.go new file mode 100644 index 0000000..93add19 --- /dev/null +++ b/internal/handler/grpc/interceptor/zap_logger.go @@ -0,0 +1,56 @@ +package interceptor + +import ( + "context" + "fmt" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" + "go-template/internal/base/logger" + "go.uber.org/zap" +) + +type Logger struct { + Logger *logger.Logger +} + +func NewLogger(logger *logger.Logger) *Logger { + return &Logger{ + Logger: logger, + } +} + +func (l *Logger) ZapLogInterceptor() logging.Logger { + return logging.LoggerFunc(func(ctx context.Context, lvl logging.Level, msg string, fields ...any) { + f := make([]zap.Field, 0, len(fields)/2) + + for i := 0; i < len(fields); i += 2 { + key := fields[i] + value := fields[i+1] + + switch v := value.(type) { + case string: + f = append(f, zap.String(key.(string), v)) + case int: + f = append(f, zap.Int(key.(string), v)) + case bool: + f = append(f, zap.Bool(key.(string), v)) + default: + f = append(f, zap.Any(key.(string), v)) + } + } + + logger2 := l.Logger.Logger.WithOptions(zap.AddCallerSkip(1)).With(f...) + + switch lvl { + case logging.LevelDebug: + logger2.Debug(msg) + case logging.LevelInfo: + logger2.Info(msg) + case logging.LevelWarn: + logger2.Warn(msg) + case logging.LevelError: + logger2.Error(msg) + default: + panic(fmt.Sprintf("unknown level %v", lvl)) + } + }) +} diff --git a/internal/handler/grpc/provider.go b/internal/handler/grpc/provider.go new file mode 100644 index 0000000..aa4f2be --- /dev/null +++ b/internal/handler/grpc/provider.go @@ -0,0 +1,47 @@ +package grpc + +import ( + "github.com/google/wire" + "leafdev.top/Leaf/leaf-library/internal/handler/grpc/documents" + "leafdev.top/Leaf/leaf-library/internal/handler/grpc/interceptor" +) + +var ProviderSet = wire.NewSet( + interceptor.NewAuth, + interceptor.NewLogger, + documents.NewDocumentService, + + NewInterceptor, + NewHandler, +) + +func NewHandler( + documentService *documents.DocumentService, + interceptor2 *Interceptor, +) *Handlers { + return &Handlers{ + DocumentService: documentService, + Interceptor: interceptor2, + } +} + +type Handlers struct { + DocumentService *documents.DocumentService + Interceptor *Interceptor +} + +type Interceptor struct { + Auth *interceptor.Auth + Logger *interceptor.Logger +} + +func NewInterceptor( + Auth *interceptor.Auth, + Logger *interceptor.Logger, +) *Interceptor { + return &Interceptor{ + Auth: Auth, + Logger: Logger, + } + +} diff --git a/internal/handler/handler.go b/internal/handler/handler.go new file mode 100644 index 0000000..a87250a --- /dev/null +++ b/internal/handler/handler.go @@ -0,0 +1,28 @@ +package handler + +import ( + "github.com/google/wire" + "go-template/internal/handler/grpc" + "go-template/internal/handler/http" +) + +var ProviderSet = wire.NewSet( + grpc.ProviderSet, + http.ProviderSet, + NewHandler, +) + +type Handler struct { + GRPC *grpc.Handlers + HTTP *http.Handlers +} + +func NewHandler( + grpcHandlers *grpc.Handlers, + httpHandlers *http.Handlers, +) *Handler { + return &Handler{ + GRPC: grpcHandlers, + HTTP: httpHandlers, + } +} diff --git a/internal/handler/http/controller/v1/test.go b/internal/handler/http/controller/v1/test.go new file mode 100644 index 0000000..69645d3 --- /dev/null +++ b/internal/handler/http/controller/v1/test.go @@ -0,0 +1,42 @@ +package v1 + +import ( + "go-template/internal/schema" + "go-template/internal/service/auth" + "net/http" + + "github.com/gin-gonic/gin" +) + +type UserController struct { + authService *auth.Service +} + +func NewUserController(authService *auth.Service) *UserController { + return &UserController{authService} +} + +// Test godoc +// @Summary Greet +// @Description 测试接口,将会返回当前用户的信息 +// @Tags ping +// @Accept json +// @Produce json +// @Security ApiKeyAuth +// @deprecated true +// @Success 200 {object} schema.ResponseBody{data=schema.CurrentUserResponse} +// @Failure 400 {object} schema.ResponseBody +// @Router /api/v1/ping [get] +func (u *UserController) Test(c *gin.Context) { + user := u.authService.GetUser(c) + + var currentUserResponse = &schema.CurrentUserResponse{ + IP: c.ClientIP(), + Valid: user.Valid, + UserEmail: user.Token.Email, + UserId: user.Token.Sub, + UserName: user.Token.Name, + } + + schema.NewResponse(c).Status(http.StatusOK).Data(currentUserResponse).Send() +} diff --git a/internal/handler/http/middleware/auth.go b/internal/handler/http/middleware/auth.go new file mode 100644 index 0000000..aae24b8 --- /dev/null +++ b/internal/handler/http/middleware/auth.go @@ -0,0 +1,45 @@ +package middleware + +import ( + "go-template/internal/schema" + "go-template/internal/service/auth" + "go-template/pkg/consts" + "net/http" + + "github.com/gin-gonic/gin" +) + +type AuthMiddleware struct { + authService *auth.Service +} + +func NewAuthMiddleware(authService *auth.Service) *AuthMiddleware { + return &AuthMiddleware{ + authService, + } +} + +func (a AuthMiddleware) RequireJWTIDToken(c *gin.Context) { + user, err := a.authService.GinMiddlewareAuth(schema.JWTIDToken, c) + + if err != nil { + c.Abort() + schema.NewResponse(c).Error(err).Status(http.StatusUnauthorized).Send() + return + } + + c.Set(consts.AuthMiddlewareKey, user) + c.Next() +} + +func (a AuthMiddleware) RequireJWTAccessToken(c *gin.Context) { + user, err := a.authService.GinMiddlewareAuth(schema.JWTAccessToken, c) + if err != nil { + c.Abort() + schema.NewResponse(c).Error(err).Status(http.StatusUnauthorized).Send() + return + } + + c.Set(consts.AuthMiddlewareKey, user) + c.Next() +} diff --git a/internal/handler/http/middleware/json_response.go b/internal/handler/http/middleware/json_response.go new file mode 100644 index 0000000..3c4d3d1 --- /dev/null +++ b/internal/handler/http/middleware/json_response.go @@ -0,0 +1,17 @@ +package middleware + +import ( + "github.com/gin-gonic/gin" +) + +type JSONResponseMiddleware struct { +} + +func (*JSONResponseMiddleware) ContentTypeJSON(c *gin.Context) { + c.Header("Content-Type", "application/json; charset=utf-8") + c.Next() +} + +func NewJSONResponseMiddleware() *JSONResponseMiddleware { + return &JSONResponseMiddleware{} +} diff --git a/internal/handler/http/middleware/zap_logger.go b/internal/handler/http/middleware/zap_logger.go new file mode 100644 index 0000000..8deedd3 --- /dev/null +++ b/internal/handler/http/middleware/zap_logger.go @@ -0,0 +1,86 @@ +package middleware + +import ( + "github.com/gin-gonic/gin" + "go-template/internal/base/logger" + "go.uber.org/zap" + "time" +) + +type GinLoggerMiddleware struct { + logger *logger.Logger +} + +func NewGinLoggerMiddleware(logger *logger.Logger) *GinLoggerMiddleware { + return &GinLoggerMiddleware{ + logger: logger, + } +} + +// GinLogger 接收gin框架默认的日志 +func (l *GinLoggerMiddleware) GinLogger(c *gin.Context) { + start := time.Now() + path := c.Request.URL.Path + query := c.Request.URL.RawQuery + c.Next() + + cost := time.Since(start) + l.logger.Logger.Info(path, + zap.Int("status", c.Writer.Status()), + zap.String("method", c.Request.Method), + zap.String("path", path), + zap.String("query", query), + zap.String("ip", c.ClientIP()), + zap.String("user-agent", c.Request.UserAgent()), + zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()), + zap.Duration("cost", cost), + ) +} + +// +//// GinRecovery recover掉项目可能出现的panic +//func GinRecovery(logger *zap.Logger, stack bool) gin.HandlerFunc { +// return func(c *gin.Context) { +// defer func() { +// if err := recover(); err != nil { +// // Check for a broken connection, as it is not really a +// // condition that warrants a panic stack trace. +// var brokenPipe bool +// if ne, ok := err.(*net.OpError); ok { +// if se, ok := ne.Err.(*os.SyscallError); ok { +// if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") { +// brokenPipe = true +// } +// } +// } +// +// httpRequest, _ := httputil.DumpRequest(c.Request, false) +// if brokenPipe { +// logger.Error(c.Request.URL.Path, +// zap.Any("error", err), +// zap.String("request", string(httpRequest)), +// ) +// // If the connection is dead, we can't write a status to it. +// c.Error(err.(error)) // nolint: errcheck +// c.Abort() +// return +// } +// +// if stack { +// logger.Error("[Recovery from panic]", +// zap.Any("error", err), +// zap.String("request", string(httpRequest)), +// zap.String("stack", string(debug.Stack())), +// ) +// } else { +// logger.Error("[Recovery from panic]", +// zap.Any("error", err), +// zap.String("request", string(httpRequest)), +// ) +// } +// c.AbortWithStatus(http.StatusInternalServerError) +// } +// }() +// c.Next() +// } +//} diff --git a/internal/handler/http/provider.go b/internal/handler/http/provider.go new file mode 100644 index 0000000..bc5b52e --- /dev/null +++ b/internal/handler/http/provider.go @@ -0,0 +1,46 @@ +package http + +import ( + "github.com/google/wire" + v1 "go-template/internal/handler/http/controller/v1" + "go-template/internal/handler/http/middleware" +) + +var ProviderSet = wire.NewSet( + middleware.NewAuthMiddleware, + middleware.NewGinLoggerMiddleware, + middleware.NewJSONResponseMiddleware, + NewMiddleware, + v1.NewUserController, + NewHandler, +) + +type Middleware struct { + GinLogger *middleware.GinLoggerMiddleware + Auth *middleware.AuthMiddleware + JSONResponse *middleware.JSONResponseMiddleware +} + +func NewMiddleware( + GinLogger *middleware.GinLoggerMiddleware, + Auth *middleware.AuthMiddleware, + JSONResponse *middleware.JSONResponseMiddleware, +) *Middleware { + return &Middleware{ + Auth: Auth, + GinLogger: GinLogger, + JSONResponse: JSONResponse, + } +} + +type Handlers struct { + User *v1.UserController +} + +func NewHandler( + user *v1.UserController, +) *Handlers { + return &Handlers{ + User: user, + } +} diff --git a/internal/migrations/1_setup.sql b/internal/migrations/1_setup.sql new file mode 100644 index 0000000..d942961 --- /dev/null +++ b/internal/migrations/1_setup.sql @@ -0,0 +1,15 @@ +-- +goose Up + +CREATE TABLE IF NOT EXISTS `users` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `username` varchar(255) NOT NULL, + `email` varchar(255) NOT NULL, + `password` varchar(255) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- +goose Down + +DROP TABLE `users`; \ No newline at end of file diff --git a/internal/migrations/init.go b/internal/migrations/init.go new file mode 100644 index 0000000..1a0d72e --- /dev/null +++ b/internal/migrations/init.go @@ -0,0 +1,11 @@ +package migrations + +import ( + "embed" + "go-template/internal/base/conf" +) + +//go:embed *.sql +var MigrationFS embed.FS + +var Config *conf.Config diff --git a/internal/router/api.go b/internal/router/api.go new file mode 100644 index 0000000..cc7ac62 --- /dev/null +++ b/internal/router/api.go @@ -0,0 +1,38 @@ +package router + +import ( + "github.com/gin-gonic/gin" + "go-template/internal/handler/http" +) + +// 两种方法都可以 +//type Api struct { +// User *v1.UserController +//} + +type Api struct { + HttpHandler *http.Handlers +} + +func NewApiRoute( + //User *v1.UserController, + HttpHandler *http.Handlers, +) *Api { + //return &Api{ + // User, + //} + + return &Api{ + HttpHandler, + } +} + +func (a *Api) InitApiRouter(r *gin.RouterGroup) { + //r.GET("/ping", a.User.Test) + + r.GET("/ping", a.HttpHandler.User.Test) +} + +func (a *Api) InitNoAuthApiRouter(r *gin.RouterGroup) { + +} diff --git a/internal/router/provider.go b/internal/router/provider.go new file mode 100644 index 0000000..cce6bce --- /dev/null +++ b/internal/router/provider.go @@ -0,0 +1,9 @@ +package router + +import "github.com/google/wire" + +// ProviderSetRouter is providers. +var ProviderSetRouter = wire.NewSet( + NewApiRoute, + NewSwaggerRoute, +) diff --git a/internal/router/swagger.go b/internal/router/swagger.go new file mode 100644 index 0000000..366de47 --- /dev/null +++ b/internal/router/swagger.go @@ -0,0 +1,21 @@ +package router + +import ( + _ "go-template/docs" + + "github.com/gin-gonic/gin" + swaggerFiles "github.com/swaggo/files" + ginSwagger "github.com/swaggo/gin-swagger" +) + +type SwaggerRouter struct { + //config *conf.Config +} + +func NewSwaggerRoute() *SwaggerRouter { + return &SwaggerRouter{} +} + +func (a *SwaggerRouter) Register(r *gin.RouterGroup) { + r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) +} diff --git a/internal/schema/entity.go b/internal/schema/entity.go new file mode 100644 index 0000000..5fed106 --- /dev/null +++ b/internal/schema/entity.go @@ -0,0 +1,17 @@ +package schema + +import ( + "strconv" +) + +type EntityId uint + +//type EntityId int64 + +func (i EntityId) String() string { + return strconv.FormatUint(uint64(i), 10) + //return strconv.FormatInt(int64(i), 10) +} +func (i EntityId) Uint() uint { + return uint(i) +} diff --git a/internal/schema/response.go b/internal/schema/response.go new file mode 100644 index 0000000..d253161 --- /dev/null +++ b/internal/schema/response.go @@ -0,0 +1,131 @@ +package schema + +import ( + "github.com/gin-gonic/gin" + "net/http" +) + +type ResponseBody struct { + Message string `json:"message"` + Error string `json:"error"` + Success bool `json:"success"` + Data any `json:"data,omitempty"` + Wrap bool `json:"-"` +} + +type HttpResponse struct { + body *ResponseBody + httpStatus int + ctx *gin.Context +} + +func NewResponse(c *gin.Context) *HttpResponse { + return &HttpResponse{ + body: &ResponseBody{ + Wrap: true, + }, + httpStatus: 0, + ctx: c, + } +} + +func (r *HttpResponse) Message(message string) *HttpResponse { + r.body.Message = message + + if r.httpStatus == 0 { + r.httpStatus = http.StatusOK + } + + return r +} + +// WithoutWrap 将不在 body 中包裹 data +func (r *HttpResponse) WithoutWrap() *HttpResponse { + r.body.Wrap = false + + return r +} + +func (r *HttpResponse) Wrap() *HttpResponse { + r.body.Wrap = true + return r +} + +func (r *HttpResponse) Data(data any) *HttpResponse { + r.body.Data = data + return r + +} + +func (r *HttpResponse) Error(err error) *HttpResponse { + if err != nil { + var errMsg = err.Error() + + if errMsg == "EOF" { + errMsg = "Request body is empty or missing some fields, make sure you have provided all the required fields" + } + + r.body.Error = errMsg + + if r.httpStatus == 0 { + r.httpStatus = http.StatusBadRequest + } + + if r.body.Message == "" { + r.Message("Something went wrong") + } + r.body.Success = false + } + + return r + +} + +func (r *HttpResponse) Status(status int) *HttpResponse { + r.httpStatus = status + return r + +} + +func (r *HttpResponse) Send() { + if r.httpStatus == 0 { + r.httpStatus = http.StatusOK + } + + // if 20x or 20x, set success + r.body.Success = r.httpStatus >= http.StatusOK && r.httpStatus < http.StatusMultipleChoices + + if r.body.Wrap { + r.ctx.JSON(r.httpStatus, r.body) + return + } + + r.ctx.JSON(r.httpStatus, r.body.Data) +} + +func (r *HttpResponse) Abort() { + r.ctx.Abort() +} + +// +//func ResponseMessage(c *gin.Context, code int, message string, data interface{}) { +// c.JSON(code, &ResponseBody{ +// Message: message, +// Data: data, +// }) +// c.Abort() +//} +// +//func ResponseError(c *gin.Context, code int, err error) { +// c.JSON(code, &ResponseBody{ +// Error: err.Error(), +// }) +// c.Abort() +//} +// +//func Response(c *gin.Context, code int, data interface{}) { +// c.JSON(code, &ResponseBody{ +// Data: data, +// }) +// c.Abort() +//} diff --git a/internal/schema/user.go b/internal/schema/user.go new file mode 100644 index 0000000..ab010ee --- /dev/null +++ b/internal/schema/user.go @@ -0,0 +1,46 @@ +package schema + +import ( + "time" +) + +type UserTokenInfo struct { + Aud string `json:"aud"` + Iss string `json:"iss"` + Iat float64 `json:"iat"` + Exp float64 `json:"exp"` + Sub UserId `json:"sub" mapstructure:"-"` + Scopes []string `json:"scopes"` + Id int `json:"id"` + Uuid string `json:"uuid"` + Avatar string `json:"avatar"` + Name string `json:"name"` + EmailVerified bool `json:"email_verified"` + RealNameVerified bool `json:"real_name_verified"` + PhoneVerified bool `json:"phone_verified"` + Email string `json:"email"` + Phone string `json:"phone"` + CreatedAt time.Time `json:"created_at"` +} + +type User struct { + Token UserTokenInfo + Valid bool +} + +type UserId string + +func (u UserId) String() string { + return string(u) +} + +type JWTTokenTypes string + +const ( + JWTAccessToken JWTTokenTypes = "access_token" + JWTIDToken JWTTokenTypes = "id_token" +) + +func (jwtTokenTypes JWTTokenTypes) String() string { + return string(jwtTokenTypes) +} diff --git a/internal/schema/user_response.go b/internal/schema/user_response.go new file mode 100644 index 0000000..8f5a646 --- /dev/null +++ b/internal/schema/user_response.go @@ -0,0 +1,9 @@ +package schema + +type CurrentUserResponse struct { + IP string `json:"ip"` + Valid bool `json:"valid"` + UserEmail string `json:"userEmail"` + UserId UserId `json:"userId"` + UserName string `json:"userName"` +} diff --git a/internal/service/auth/auth.go b/internal/service/auth/auth.go new file mode 100644 index 0000000..7f60a0d --- /dev/null +++ b/internal/service/auth/auth.go @@ -0,0 +1,136 @@ +package auth + +import ( + "context" + "go-template/internal/base/conf" + "go-template/internal/base/logger" + "go-template/internal/schema" + "go-template/internal/service/jwks" + "go-template/pkg/consts" + "strings" + + "github.com/gin-gonic/gin" + "github.com/mitchellh/mapstructure" +) + +type Service struct { + config *conf.Config + jwks *jwks.JWKS + logger *logger.Logger +} + +func NewAuthService(config *conf.Config, jwks *jwks.JWKS, logger *logger.Logger) *Service { + return &Service{ + config: config, + jwks: jwks, + logger: logger, + } +} + +func (a *Service) GinMiddlewareAuth(tokenType schema.JWTTokenTypes, c *gin.Context) (*schema.User, error) { + if a.config.Debug.Enabled { + return a.parseUserJWT(tokenType, "") + } + + authorization := c.Request.Header.Get(consts.AuthHeader) + + if authorization == "" { + return nil, consts.ErrJWTFormatError + } + + authSplit := strings.Split(authorization, " ") + if len(authSplit) != 2 { + return nil, consts.ErrJWTFormatError + } + + if authSplit[0] != consts.AuthPrefix { + return nil, consts.ErrNotBearerType + } + + return a.parseUserJWT(tokenType, authSplit[1]) +} + +func (a *Service) AuthFromToken(tokenType schema.JWTTokenTypes, token string) (*schema.User, error) { + if a.config.Debug.Enabled { + return a.parseUserJWT(tokenType, "") + } + + return a.parseUserJWT(tokenType, token) +} + +func (a *Service) GetUserFromIdToken(idToken string) (*schema.User, error) { + return a.parseUserJWT(schema.JWTIDToken, idToken) +} + +func (a *Service) GinUser(c *gin.Context) *schema.User { + user, _ := c.Get(consts.AuthMiddlewareKey) + return user.(*schema.User) +} + +func (a *Service) GetUserId(ctx context.Context) schema.UserId { + user := a.GetUser(ctx) + + return user.Token.Sub +} + +func (a *Service) GetUser(ctx context.Context) *schema.User { + user := ctx.Value(consts.AuthMiddlewareKey) + + return user.(*schema.User) +} + +func (a *Service) SetUser(ctx context.Context, user *schema.User) context.Context { + context.WithValue(ctx, consts.AuthMiddlewareKey, user) + return context.WithValue(ctx, consts.AuthMiddlewareKey, user) +} + +func (a *Service) parseUserJWT(tokenType schema.JWTTokenTypes, jwtToken string) (*schema.User, error) { + var sub = consts.AnonymousUser + var jwtIdToken = &schema.User{} + + if a.config.Debug.Enabled { + jwtIdToken.Token.Sub = sub + jwtIdToken.Valid = true + return jwtIdToken, nil + } else { + token, err := a.jwks.ParseJWT(jwtToken) + if err != nil { + return nil, consts.ErrNotValidToken + } + + subStr, err := token.Claims.GetSubject() + if err != nil { + return nil, consts.ErrNotValidToken + } + + //subInt, err := strconv.Atoi(subStr) + //if err != nil { + // return nil, consts.ErrNotValidToken + //} + + sub = schema.UserId(subStr) + + // 如果 token.Header 中没有 typ + if token.Header["typ"] == "" { + return nil, consts.ErrEmptyResponse + } + + // 验证 token 类型 + if tokenType != "" && tokenType.String() != token.Header["typ"] { + return nil, consts.ErrTokenError + } + + jwtIdToken.Valid = true + + err = mapstructure.Decode(token.Claims, &jwtIdToken.Token) + if err != nil { + a.logger.Logger.Error("Failed to map token claims to JwtIDToken struct.\nError: " + err.Error()) + return nil, nil + } + + // 手动指定,因为 mapstructure 无法转换 UserID 类型 + jwtIdToken.Token.Sub = sub + } + + return jwtIdToken, nil +} diff --git a/internal/service/auth/compare.go b/internal/service/auth/compare.go new file mode 100644 index 0000000..38c910c --- /dev/null +++ b/internal/service/auth/compare.go @@ -0,0 +1,14 @@ +package auth + +import ( + "context" + "go-template/internal/schema" +) + +type HasUserInterface interface { + GetUserId() schema.UserId +} + +func (a *Service) Compare(ctx context.Context, entity HasUserInterface) bool { + return entity.GetUserId() == a.GetUserId(ctx) +} diff --git a/internal/service/embedding/embedding.go b/internal/service/embedding/embedding.go new file mode 100644 index 0000000..2404bf7 --- /dev/null +++ b/internal/service/embedding/embedding.go @@ -0,0 +1,108 @@ +package embedding + +import ( + "context" +) + +func (s *Service) TextEmbedding(ctx context.Context, input []string) ([][]float32, error) { + + var r = make([][]float32, len(input)-1) + + for _, v := range input { + embedding2, err := s.OpenAI.CreateEmbedding(ctx, []string{v}) + if err != nil { + return nil, err + } + + r = append(r, embedding2[0]) + + //embedding, err := s.getCache(ctx, v) + //if err != nil { + // return r, err + //} + // + //if embedding != nil { + // r = append(r, embedding) + // continue + //} else { + // embedding2, err := s.OpenAI.CreateEmbedding(ctx, []string{v}) + // if err != nil { + // return nil, err + // } + // + // r = append(r, embedding2[0]) + // + // err = s.setCache(ctx, v, embedding2[0]) + // if err != nil { + // return nil, err + // } + //} + } + + return r, nil +} + +// +//func (s *Service) getCache(ctx context.Context, input string) ([]float32, error) { +// md5Str, err := md5.Md5(input) +// if err != nil { +// return nil, err +// } +// +// c, err := s.dao.WithContext(ctx).Embedding.Where(s.dao.Embedding.TextMd5.Eq(md5Str)). +// Where(s.dao.Embedding.EmbeddingModel.Eq(s.config.OpenAI.EmbeddingModel)). +// Count() +// if c == 0 { +// return nil, err +// } +// +// first, err := s.dao.WithContext(ctx).Embedding.Where(s.dao.Embedding.TextMd5.Eq(md5Str)). +// Where(s.dao.Embedding.EmbeddingModel.Eq(s.config.OpenAI.EmbeddingModel)). +// First() +// if err != nil { +// return nil, err +// } +// +// // byte to float32 +// return first.Vector, nil +//} +// +//func (s *Service) setCache(ctx context.Context, input string, embedding []float32) error { +// md5Str, err := md5.Md5(input) +// if err != nil { +// return err +// } +// +// // redis 锁 +// var key = "lock_" + md5Str +// lock, err := s.redis.Locker.Obtain(ctx, key, 3*time.Second, nil) +// if errors.Is(err, redislock.ErrNotObtained) { +// s.Logger.Sugar.Warnf("redis lock %s not obtained", md5Str) +// } else if err != nil { +// return err +// } +// defer func(lock *redislock.Lock, ctx context.Context) { +// err := lock.Release(ctx) +// if err != nil { +// s.Logger.Sugar.Error(err) +// } +// }(lock, ctx) +// +// // 如果没有 cache,则设置 +// c, err := s.dao.WithContext(ctx).Embedding.Where(s.dao.Embedding.TextMd5.Eq(md5Str)). +// Where(s.dao.Embedding.EmbeddingModel.Eq(s.config.OpenAI.EmbeddingModel)). +// Count() +// if err != nil { +// return err +// } +// if c == 0 { +// return s.dao.WithContext(ctx).Embedding.Create(&entity.Embedding{ +// Text: input, +// TextMd5: md5Str, +// Vector: embedding, +// EmbeddingModel: s.config.OpenAI.EmbeddingModel, +// }) +// } +// +// return nil +//} diff --git a/internal/service/embedding/provider.go b/internal/service/embedding/provider.go new file mode 100644 index 0000000..b7ca8fc --- /dev/null +++ b/internal/service/embedding/provider.go @@ -0,0 +1,31 @@ +package embedding + +import ( + "github.com/tmc/langchaingo/llms/openai" + "leafdev.top/Leaf/leaf-library/internal/base/conf" + "leafdev.top/Leaf/leaf-library/internal/base/logger" + "leafdev.top/Leaf/leaf-library/internal/base/redis" + "leafdev.top/Leaf/leaf-library/internal/dao" +) + +type Service struct { + OpenAI *openai.LLM + Logger *logger.Logger + config *conf.Config + dao *dao.Query + redis *redis.Redis +} + +func NewService(config *conf.Config, logger *logger.Logger, dao *dao.Query, redis *redis.Redis) *Service { + llm, err := openai.New( + openai.WithToken(config.OpenAI.ApiKey), + openai.WithBaseURL(config.OpenAI.InternalBaseUrl), + openai.WithEmbeddingModel(config.OpenAI.EmbeddingModel), + ) + + if err != nil { + panic(err) + } + + return &Service{llm, logger, config, dao, redis} +} diff --git a/internal/service/jwks/auth_refresh.go b/internal/service/jwks/auth_refresh.go new file mode 100644 index 0000000..2eb7c3d --- /dev/null +++ b/internal/service/jwks/auth_refresh.go @@ -0,0 +1,23 @@ +package jwks + +import "time" + +var refreshRate = 1 * time.Hour + +func (j *JWKS) SetupAuthRefresh() { + // 先刷新一次 + j.RefreshJWKS() + var firstRefreshed = true + + // 启动一个定时器 + go func() { + for { + if firstRefreshed { + firstRefreshed = false + } else { + j.RefreshJWKS() + } + time.Sleep(refreshRate) + } + }() +} diff --git a/internal/service/jwks/jwks.go b/internal/service/jwks/jwks.go new file mode 100644 index 0000000..31e361e --- /dev/null +++ b/internal/service/jwks/jwks.go @@ -0,0 +1,57 @@ +package jwks + +import ( + "errors" + "go-template/internal/base/conf" + "go-template/internal/base/logger" + + "github.com/MicahParks/keyfunc/v3" + "github.com/golang-jwt/jwt/v5" +) + +var Jwks keyfunc.Keyfunc + +var ( + ErrJWKSNotInitialized = errors.New("JWKS is not initialized") +) + +type JWKS struct { + url string + logger *logger.Logger + config *conf.Config +} + +func NewJWKS(config *conf.Config, logger *logger.Logger) *JWKS { + return &JWKS{ + url: config.JWKS.Url, + logger: logger, + config: config, + } +} + +func (j *JWKS) RefreshJWKS() { + if j.config.Debug.Enabled { + return + } + + j.logger.Logger.Info("Refreshing JWKS...") + + var err error + + Jwks, err = keyfunc.NewDefault([]string{j.url}) + if err != nil { + j.logger.Logger.Error("Failed to create JWK Set from resource at the given URL.\nError: " + err.Error()) + } + + j.logger.Logger.Info("JWKS refreshed.") +} + +func (j *JWKS) ParseJWT(jwtB64 string) (*jwt.Token, error) { + if Jwks.Keyfunc == nil { + return nil, ErrJWKSNotInitialized + } + + token, err := jwt.Parse(jwtB64, Jwks.Keyfunc) + + return token, err +} diff --git a/internal/service/provider.go b/internal/service/provider.go new file mode 100644 index 0000000..0b57a79 --- /dev/null +++ b/internal/service/provider.go @@ -0,0 +1,34 @@ +package service + +import ( + "go-template/internal/base/logger" + "go-template/internal/service/auth" + "go-template/internal/service/jwks" + + "github.com/google/wire" +) + +type Service struct { + logger *logger.Logger + Jwks *jwks.JWKS + Auth *auth.Service +} + +var Provider = wire.NewSet( + jwks.NewJWKS, + auth.NewAuthService, + NewService, +) + +func NewService( + logger *logger.Logger, + jwks *jwks.JWKS, + auth *auth.Service, + +) *Service { + return &Service{ + logger, + jwks, + auth, + } +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..757ee12 --- /dev/null +++ b/main.go @@ -0,0 +1,18 @@ +package main + +import ( + "go-template/cmd" +) + +// @title API Docs +// @version 1.0 +// @securityDefinitions.apikey ApiKeyAuth +// @in header +// @name Authorization +func main() { + err := cmd.RootCmd.Execute() + if err != nil { + panic(err) + return + } +} diff --git a/pkg/consts/auth.go b/pkg/consts/auth.go new file mode 100644 index 0000000..a0de006 --- /dev/null +++ b/pkg/consts/auth.go @@ -0,0 +1,29 @@ +package consts + +import ( + "errors" + "go-template/internal/schema" +) + +const ( + AuthHeader = "Authorization" + AuthPrefix = "Bearer" + + //AnonymousUser schema.UserId = 1 + AnonymousUser schema.UserId = "anonymous" + + AuthMiddlewareKey = "auth.user" + AuthAssistantShareMiddlewareKey = "auth.assistant.share" +) + +var ( + ErrNotValidToken = errors.New("无效的 JWT 令牌") + ErrJWTFormatError = errors.New("JWT 格式错误") + ErrNotBearerType = errors.New("不是 Bearer 类型") + ErrEmptyResponse = errors.New("我们的服务器返回了空请求,可能某些环节出了问题") + ErrTokenError = errors.New("token 类型错误") + ErrBearerToken = errors.New("无效的 Bearer 令牌") + + ErrNotYourResource = errors.New("你不能修改这个资源,因为它不是你创建的。") + ErrPermissionDenied = errors.New("没有权限访问此资源") +) diff --git a/pkg/consts/model.go b/pkg/consts/model.go new file mode 100644 index 0000000..58eeb88 --- /dev/null +++ b/pkg/consts/model.go @@ -0,0 +1,7 @@ +package consts + +import "errors" + +var ( + ErrPageNotFound = errors.New("page not found") +) diff --git a/pkg/protos/document/apidocs.swagger.json b/pkg/protos/document/apidocs.swagger.json new file mode 100644 index 0000000..df08354 --- /dev/null +++ b/pkg/protos/document/apidocs.swagger.json @@ -0,0 +1,57 @@ +{ + "swagger": "2.0", + "info": { + "title": "pkg/protos/documentService/service.proto", + "version": "version not set" + }, + "tags": [ + { + "name": "DocumentService" + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": {}, + "definitions": { + "ListDocumentsResponse": { + "type": "object", + "properties": { + "Message": { + "type": "string" + } + } + }, + "protobufAny": { + "type": "object", + "properties": { + "@type": { + "type": "string" + } + }, + "additionalProperties": {} + }, + "rpcStatus": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/protobufAny" + } + } + } + } + } +} diff --git a/pkg/protos/documentService/apidocs.swagger.json b/pkg/protos/documentService/apidocs.swagger.json new file mode 100644 index 0000000..190e874 --- /dev/null +++ b/pkg/protos/documentService/apidocs.swagger.json @@ -0,0 +1,52 @@ +{ + "swagger": "2.0", + "info": { + "title": "pkg/protos/document/service.proto", + "version": "version not set" + }, + "tags": [ + { + "name": "DocumentService" + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": {}, + "definitions": { + "ListDocumentsResponse": { + "type": "object" + }, + "protobufAny": { + "type": "object", + "properties": { + "@type": { + "type": "string" + } + }, + "additionalProperties": {} + }, + "rpcStatus": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/protobufAny" + } + } + } + } + } +} diff --git a/pkg/protos/documentService/service.pb.go b/pkg/protos/documentService/service.pb.go new file mode 100644 index 0000000..e599ffc --- /dev/null +++ b/pkg/protos/documentService/service.pb.go @@ -0,0 +1,214 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc (unknown) +// source: pkg/protos/documentService/service.proto + +package documentService + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ListDocumentsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=Message,proto3" json:"Message,omitempty"` +} + +func (x *ListDocumentsRequest) Reset() { + *x = ListDocumentsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_protos_documentService_service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListDocumentsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListDocumentsRequest) ProtoMessage() {} + +func (x *ListDocumentsRequest) ProtoReflect() protoreflect.Message { + mi := &file_pkg_protos_documentService_service_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListDocumentsRequest.ProtoReflect.Descriptor instead. +func (*ListDocumentsRequest) Descriptor() ([]byte, []int) { + return file_pkg_protos_documentService_service_proto_rawDescGZIP(), []int{0} +} + +func (x *ListDocumentsRequest) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +type ListDocumentsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=Message,proto3" json:"Message,omitempty"` +} + +func (x *ListDocumentsResponse) Reset() { + *x = ListDocumentsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_protos_documentService_service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListDocumentsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListDocumentsResponse) ProtoMessage() {} + +func (x *ListDocumentsResponse) ProtoReflect() protoreflect.Message { + mi := &file_pkg_protos_documentService_service_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListDocumentsResponse.ProtoReflect.Descriptor instead. +func (*ListDocumentsResponse) Descriptor() ([]byte, []int) { + return file_pkg_protos_documentService_service_proto_rawDescGZIP(), []int{1} +} + +func (x *ListDocumentsResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +var File_pkg_protos_documentService_service_proto protoreflect.FileDescriptor + +var file_pkg_protos_documentService_service_proto_rawDesc = []byte{ + 0x0a, 0x28, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x64, 0x6f, 0x63, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x30, 0x0a, 0x14, 0x4c, 0x69, + 0x73, 0x74, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x31, 0x0a, 0x15, + 0x4c, 0x69, 0x73, 0x74, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, + 0x51, 0x0a, 0x0f, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x12, 0x15, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x42, 0x13, 0x5a, 0x11, 0x2e, 0x3b, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_pkg_protos_documentService_service_proto_rawDescOnce sync.Once + file_pkg_protos_documentService_service_proto_rawDescData = file_pkg_protos_documentService_service_proto_rawDesc +) + +func file_pkg_protos_documentService_service_proto_rawDescGZIP() []byte { + file_pkg_protos_documentService_service_proto_rawDescOnce.Do(func() { + file_pkg_protos_documentService_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_pkg_protos_documentService_service_proto_rawDescData) + }) + return file_pkg_protos_documentService_service_proto_rawDescData +} + +var file_pkg_protos_documentService_service_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_pkg_protos_documentService_service_proto_goTypes = []interface{}{ + (*ListDocumentsRequest)(nil), // 0: ListDocumentsRequest + (*ListDocumentsResponse)(nil), // 1: ListDocumentsResponse +} +var file_pkg_protos_documentService_service_proto_depIdxs = []int32{ + 0, // 0: DocumentService.ListDocuments:input_type -> ListDocumentsRequest + 1, // 1: DocumentService.ListDocuments:output_type -> ListDocumentsResponse + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_pkg_protos_documentService_service_proto_init() } +func file_pkg_protos_documentService_service_proto_init() { + if File_pkg_protos_documentService_service_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_pkg_protos_documentService_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListDocumentsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_protos_documentService_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListDocumentsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_pkg_protos_documentService_service_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_pkg_protos_documentService_service_proto_goTypes, + DependencyIndexes: file_pkg_protos_documentService_service_proto_depIdxs, + MessageInfos: file_pkg_protos_documentService_service_proto_msgTypes, + }.Build() + File_pkg_protos_documentService_service_proto = out.File + file_pkg_protos_documentService_service_proto_rawDesc = nil + file_pkg_protos_documentService_service_proto_goTypes = nil + file_pkg_protos_documentService_service_proto_depIdxs = nil +} diff --git a/pkg/protos/documentService/service.pb.gw.go b/pkg/protos/documentService/service.pb.gw.go new file mode 100644 index 0000000..d55bfe8 --- /dev/null +++ b/pkg/protos/documentService/service.pb.gw.go @@ -0,0 +1,164 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: pkg/protos/documentService/service.proto + +/* +Package documentService is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package documentService + +import ( + "context" + "io" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = metadata.Join + +func request_DocumentService_ListDocuments_0(ctx context.Context, marshaler runtime.Marshaler, client DocumentServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListDocumentsRequest + var metadata runtime.ServerMetadata + + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ListDocuments(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_DocumentService_ListDocuments_0(ctx context.Context, marshaler runtime.Marshaler, server DocumentServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListDocumentsRequest + var metadata runtime.ServerMetadata + + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ListDocuments(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterDocumentServiceHandlerServer registers the http handlers for service DocumentService to "mux". +// UnaryRPC :call DocumentServiceServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterDocumentServiceHandlerFromEndpoint instead. +// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call. +func RegisterDocumentServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server DocumentServiceServer) error { + + mux.Handle("POST", pattern_DocumentService_ListDocuments_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/.DocumentService/ListDocuments", runtime.WithHTTPPathPattern("/DocumentService/ListDocuments")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_DocumentService_ListDocuments_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_DocumentService_ListDocuments_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterDocumentServiceHandlerFromEndpoint is same as RegisterDocumentServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterDocumentServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.NewClient(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterDocumentServiceHandler(ctx, mux, conn) +} + +// RegisterDocumentServiceHandler registers the http handlers for service DocumentService to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterDocumentServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterDocumentServiceHandlerClient(ctx, mux, NewDocumentServiceClient(conn)) +} + +// RegisterDocumentServiceHandlerClient registers the http handlers for service DocumentService +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "DocumentServiceClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "DocumentServiceClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "DocumentServiceClient" to call the correct interceptors. This client ignores the HTTP middlewares. +func RegisterDocumentServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client DocumentServiceClient) error { + + mux.Handle("POST", pattern_DocumentService_ListDocuments_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/.DocumentService/ListDocuments", runtime.WithHTTPPathPattern("/DocumentService/ListDocuments")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_DocumentService_ListDocuments_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_DocumentService_ListDocuments_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_DocumentService_ListDocuments_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"DocumentService", "ListDocuments"}, "")) +) + +var ( + forward_DocumentService_ListDocuments_0 = runtime.ForwardResponseMessage +) diff --git a/pkg/protos/documentService/service.proto b/pkg/protos/documentService/service.proto new file mode 100644 index 0000000..dda3e71 --- /dev/null +++ b/pkg/protos/documentService/service.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +option go_package = ".;documentService"; + +service DocumentService { + rpc ListDocuments(ListDocumentsRequest) returns (ListDocumentsResponse); +} + +message ListDocumentsRequest { + string Message = 1; +} + +message ListDocumentsResponse { + string Message = 1; +} \ No newline at end of file diff --git a/pkg/protos/documentService/service_grpc.pb.go b/pkg/protos/documentService/service_grpc.pb.go new file mode 100644 index 0000000..87cb1e1 --- /dev/null +++ b/pkg/protos/documentService/service_grpc.pb.go @@ -0,0 +1,105 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc (unknown) +// source: pkg/protos/documentService/service.proto + +package documentService + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// DocumentServiceClient is the client API for DocumentService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type DocumentServiceClient interface { + ListDocuments(ctx context.Context, in *ListDocumentsRequest, opts ...grpc.CallOption) (*ListDocumentsResponse, error) +} + +type documentServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewDocumentServiceClient(cc grpc.ClientConnInterface) DocumentServiceClient { + return &documentServiceClient{cc} +} + +func (c *documentServiceClient) ListDocuments(ctx context.Context, in *ListDocumentsRequest, opts ...grpc.CallOption) (*ListDocumentsResponse, error) { + out := new(ListDocumentsResponse) + err := c.cc.Invoke(ctx, "/DocumentService/ListDocuments", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// DocumentServiceServer is the server API for DocumentService service. +// All implementations must embed UnimplementedDocumentServiceServer +// for forward compatibility +type DocumentServiceServer interface { + ListDocuments(context.Context, *ListDocumentsRequest) (*ListDocumentsResponse, error) + mustEmbedUnimplementedDocumentServiceServer() +} + +// UnimplementedDocumentServiceServer must be embedded to have forward compatible implementations. +type UnimplementedDocumentServiceServer struct { +} + +func (UnimplementedDocumentServiceServer) ListDocuments(context.Context, *ListDocumentsRequest) (*ListDocumentsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListDocuments not implemented") +} +func (UnimplementedDocumentServiceServer) mustEmbedUnimplementedDocumentServiceServer() {} + +// UnsafeDocumentServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to DocumentServiceServer will +// result in compilation errors. +type UnsafeDocumentServiceServer interface { + mustEmbedUnimplementedDocumentServiceServer() +} + +func RegisterDocumentServiceServer(s grpc.ServiceRegistrar, srv DocumentServiceServer) { + s.RegisterService(&DocumentService_ServiceDesc, srv) +} + +func _DocumentService_ListDocuments_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListDocumentsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DocumentServiceServer).ListDocuments(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/DocumentService/ListDocuments", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DocumentServiceServer).ListDocuments(ctx, req.(*ListDocumentsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// DocumentService_ServiceDesc is the grpc.ServiceDesc for DocumentService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var DocumentService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "DocumentService", + HandlerType: (*DocumentServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ListDocuments", + Handler: _DocumentService_ListDocuments_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "pkg/protos/documentService/service.proto", +}