diff --git a/cmd/internal/build.go b/cmd/internal/build.go index 1adf7b5..c23639b 100644 --- a/cmd/internal/build.go +++ b/cmd/internal/build.go @@ -3,7 +3,10 @@ package internal import ( "embed" "fmt" + "io/fs" "log" + "path/filepath" + "strings" "text/template" "github.com/shenghui0779/yiigo" @@ -15,74 +18,151 @@ type Params struct { Module string AppPkg string AppName string + DockerF string } func InitHttpProject(root, mod string, apps ...string) { // 创建项目 - initProject(root, mod, http.Project, http.FS) + initProject(root, mod, http.FS) // 创建App(单应用) if len(apps) == 0 { - initApp(root, mod, "", http.App, http.FS) + initApp(root, mod, "", http.FS) return } // 创建App(多应用) for _, name := range apps { - initApp(root, mod, name, http.App, http.FS) + initApp(root, mod, name, http.FS) } } func InitHttpApp(root, mod, name string) { - initApp(root, mod, name, http.App, http.FS) + initApp(root, mod, name, http.FS) } func InitGrpcProject(root, mod string, apps ...string) { // 创建项目 - initProject(root, mod, grpc.Project, grpc.FS) + initProject(root, mod, grpc.FS) // 创建App(单应用) if len(apps) == 0 { - initApp(root, mod, "", grpc.App, grpc.FS) + initApp(root, mod, "", grpc.FS) return } // 创建App(多应用) for _, name := range apps { - initApp(root, mod, name, grpc.App, grpc.FS) + initApp(root, mod, name, grpc.FS) } } func InitGrpcApp(root, mod, name string) { - initApp(root, mod, name, grpc.App, grpc.FS) + initApp(root, mod, name, grpc.FS) } -func initProject(root, mod string, tmpls []map[string]string, fs embed.FS) { +func initProject(root, mod string, fsys embed.FS) { params := &Params{Module: mod} - // 创建项目 - for _, tmpl := range tmpls { - output := root + "/" + tmpl["output"] - buildTmpl(fs, tmpl["name"], tmpl["path"], output, params) + // 项目根目录文件 + files, _ := fs.ReadDir(fsys, ".") + for _, v := range files { + if v.IsDir() || filepath.Ext(v.Name()) == ".go" { + continue + } + output := genOutput(root, v.Name(), "") + buildTmpl(fsys, v.Name(), output, params) } + // lib目录文件 + _ = fs.WalkDir(fsys, "pkg/internal", func(path string, d fs.DirEntry, err error) error { + if d.IsDir() || filepath.Ext(path) == ".go" { + return nil + } + output := genOutput(root, path, "") + buildTmpl(fsys, path, output, params) + return nil + }) } -func initApp(root, mod, name string, tmpls []map[string]string, fs embed.FS) { - prefix := root + "/pkg/app" +func initApp(root, mod, name string, fsys embed.FS) { params := &Params{ Module: mod, AppPkg: "app", AppName: root, + DockerF: "Dockerfile", } if len(name) != 0 { - prefix += "/" + name params.AppPkg = "app/" + name params.AppName = name + params.DockerF = name + ".dockerfile" + } + // app目录文件 + _ = fs.WalkDir(fsys, "pkg/app", func(path string, d fs.DirEntry, err error) error { + if d.IsDir() { + return nil + } + if filepath.Ext(path) == ".go" { + return nil + } + output := genOutput(root, path, name) + buildTmpl(fsys, path, output, params) + return nil + }) +} + +func genOutput(root, path, appName string) string { + var builder strings.Builder + // 项目根目录 + builder.WriteString(root) + builder.WriteString("/") + // 解析path + dir, name := filepath.Split(path) + // dockerfile + switch name { + case "Dockerfile": + if len(appName) != 0 { + builder.WriteString(appName) + builder.WriteString(".dockerfile") + } else { + builder.WriteString("Dockerfile") + } + return filepath.Clean(builder.String()) + case "dockerun.sh": + if len(appName) != 0 { + builder.WriteString(appName) + builder.WriteString("_dockerun.sh") + } else { + builder.WriteString("dockerun.sh") + } + return filepath.Clean(builder.String()) + } + // 文件目录 + if len(dir) != 0 { + builder.WriteString(dir) } - for _, tmpl := range tmpls { - output := prefix + "/" + tmpl["output"] - buildTmpl(fs, tmpl["name"], tmpl["path"], output, params) + // 文件名称 + switch ext := filepath.Ext(path); ext { + case ".yiigo": + builder.WriteString(name[:len(name)-6]) + builder.WriteString(".go") + case "": + if strings.Contains(name, "ignore") { + builder.WriteString(".") + } + builder.WriteString(name) + default: + builder.WriteString(name) } + // 新的文件路径 + output := builder.String() + if len(appName) != 0 { + output = strings.Replace(output, "/app", "/app/"+appName, 1) + } + return filepath.Clean(output) } -func buildTmpl(fs embed.FS, name, path, output string, params *Params) { +func buildTmpl(fsys embed.FS, path, output string, params *Params) { + b, err := fsys.ReadFile(path) + if err != nil { + log.Fatalln(err) + } // 模板解析 - t, err := template.New(name).ParseFS(fs, path) + t, err := template.New(path).Parse(string(b)) if err != nil { log.Fatalln(err) } diff --git a/cmd/internal/grpc/README.tmpl b/cmd/internal/grpc/README.md similarity index 100% rename from cmd/internal/grpc/README.tmpl rename to cmd/internal/grpc/README.md diff --git a/cmd/internal/grpc/dockerignore.tmpl b/cmd/internal/grpc/dockerignore similarity index 100% rename from cmd/internal/grpc/dockerignore.tmpl rename to cmd/internal/grpc/dockerignore diff --git a/cmd/internal/grpc/embed.go b/cmd/internal/grpc/embed.go index 4f51a32..537efa5 100644 --- a/cmd/internal/grpc/embed.go +++ b/cmd/internal/grpc/embed.go @@ -4,229 +4,3 @@ import "embed" //go:embed all:* var FS embed.FS - -var Project = []map[string]string{ - { - "name": "config_toml.tmpl", - "path": "config_toml.tmpl", - "output": "config.toml.example", - }, - { - "name": "dockerignore.tmpl", - "path": "dockerignore.tmpl", - "output": ".dockerignore", - }, - { - "name": "gitignore.tmpl", - "path": "gitignore.tmpl", - "output": ".gitignore", - }, - { - "name": "pkg_lib_db_db.tmpl", - "path": "pkg_lib_db_db.tmpl", - "output": "pkg/lib/db/db.go", - }, - { - "name": "pkg_lib_db_mixin.tmpl", - "path": "pkg_lib_db_mixin.tmpl", - "output": "pkg/lib/db/mixin.go", - }, - { - "name": "pkg_lib_db_redis.tmpl", - "path": "pkg_lib_db_redis.tmpl", - "output": "pkg/lib/db/redis.go", - }, - { - "name": "pkg_lib_identity_identity.tmpl", - "path": "pkg_lib_identity_identity.tmpl", - "output": "pkg/lib/identity/identity.go", - }, - { - "name": "pkg_lib_log_init.tmpl", - "path": "pkg_lib_log_init.tmpl", - "output": "pkg/lib/log/init.go", - }, - { - "name": "pkg_lib_log_log.tmpl", - "path": "pkg_lib_log_log.tmpl", - "output": "pkg/lib/log/log.go", - }, - { - "name": "pkg_lib_log_traceid.tmpl", - "path": "pkg_lib_log_traceid.tmpl", - "output": "pkg/lib/log/trace_id.go", - }, - { - "name": "pkg_lib_middleware_log.tmpl", - "path": "pkg_lib_middleware_log.tmpl", - "output": "pkg/lib/middleware/log.go", - }, - { - "name": "pkg_lib_middleware_monitor.tmpl", - "path": "pkg_lib_middleware_monitor.tmpl", - "output": "pkg/lib/middleware/monitor.go", - }, - { - "name": "pkg_lib_middleware_recovery.tmpl", - "path": "pkg_lib_middleware_recovery.tmpl", - "output": "pkg/lib/middleware/recovery.go", - }, - { - "name": "pkg_lib_middleware_traceid.tmpl", - "path": "pkg_lib_middleware_traceid.tmpl", - "output": "pkg/lib/middleware/trace_id.go", - }, - { - "name": "pkg_lib_middleware_validator.tmpl", - "path": "pkg_lib_middleware_validator.tmpl", - "output": "pkg/lib/middleware/validator.go", - }, - { - "name": "pkg_lib_result_code.tmpl", - "path": "pkg_lib_result_code.tmpl", - "output": "pkg/lib/result/code.go", - }, - { - "name": "pkg_lib_result_status.tmpl", - "path": "pkg_lib_result_status.tmpl", - "output": "pkg/lib/result/status.go", - }, - { - "name": "pkg_lib_util_validator.tmpl", - "path": "pkg_lib_util_validator.tmpl", - "output": "pkg/lib/util/validator.go", - }, - { - "name": "README.tmpl", - "path": "README.tmpl", - "output": "README.md", - }, -} - -var App = []map[string]string{ - { - "name": "buf.tmpl", - "path": "app/buf.tmpl", - "output": "buf.yaml", - }, - { - "name": "buf_gen.tmpl", - "path": "app/buf_gen.tmpl", - "output": "buf.gen.yaml", - }, - { - "name": "proto_buf_validate.tmpl", - "path": "app/proto_buf_validate.tmpl", - "output": "api/buf/validate/validate.proto", - }, - { - "name": "proto_google_annotations.tmpl", - "path": "app/proto_google_annotations.tmpl", - "output": "api/google/api/annotations.proto", - }, - { - "name": "proto_google_http.tmpl", - "path": "app/proto_google_http.tmpl", - "output": "api/google/api/http.proto", - }, - { - "name": "proto_greeter.tmpl", - "path": "app/proto_greeter.tmpl", - "output": "api/greeter.proto", - }, - { - "name": "pb_greeter.tmpl", - "path": "app/pb_greeter.tmpl", - "output": "api/greeter.pb.go", - }, - { - "name": "pb_greeter_grpc.tmpl", - "path": "app/pb_greeter_grpc.tmpl", - "output": "api/greeter_grpc.pb.go", - }, - { - "name": "pb_greeter_gw.tmpl", - "path": "app/pb_greeter_gw.tmpl", - "output": "api/greeter.pb.gw.go", - }, - { - "name": "swagger_api.tmpl", - "path": "app/swagger_api.tmpl", - "output": "api/api.swagger.json", - }, - { - "name": "pkg_app_cmd_hello.tmpl", - "path": "app/pkg_app_cmd_hello.tmpl", - "output": "cmd/hello.go", - }, - { - "name": "pkg_app_cmd_init.tmpl", - "path": "app/pkg_app_cmd_init.tmpl", - "output": "cmd/init.go", - }, - { - "name": "pkg_app_cmd_root.tmpl", - "path": "app/pkg_app_cmd_root.tmpl", - "output": "cmd/root.go", - }, - { - "name": "pkg_app_ent_db.tmpl", - "path": "app/pkg_app_ent_db.tmpl", - "output": "ent/db.go", - }, - { - "name": "pkg_app_ent_generate.tmpl", - "path": "app/pkg_app_ent_generate.tmpl", - "output": "ent/generate.go", - }, - { - "name": "pkg_app_ent_gitignore.tmpl", - "path": "app/pkg_app_ent_gitignore.tmpl", - "output": "ent/.gitignore", - }, - { - "name": "pkg_app_ent_schema_demo.tmpl", - "path": "app/pkg_app_ent_schema_demo.tmpl", - "output": "ent/schema/demo.go", - }, - { - "name": "pkg_app_server_grpc.tmpl", - "path": "app/pkg_app_server_grpc.tmpl", - "output": "server/grpc.go", - }, - { - "name": "pkg_app_server_http.tmpl", - "path": "app/pkg_app_server_http.tmpl", - "output": "server/http.go", - }, - { - "name": "pkg_app_service_greeter.tmpl", - "path": "app/pkg_app_service_greeter.tmpl", - "output": "service/greeter.go", - }, - { - "name": "pkg_app_service_test.tmpl", - "path": "app/pkg_app_service_test.tmpl", - "output": "service/service_test.go", - }, - { - "name": "pkg_app_main.tmpl", - "path": "app/pkg_app_main.tmpl", - "output": "main.go", - }, - { - "name": "dockerfile.tmpl", - "path": "app/dockerfile.tmpl", - "output": "Dockerfile", - }, - { - "name": "config_toml.tmpl", - "path": "config_toml.tmpl", - "output": "config.toml", - }, - { - "name": "README.tmpl", - "path": "app/README.tmpl", - "output": "README.md", - }, -} diff --git a/cmd/internal/grpc/gitignore.tmpl b/cmd/internal/grpc/gitignore similarity index 100% rename from cmd/internal/grpc/gitignore.tmpl rename to cmd/internal/grpc/gitignore diff --git a/cmd/internal/grpc/app/dockerfile.tmpl b/cmd/internal/grpc/pkg/app/Dockerfile similarity index 77% rename from cmd/internal/grpc/app/dockerfile.tmpl rename to cmd/internal/grpc/pkg/app/Dockerfile index 484dba9..e9af4c6 100644 --- a/cmd/internal/grpc/app/dockerfile.tmpl +++ b/cmd/internal/grpc/pkg/app/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.23.1 AS builder +FROM golang:1.23 AS builder WORKDIR /project @@ -9,7 +9,7 @@ RUN go mod download RUN go generate ./pkg/{{.AppPkg}}/ent RUN go mod tidy -RUN CGO_ENABLED=0 go build ./pkg/{{.AppPkg}} -o ./bin/main +RUN CGO_ENABLED=0 go build -o ./bin/main ./pkg/{{.AppPkg}} FROM scratch diff --git a/cmd/internal/grpc/app/README.tmpl b/cmd/internal/grpc/pkg/app/README.md similarity index 100% rename from cmd/internal/grpc/app/README.tmpl rename to cmd/internal/grpc/pkg/app/README.md diff --git a/cmd/internal/grpc/app/swagger_api.tmpl b/cmd/internal/grpc/pkg/app/api/api.swagger.json similarity index 100% rename from cmd/internal/grpc/app/swagger_api.tmpl rename to cmd/internal/grpc/pkg/app/api/api.swagger.json diff --git a/cmd/internal/grpc/app/proto_buf_validate.tmpl b/cmd/internal/grpc/pkg/app/api/buf/validate/validate.proto similarity index 100% rename from cmd/internal/grpc/app/proto_buf_validate.tmpl rename to cmd/internal/grpc/pkg/app/api/buf/validate/validate.proto diff --git a/cmd/internal/grpc/app/proto_google_annotations.tmpl b/cmd/internal/grpc/pkg/app/api/google/api/annotations.proto similarity index 100% rename from cmd/internal/grpc/app/proto_google_annotations.tmpl rename to cmd/internal/grpc/pkg/app/api/google/api/annotations.proto diff --git a/cmd/internal/grpc/app/proto_google_http.tmpl b/cmd/internal/grpc/pkg/app/api/google/api/http.proto similarity index 100% rename from cmd/internal/grpc/app/proto_google_http.tmpl rename to cmd/internal/grpc/pkg/app/api/google/api/http.proto diff --git a/cmd/internal/grpc/app/pb_greeter_gw.tmpl b/cmd/internal/grpc/pkg/app/api/greeter.pb.gw.yiigo similarity index 100% rename from cmd/internal/grpc/app/pb_greeter_gw.tmpl rename to cmd/internal/grpc/pkg/app/api/greeter.pb.gw.yiigo diff --git a/cmd/internal/grpc/app/pb_greeter.tmpl b/cmd/internal/grpc/pkg/app/api/greeter.pb.yiigo similarity index 100% rename from cmd/internal/grpc/app/pb_greeter.tmpl rename to cmd/internal/grpc/pkg/app/api/greeter.pb.yiigo diff --git a/cmd/internal/grpc/app/proto_greeter.tmpl b/cmd/internal/grpc/pkg/app/api/greeter.proto similarity index 100% rename from cmd/internal/grpc/app/proto_greeter.tmpl rename to cmd/internal/grpc/pkg/app/api/greeter.proto diff --git a/cmd/internal/grpc/app/pb_greeter_grpc.tmpl b/cmd/internal/grpc/pkg/app/api/greeter_grpc.pb.yiigo similarity index 100% rename from cmd/internal/grpc/app/pb_greeter_grpc.tmpl rename to cmd/internal/grpc/pkg/app/api/greeter_grpc.pb.yiigo diff --git a/cmd/internal/grpc/app/buf.tmpl b/cmd/internal/grpc/pkg/app/buf.yaml similarity index 100% rename from cmd/internal/grpc/app/buf.tmpl rename to cmd/internal/grpc/pkg/app/buf.yaml diff --git a/cmd/internal/grpc/app/buf_gen.tmpl b/cmd/internal/grpc/pkg/app/buf_gen.yaml similarity index 100% rename from cmd/internal/grpc/app/buf_gen.tmpl rename to cmd/internal/grpc/pkg/app/buf_gen.yaml diff --git a/cmd/internal/grpc/app/pkg_app_cmd_hello.tmpl b/cmd/internal/grpc/pkg/app/cmd/hello.yiigo similarity index 100% rename from cmd/internal/grpc/app/pkg_app_cmd_hello.tmpl rename to cmd/internal/grpc/pkg/app/cmd/hello.yiigo diff --git a/cmd/internal/grpc/app/pkg_app_cmd_init.tmpl b/cmd/internal/grpc/pkg/app/cmd/init.yiigo similarity index 92% rename from cmd/internal/grpc/app/pkg_app_cmd_init.tmpl rename to cmd/internal/grpc/pkg/app/cmd/init.yiigo index 5dccabe..2c997fd 100644 --- a/cmd/internal/grpc/app/pkg_app_cmd_init.tmpl +++ b/cmd/internal/grpc/pkg/app/cmd/init.yiigo @@ -3,9 +3,9 @@ package cmd import ( "context" - "go.uber.org/zap" + "{{.Module}}/pkg/internal/log" - "{{.Module}}/pkg/lib/log" + "go.uber.org/zap" ) func Init() { diff --git a/cmd/internal/grpc/app/pkg_app_cmd_root.tmpl b/cmd/internal/grpc/pkg/app/cmd/root.yiigo similarity index 94% rename from cmd/internal/grpc/app/pkg_app_cmd_root.tmpl rename to cmd/internal/grpc/pkg/app/cmd/root.yiigo index a7d07c9..46aa657 100644 --- a/cmd/internal/grpc/app/pkg_app_cmd_root.tmpl +++ b/cmd/internal/grpc/pkg/app/cmd/root.yiigo @@ -6,14 +6,14 @@ import ( "os/signal" "syscall" + "{{.Module}}/pkg/{{.AppPkg}}/ent" + "{{.Module}}/pkg/{{.AppPkg}}/server" + "{{.Module}}/pkg/internal/log" + "{{.Module}}/pkg/internal/validator" + "github.com/spf13/cobra" "github.com/spf13/viper" "go.uber.org/zap" - - "{{.Module}}/pkg/{{.AppPkg}}/ent" - "{{.Module}}/pkg/{{.AppPkg}}/server" - "{{.Module}}/pkg/lib/log" - "{{.Module}}/pkg/lib/util" ) var cfgFile string @@ -46,8 +46,8 @@ var root = &cobra.Command{ func preInit(ctx context.Context) { // 初始化日志 log.Init() - // 初始化Util - util.Init(ctx) + // 初始化Validator + validator.Init(ctx) // 初始化数据库 ent.Init(ctx) } diff --git a/cmd/internal/grpc/config_toml.tmpl b/cmd/internal/grpc/pkg/app/config.toml similarity index 100% rename from cmd/internal/grpc/config_toml.tmpl rename to cmd/internal/grpc/pkg/app/config.toml diff --git a/cmd/internal/grpc/pkg/app/dockerun.sh b/cmd/internal/grpc/pkg/app/dockerun.sh new file mode 100644 index 0000000..be3089b --- /dev/null +++ b/cmd/internal/grpc/pkg/app/dockerun.sh @@ -0,0 +1,12 @@ +#!/bin/bash +docker rm -f app_{{.AppName}} +docker rmi -f img_{{.AppName}} + +{{- if eq .DockerF "Dockerfile"}} +docker build -t img_{{.AppName}} . +{{- else}} +docker build -f {{.DockerF}} -t img_{{.AppName}} . +{{- end}} +docker image prune -f + +docker run -d --name=app_{{.AppName}} --restart=always --privileged -p 10085:50051 -p 10086:8000 -v /data/app_{{.AppName}}:/data img_{{.AppName}} diff --git a/cmd/internal/http/app/pkg_app_ent_db.tmpl b/cmd/internal/grpc/pkg/app/ent/db.yiigo similarity index 94% rename from cmd/internal/http/app/pkg_app_ent_db.tmpl rename to cmd/internal/grpc/pkg/app/ent/db.yiigo index 7c017d7..a8defd2 100644 --- a/cmd/internal/http/app/pkg_app_ent_db.tmpl +++ b/cmd/internal/grpc/pkg/app/ent/db.yiigo @@ -5,11 +5,11 @@ import ( "fmt" "runtime/debug" + "{{.Module}}/pkg/internal/db" + "{{.Module}}/pkg/internal/log" + "entgo.io/ent/dialect" "go.uber.org/zap" - - "{{.Module}}/pkg/lib/db" - "{{.Module}}/pkg/lib/log" ) var DB *Client diff --git a/cmd/internal/grpc/app/pkg_app_ent_generate.tmpl b/cmd/internal/grpc/pkg/app/ent/generate.yiigo similarity index 100% rename from cmd/internal/grpc/app/pkg_app_ent_generate.tmpl rename to cmd/internal/grpc/pkg/app/ent/generate.yiigo diff --git a/cmd/internal/grpc/app/pkg_app_ent_gitignore.tmpl b/cmd/internal/grpc/pkg/app/ent/gitignore similarity index 100% rename from cmd/internal/grpc/app/pkg_app_ent_gitignore.tmpl rename to cmd/internal/grpc/pkg/app/ent/gitignore diff --git a/cmd/internal/grpc/app/pkg_app_ent_schema_demo.tmpl b/cmd/internal/grpc/pkg/app/ent/schema/demo.yiigo similarity index 96% rename from cmd/internal/grpc/app/pkg_app_ent_schema_demo.tmpl rename to cmd/internal/grpc/pkg/app/ent/schema/demo.yiigo index 365c531..db29ee6 100644 --- a/cmd/internal/grpc/app/pkg_app_ent_schema_demo.tmpl +++ b/cmd/internal/grpc/pkg/app/ent/schema/demo.yiigo @@ -1,12 +1,12 @@ package schema import ( + "{{.Module}}/pkg/internal/db" + "entgo.io/ent" "entgo.io/ent/dialect/entsql" "entgo.io/ent/schema" "entgo.io/ent/schema/field" - - "{{.Module}}/pkg/lib/db" ) // Demo holds the schema definition for the Demo entity. diff --git a/cmd/internal/grpc/app/pkg_app_main.tmpl b/cmd/internal/grpc/pkg/app/main.yiigo similarity index 100% rename from cmd/internal/grpc/app/pkg_app_main.tmpl rename to cmd/internal/grpc/pkg/app/main.yiigo diff --git a/cmd/internal/grpc/app/pkg_app_server_grpc.tmpl b/cmd/internal/grpc/pkg/app/server/grpc.yiigo similarity index 93% rename from cmd/internal/grpc/app/pkg_app_server_grpc.tmpl rename to cmd/internal/grpc/pkg/app/server/grpc.yiigo index 144df4a..3c9ebf8 100644 --- a/cmd/internal/grpc/app/pkg_app_server_grpc.tmpl +++ b/cmd/internal/grpc/pkg/app/server/grpc.yiigo @@ -4,16 +4,16 @@ import ( "context" "net" + "{{.Module}}/pkg/{{.AppPkg}}/api" + "{{.Module}}/pkg/{{.AppPkg}}/service" + "{{.Module}}/pkg/internal/log" + "{{.Module}}/pkg/internal/middleware" + "github.com/spf13/viper" "go.uber.org/zap" "google.golang.org/grpc" "google.golang.org/grpc/health" "google.golang.org/grpc/health/grpc_health_v1" - - "{{.Module}}/pkg/{{.AppPkg}}/api" - "{{.Module}}/pkg/{{.AppPkg}}/service" - "{{.Module}}/pkg/lib/log" - "{{.Module}}/pkg/lib/middleware" ) func ServeGrpc(ctx context.Context) { diff --git a/cmd/internal/grpc/app/pkg_app_server_http.tmpl b/cmd/internal/grpc/pkg/app/server/http.yiigo similarity index 81% rename from cmd/internal/grpc/app/pkg_app_server_http.tmpl rename to cmd/internal/grpc/pkg/app/server/http.yiigo index b6286f0..f2ec8cd 100644 --- a/cmd/internal/grpc/app/pkg_app_server_http.tmpl +++ b/cmd/internal/grpc/pkg/app/server/http.yiigo @@ -7,6 +7,10 @@ import ( "strings" "time" + "{{.Module}}/pkg/{{.AppPkg}}/api" + "{{.Module}}/pkg/internal/log" + "{{.Module}}/pkg/internal/trace" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/rs/cors" "github.com/spf13/viper" @@ -14,9 +18,6 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/health/grpc_health_v1" - - "{{.Module}}/pkg/{{.AppPkg}}/api" - "{{.Module}}/pkg/lib/log" ) func ServeHttp(ctx context.Context) { @@ -37,11 +38,18 @@ func ServeHttp(ctx context.Context) { return strings.ToLower(s), true }), runtime.WithOutgoingHeaderMatcher(func(s string) (string, bool) { - if s == log.TraceId { + if s == trace.TraceId { return s, true } return runtime.MetadataHeaderPrefix + s, true }), + runtime.WithErrorHandler(func(ctx context.Context, mux *runtime.ServeMux, m runtime.Marshaler, w http.ResponseWriter, r *http.Request, err error) { + _err := &runtime.HTTPStatusError{ + HTTPStatus: http.StatusOK, + Err: err, + } + runtime.DefaultHTTPErrorHandler(ctx, mux, m, w, r, _err) + }), ) // Register http handler if err = registerHttp(ctx, mux, conn); err != nil { @@ -55,7 +63,7 @@ func ServeHttp(ctx context.Context) { }, AllowedMethods: []string{http.MethodGet, http.MethodPost, http.MethodPatch, http.MethodPut, http.MethodDelete, http.MethodOptions}, AllowedHeaders: []string{"Authorization", "Content-Type", "withCredentials"}, - ExposedHeaders: []string{log.TraceId}, // 服务器暴露一些自定义的头信息,允许客户端访问 + ExposedHeaders: []string{trace.TraceId}, // 服务器暴露一些自定义的头信息,允许客户端访问 AllowCredentials: true, }).Handler(mux) // Serve HTTP server diff --git a/cmd/internal/grpc/app/pkg_app_service_greeter.tmpl b/cmd/internal/grpc/pkg/app/service/greeter.yiigo similarity index 100% rename from cmd/internal/grpc/app/pkg_app_service_greeter.tmpl rename to cmd/internal/grpc/pkg/app/service/greeter.yiigo diff --git a/cmd/internal/grpc/app/pkg_app_service_test.tmpl b/cmd/internal/grpc/pkg/app/service/service_test.yiigo similarity index 93% rename from cmd/internal/grpc/app/pkg_app_service_test.tmpl rename to cmd/internal/grpc/pkg/app/service/service_test.yiigo index 34d337c..40577d7 100644 --- a/cmd/internal/grpc/app/pkg_app_service_test.tmpl +++ b/cmd/internal/grpc/pkg/app/service/service_test.yiigo @@ -4,10 +4,10 @@ import ( "context" "testing" - "github.com/spf13/viper" - "{{.Module}}/pkg/{{.AppPkg}}/ent" - "{{.Module}}/pkg/lib/log" + "{{.Module}}/pkg/internal/log" + + "github.com/spf13/viper" ) func TestMain(m *testing.M) { diff --git a/cmd/internal/http/pkg_lib_db_db.tmpl b/cmd/internal/grpc/pkg/internal/db/db.yiigo similarity index 97% rename from cmd/internal/http/pkg_lib_db_db.tmpl rename to cmd/internal/grpc/pkg/internal/db/db.yiigo index 1935ea5..4c01814 100644 --- a/cmd/internal/http/pkg_lib_db_db.tmpl +++ b/cmd/internal/grpc/pkg/internal/db/db.yiigo @@ -5,14 +5,14 @@ import ( "fmt" "time" + "{{.Module}}/pkg/internal/log" + "entgo.io/ent/dialect" entsql "entgo.io/ent/dialect/sql" "github.com/shenghui0779/yiigo" "github.com/spf13/cast" "github.com/spf13/viper" "go.uber.org/zap" - - "{{.Module}}/pkg/lib/log" ) // InitDBDriver 初始化Ent实例(如有多个实例,在此方法中初始化) diff --git a/cmd/internal/grpc/pkg_lib_db_mixin.tmpl b/cmd/internal/grpc/pkg/internal/db/mixin.yiigo similarity index 100% rename from cmd/internal/grpc/pkg_lib_db_mixin.tmpl rename to cmd/internal/grpc/pkg/internal/db/mixin.yiigo diff --git a/cmd/internal/grpc/pkg_lib_db_redis.tmpl b/cmd/internal/grpc/pkg/internal/db/redis.yiigo similarity index 100% rename from cmd/internal/grpc/pkg_lib_db_redis.tmpl rename to cmd/internal/grpc/pkg/internal/db/redis.yiigo diff --git a/cmd/internal/http/pkg_lib_identity_identity.tmpl b/cmd/internal/grpc/pkg/internal/identity/identity.yiigo similarity index 98% rename from cmd/internal/http/pkg_lib_identity_identity.tmpl rename to cmd/internal/grpc/pkg/internal/identity/identity.yiigo index a9271e3..9d39ca6 100644 --- a/cmd/internal/http/pkg_lib_identity_identity.tmpl +++ b/cmd/internal/grpc/pkg/internal/identity/identity.yiigo @@ -7,11 +7,11 @@ import ( "encoding/json" "fmt" + "{{.Module}}/pkg/internal/log" + "github.com/shenghui0779/yiigo/xcrypto" "github.com/spf13/viper" "go.uber.org/zap" - - "{{.Module}}/pkg/lib/log" ) // Identity 授权身份 diff --git a/cmd/internal/grpc/pkg_lib_log_init.tmpl b/cmd/internal/grpc/pkg/internal/log/init.yiigo similarity index 100% rename from cmd/internal/grpc/pkg_lib_log_init.tmpl rename to cmd/internal/grpc/pkg/internal/log/init.yiigo diff --git a/cmd/internal/grpc/pkg_lib_log_log.tmpl b/cmd/internal/grpc/pkg/internal/log/log.yiigo similarity index 56% rename from cmd/internal/grpc/pkg_lib_log_log.tmpl rename to cmd/internal/grpc/pkg/internal/log/log.yiigo index 087077b..24db746 100644 --- a/cmd/internal/grpc/pkg_lib_log_log.tmpl +++ b/cmd/internal/grpc/pkg/internal/log/log.yiigo @@ -5,35 +5,39 @@ import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" + "google.golang.org/grpc/metadata" ) func Info(ctx context.Context, msg string, fields ...zap.Field) { - Log(ctx, zapcore.InfoLevel, msg, fields...) + logCtx(ctx, zapcore.InfoLevel, msg, fields...) } func Warn(ctx context.Context, msg string, fields ...zap.Field) { - Log(ctx, zapcore.WarnLevel, msg, fields...) + logCtx(ctx, zapcore.WarnLevel, msg, fields...) } func Error(ctx context.Context, msg string, fields ...zap.Field) { - Log(ctx, zapcore.ErrorLevel, msg, fields...) + logCtx(ctx, zapcore.ErrorLevel, msg, fields...) } func Panic(ctx context.Context, msg string, fields ...zap.Field) { - Log(ctx, zapcore.PanicLevel, msg, fields...) + logCtx(ctx, zapcore.PanicLevel, msg, fields...) } func Fatal(ctx context.Context, msg string, fields ...zap.Field) { - Log(ctx, zapcore.FatalLevel, msg, fields...) + logCtx(ctx, zapcore.FatalLevel, msg, fields...) } -func Log(ctx context.Context, level zapcore.Level, msg string, fields ...zap.Field) { - traceId, fullMethod := GetTraceInfo(ctx) - fields = append(fields, - zap.String("hostname", hostname), - zap.String("trace_id", traceId), - zap.String("method", fullMethod), - ) +func logCtx(ctx context.Context, level zapcore.Level, msg string, fields ...zap.Field) { + if md, ok := metadata.FromIncomingContext(ctx); ok && len(md) != 0 { + for k, v := range md { + if len(v) == 1 { + fields = append(fields, zap.String(k, v[0])) + } else { + fields = append(fields, zap.Strings(k, v)) + } + } + } switch level { case zapcore.InfoLevel: logger.Info(msg, fields...) diff --git a/cmd/internal/grpc/pkg_lib_middleware_log.tmpl b/cmd/internal/grpc/pkg/internal/middleware/log.yiigo similarity index 57% rename from cmd/internal/grpc/pkg_lib_middleware_log.tmpl rename to cmd/internal/grpc/pkg/internal/middleware/log.yiigo index da0fbb3..6abcc2f 100644 --- a/cmd/internal/grpc/pkg_lib_middleware_log.tmpl +++ b/cmd/internal/grpc/pkg/internal/middleware/log.yiigo @@ -5,10 +5,10 @@ import ( "encoding/json" "time" + "{{.Module}}/pkg/internal/log" + "go.uber.org/zap" "google.golang.org/grpc" - - "{{.Module}}/pkg/lib/log" ) const HealthCheckMethod = "/grpc.health.v1.Health/Check" @@ -19,12 +19,19 @@ func Log(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handl if info.FullMethod == HealthCheckMethod { return } - b1, _ := json.Marshal(req) - b2, _ := json.Marshal(resp) - log.Info(ctx, "Request info", - zap.ByteString("request", b1), - zap.ByteString("response", b2), - zap.Error(err), + // request + reqBody, _ := json.Marshal(req) + // response + var respBody []byte + if err != nil { + respBody = []byte(err.Error()) + } else { + respBody, _ = json.Marshal(resp) + } + log.Info(ctx, "request info", + zap.String("method", info.FullMethod), + zap.ByteString("request", reqBody), + zap.ByteString("response", respBody), zap.String("duration", time.Since(now).String()), ) }() diff --git a/cmd/internal/grpc/pkg_lib_middleware_monitor.tmpl b/cmd/internal/grpc/pkg/internal/middleware/monitor.yiigo similarity index 81% rename from cmd/internal/grpc/pkg_lib_middleware_monitor.tmpl rename to cmd/internal/grpc/pkg/internal/middleware/monitor.yiigo index bd41b8e..f21e435 100644 --- a/cmd/internal/grpc/pkg_lib_middleware_monitor.tmpl +++ b/cmd/internal/grpc/pkg/internal/middleware/monitor.yiigo @@ -6,8 +6,6 @@ import ( "github.com/prometheus/client_golang/prometheus" "google.golang.org/grpc" - - "{{.Module}}/pkg/lib/log" ) var ( @@ -32,12 +30,11 @@ func init() { } // Monitor 监控请求次数,时长 -func Monitor(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { +func Monitor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { now := time.Now().Local() - _, fullMethod := log.GetTraceInfo(ctx) // 请求次数 requestCounter.With(prometheus.Labels{ - "method": fullMethod, + "method": info.FullMethod, }).Inc() // 请求时长 defer func() { @@ -46,7 +43,7 @@ func Monitor(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, hand status = "ERR" } requestDuration.With(prometheus.Labels{ - "method": fullMethod, + "method": info.FullMethod, "status": status, }).Observe(time.Since(now).Seconds()) }() diff --git a/cmd/internal/grpc/pkg_lib_middleware_recovery.tmpl b/cmd/internal/grpc/pkg/internal/middleware/recovery.yiigo similarity index 93% rename from cmd/internal/grpc/pkg_lib_middleware_recovery.tmpl rename to cmd/internal/grpc/pkg/internal/middleware/recovery.yiigo index 069beec..bf8411b 100644 --- a/cmd/internal/grpc/pkg_lib_middleware_recovery.tmpl +++ b/cmd/internal/grpc/pkg/internal/middleware/recovery.yiigo @@ -7,7 +7,7 @@ import ( "go.uber.org/zap" "google.golang.org/grpc" - "{{.Module}}/pkg/lib/log" + "{{.Module}}/pkg/internal/log" ) func Recovery(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { diff --git a/cmd/internal/grpc/pkg_lib_middleware_traceid.tmpl b/cmd/internal/grpc/pkg/internal/middleware/trace_id.yiigo similarity index 57% rename from cmd/internal/grpc/pkg_lib_middleware_traceid.tmpl rename to cmd/internal/grpc/pkg/internal/middleware/trace_id.yiigo index d3fba72..2d0af58 100644 --- a/cmd/internal/grpc/pkg_lib_middleware_traceid.tmpl +++ b/cmd/internal/grpc/pkg/internal/middleware/trace_id.yiigo @@ -3,10 +3,10 @@ package middleware import ( "context" + "{{.Module}}/pkg/internal/trace" + "google.golang.org/grpc" "google.golang.org/grpc/metadata" - - "{{.Module}}/pkg/lib/log" ) // TraceId is a middleware that injects a trace ID into the context of each request. @@ -15,11 +15,16 @@ func TraceId(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, h if !ok { md = metadata.Pairs() } - if v := md.Get(log.TraceId); len(v) == 0 { - md.Set(log.TraceId, log.NewTraceId()) + md.Set(trace.TraceHost, trace.Hostname()) + // traceId + var traceId string + if vals := md.Get(trace.TraceId); len(vals) != 0 { + traceId = vals[0] + } else { + traceId = trace.NewTraceId() + md.Set(trace.TraceId, traceId) } - md.Set(log.TraceMethod, info.FullMethod) - // set the response header - _ = grpc.SetHeader(ctx, metadata.Pairs(log.TraceId, md.Get(log.TraceId)[0])) + // set response header + _ = grpc.SetHeader(ctx, metadata.Pairs(trace.TraceId, traceId)) return handler(metadata.NewIncomingContext(ctx, md), req) } diff --git a/cmd/internal/grpc/pkg_lib_middleware_validator.tmpl b/cmd/internal/grpc/pkg/internal/middleware/validator.yiigo similarity index 76% rename from cmd/internal/grpc/pkg_lib_middleware_validator.tmpl rename to cmd/internal/grpc/pkg/internal/middleware/validator.yiigo index 50fb857..afb559b 100644 --- a/cmd/internal/grpc/pkg_lib_middleware_validator.tmpl +++ b/cmd/internal/grpc/pkg/internal/middleware/validator.yiigo @@ -4,17 +4,17 @@ import ( "context" "fmt" + "{{.Module}}/pkg/internal/result" + "{{.Module}}/pkg/internal/validator" + "google.golang.org/grpc" "google.golang.org/protobuf/proto" - - "{{.Module}}/pkg/lib/result" - "{{.Module}}/pkg/lib/util" ) func Validator(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { msg, ok := req.(proto.Message) if ok { - if err := util.Validate(msg); err != nil { + if err := validator.Check(msg); err != nil { return nil, result.ErrParams(fmt.Errorf("params invalid: %w", err)).Error(ctx) } } diff --git a/cmd/internal/grpc/pkg/internal/pgarray/array.yiigo b/cmd/internal/grpc/pkg/internal/pgarray/array.yiigo new file mode 100644 index 0000000..222e5e3 --- /dev/null +++ b/cmd/internal/grpc/pkg/internal/pgarray/array.yiigo @@ -0,0 +1,64 @@ +package pgarray + +import ( + "entgo.io/ent/dialect/sql" + "github.com/shenghui0779/yiigo/pgtype" +) + +// 查找包含单个元素的数组 +func Any(field string, value any) func(*sql.Selector) { + return func(s *sql.Selector) { + s.Where(sql.P(func(b *sql.Builder) { + b.Arg(value) + b.WriteString(" = ANY(") + b.Ident(s.C(field)) + b.WriteString(")") + })) + } +} + +// 查找包含多个元素的数组(oid指定pg数组元素类型) +func Contains[T ~[]E, E comparable](field string, values T, oid pgtype.Oid) func(*sql.Selector) { + if len(values) == 0 { + return func(selector *sql.Selector) {} + } + return func(s *sql.Selector) { + s.Where(sql.P(func(b *sql.Builder) { + b.Ident(s.C(field)) + b.WriteString(" @> ARRAY[") + if len(values) != 0 { + b.Arg(values[0]) + for _, v := range values[1:] { + b.WriteString(", ") + b.Arg(v) + } + } + b.WriteString("]::") + b.WriteString(pgtype.TypeName[oid]) + b.WriteString("[]") + })) + } +} + +// 查找有交集元素的数组(oid指定pg数组元素类型) +func Intersect[T ~[]E, E comparable](field string, values T, oid pgtype.Oid) func(*sql.Selector) { + if len(values) == 0 { + return func(selector *sql.Selector) {} + } + return func(s *sql.Selector) { + s.Where(sql.P(func(b *sql.Builder) { + b.Ident(s.C(field)) + b.WriteString(" && ARRAY[") + if len(values) != 0 { + b.Arg(values[0]) + for _, v := range values[1:] { + b.WriteString(", ") + b.Arg(v) + } + } + b.WriteString("]::") + b.WriteString(pgtype.TypeName[oid]) + b.WriteString("[]") + })) + } +} diff --git a/cmd/internal/grpc/pkg_lib_result_code.tmpl b/cmd/internal/grpc/pkg/internal/result/code.yiigo similarity index 100% rename from cmd/internal/grpc/pkg_lib_result_code.tmpl rename to cmd/internal/grpc/pkg/internal/result/code.yiigo diff --git a/cmd/internal/grpc/pkg_lib_result_status.tmpl b/cmd/internal/grpc/pkg/internal/result/status.yiigo similarity index 67% rename from cmd/internal/grpc/pkg_lib_result_status.tmpl rename to cmd/internal/grpc/pkg/internal/result/status.yiigo index 07eb8aa..2f33ffa 100644 --- a/cmd/internal/grpc/pkg_lib_result_status.tmpl +++ b/cmd/internal/grpc/pkg/internal/result/status.yiigo @@ -4,10 +4,11 @@ import ( "context" "fmt" + "{{.Module}}/pkg/internal/trace" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" - - "{{.Module}}/pkg/lib/log" ) type Status interface { @@ -20,7 +21,12 @@ type errstatus struct { } func (s *errstatus) Error(ctx context.Context) error { - traceId, _ := log.GetTraceInfo(ctx) + var traceId string + if md, ok := metadata.FromIncomingContext(ctx); ok { + if vals := md.Get(trace.TraceId); len(vals) != 0 { + traceId = vals[0] + } + } return status.Error(s.code, fmt.Sprintf("[%s] %+v", traceId, s.err)) } diff --git a/cmd/internal/grpc/pkg/internal/span/span.yiigo b/cmd/internal/grpc/pkg/internal/span/span.yiigo new file mode 100644 index 0000000..c45e13d --- /dev/null +++ b/cmd/internal/grpc/pkg/internal/span/span.yiigo @@ -0,0 +1,43 @@ +package span + +import ( + "context" + "runtime" + "strings" + "time" + + "{{.Module}}/pkg/internal/log" + + "go.uber.org/zap" +) + +type Span struct { + c context.Context + f string + l int + n string + t time.Time +} + +func (s *Span) Finish() { + log.Info(s.c, "time consume", zap.String("function", s.n), zap.String("duration", time.Since(s.t).String()), zap.String("file", s.f), zap.Int("line", s.l)) +} + +// New returns a span to log the time consume. +// +// Example: +// +// sp := span.New(ctx) +// defer sp.Finish() +func New(ctx context.Context) *Span { + sp := &Span{c: ctx, t: time.Now()} + // Skip level 1 to get the caller function + pc, file, line, _ := runtime.Caller(1) + sp.f, sp.l = file, line + // Get the function details + if fn := runtime.FuncForPC(pc); fn != nil { + name := fn.Name() + sp.n = name[strings.Index(name, ".")+1:] + } + return sp +} diff --git a/cmd/internal/grpc/pkg/internal/trace/trace_id.yiigo b/cmd/internal/grpc/pkg/internal/trace/trace_id.yiigo new file mode 100644 index 0000000..c643c32 --- /dev/null +++ b/cmd/internal/grpc/pkg/internal/trace/trace_id.yiigo @@ -0,0 +1,51 @@ +package trace + +import ( + "crypto/rand" + "encoding/base64" + "fmt" + "os" + "strings" + "sync/atomic" + + "github.com/shenghui0779/yiigo/xhash" +) + +const ( + TraceId = "x-trace-id" + TraceHost = "hostname" +) + +var ( + hostname string + prefix string + sequence uint64 +) + +func init() { + hostname, _ = os.Hostname() + if len(hostname) == 0 { + hostname = "localhost" + } + + var ( + buf [12]byte + b64 string + ) + for len(b64) < 10 { + _, _ = rand.Read(buf[:]) + b64 = base64.StdEncoding.EncodeToString(buf[:]) + b64 = strings.NewReplacer("+", "", "/", "").Replace(b64) + } + prefix = fmt.Sprintf("%s/%s", hostname, b64) +} + +func Hostname() string { + return hostname +} + +// NewTraceId generates a new trace ID in the sequence. +func NewTraceId() string { + seq := atomic.AddUint64(&sequence, 1) + return xhash.MD5(fmt.Sprintf("%s-%d", prefix, seq)) +} diff --git a/cmd/internal/http/pkg_lib_util.tmpl b/cmd/internal/grpc/pkg/internal/util/util.yiigo similarity index 93% rename from cmd/internal/http/pkg_lib_util.tmpl rename to cmd/internal/grpc/pkg/internal/util/util.yiigo index 91f8118..1df71b6 100644 --- a/cmd/internal/http/pkg_lib_util.tmpl +++ b/cmd/internal/grpc/pkg/internal/util/util.yiigo @@ -1,14 +1,14 @@ -package lib +package util import ( "context" "fmt" "runtime/debug" + "{{.Module}}/pkg/internal/log" + "github.com/shenghui0779/yiigo" "go.uber.org/zap" - - "{{.Module}}/pkg/lib/log" ) // Safe recover for goroutine when panic @@ -18,7 +18,6 @@ func Safe(ctx context.Context, fn func(ctx context.Context)) { log.Error(ctx, "Goroutine panic recovered", zap.Any("error", err), zap.ByteString("stack", debug.Stack())) } }() - fn(ctx) } diff --git a/cmd/internal/grpc/pkg_lib_util_validator.tmpl b/cmd/internal/grpc/pkg/internal/validator/validator.yiigo similarity index 79% rename from cmd/internal/grpc/pkg_lib_util_validator.tmpl rename to cmd/internal/grpc/pkg/internal/validator/validator.yiigo index acf0bce..3d49da1 100644 --- a/cmd/internal/grpc/pkg_lib_util_validator.tmpl +++ b/cmd/internal/grpc/pkg/internal/validator/validator.yiigo @@ -1,13 +1,13 @@ -package util +package validator import ( "context" + "{{.Module}}/pkg/internal/log" + "github.com/bufbuild/protovalidate-go" "go.uber.org/zap" "google.golang.org/protobuf/proto" - - "{{.Module}}/pkg/lib/log" ) var v *protovalidate.Validator @@ -20,6 +20,6 @@ func Init(ctx context.Context) { } } -func Validate(msg proto.Message) error { +func Check(msg proto.Message) error { return v.Validate(msg) } diff --git a/cmd/internal/grpc/pkg_lib_log_traceid.tmpl b/cmd/internal/grpc/pkg_lib_log_traceid.tmpl deleted file mode 100644 index 406aa3d..0000000 --- a/cmd/internal/grpc/pkg_lib_log_traceid.tmpl +++ /dev/null @@ -1,65 +0,0 @@ -package log - -import ( - "context" - "crypto/rand" - "encoding/base64" - "fmt" - "os" - "strings" - "sync/atomic" - - "github.com/shenghui0779/yiigo/xhash" - "google.golang.org/grpc/metadata" -) - -const ( - TraceId = "x-trace-id" - TraceMethod = "x-trace-method" -) - -var ( - hostname string - tracePrefix string - traceSeq uint64 -) - -func init() { - hostname, _ = os.Hostname() - if len(hostname) == 0 { - hostname = "localhost" - } - - var ( - buf [12]byte - b64 string - ) - for len(b64) < 10 { - _, _ = rand.Read(buf[:]) - b64 = base64.StdEncoding.EncodeToString(buf[:]) - b64 = strings.NewReplacer("+", "", "/", "").Replace(b64) - } - tracePrefix = fmt.Sprintf("%s/%s", hostname, b64) -} - -// NewTraceId generates a new trace ID in the sequence. -func NewTraceId() string { - seq := atomic.AddUint64(&traceSeq, 1) - return xhash.MD5(fmt.Sprintf("%s-%d", tracePrefix, seq)) -} - -func GetTraceInfo(ctx context.Context) (traceId, fullMethod string) { - traceId = "-" - fullMethod = "-" - md, ok := metadata.FromIncomingContext(ctx) - if !ok { - return - } - if v := md.Get(TraceId); len(v) != 0 { - traceId = v[0] - } - if v := md.Get(TraceMethod); len(v) != 0 { - fullMethod = v[0] - } - return -} diff --git a/cmd/internal/http/README.tmpl b/cmd/internal/http/README.md similarity index 100% rename from cmd/internal/http/README.tmpl rename to cmd/internal/http/README.md diff --git a/cmd/internal/http/dockerignore.tmpl b/cmd/internal/http/dockerignore similarity index 100% rename from cmd/internal/http/dockerignore.tmpl rename to cmd/internal/http/dockerignore diff --git a/cmd/internal/http/embed.go b/cmd/internal/http/embed.go index 35fee26..84f2bdc 100644 --- a/cmd/internal/http/embed.go +++ b/cmd/internal/http/embed.go @@ -4,204 +4,3 @@ import "embed" //go:embed all:* var FS embed.FS - -var Project = []map[string]string{ - { - "name": "config_toml.tmpl", - "path": "config_toml.tmpl", - "output": "config.toml.example", - }, - { - "name": "dockerignore.tmpl", - "path": "dockerignore.tmpl", - "output": ".dockerignore", - }, - { - "name": "gitignore.tmpl", - "path": "gitignore.tmpl", - "output": ".gitignore", - }, - { - "name": "pkg_lib_binding.tmpl", - "path": "pkg_lib_binding.tmpl", - "output": "pkg/lib/binding.go", - }, - { - "name": "pkg_lib_embed.tmpl", - "path": "pkg_lib_embed.tmpl", - "output": "pkg/lib/embed.go", - }, - { - "name": "pkg_lib_util.tmpl", - "path": "pkg_lib_util.tmpl", - "output": "pkg/lib/util.go", - }, - { - "name": "pkg_lib_db_db.tmpl", - "path": "pkg_lib_db_db.tmpl", - "output": "pkg/lib/db/db.go", - }, - { - "name": "pkg_lib_db_mixin.tmpl", - "path": "pkg_lib_db_mixin.tmpl", - "output": "pkg/lib/db/mixin.go", - }, - { - "name": "pkg_lib_db_redis.tmpl", - "path": "pkg_lib_db_redis.tmpl", - "output": "pkg/lib/db/redis.go", - }, - { - "name": "pkg_lib_identity_identity.tmpl", - "path": "pkg_lib_identity_identity.tmpl", - "output": "pkg/lib/identity/identity.go", - }, - { - "name": "pkg_lib_log_init.tmpl", - "path": "pkg_lib_log_init.tmpl", - "output": "pkg/lib/log/init.go", - }, - { - "name": "pkg_lib_log_log.tmpl", - "path": "pkg_lib_log_log.tmpl", - "output": "pkg/lib/log/log.go", - }, - { - "name": "pkg_lib_log_traceid.tmpl", - "path": "pkg_lib_log_traceid.tmpl", - "output": "pkg/lib/log/trace_id.go", - }, - { - "name": "pkg_lib_middleware_log.tmpl", - "path": "pkg_lib_middleware_log.tmpl", - "output": "pkg/lib/middleware/log.go", - }, - { - "name": "pkg_lib_middleware_monitor.tmpl", - "path": "pkg_lib_middleware_monitor.tmpl", - "output": "pkg/lib/middleware/monitor.go", - }, - { - "name": "pkg_lib_middleware_recovery.tmpl", - "path": "pkg_lib_middleware_recovery.tmpl", - "output": "pkg/lib/middleware/recovery.go", - }, - { - "name": "pkg_lib_middleware_traceid.tmpl", - "path": "pkg_lib_middleware_traceid.tmpl", - "output": "pkg/lib/middleware/trace_id.go", - }, - { - "name": "pkg_lib_result_code.tmpl", - "path": "pkg_lib_result_code.tmpl", - "output": "pkg/lib/result/code.go", - }, - { - "name": "pkg_lib_result_result.tmpl", - "path": "pkg_lib_result_result.tmpl", - "output": "pkg/lib/result/result.go", - }, - { - "name": "README.tmpl", - "path": "README.tmpl", - "output": "README.md", - }, -} - -var App = []map[string]string{ - { - "name": "pkg_app_api_greeter.tmpl", - "path": "app/pkg_app_api_greeter.tmpl", - "output": "api/greeter.go", - }, - { - "name": "pkg_app_middleware_auth.tmpl", - "path": "app/pkg_app_middleware_auth.tmpl", - "output": "middleware/auth.go", - }, - { - "name": "pkg_app_router_app.tmpl", - "path": "app/pkg_app_router_app.tmpl", - "output": "router/app.go", - }, - { - "name": "pkg_app_service_greeter.tmpl", - "path": "app/pkg_app_service_greeter.tmpl", - "output": "service/greeter/hello.go", - }, - { - "name": "pkg_app_service_test.tmpl", - "path": "app/pkg_app_service_test.tmpl", - "output": "service/greeter/greeter_test.go", - }, - { - "name": "pkg_app_cmd_hello.tmpl", - "path": "app/pkg_app_cmd_hello.tmpl", - "output": "cmd/hello.go", - }, - { - "name": "pkg_app_cmd_init.tmpl", - "path": "app/pkg_app_cmd_init.tmpl", - "output": "cmd/init.go", - }, - { - "name": "pkg_app_cmd_root.tmpl", - "path": "app/pkg_app_cmd_root.tmpl", - "output": "cmd/root.go", - }, - { - "name": "pkg_app_ent_db.tmpl", - "path": "app/pkg_app_ent_db.tmpl", - "output": "ent/db.go", - }, - { - "name": "pkg_app_ent_generate.tmpl", - "path": "app/pkg_app_ent_generate.tmpl", - "output": "ent/generate.go", - }, - { - "name": "pkg_app_ent_gitignore.tmpl", - "path": "app/pkg_app_ent_gitignore.tmpl", - "output": "ent/.gitignore", - }, - { - "name": "pkg_app_ent_schema_demo.tmpl", - "path": "app/pkg_app_ent_schema_demo.tmpl", - "output": "ent/schema/demo.go", - }, - { - "name": "pkg_app_main.tmpl", - "path": "app/pkg_app_main.tmpl", - "output": "main.go", - }, - { - "name": "dockerfile.tmpl", - "path": "app/dockerfile.tmpl", - "output": "Dockerfile", - }, - { - "name": "config_toml.tmpl", - "path": "config_toml.tmpl", - "output": "config.toml", - }, - { - "name": "pkg_app_web_dist_index.tmpl", - "path": "app/pkg_app_web_dist_index.tmpl", - "output": "web/dist/index.html", - }, - { - "name": "pkg_app_web_dist_style.tmpl", - "path": "app/pkg_app_web_dist_style.tmpl", - "output": "web/dist/style.css", - }, - { - "name": "pkg_app_web_embed.tmpl", - "path": "app/pkg_app_web_embed.tmpl", - "output": "web/embed.go", - }, - { - "name": "README.tmpl", - "path": "app/README.tmpl", - "output": "README.md", - }, -} diff --git a/cmd/internal/http/gitignore.tmpl b/cmd/internal/http/gitignore similarity index 100% rename from cmd/internal/http/gitignore.tmpl rename to cmd/internal/http/gitignore diff --git a/cmd/internal/http/app/dockerfile.tmpl b/cmd/internal/http/pkg/app/Dockerfile similarity index 77% rename from cmd/internal/http/app/dockerfile.tmpl rename to cmd/internal/http/pkg/app/Dockerfile index bb701a3..c3cd4b1 100644 --- a/cmd/internal/http/app/dockerfile.tmpl +++ b/cmd/internal/http/pkg/app/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.23.1 AS builder +FROM golang:1.23 AS builder WORKDIR /project @@ -9,7 +9,7 @@ RUN go mod download RUN go generate ./pkg/{{.AppPkg}}/ent RUN go mod tidy -RUN CGO_ENABLED=0 go build ./pkg/{{.AppPkg}} -o ./bin/main +RUN CGO_ENABLED=0 go build -o ./bin/main ./pkg/{{.AppPkg}} FROM scratch diff --git a/cmd/internal/http/app/README.tmpl b/cmd/internal/http/pkg/app/README.md similarity index 100% rename from cmd/internal/http/app/README.tmpl rename to cmd/internal/http/pkg/app/README.md diff --git a/cmd/internal/http/app/pkg_app_api_greeter.tmpl b/cmd/internal/http/pkg/app/api/greeter.yiigo similarity index 73% rename from cmd/internal/http/app/pkg_app_api_greeter.tmpl rename to cmd/internal/http/pkg/app/api/greeter.yiigo index 9f5dca0..b43c844 100644 --- a/cmd/internal/http/app/pkg_app_api_greeter.tmpl +++ b/cmd/internal/http/pkg/app/api/greeter.yiigo @@ -3,20 +3,20 @@ package api import ( "net/http" + "{{.Module}}/pkg/{{.AppPkg}}/service/greeter" + "{{.Module}}/pkg/internal/log" + "{{.Module}}/pkg/internal/result" + "{{.Module}}/pkg/internal/util" + "github.com/pkg/errors" "go.uber.org/zap" - - "{{.Module}}/pkg/{{.AppPkg}}/service/greeter" - "{{.Module}}/pkg/lib" - "{{.Module}}/pkg/lib/log" - "{{.Module}}/pkg/lib/result" ) func Hello(w http.ResponseWriter, r *http.Request) { ctx := r.Context() req := new(greeter.ReqHello) - if err := lib.BindJSON(r, req); err != nil { + if err := util.BindJSON(r, req); err != nil { log.Error(ctx, "Error params", zap.Error(err)) result.ErrParams(result.E(errors.WithMessage(err, "参数错误"))).JSON(w, r) return diff --git a/cmd/internal/http/app/pkg_app_cmd_hello.tmpl b/cmd/internal/http/pkg/app/cmd/hello.yiigo similarity index 100% rename from cmd/internal/http/app/pkg_app_cmd_hello.tmpl rename to cmd/internal/http/pkg/app/cmd/hello.yiigo diff --git a/cmd/internal/http/app/pkg_app_cmd_init.tmpl b/cmd/internal/http/pkg/app/cmd/init.yiigo similarity index 92% rename from cmd/internal/http/app/pkg_app_cmd_init.tmpl rename to cmd/internal/http/pkg/app/cmd/init.yiigo index 5dccabe..2c997fd 100644 --- a/cmd/internal/http/app/pkg_app_cmd_init.tmpl +++ b/cmd/internal/http/pkg/app/cmd/init.yiigo @@ -3,9 +3,9 @@ package cmd import ( "context" - "go.uber.org/zap" + "{{.Module}}/pkg/internal/log" - "{{.Module}}/pkg/lib/log" + "go.uber.org/zap" ) func Init() { diff --git a/cmd/internal/http/app/pkg_app_cmd_root.tmpl b/cmd/internal/http/pkg/app/cmd/root.yiigo similarity index 92% rename from cmd/internal/http/app/pkg_app_cmd_root.tmpl rename to cmd/internal/http/pkg/app/cmd/root.yiigo index 2340a21..a6e3962 100644 --- a/cmd/internal/http/app/pkg_app_cmd_root.tmpl +++ b/cmd/internal/http/pkg/app/cmd/root.yiigo @@ -8,17 +8,18 @@ import ( "syscall" "time" + "{{.Module}}/pkg/{{.AppPkg}}/ent" + "{{.Module}}/pkg/{{.AppPkg}}/router" + "{{.Module}}/pkg/internal/log" + "{{.Module}}/pkg/internal/middleware" + "{{.Module}}/pkg/internal/trace" + "github.com/go-chi/chi/v5" chi_middleware "github.com/go-chi/chi/v5/middleware" "github.com/rs/cors" "github.com/spf13/cobra" "github.com/spf13/viper" "go.uber.org/zap" - - "{{.Module}}/pkg/{{.AppPkg}}/ent" - "{{.Module}}/pkg/{{.AppPkg}}/router" - "{{.Module}}/pkg/lib/log" - "{{.Module}}/pkg/lib/middleware" ) var cfgFile string @@ -85,7 +86,7 @@ func serveHttp(ctx context.Context) { }, AllowedMethods: []string{http.MethodGet, http.MethodPost, http.MethodPatch, http.MethodPut, http.MethodDelete, http.MethodOptions}, AllowedHeaders: []string{"Authorization", "Content-Type", "withCredentials"}, - ExposedHeaders: []string{log.TraceId}, // 服务器暴露一些自定义的头信息,允许客户端访问 + ExposedHeaders: []string{trace.TraceId}, // 服务器暴露一些自定义的头信息,允许客户端访问 AllowCredentials: true, }) diff --git a/cmd/internal/http/config_toml.tmpl b/cmd/internal/http/pkg/app/config.toml similarity index 100% rename from cmd/internal/http/config_toml.tmpl rename to cmd/internal/http/pkg/app/config.toml diff --git a/cmd/internal/http/pkg/app/dockerun.sh b/cmd/internal/http/pkg/app/dockerun.sh new file mode 100644 index 0000000..0779020 --- /dev/null +++ b/cmd/internal/http/pkg/app/dockerun.sh @@ -0,0 +1,12 @@ +#!/bin/bash +docker rm -f app_{{.AppName}} +docker rmi -f img_{{.AppName}} + +{{- if eq .DockerF "Dockerfile"}} +docker build -t img_{{.AppName}} . +{{- else}} +docker build -f {{.DockerF}} -t img_{{.AppName}} . +{{- end}} +docker image prune -f + +docker run -d --name=app_{{.AppName}} --restart=always --privileged -p 10086:8000 -v /data/app_{{.AppName}}:/data img_{{.AppName}} diff --git a/cmd/internal/grpc/app/pkg_app_ent_db.tmpl b/cmd/internal/http/pkg/app/ent/db.yiigo similarity index 94% rename from cmd/internal/grpc/app/pkg_app_ent_db.tmpl rename to cmd/internal/http/pkg/app/ent/db.yiigo index 7c017d7..a8defd2 100644 --- a/cmd/internal/grpc/app/pkg_app_ent_db.tmpl +++ b/cmd/internal/http/pkg/app/ent/db.yiigo @@ -5,11 +5,11 @@ import ( "fmt" "runtime/debug" + "{{.Module}}/pkg/internal/db" + "{{.Module}}/pkg/internal/log" + "entgo.io/ent/dialect" "go.uber.org/zap" - - "{{.Module}}/pkg/lib/db" - "{{.Module}}/pkg/lib/log" ) var DB *Client diff --git a/cmd/internal/http/app/pkg_app_ent_generate.tmpl b/cmd/internal/http/pkg/app/ent/generate.yiigo similarity index 100% rename from cmd/internal/http/app/pkg_app_ent_generate.tmpl rename to cmd/internal/http/pkg/app/ent/generate.yiigo diff --git a/cmd/internal/http/app/pkg_app_ent_gitignore.tmpl b/cmd/internal/http/pkg/app/ent/gitignore similarity index 100% rename from cmd/internal/http/app/pkg_app_ent_gitignore.tmpl rename to cmd/internal/http/pkg/app/ent/gitignore diff --git a/cmd/internal/http/app/pkg_app_ent_schema_demo.tmpl b/cmd/internal/http/pkg/app/ent/schema/demo.yiigo similarity index 96% rename from cmd/internal/http/app/pkg_app_ent_schema_demo.tmpl rename to cmd/internal/http/pkg/app/ent/schema/demo.yiigo index 365c531..db29ee6 100644 --- a/cmd/internal/http/app/pkg_app_ent_schema_demo.tmpl +++ b/cmd/internal/http/pkg/app/ent/schema/demo.yiigo @@ -1,12 +1,12 @@ package schema import ( + "{{.Module}}/pkg/internal/db" + "entgo.io/ent" "entgo.io/ent/dialect/entsql" "entgo.io/ent/schema" "entgo.io/ent/schema/field" - - "{{.Module}}/pkg/lib/db" ) // Demo holds the schema definition for the Demo entity. diff --git a/cmd/internal/http/app/pkg_app_main.tmpl b/cmd/internal/http/pkg/app/main.yiigo similarity index 100% rename from cmd/internal/http/app/pkg_app_main.tmpl rename to cmd/internal/http/pkg/app/main.yiigo diff --git a/cmd/internal/http/app/pkg_app_middleware_auth.tmpl b/cmd/internal/http/pkg/app/middleware/auth.yiigo similarity index 82% rename from cmd/internal/http/app/pkg_app_middleware_auth.tmpl rename to cmd/internal/http/pkg/app/middleware/auth.yiigo index 0a55abd..491b7a7 100644 --- a/cmd/internal/http/app/pkg_app_middleware_auth.tmpl +++ b/cmd/internal/http/pkg/app/middleware/auth.yiigo @@ -3,8 +3,8 @@ package middleware import ( "net/http" - "{{.Module}}/pkg/lib/identity" - "{{.Module}}/pkg/lib/result" + "{{.Module}}/pkg/internal/identity" + "{{.Module}}/pkg/internal/result" ) // Auth App授权中间件 diff --git a/cmd/internal/http/app/pkg_app_router_app.tmpl b/cmd/internal/http/pkg/app/router/app.yiigo similarity index 83% rename from cmd/internal/http/app/pkg_app_router_app.tmpl rename to cmd/internal/http/pkg/app/router/app.yiigo index 787ca98..4a27bf8 100644 --- a/cmd/internal/http/app/pkg_app_router_app.tmpl +++ b/cmd/internal/http/pkg/app/router/app.yiigo @@ -3,17 +3,17 @@ package router import ( "net/http" - "github.com/go-chi/chi/v5" - "{{.Module}}/pkg/{{.AppPkg}}/api" "{{.Module}}/pkg/{{.AppPkg}}/web" - "{{.Module}}/pkg/lib" - lib_middleware "{{.Module}}/pkg/lib/middleware" + lib_middleware "{{.Module}}/pkg/internal/middleware" + "{{.Module}}/pkg/internal/util" + + "github.com/go-chi/chi/v5" ) // register app routes func App(r chi.Router) { - lib.FileServer(r, "/", http.FS(web.Asserts())) + util.FileServer(r, "/", http.FS(web.Asserts())) // 浏览器访问会主动发送 /favicon.ico 请求 // r.Get("/favicon.ico", func(w http.ResponseWriter, r *http.Request) { diff --git a/cmd/internal/http/app/pkg_app_service_greeter.tmpl b/cmd/internal/http/pkg/app/service/greeter/hello.yiigo similarity index 90% rename from cmd/internal/http/app/pkg_app_service_greeter.tmpl rename to cmd/internal/http/pkg/app/service/greeter/hello.yiigo index 8660bac..f912de8 100644 --- a/cmd/internal/http/app/pkg_app_service_greeter.tmpl +++ b/cmd/internal/http/pkg/app/service/greeter/hello.yiigo @@ -3,7 +3,7 @@ package greeter import ( "context" - "{{.Module}}/pkg/lib/result" + "{{.Module}}/pkg/internal/result" ) type ReqHello struct { diff --git a/cmd/internal/http/app/pkg_app_service_test.tmpl b/cmd/internal/http/pkg/app/service/greeter/service_test.yiigo similarity index 95% rename from cmd/internal/http/app/pkg_app_service_test.tmpl rename to cmd/internal/http/pkg/app/service/greeter/service_test.yiigo index 5f1e5a8..a18db85 100644 --- a/cmd/internal/http/app/pkg_app_service_test.tmpl +++ b/cmd/internal/http/pkg/app/service/greeter/service_test.yiigo @@ -5,10 +5,10 @@ import ( "fmt" "testing" - "github.com/spf13/viper" - "{{.Module}}/pkg/{{.AppPkg}}/ent" - "{{.Module}}/pkg/lib/log" + "{{.Module}}/pkg/internal/log" + + "github.com/spf13/viper" ) func TestMain(m *testing.M) { diff --git a/cmd/internal/http/app/pkg_app_web_dist_index.tmpl b/cmd/internal/http/pkg/app/web/dist/index.html similarity index 100% rename from cmd/internal/http/app/pkg_app_web_dist_index.tmpl rename to cmd/internal/http/pkg/app/web/dist/index.html diff --git a/cmd/internal/http/app/pkg_app_web_dist_style.tmpl b/cmd/internal/http/pkg/app/web/dist/style.css similarity index 100% rename from cmd/internal/http/app/pkg_app_web_dist_style.tmpl rename to cmd/internal/http/pkg/app/web/dist/style.css diff --git a/cmd/internal/http/app/pkg_app_web_embed.tmpl b/cmd/internal/http/pkg/app/web/embed.yiigo similarity index 100% rename from cmd/internal/http/app/pkg_app_web_embed.tmpl rename to cmd/internal/http/pkg/app/web/embed.yiigo diff --git a/cmd/internal/grpc/pkg_lib_db_db.tmpl b/cmd/internal/http/pkg/internal/db/db.yiigo similarity index 97% rename from cmd/internal/grpc/pkg_lib_db_db.tmpl rename to cmd/internal/http/pkg/internal/db/db.yiigo index 1935ea5..4c01814 100644 --- a/cmd/internal/grpc/pkg_lib_db_db.tmpl +++ b/cmd/internal/http/pkg/internal/db/db.yiigo @@ -5,14 +5,14 @@ import ( "fmt" "time" + "{{.Module}}/pkg/internal/log" + "entgo.io/ent/dialect" entsql "entgo.io/ent/dialect/sql" "github.com/shenghui0779/yiigo" "github.com/spf13/cast" "github.com/spf13/viper" "go.uber.org/zap" - - "{{.Module}}/pkg/lib/log" ) // InitDBDriver 初始化Ent实例(如有多个实例,在此方法中初始化) diff --git a/cmd/internal/http/pkg_lib_db_mixin.tmpl b/cmd/internal/http/pkg/internal/db/mixin.yiigo similarity index 100% rename from cmd/internal/http/pkg_lib_db_mixin.tmpl rename to cmd/internal/http/pkg/internal/db/mixin.yiigo diff --git a/cmd/internal/http/pkg_lib_db_redis.tmpl b/cmd/internal/http/pkg/internal/db/redis.yiigo similarity index 100% rename from cmd/internal/http/pkg_lib_db_redis.tmpl rename to cmd/internal/http/pkg/internal/db/redis.yiigo diff --git a/cmd/internal/grpc/pkg_lib_identity_identity.tmpl b/cmd/internal/http/pkg/internal/identity/identity.yiigo similarity index 98% rename from cmd/internal/grpc/pkg_lib_identity_identity.tmpl rename to cmd/internal/http/pkg/internal/identity/identity.yiigo index a9271e3..9d39ca6 100644 --- a/cmd/internal/grpc/pkg_lib_identity_identity.tmpl +++ b/cmd/internal/http/pkg/internal/identity/identity.yiigo @@ -7,11 +7,11 @@ import ( "encoding/json" "fmt" + "{{.Module}}/pkg/internal/log" + "github.com/shenghui0779/yiigo/xcrypto" "github.com/spf13/viper" "go.uber.org/zap" - - "{{.Module}}/pkg/lib/log" ) // Identity 授权身份 diff --git a/cmd/internal/http/pkg_lib_log_init.tmpl b/cmd/internal/http/pkg/internal/log/init.yiigo similarity index 100% rename from cmd/internal/http/pkg_lib_log_init.tmpl rename to cmd/internal/http/pkg/internal/log/init.yiigo diff --git a/cmd/internal/http/pkg_lib_log_log.tmpl b/cmd/internal/http/pkg/internal/log/log.yiigo similarity index 55% rename from cmd/internal/http/pkg_lib_log_log.tmpl rename to cmd/internal/http/pkg/internal/log/log.yiigo index 4b5fa27..68fb0bd 100644 --- a/cmd/internal/http/pkg_lib_log_log.tmpl +++ b/cmd/internal/http/pkg/internal/log/log.yiigo @@ -3,37 +3,41 @@ package log import ( "context" + "github.com/shenghui0779/yiigo/metadata" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) func Info(ctx context.Context, msg string, fields ...zap.Field) { - Log(ctx, zapcore.InfoLevel, msg, fields...) + logCtx(ctx, zapcore.InfoLevel, msg, fields...) } func Warn(ctx context.Context, msg string, fields ...zap.Field) { - Log(ctx, zapcore.WarnLevel, msg, fields...) + logCtx(ctx, zapcore.WarnLevel, msg, fields...) } func Error(ctx context.Context, msg string, fields ...zap.Field) { - Log(ctx, zapcore.ErrorLevel, msg, fields...) + logCtx(ctx, zapcore.ErrorLevel, msg, fields...) } func Panic(ctx context.Context, msg string, fields ...zap.Field) { - Log(ctx, zapcore.PanicLevel, msg, fields...) + logCtx(ctx, zapcore.PanicLevel, msg, fields...) } func Fatal(ctx context.Context, msg string, fields ...zap.Field) { - Log(ctx, zapcore.FatalLevel, msg, fields...) + logCtx(ctx, zapcore.FatalLevel, msg, fields...) } -func Log(ctx context.Context, level zapcore.Level, msg string, fields ...zap.Field) { - traceId, path := GetTraceInfo(ctx) - fields = append(fields, - zap.String("hostname", hostname), - zap.String("trace_id", traceId), - zap.String("path", path), - ) +func logCtx(ctx context.Context, level zapcore.Level, msg string, fields ...zap.Field) { + if md, ok := metadata.FromIncomingContext(ctx); ok && len(md) != 0 { + for k, v := range md { + if len(v) == 1 { + fields = append(fields, zap.String(k, v[0])) + } else { + fields = append(fields, zap.Strings(k, v)) + } + } + } switch level { case zapcore.InfoLevel: logger.Info(msg, fields...) diff --git a/cmd/internal/http/pkg_lib_middleware_log.tmpl b/cmd/internal/http/pkg/internal/middleware/log.yiigo similarity index 89% rename from cmd/internal/http/pkg_lib_middleware_log.tmpl rename to cmd/internal/http/pkg/internal/middleware/log.yiigo index 98d0059..83dacac 100644 --- a/cmd/internal/http/pkg_lib_middleware_log.tmpl +++ b/cmd/internal/http/pkg/internal/middleware/log.yiigo @@ -6,14 +6,14 @@ import ( "net/http" "time" + "{{.Module}}/pkg/internal/identity" + "{{.Module}}/pkg/internal/log" + "{{.Module}}/pkg/internal/result" + "github.com/pkg/errors" "github.com/shenghui0779/yiigo" "github.com/tidwall/pretty" "go.uber.org/zap" - - "{{.Module}}/pkg/lib/identity" - "{{.Module}}/pkg/lib/log" - "{{.Module}}/pkg/lib/result" ) // Log 日志中间件 @@ -52,8 +52,9 @@ func Log(next http.Handler) http.Handler { } } defer func() { - log.Info(r.Context(), "Request info", + log.Info(r.Context(), "request info", zap.String("method", r.Method), + zap.String("path", r.URL.Path), zap.String("ip", r.RemoteAddr), zap.String("body", body), zap.String("identity", identity.FromContext(r.Context()).String()), diff --git a/cmd/internal/http/pkg_lib_middleware_monitor.tmpl b/cmd/internal/http/pkg/internal/middleware/monitor.yiigo similarity index 100% rename from cmd/internal/http/pkg_lib_middleware_monitor.tmpl rename to cmd/internal/http/pkg/internal/middleware/monitor.yiigo diff --git a/cmd/internal/http/pkg_lib_middleware_recovery.tmpl b/cmd/internal/http/pkg/internal/middleware/recovery.yiigo similarity index 86% rename from cmd/internal/http/pkg_lib_middleware_recovery.tmpl rename to cmd/internal/http/pkg/internal/middleware/recovery.yiigo index ee0d48b..1c726bf 100644 --- a/cmd/internal/http/pkg_lib_middleware_recovery.tmpl +++ b/cmd/internal/http/pkg/internal/middleware/recovery.yiigo @@ -4,11 +4,11 @@ import ( "net/http" "runtime/debug" - "go.uber.org/zap" + "{{.Module}}/pkg/internal/identity" + "{{.Module}}/pkg/internal/log" + "{{.Module}}/pkg/internal/result" - "{{.Module}}/pkg/lib/identity" - "{{.Module}}/pkg/lib/log" - "{{.Module}}/pkg/lib/result" + "go.uber.org/zap" ) // Recovery panic recover middleware diff --git a/cmd/internal/http/pkg_lib_middleware_traceid.tmpl b/cmd/internal/http/pkg/internal/middleware/trace_id.yiigo similarity index 53% rename from cmd/internal/http/pkg_lib_middleware_traceid.tmpl rename to cmd/internal/http/pkg/internal/middleware/trace_id.yiigo index 1a7db3b..abad478 100644 --- a/cmd/internal/http/pkg_lib_middleware_traceid.tmpl +++ b/cmd/internal/http/pkg/internal/middleware/trace_id.yiigo @@ -3,9 +3,9 @@ package middleware import ( "net/http" - "github.com/shenghui0779/yiigo/metadata" + "{{.Module}}/pkg/internal/trace" - "{{.Module}}/pkg/lib/log" + "github.com/shenghui0779/yiigo/metadata" ) // TraceId is a middleware that injects a trace ID into the context of each request. @@ -15,21 +15,22 @@ func TraceId(next http.Handler) http.Handler { if !ok { md = metadata.Pairs() } - md.Set(log.TracePath, r.URL.Path) - // traceId已存在,则复用 - if len(md.Get(log.TraceId)) != 0 { - next.ServeHTTP(w, r) - return - } - // 去header取traceId - traceId := r.Header.Get(log.TraceId) - if len(traceId) == 0 { - traceId = log.NewTraceId() + md.Set(trace.TraceHost, trace.Hostname()) + // traceId + var traceId string + if vals := md.Get(trace.TraceId); len(vals) != 0 { + traceId = vals[0] + } else { + traceId = r.Header.Get(trace.TraceId) + if len(traceId) == 0 { + traceId = trace.NewTraceId() + } + md.Set(trace.TraceId, traceId) } - // 设置traceId - md.Set(log.TraceId, traceId) + // set response header + w.Header().Set(trace.TraceId, traceId) + // reset request context ctx := metadata.NewIncomingContext(r.Context(), md) - w.Header().Set(log.TraceId, traceId) next.ServeHTTP(w, r.WithContext(ctx)) }) } diff --git a/cmd/internal/http/pkg/internal/pgarray/array.yiigo b/cmd/internal/http/pkg/internal/pgarray/array.yiigo new file mode 100644 index 0000000..222e5e3 --- /dev/null +++ b/cmd/internal/http/pkg/internal/pgarray/array.yiigo @@ -0,0 +1,64 @@ +package pgarray + +import ( + "entgo.io/ent/dialect/sql" + "github.com/shenghui0779/yiigo/pgtype" +) + +// 查找包含单个元素的数组 +func Any(field string, value any) func(*sql.Selector) { + return func(s *sql.Selector) { + s.Where(sql.P(func(b *sql.Builder) { + b.Arg(value) + b.WriteString(" = ANY(") + b.Ident(s.C(field)) + b.WriteString(")") + })) + } +} + +// 查找包含多个元素的数组(oid指定pg数组元素类型) +func Contains[T ~[]E, E comparable](field string, values T, oid pgtype.Oid) func(*sql.Selector) { + if len(values) == 0 { + return func(selector *sql.Selector) {} + } + return func(s *sql.Selector) { + s.Where(sql.P(func(b *sql.Builder) { + b.Ident(s.C(field)) + b.WriteString(" @> ARRAY[") + if len(values) != 0 { + b.Arg(values[0]) + for _, v := range values[1:] { + b.WriteString(", ") + b.Arg(v) + } + } + b.WriteString("]::") + b.WriteString(pgtype.TypeName[oid]) + b.WriteString("[]") + })) + } +} + +// 查找有交集元素的数组(oid指定pg数组元素类型) +func Intersect[T ~[]E, E comparable](field string, values T, oid pgtype.Oid) func(*sql.Selector) { + if len(values) == 0 { + return func(selector *sql.Selector) {} + } + return func(s *sql.Selector) { + s.Where(sql.P(func(b *sql.Builder) { + b.Ident(s.C(field)) + b.WriteString(" && ARRAY[") + if len(values) != 0 { + b.Arg(values[0]) + for _, v := range values[1:] { + b.WriteString(", ") + b.Arg(v) + } + } + b.WriteString("]::") + b.WriteString(pgtype.TypeName[oid]) + b.WriteString("[]") + })) + } +} diff --git a/cmd/internal/http/pkg_lib_result_code.tmpl b/cmd/internal/http/pkg/internal/result/code.yiigo similarity index 100% rename from cmd/internal/http/pkg_lib_result_code.tmpl rename to cmd/internal/http/pkg/internal/result/code.yiigo diff --git a/cmd/internal/http/pkg_lib_result_result.tmpl b/cmd/internal/http/pkg/internal/result/result.yiigo similarity index 88% rename from cmd/internal/http/pkg_lib_result_result.tmpl rename to cmd/internal/http/pkg/internal/result/result.yiigo index 7d7c4ac..006ca5e 100644 --- a/cmd/internal/http/pkg_lib_result_result.tmpl +++ b/cmd/internal/http/pkg/internal/result/result.yiigo @@ -4,10 +4,10 @@ import ( "encoding/json" "net/http" + "{{.Module}}/pkg/internal/log" + "github.com/shenghui0779/yiigo" "go.uber.org/zap" - - "{{.Module}}/pkg/lib/log" ) const CodeOK = 0 @@ -25,7 +25,7 @@ func (resp *response) JSON(w http.ResponseWriter, r *http.Request) { w.Header().Set(yiigo.HeaderContentType, yiigo.ContentJSON) w.WriteHeader(http.StatusOK) if err := json.NewEncoder(w).Encode(resp.x); err != nil { - log.Error(r.Context(), "Error write response", zap.Error(err)) + log.Error(r.Context(), "write response", zap.Error(err)) } } @@ -65,18 +65,11 @@ func New(code int, msg string, options ...Option) Result { resp := &response{ x: yiigo.X{ "code": code, - "err": false, "msg": msg, }, } - - if code != CodeOK { - resp.x["err"] = true - } - for _, f := range options { f(resp) } - return resp } diff --git a/cmd/internal/http/pkg/internal/span/span.yiigo b/cmd/internal/http/pkg/internal/span/span.yiigo new file mode 100644 index 0000000..c45e13d --- /dev/null +++ b/cmd/internal/http/pkg/internal/span/span.yiigo @@ -0,0 +1,43 @@ +package span + +import ( + "context" + "runtime" + "strings" + "time" + + "{{.Module}}/pkg/internal/log" + + "go.uber.org/zap" +) + +type Span struct { + c context.Context + f string + l int + n string + t time.Time +} + +func (s *Span) Finish() { + log.Info(s.c, "time consume", zap.String("function", s.n), zap.String("duration", time.Since(s.t).String()), zap.String("file", s.f), zap.Int("line", s.l)) +} + +// New returns a span to log the time consume. +// +// Example: +// +// sp := span.New(ctx) +// defer sp.Finish() +func New(ctx context.Context) *Span { + sp := &Span{c: ctx, t: time.Now()} + // Skip level 1 to get the caller function + pc, file, line, _ := runtime.Caller(1) + sp.f, sp.l = file, line + // Get the function details + if fn := runtime.FuncForPC(pc); fn != nil { + name := fn.Name() + sp.n = name[strings.Index(name, ".")+1:] + } + return sp +} diff --git a/cmd/internal/http/pkg/internal/trace/trace_id.yiigo b/cmd/internal/http/pkg/internal/trace/trace_id.yiigo new file mode 100644 index 0000000..c643c32 --- /dev/null +++ b/cmd/internal/http/pkg/internal/trace/trace_id.yiigo @@ -0,0 +1,51 @@ +package trace + +import ( + "crypto/rand" + "encoding/base64" + "fmt" + "os" + "strings" + "sync/atomic" + + "github.com/shenghui0779/yiigo/xhash" +) + +const ( + TraceId = "x-trace-id" + TraceHost = "hostname" +) + +var ( + hostname string + prefix string + sequence uint64 +) + +func init() { + hostname, _ = os.Hostname() + if len(hostname) == 0 { + hostname = "localhost" + } + + var ( + buf [12]byte + b64 string + ) + for len(b64) < 10 { + _, _ = rand.Read(buf[:]) + b64 = base64.StdEncoding.EncodeToString(buf[:]) + b64 = strings.NewReplacer("+", "", "/", "").Replace(b64) + } + prefix = fmt.Sprintf("%s/%s", hostname, b64) +} + +func Hostname() string { + return hostname +} + +// NewTraceId generates a new trace ID in the sequence. +func NewTraceId() string { + seq := atomic.AddUint64(&sequence, 1) + return xhash.MD5(fmt.Sprintf("%s-%d", prefix, seq)) +} diff --git a/cmd/internal/http/pkg_lib_binding.tmpl b/cmd/internal/http/pkg/internal/util/binding.yiigo similarity index 98% rename from cmd/internal/http/pkg_lib_binding.tmpl rename to cmd/internal/http/pkg/internal/util/binding.yiigo index ac55539..6a4baa8 100644 --- a/cmd/internal/http/pkg_lib_binding.tmpl +++ b/cmd/internal/http/pkg/internal/util/binding.yiigo @@ -1,4 +1,4 @@ -package lib +package util import ( "encoding/json" diff --git a/cmd/internal/http/pkg_lib_embed.tmpl b/cmd/internal/http/pkg/internal/util/embed.yiigo similarity index 98% rename from cmd/internal/http/pkg_lib_embed.tmpl rename to cmd/internal/http/pkg/internal/util/embed.yiigo index 62c308c..1f27cb6 100644 --- a/cmd/internal/http/pkg_lib_embed.tmpl +++ b/cmd/internal/http/pkg/internal/util/embed.yiigo @@ -1,4 +1,4 @@ -package lib +package util import ( "net/http" diff --git a/cmd/internal/http/pkg/internal/util/util.yiigo b/cmd/internal/http/pkg/internal/util/util.yiigo new file mode 100644 index 0000000..1df71b6 --- /dev/null +++ b/cmd/internal/http/pkg/internal/util/util.yiigo @@ -0,0 +1,34 @@ +package util + +import ( + "context" + "fmt" + "runtime/debug" + + "{{.Module}}/pkg/internal/log" + + "github.com/shenghui0779/yiigo" + "go.uber.org/zap" +) + +// Safe recover for goroutine when panic +func Safe(ctx context.Context, fn func(ctx context.Context)) { + defer func() { + if err := recover(); err != nil { + log.Error(ctx, "Goroutine panic recovered", zap.Any("error", err), zap.ByteString("stack", debug.Stack())) + } + }() + fn(ctx) +} + +func CheckFields(fields, columns []string) error { + if len(fields) == 0 { + return nil + } + for _, v := range fields { + if !yiigo.SliceIn(columns, v) { + return fmt.Errorf("invalid field: %s", v) + } + } + return nil +} diff --git a/cmd/internal/http/pkg_lib_log_traceid.tmpl b/cmd/internal/http/pkg_lib_log_traceid.tmpl deleted file mode 100644 index 6669381..0000000 --- a/cmd/internal/http/pkg_lib_log_traceid.tmpl +++ /dev/null @@ -1,63 +0,0 @@ -package log - -import ( - "context" - "crypto/rand" - "encoding/base64" - "fmt" - "os" - "strings" - "sync/atomic" - - "github.com/shenghui0779/yiigo/metadata" - "github.com/shenghui0779/yiigo/xhash" -) - -const TraceId = "x-trace-id" -const TracePath = "x-trace-path" - -var ( - hostname string - tracePrefix string - traceSeq uint64 -) - -func init() { - hostname, _ = os.Hostname() - if len(hostname) == 0 { - hostname = "localhost" - } - - var ( - buf [12]byte - b64 string - ) - for len(b64) < 10 { - _, _ = rand.Read(buf[:]) - b64 = base64.StdEncoding.EncodeToString(buf[:]) - b64 = strings.NewReplacer("+", "", "/", "").Replace(b64) - } - tracePrefix = fmt.Sprintf("%s/%s", hostname, b64) -} - -// NewTraceId generates a new trace ID in the sequence. -func NewTraceId() string { - seq := atomic.AddUint64(&traceSeq, 1) - return xhash.MD5(fmt.Sprintf("%s-%d", tracePrefix, seq)) -} - -func GetTraceInfo(ctx context.Context) (traceId, path string) { - traceId = "-" - path = "-" - md, ok := metadata.FromIncomingContext(ctx) - if !ok { - return - } - if v := md.Get(TraceId); len(v) != 0 { - traceId = v[0] - } - if v := md.Get(TracePath); len(v) != 0 { - path = v[0] - } - return -} diff --git a/cmd/yiigo/main.go b/cmd/yiigo/main.go index 86b0c4f..0db93ff 100644 --- a/cmd/yiigo/main.go +++ b/cmd/yiigo/main.go @@ -7,12 +7,11 @@ import ( "os" "os/exec" + "github.com/shenghui0779/yiigo" + "github.com/shenghui0779/yiigo/cmd/internal" "github.com/spf13/cobra" "go.uber.org/zap" "golang.org/x/mod/modfile" - - "github.com/shenghui0779/yiigo" - "github.com/shenghui0779/yiigo/cmd/internal" ) func main() { diff --git a/go.mod b/go.mod index e5354d8..a9a477c 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/shenghui0779/yiigo -go 1.22.0 +go 1.23.0 require ( git.sr.ht/~sbinet/gg v0.6.0 @@ -12,7 +12,7 @@ require ( github.com/go-sql-driver/mysql v1.8.1 github.com/google/uuid v1.6.0 github.com/hashicorp/go-version v1.7.0 - github.com/jackc/pgx/v5 v5.7.1 + github.com/jackc/pgx/v5 v5.7.2 github.com/jmoiron/sqlx v1.4.0 github.com/mattn/go-sqlite3 v1.14.24 github.com/redis/go-redis/v9 v9.7.0 @@ -21,7 +21,7 @@ require ( github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.10.0 go.uber.org/zap v1.27.0 - golang.org/x/crypto v0.30.0 + golang.org/x/crypto v0.31.0 golang.org/x/mod v0.22.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) @@ -44,7 +44,7 @@ require ( github.com/spf13/pflag v1.0.5 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/image v0.23.0 // indirect - golang.org/x/net v0.32.0 // indirect + golang.org/x/net v0.33.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect diff --git a/go.sum b/go.sum index 51f4bee..f922fde 100644 --- a/go.sum +++ b/go.sum @@ -46,8 +46,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI 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/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI= +github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= 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/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= @@ -89,15 +89,15 @@ 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.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= -golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= -golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= diff --git a/util.go b/util.go index 168cb7c..b42c7a1 100644 --- a/util.go +++ b/util.go @@ -21,9 +21,10 @@ type Step struct { } // Steps calculates the steps. -// example: // -// arr := []int{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20} +// Example: +// +// arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} // for _, step := range yiigo.Steps(len(arr), 6) { // cur := arr[step.Head:step.Tail] // // todo: do something