This commit is contained in:
ivamp 2024-11-06 18:47:56 +08:00
parent 7468eff05e
commit 3d1ebb8f8e
76 changed files with 4513 additions and 0 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
build
config.yaml

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/build
/config.yaml
.idea
.vscode

15
Dockerfile Normal file
View File

@ -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"]

8
Dockerfile2 Normal file
View File

@ -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"]

12
Makefile Normal file
View File

@ -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

70
buf.gen.yaml Normal file
View File

@ -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: ""

55
cmd/document.go Normal file
View File

@ -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)
}
},
}

100
cmd/http.go Normal file
View File

@ -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)
}
}

162
cmd/migrate.go Normal file
View File

@ -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 迁移,用法 <command>",
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-<name>.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<FuncName>, Down<FuncName>)
}
func Up<FuncName>(ctx context.Context, tx *sql.Tx) error {
_, err := tx.ExecContext(ctx, "UPDATE users SET username='admin' WHERE username='root';")
return err
}
func Down<FuncName>(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>", funcName+name)
err := os.WriteFile("internal/migrations/"+fileName, []byte(template), 0644)
if err != nil {
log.Fatalf("failed creating migration file: %v", err)
}
}

14
cmd/root.go Normal file
View File

@ -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
}
},
}

39
cmd/schedule.go Normal file
View File

@ -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()
}

42
cmd/wire.go Normal file
View File

@ -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
}

63
cmd/wire_gen.go Normal file
View File

@ -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)

6
configs/config.go Normal file
View File

@ -0,0 +1,6 @@
package configs
import _ "embed"
//go:embed config.yaml
var Config []byte

70
configs/config.yaml Normal file
View File

@ -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: ""

128
docs/docs.go Normal file
View File

@ -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)
}

101
docs/swagger.json Normal file
View File

@ -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"
}
}
}

62
docs/swagger.yaml Normal file
View File

@ -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"

111
go.mod Normal file
View File

@ -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
)

391
go.sum Normal file
View File

@ -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=

1
hack/gorm-gen/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
dao

35
hack/gorm-gen/gorm.go Normal file
View File

@ -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()
}

135
hack/rename/rename.go Normal file
View File

@ -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/<your-username>/<your-project-name>): ")
_, 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
}

57
internal/base/app.go Normal file
View File

@ -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,
}
}

107
internal/base/conf/conf.go Normal file
View File

@ -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"`
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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}
}

View File

@ -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
}

View File

@ -0,0 +1,6 @@
package orm
type User struct {
Username string
Password string
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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,
}
}

105
internal/base/server/gin.go Normal file
View File

@ -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
}

View File

@ -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}
}

103
internal/dao/gen.go Normal file
View File

@ -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
}

7
internal/dao/provider.go Normal file
View File

@ -0,0 +1,7 @@
package dao
import "gorm.io/gorm"
func NewQuery(db *gorm.DB) *Query {
return Use(db)
}

392
internal/dao/users.gen.go Normal file
View File

@ -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
}

14
internal/entity/Model.go Normal file
View File

@ -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"`
}

10
internal/entity/User.go Normal file
View File

@ -0,0 +1,10 @@
package entity
type User struct {
Model
Name string `json:"name"`
}
func (u *User) TableName() string {
return "user"
}

View File

@ -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
}

View File

@ -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,
}
}

View File

@ -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
}

View File

@ -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))
}
})
}

View File

@ -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,
}
}

View File

@ -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,
}
}

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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{}
}

View File

@ -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()
// }
//}

View File

@ -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,
}
}

View File

@ -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`;

View File

@ -0,0 +1,11 @@
package migrations
import (
"embed"
"go-template/internal/base/conf"
)
//go:embed *.sql
var MigrationFS embed.FS
var Config *conf.Config

38
internal/router/api.go Normal file
View File

@ -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) {
}

View File

@ -0,0 +1,9 @@
package router
import "github.com/google/wire"
// ProviderSetRouter is providers.
var ProviderSetRouter = wire.NewSet(
NewApiRoute,
NewSwaggerRoute,
)

View File

@ -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))
}

17
internal/schema/entity.go Normal file
View File

@ -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)
}

131
internal/schema/response.go Normal file
View File

@ -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()
//}

46
internal/schema/user.go Normal file
View File

@ -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)
}

View File

@ -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"`
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
//}

View File

@ -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}
}

View File

@ -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)
}
}()
}

View File

@ -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
}

View File

@ -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,
}
}

18
main.go Normal file
View File

@ -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
}
}

29
pkg/consts/auth.go Normal file
View File

@ -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("没有权限访问此资源")
)

7
pkg/consts/model.go Normal file
View File

@ -0,0 +1,7 @@
package consts
import "errors"
var (
ErrPageNotFound = errors.New("page not found")
)

View File

@ -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"
}
}
}
}
}
}

View File

@ -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"
}
}
}
}
}
}

View File

@ -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
}

View File

@ -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
)

View File

@ -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;
}

View File

@ -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",
}