This commit is contained in:
ivamp 2024-12-06 01:44:29 +08:00
parent 19999919d2
commit 39ec698bef
86 changed files with 5337 additions and 0 deletions

2
.dockerignore Normal file
View File

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

View File

@ -0,0 +1,97 @@
name: Build
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- id: commit
uses: prompt/actions-commit-hash@v3
- name: Setup go
uses: actions/setup-go@v4
with:
go-version: '^1.22'
- name: Get dependencies
run: |
go env -w GO111MODULE=on && go env -w GOPROXY=https://goproxy.cn,direct
go mod download
- name: Build
run: |
CGO_ENABLED=0 go build -ldflags "-w -s" -gcflags "-N -l" -o main .
- name: 'Login to Container Registry'
uses: docker/login-action@v3
with:
registry: leafdev.top
username: ${{ gitea.actor }}
password: ${{ secrets.REGISTRY_TOKEN }}
- name: 'Build Inventory Image'
run: |
docker build . -f Dockerfile2 --tag leafdev.top/leaf/rag-new:${{ steps.commit.outputs.short }}
docker push leafdev.top/leaf/rag-new:${{ steps.commit.outputs.short }}
docker tag leafdev.top/leaf/rag-new:${{ steps.commit.outputs.short }} leafdev.top/leaf/rag-new:latest
- name: 'Checkout Manifests branch'
uses: actions/checkout@v4
with:
ref: manifests
- name: 'Patch API Manifest'
uses: fjogeleit/yaml-update-action@main
with:
valueFile: 'manifests/deployment-api.yaml'
propertyPath: 'spec.template.spec.containers[0].image'
value: 'leafdev.top/leaf/rag-new:${{ steps.commit.outputs.short }}'
commitChange: false
- name: 'Patch Schedule Manifest'
uses: fjogeleit/yaml-update-action@main
with:
valueFile: 'manifests/deployment-schedule.yaml'
propertyPath: 'spec.template.spec.containers[0].image'
value: 'leafdev.top/leaf/rag-new:${{ steps.commit.outputs.short }}'
commitChange: false
- name: Push
run: |
git config user.name ${{ gitea.actor }}
git config user.email ${{ gitea.actor }}@users.noreply.leafdev.top
git add manifests/deployment-api.yaml
git add manifests/deployment-schedule.yaml
git commit -m "Update manifests"
git push origin manifests
# - name: docker
# run: |
# - name: Build Docker Image
# run: |
# docker build -t ${{ env.REGISTRY }}/${{ steps.meta.outputs.tags }} -f ./docker/nginx/Dockerfile .
# - name: Push Docker Image
# run: |
# docker login --username=${{ secrets.DOCKER_USERNAME }} --password ${{ secrets.DOCKER_PASSWORD }} ${{ env.REGISTRY }}
# docker push ${{ env.REGISTRY }}/${{ steps.meta.outputs.tags }}
# - name: Artifact
# uses: christopherhx/gitea-upload-artifact@v4
# with:
# name: artifact
# path: cmd/main
# push:
# runs-on: ubuntu-latest
## needs: [build]
# steps:
# - uses: https://github.com/actions/checkout@v4
# - name: Set up Docker Buildx
# uses: https://github.com/docker/setup-buildx-action@v3
# with:
# config-inline: |
# [registry."<my-private-unsecure-git-repository-ip-address>:5000"]
# http = true
# insecure = true
# - name: Build and push Docker image
# uses: https://github.com/docker/build-push-action@v5
# with:
# context: .
# file: ./Dockerfile
# push: true
# tags: "<my-private-unsecure-git-repository-ip-address>:5000/<my-docker-image>:${{gitea.sha}},<my-private-unsecure-git-repository-ip-address>:5000/<my-docker-image>:latest"

View File

@ -0,0 +1,25 @@
name: Deploy
on: [release]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set output
id: vars
run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
- name: Check output
env:
GITHUB_RELEASE_VERSION: ${{ steps.vars.outputs.tag }}
run: |
# check if GITHUB_RELEASE_VERSION is not empty
- name: Deploy
uses: docker/build-push-action@v3
with:
context: .
file: ./Dockerfile
push: true
tags: leafdev.top/rag-new:latest

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

22
Makefile Normal file
View File

@ -0,0 +1,22 @@
.PHONY: install-deps generate swag gorm proto buf
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
echo "You need install buf manually, https://github.com/bufbuild/buf"
generate:
go generate ./...
swag:
swag init -g main.go --parseDependency
gorm:
cd hack/gorm-gen && go run .
buf:
buf dep update
proto:
buf generate

21
buf.gen.yaml Normal file
View File

@ -0,0 +1,21 @@
version: v2
plugins:
- remote: buf.build/grpc/go:v1.5.1
out: proto/gen
opt:
- paths=source_relative
- remote: buf.build/protocolbuffers/go:v1.34.2
out: proto/gen
opt:
- paths=source_relative
- remote: buf.build/grpc-ecosystem/gateway:v2.23.0
out: proto/gen
opt:
- paths=source_relative
- generate_unbound_methods=true
- remote: buf.build/grpc-ecosystem/openapiv2:v2.23.0
out: proto/gen
opt:
- allow_merge
# - merge_file_name=mce-spider

4
buf.yaml Normal file
View File

@ -0,0 +1,4 @@
version: v2
deps:
- buf.build/googleapis/googleapis

96
cmd/grpc.go Normal file
View File

@ -0,0 +1,96 @@
package cmd
import (
"context"
"net"
"net/http"
"sync"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/reflection"
v1 "leafdev.top/Leaf/leaf-library-3/proto/gen/proto/api/v1"
)
func init() {
RootCmd.AddCommand(documentServiceCommand)
}
var documentServiceCommand = &cobra.Command{
Use: "grpc",
Short: "Start gRPC",
Run: func(cmd *cobra.Command, args []string) {
app, err := CreateApp()
if err != nil {
panic(err)
return
}
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.Api.GRPC.Interceptor.Logger.ZapLogInterceptor()),
app.Api.GRPC.Interceptor.Auth.UnaryJWTAuth(),
),
grpc.ChainStreamInterceptor(
logging.StreamServerInterceptor(app.Api.GRPC.Interceptor.Logger.ZapLogInterceptor()),
app.Api.GRPC.Interceptor.Auth.StreamJWTAuth(),
),
}
grpcServer := grpc.NewServer(opts...)
// 注册服务
v1.RegisterDocumentServiceServer(grpcServer, app.Api.GRPC.DocumentApi)
// 反射
reflection.Register(grpcServer)
app.Logger.Sugar.Infof("gRPC listening on %s, http gateway listening on %s",
app.Config.Grpc.Address, app.Config.Grpc.AddressGateway)
var wg sync.WaitGroup
wg.Add(1)
go func() {
if err := grpcServer.Serve(lis); err != nil {
app.Logger.Sugar.Fatal(err)
}
}()
wg.Add(1)
go func() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var dialOption = []grpc.DialOption{
grpc.WithTransportCredentials(insecure.NewCredentials()),
}
mux := runtime.NewServeMux()
var err error
// 注册服务到网关并转为 Http 请求
err = v1.RegisterDocumentServiceHandlerFromEndpoint(ctx, mux, app.Config.Grpc.Address, dialOption)
if err != nil {
app.Logger.Sugar.Fatal(err)
}
if err := http.ListenAndServe(app.Config.Grpc.AddressGateway, mux); err != nil {
app.Logger.Sugar.Fatal(err)
}
}()
wg.Wait()
},
}

102
cmd/http.go Normal file
View File

@ -0,0 +1,102 @@
package cmd
import (
"context"
"errors"
"github.com/gofiber/fiber/v2/middleware/adaptor"
"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 = adaptor.FiberApp(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 = adaptor.FiberApp(app.HttpServer.MetricRouter())
err = metricServer.ListenAndServe()
if err != nil && !errors.Is(http.ErrServerClosed, err) {
panic(err)
return
}
}()
}
quit := make(chan os.Signal)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
app.Logger.Sugar.Info("Shutdown http server")
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
defer cancel()
if err := bizServer.Shutdown(ctx); err != nil {
app.Logger.Sugar.Fatalf("Biz Server Shutdown Error: %s", err)
}
if err := metricServer.Shutdown(ctx); err != nil {
app.Logger.Sugar.Fatalf("Metric Server Shutdown Error: %s", err)
}
}

158
cmd/migrate.go Normal file
View File

@ -0,0 +1,158 @@
package cmd
import (
"context"
"fmt"
"os"
"strings"
"time"
"leafdev.top/Leaf/leaf-library-3/internal/database/migrations"
"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 goose",
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
_ = cmd.Help()
return
}
RunMigrate(args)
},
}
var createGoMigrateCommand = &cobra.Command{
Use: "create-migrate",
Short: "create go migration",
Long: "create go migration using goose.",
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)
}
migrations.Config = app.Config
goose.SetBaseFS(migrations.MigrationFS)
err = goose.SetDialect("postgres")
if err != nil {
panic(err)
}
db, err := app.GORM.DB()
if err != nil {
panic(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 {
panic(err)
}
defer func() {
if err := db.Close(); err != nil {
panic(err)
}
}()
}
func createGooseMigration(name string) {
// 在 internal/database/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/database/migrations/"+fileName, []byte(template), 0644)
if err != nil {
panic(fmt.Sprintf("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: "app",
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 (
"leafdev.top/Leaf/leaf-library-3/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()
}

44
cmd/wire.go Normal file
View File

@ -0,0 +1,44 @@
//go:build wireinject
// +build wireinject
package cmd
import (
"leafdev.top/Leaf/leaf-library-3/internal/api"
"leafdev.top/Leaf/leaf-library-3/internal/base"
"leafdev.top/Leaf/leaf-library-3/internal/base/conf"
"leafdev.top/Leaf/leaf-library-3/internal/base/logger"
"leafdev.top/Leaf/leaf-library-3/internal/base/milvus"
"leafdev.top/Leaf/leaf-library-3/internal/base/orm"
"leafdev.top/Leaf/leaf-library-3/internal/base/redis"
"leafdev.top/Leaf/leaf-library-3/internal/base/s3"
"leafdev.top/Leaf/leaf-library-3/internal/base/server"
"leafdev.top/Leaf/leaf-library-3/internal/batch"
"leafdev.top/Leaf/leaf-library-3/internal/dao"
"leafdev.top/Leaf/leaf-library-3/internal/router"
"leafdev.top/Leaf/leaf-library-3/internal/service"
"github.com/google/wire"
)
var ProviderSet = wire.NewSet(
conf.NewConfig,
logger.NewZapLogger,
orm.NewGORM,
dao.NewQuery,
redis.NewRedis,
s3.NewS3,
milvus.NewService,
batch.NewBatch,
service.Provide,
api.Provide,
router.Provide,
server.NewHTTPServer,
base.NewApplication,
)
func CreateApp() (*base.Application, error) {
wire.Build(ProviderSet)
return nil, nil
}

67
cmd/wire_gen.go Normal file
View File

@ -0,0 +1,67 @@
// 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"
"leafdev.top/Leaf/leaf-library-3/internal/api"
"leafdev.top/Leaf/leaf-library-3/internal/api/grpc"
"leafdev.top/Leaf/leaf-library-3/internal/api/grpc/interceptor"
"leafdev.top/Leaf/leaf-library-3/internal/api/grpc/v1/documents"
"leafdev.top/Leaf/leaf-library-3/internal/api/http"
"leafdev.top/Leaf/leaf-library-3/internal/api/http/v1"
"leafdev.top/Leaf/leaf-library-3/internal/base"
"leafdev.top/Leaf/leaf-library-3/internal/base/conf"
"leafdev.top/Leaf/leaf-library-3/internal/base/logger"
"leafdev.top/Leaf/leaf-library-3/internal/base/milvus"
"leafdev.top/Leaf/leaf-library-3/internal/base/orm"
"leafdev.top/Leaf/leaf-library-3/internal/base/redis"
"leafdev.top/Leaf/leaf-library-3/internal/base/s3"
"leafdev.top/Leaf/leaf-library-3/internal/base/server"
"leafdev.top/Leaf/leaf-library-3/internal/batch"
"leafdev.top/Leaf/leaf-library-3/internal/dao"
"leafdev.top/Leaf/leaf-library-3/internal/router"
"leafdev.top/Leaf/leaf-library-3/internal/service"
"leafdev.top/Leaf/leaf-library-3/internal/service/auth"
"leafdev.top/Leaf/leaf-library-3/internal/service/jwks"
"leafdev.top/Leaf/leaf-library-3/internal/service/stream"
)
// Injectors from wire.go:
func CreateApp() (*base.Application, error) {
config := conf.NewConfig()
loggerLogger := logger.NewZapLogger(config)
jwksJWKS := jwks.NewJWKS(config, loggerLogger)
authService := auth.NewService(config, jwksJWKS, loggerLogger)
userController := v1.NewUserController(authService)
handlers := http.NewHandler(userController)
middleware := http.NewMiddleware(config, loggerLogger, authService)
routerApi := router.NewApiRoute(handlers, middleware)
swaggerRouter := router.NewSwaggerRoute()
httpServer := server.NewHTTPServer(config, routerApi, swaggerRouter, middleware, loggerLogger)
db := orm.NewGORM(config, loggerLogger)
query := dao.NewQuery(db)
handler := documents.NewHandler(query)
interceptorAuth := interceptor.NewAuth(authService, loggerLogger, config)
interceptorLogger := interceptor.NewLogger(loggerLogger)
grpcInterceptor := grpc.NewInterceptor(interceptorAuth, interceptorLogger)
grpcHandlers := grpc.NewHandler(handler, grpcInterceptor)
apiApi := api.NewApi(grpcHandlers, handlers)
streamService := stream.NewService(config)
serviceService := service.NewService(loggerLogger, jwksJWKS, authService, streamService)
redisRedis := redis.NewRedis(config)
batchBatch := batch.NewBatch(loggerLogger)
s3S3 := s3.NewS3(config)
client := milvus.NewService(config, loggerLogger)
application := base.NewApplication(config, httpServer, apiApi, loggerLogger, serviceService, redisRedis, batchBatch, s3S3, db, query, client)
return application, nil
}
// wire.go:
var ProviderSet = wire.NewSet(conf.NewConfig, logger.NewZapLogger, orm.NewGORM, dao.NewQuery, redis.NewRedis, s3.NewS3, milvus.NewService, batch.NewBatch, service.Provide, api.Provide, router.Provide, server.NewHTTPServer, base.NewApplication)

9
configs/config.go Normal file
View File

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

90
configs/config.yaml Normal file
View File

@ -0,0 +1,90 @@
app:
name: template
allowed_audiences:
- "1" # UserLand Personal Access Client
http:
port: 8080
host: 0.0.0.0
# the production url
url: http://127.0.0.1:8080
cors:
enabled: true
allow_origins:
- "http://localhost:8080"
allow_methods:
- "GET"
- "HEAD"
- "PUT"
- "PATCH"
- "POST"
- "DELETE"
allow_headers:
- "Origin"
- "Content-Length"
- "Content-Type"
- "X-Requested-With"
- "Authorization"
allow_credentials: true
expose_headers: [ ]
max_age: 720
grpc:
address: 0.0.0.0:9090
address_gateway: 0.0.0.0:9091
debug:
enabled: false
database:
host: 127.0.0.1
port: 5432
user: root
password: root
name: db_name
sslmode: disable
timezone: "Asia/Shanghai"
redis:
host: 127.0.0.1
port: 6379
password: ""
db: 0
jwks:
url: "https://auth.leaflow.cn/.well-known/jwks"
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:
kafka:
bootstrap_servers:
- 127.0.0.1:9092
topic: "amber"
group_id: ""
# Plain
username: ""
password: ""
milvus:
host: 127.0.0.1
port: 19530
db_name: library
document_collection: documents
user:
password:
third_party:
openai_api_key: ""

19
configs/rbac_model.conf Normal file
View File

@ -0,0 +1,19 @@
# Request definition 即为请求定义,代表了可以传入什么样的参数来确定权限
[request_definition]
r = sub, obj, act
# Policy definition 代表了规则的组成
[policy_definition]
p = sub, obj, act
# g 是一个 RBAC系统, _, _表示角色继承关系的前项和后项即前项继承后项角色的权限
[role_definition]
g = _, _
# Policy effect 则表示什么样的规则可以被允许, e = some(where (p.eft == allow)) 这句就表示当前请求中包含的任何一个规则被允许的话,这个权限就会被允许
[policy_effect]
e = some(where (p.eft == allow))
# 是策略匹配程序的定义。表示请求与规则是如何起作用的
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

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/response.Body"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/schema.CurrentUserResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.Body"
}
}
}
}
}
},
"definitions": {
"response.Body": {
"type": "object",
"properties": {
"data": {},
"error": {
"type": "string"
},
"message": {
"type": "string"
},
"success": {
"type": "boolean"
}
}
},
"schema.CurrentUserResponse": {
"type": "object",
"properties": {
"ip": {
"type": "string"
},
"userEmail": {
"type": "string"
},
"userId": {
"type": "string"
},
"userName": {
"type": "string"
},
"valid": {
"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/response.Body"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/schema.CurrentUserResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.Body"
}
}
}
}
}
},
"definitions": {
"response.Body": {
"type": "object",
"properties": {
"data": {},
"error": {
"type": "string"
},
"message": {
"type": "string"
},
"success": {
"type": "boolean"
}
}
},
"schema.CurrentUserResponse": {
"type": "object",
"properties": {
"ip": {
"type": "string"
},
"userEmail": {
"type": "string"
},
"userId": {
"type": "string"
},
"userName": {
"type": "string"
},
"valid": {
"type": "boolean"
}
}
}
},
"securityDefinitions": {
"ApiKeyAuth": {
"type": "apiKey",
"name": "Authorization",
"in": "header"
}
}
}

62
docs/swagger.yaml Normal file
View File

@ -0,0 +1,62 @@
definitions:
response.Body:
properties:
data: {}
error:
type: string
message:
type: string
success:
type: boolean
type: object
schema.CurrentUserResponse:
properties:
ip:
type: string
userEmail:
type: string
userId:
type: string
userName:
type: string
valid:
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/response.Body'
- properties:
data:
$ref: '#/definitions/schema.CurrentUserResponse'
type: object
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.Body'
security:
- ApiKeyAuth: []
summary: Greet
tags:
- ping
securityDefinitions:
ApiKeyAuth:
in: header
name: Authorization
type: apiKey
swagger: "2.0"

133
go.mod Normal file
View File

@ -0,0 +1,133 @@
module leafdev.top/Leaf/leaf-library-3
go 1.23.2
require (
github.com/MicahParks/keyfunc/v3 v3.3.5
github.com/ansrivas/fiberprometheus/v2 v2.7.0
github.com/bsm/redislock v0.9.4
github.com/bytedance/sonic v1.12.5
github.com/gofiber/contrib/fiberzap/v2 v2.1.4
github.com/gofiber/fiber/v2 v2.52.5
github.com/gofiber/swagger v1.1.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.23.0
github.com/milvus-io/milvus-sdk-go/v2 v2.4.2
github.com/minio/minio-go/v7 v7.0.80
github.com/mitchellh/mapstructure v1.5.0
github.com/pressly/goose/v3 v3.22.1
github.com/redis/go-redis/v9 v9.7.0
github.com/segmentio/kafka-go v0.4.47
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
github.com/swaggo/swag v1.16.4
go.uber.org/zap v1.27.0
google.golang.org/grpc v1.68.0
google.golang.org/protobuf v1.35.2
gorm.io/driver/postgres v1.5.10
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/andybalholm/brotli v1.1.1 // indirect
github.com/beorn7/perks v1.0.1 // 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/cockroachdb/errors v1.11.3 // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
github.com/cockroachdb/redact v1.1.5 // 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.8.0 // indirect
github.com/getsentry/sentry-go v0.29.1 // 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-sql-driver/mysql v1.8.1 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.7.1 // indirect
github.com/jackc/puddle/v2 v2.2.2 // 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/klauspost/compress v1.17.11 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mattn/go-sqlite3 v1.14.17 // indirect
github.com/mfridman/interpolate v0.0.2 // indirect
github.com/milvus-io/milvus-proto/go-api/v2 v2.4.16 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.13.1 // 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/swaggo/files/v2 v2.0.1 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.57.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/crypto v0.29.0 // indirect
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.31.0 // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.20.0 // indirect
golang.org/x/time v0.8.0 // indirect
golang.org/x/tools v0.27.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241113202542-65e8d215514f // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/datatypes v1.2.4 // indirect
gorm.io/driver/mysql v1.5.7 // indirect
gorm.io/driver/sqlserver v1.5.3 // indirect
gorm.io/hints v1.1.2 // indirect
modernc.org/sqlite v1.34.2 // indirect
)

568
go.sum Normal file
View File

@ -0,0 +1,568 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM=
cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg=
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0/go.mod h1:Q28U+75mpCaSCDowNEmhIo/rmgdkqmkmzI7N6TGR4UY=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0/go.mod h1:cw4zVQgBby0Z5f2v0itn6se2dDP17nTjbZFXW5uPyHA=
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
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/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/ansrivas/fiberprometheus/v2 v2.7.0 h1:09XiSzG0J7aZp7RviklngdWdDbSybKjhuWAstp003Gg=
github.com/ansrivas/fiberprometheus/v2 v2.7.0/go.mod h1:hSJdO65lfnWW70Qn9uGdXXsUUSkckbhuw5r/KesygpU=
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.5 h1:hoZxY8uW+mT+OpkcUWw4k0fDINtOcVavEsGfzwzFU/w=
github.com/bytedance/sonic v1.12.5/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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I=
github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8=
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/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/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
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.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/getsentry/sentry-go v0.29.1 h1:DyZuChN8Hz3ARxGVV8ePaNXh1dQ7d76AiB117xcREwA=
github.com/getsentry/sentry-go v0.29.1/go.mod h1:x3AtIzN01d6SiWkderzaH28Tm0lgkafpJ5Bm3li39O0=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-faker/faker/v4 v4.1.0 h1:ffuWmpDrducIUOO0QSKSF5Q2dxAht+dhsT9FvVHhPEI=
github.com/go-faker/faker/v4 v4.1.0/go.mod h1:uuNc0PSRxF8nMgjGrrrU4Nw5cF30Jc6Kd0/FUTTYbhg=
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-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
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-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/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
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/gofiber/contrib/fiberzap/v2 v2.1.4 h1:GCtCQnT4Cr9az4qab2Ozmqsomkxm4Ei86MfKk/1p5+0=
github.com/gofiber/contrib/fiberzap/v2 v2.1.4/go.mod h1:PkdXgUzw+oj4m6ksfKJ0Hs3H7iPhwvhfI4b2LSA9hhA=
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
github.com/gofiber/swagger v1.1.0 h1:ff3rg1fB+Rp5JN/N8jfxTiZtMKe/9tB9QDc79fPiJKQ=
github.com/gofiber/swagger v1.1.0/go.mod h1:pRZL0Np35sd+lTODTE5The0G+TMHfNY+oC4hM2/i5m8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
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/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
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.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru v0.5.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/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.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/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
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.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
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/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-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
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-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
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.6.0/go.mod h1:00mDtPbeQCRGC1HwOOR5K/gr30P1NcEG0vx6Kbv2aJU=
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/milvus-io/milvus-proto/go-api/v2 v2.4.16 h1:XcdubT6Vy0PvNrWDJZ4cy6ytXWRENEYgYBCLkI+YpTE=
github.com/milvus-io/milvus-proto/go-api/v2 v2.4.16/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs=
github.com/milvus-io/milvus-sdk-go/v2 v2.4.2 h1:Xqf+S7iicElwYoS2Zly8Nf/zKHuZsNy1xQajfdtygVY=
github.com/milvus-io/milvus-sdk-go/v2 v2.4.2/go.mod h1:ulO1YUXKH0PGg50q27grw048GDY9ayB4FPmh7D+FFTA=
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.80 h1:2mdUHXEykRdY/BigLt3Iuu1otL0JTogT0Nmltg0wujk=
github.com/minio/minio-go/v7 v7.0.80/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/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/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/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
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/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/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.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
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.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
github.com/prometheus/common v0.60.1/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.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
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/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
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/segmentio/kafka-go v0.4.47 h1:IqziR4pA3vrZq7YdRxaT3w1/5fvIH5qpCwstUanQQB0=
github.com/segmentio/kafka-go v0.4.47/go.mod h1:HjF6XbOKh0Pjlkr5GVZxt6CsjjwnmhVOfURM5KMd8qg=
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/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
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.1.1/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 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/swaggo/files/v2 v2.0.1 h1:XCVJO/i/VosCDsJu1YLpdejGsGnBE9deRMpjN4pJLHk=
github.com/swaggo/files/v2 v2.0.1/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM=
github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tinylib/msgp v1.1.9 h1:SHf3yoO2sGA0veCJeCBYLHuttAVFHGm2RHgNodW7wQU=
github.com/tinylib/msgp v1.1.9/go.mod h1:BCXGB54lDD8qUEPmiG0cQQUANC4IUQyB2ItS2UDlO/k=
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/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.57.0 h1:Xw8SjWGEP/+wAAgyy5XTvgrWlOD1+TxbbvNADYCm1Tg=
github.com/valyala/fasthttp v1.57.0/go.mod h1:h6ZBaPRlzpZ6O3H5t2gEk1Qi33+TmLvfwgLLp0t9CpE=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
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.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
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.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.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.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-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.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
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-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-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-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
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.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.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.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/time v0.8.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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
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.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto/googleapis/api v0.0.0-20241113202542-65e8d215514f h1:M65LEviCfuZTfrfzwwEoxVtgvfkFkBUbFnRbxCXuXhU=
google.golang.org/genproto/googleapis/api v0.0.0-20241113202542-65e8d215514f/go.mod h1:Yo94eF2nj7igQt+TiJ49KxjIH8ndLYPZMIRSiRcEbg0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f h1:C1QccEa9kUwvMgEUORqQD9S17QesQijxjZ84sO82mfo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
google.golang.org/grpc/examples v0.0.0-20220617181431-3e7b97febc7f h1:rqzndB2lIQGivcXdTuY3Y9NBvr70X+y77woofSRluec=
google.golang.org/grpc/examples v0.0.0-20220617181431-3e7b97febc7f/go.mod h1:gxndsbNG1n4TZcHGgsYEfVGnTxqfEdfiDv6/DADXX9o=
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
google.golang.org/protobuf v1.35.2/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.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-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.4 h1:uZmGAcK/QZ0uyfCuVg0VQY1ZmV9h1fuG0tMwKByO1z4=
gorm.io/datatypes v1.2.4/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.10 h1:7Lggqempgy496c0WfHXsYWxk3Th+ZcW66/21QhVFdeE=
gorm.io/driver/postgres v1.5.10/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
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.5.3 h1:rjupPS4PVw+rjJkfvr8jn2lJ8BMhT4UW5FwuJY0P3Z0=
gorm.io/driver/sqlserver v1.5.3/go.mod h1:B+CZ0/7oFJ6tAlefsKoyxdgDCXJKSgwS2bMOQZT0I00=
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-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
gorm.io/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=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
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/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U=
modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w=
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.34.2 h1:J9n76TPsfYYkFkZ9Uy1QphILYifiVEwwOT7yP5b++2Y=
modernc.org/sqlite v1.34.2/go.mod h1:dnR723UrTtjKpoHCAMN0Q/gZ9MT4r+iRvIBb9umWFkU=
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 (
"leafdev.top/Leaf/leaf-library-3/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/Leaf/leaf-library-3"
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
}

28
internal/api/api.go Normal file
View File

@ -0,0 +1,28 @@
package api
import (
"github.com/google/wire"
"leafdev.top/Leaf/leaf-library-3/internal/api/grpc"
"leafdev.top/Leaf/leaf-library-3/internal/api/http"
)
var Provide = wire.NewSet(
grpc.ProviderSet,
http.ProviderSet,
NewApi,
)
type Api struct {
GRPC *grpc.Handlers
HTTP *http.Handlers
}
func NewApi(
grpcHandlers *grpc.Handlers,
httpHandlers *http.Handlers,
) *Api {
return &Api{
GRPC: grpcHandlers,
HTTP: httpHandlers,
}
}

View File

@ -0,0 +1,149 @@
package interceptor
import (
"context"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
"google.golang.org/grpc"
"leafdev.top/Leaf/leaf-library-3/internal/base/conf"
"leafdev.top/Leaf/leaf-library-3/internal/base/logger"
"leafdev.top/Leaf/leaf-library-3/internal/consts"
"leafdev.top/Leaf/leaf-library-3/internal/schema"
auth2 "leafdev.top/Leaf/leaf-library-3/internal/service/auth"
)
var ignoreAuthApis = map[string]bool{
// 反射
"/grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo": true,
// 业务 API
"/api.v1.WorkspaceService/List": true,
}
type Auth struct {
authService *auth2.Service
logger *logger.Logger
config *conf.Config
}
func NewAuth(
authService *auth2.Service,
logger *logger.Logger,
config *conf.Config,
) *Auth {
return &Auth{
authService: authService,
logger: logger,
config: config,
}
}
func (a *Auth) notRequireAuth(fullMethodName string) bool {
var b = ignoreAuthApis[fullMethodName]
if a.config.Debug.Enabled {
if b {
a.logger.Sugar.Debugf("[GRPC Auth] Ignore auth for Method: %s", fullMethodName)
} else {
a.logger.Sugar.Debugf("[GRPC Auth] Require auth for Method: %s", fullMethodName)
}
}
return b
}
func (a *Auth) authCtx(ctx context.Context) (context.Context, error) {
var tokenString string
var err error
tokenString, err = auth.AuthFromMD(ctx, "bearer")
if err != nil {
// 如果是调试模式,就不处理报错,并且继续执行
if a.config.Debug.Enabled {
tokenString = ""
a.logger.Sugar.Debugf("[GRPC Auth] error, %s", err)
} else {
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{consts.AuthMiddlewareKey, token.Token.Sub})
ctx = context.WithValue(ctx, consts.AuthMiddlewareKey, token)
return ctx, nil
}
func (a *Auth) UnaryJWTAuth() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
if a.notRequireAuth(info.FullMethod) {
return handler(ctx, req)
}
ctx, err := a.authCtx(ctx)
if err != nil {
return nil, err
}
result, err := handler(ctx, req)
if err != nil {
return nil, err
}
return result, err
}
}
func (a *Auth) StreamJWTAuth() grpc.StreamServerInterceptor {
return func(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
var ctx = ss.Context()
if a.notRequireAuth(info.FullMethod) {
return handler(srv, ss)
}
ctx, err := a.authCtx(ctx)
if err != nil {
return err
}
err = handler(srv, ss)
if err != nil {
return err
}
return nil
}
}
//
//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{consts.AuthMiddlewareKey, token.Token.Sub})
//
// return context.WithValue(ctx, consts.AuthMiddlewareKey, token), nil
//}

View File

@ -0,0 +1,57 @@
package interceptor
import (
"context"
"fmt"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
"go.uber.org/zap"
"leafdev.top/Leaf/leaf-library-3/internal/base/logger"
)
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,46 @@
package grpc
import (
"github.com/google/wire"
"leafdev.top/Leaf/leaf-library-3/internal/api/grpc/interceptor"
"leafdev.top/Leaf/leaf-library-3/internal/api/grpc/v1/documents"
)
var ProviderSet = wire.NewSet(
interceptor.NewAuth,
interceptor.NewLogger,
documents.NewHandler,
NewInterceptor,
NewHandler,
)
func NewHandler(
documentApi *documents.Handler,
interceptor2 *Interceptor,
) *Handlers {
return &Handlers{
DocumentApi: documentApi,
Interceptor: interceptor2,
}
}
type Handlers struct {
DocumentApi *documents.Handler
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,11 @@
package documents
import (
"context"
v1 "leafdev.top/Leaf/leaf-library-3/proto/gen/proto/api/v1"
)
func (d *Handler) ListDocuments(ctx context.Context, req *v1.ListDocumentsRequest) (*v1.ListDocumentsResponse, error) {
return &v1.ListDocumentsResponse{}, nil
}

View File

@ -0,0 +1,18 @@
package documents
import (
"leafdev.top/Leaf/leaf-library-3/internal/dao"
v1 "leafdev.top/Leaf/leaf-library-3/proto/gen/proto/api/v1"
)
type Handler struct {
v1.UnimplementedDocumentServiceServer
dao *dao.Query
}
func NewHandler(dao *dao.Query) *Handler {
return &Handler{
v1.UnimplementedDocumentServiceServer{},
dao,
}
}

View File

@ -0,0 +1,87 @@
package middleware
import (
"net/http"
"slices"
"strings"
"github.com/gofiber/fiber/v2"
"leafdev.top/Leaf/leaf-library-3/internal/api/http/response"
"leafdev.top/Leaf/leaf-library-3/internal/base/conf"
"leafdev.top/Leaf/leaf-library-3/internal/consts"
"leafdev.top/Leaf/leaf-library-3/internal/pkg/user"
"leafdev.top/Leaf/leaf-library-3/internal/schema"
"leafdev.top/Leaf/leaf-library-3/internal/service/auth"
)
type Auth struct {
config *conf.Config
authService *auth.Service
}
var audienceLength int
func NewAuth(config *conf.Config, authService *auth.Service) *Auth {
audienceLength = len(config.App.AllowedAudiences)
return &Auth{
config,
authService,
}
}
func (a *Auth) Handler() fiber.Handler {
return func(c *fiber.Ctx) error {
var r = response.Ctx(c)
var err error
var token = new(user.User)
if a.config.Debug.Enabled {
token, err = a.authService.AuthFromToken(schema.JWTAccessToken, "")
if err != nil {
return r.Error(err).Send()
}
c.Locals(consts.AuthMiddlewareKey, token)
return c.Next()
}
authorization := c.Get(consts.AuthHeader)
if authorization == "" {
return r.Error(consts.ErrJWTFormatError).Send()
}
authSplit := strings.Split(authorization, " ")
if len(authSplit) != 2 {
return r.Error(consts.ErrJWTFormatError).Send()
}
if authSplit[0] != consts.AuthPrefix {
return r.Error(consts.ErrNotBearerType).Send()
}
token, err = a.authService.AuthFromToken(schema.JWTIDToken, authSplit[1])
if err != nil {
return r.Error(err).Status(http.StatusUnauthorized).Send()
}
if token == nil {
return r.Error(err).Status(http.StatusUnauthorized).Send()
}
if audienceLength > 0 {
// 检测 aud
if !slices.Contains(a.config.App.AllowedAudiences, token.Token.Aud) {
return r.Error(consts.ErrNotValidToken).Send()
}
}
c.Locals(consts.AuthMiddlewareKey, token)
return c.Next()
}
}

View File

@ -0,0 +1,20 @@
package middleware
import (
"github.com/gofiber/fiber/v2"
)
type JSONResponse struct {
}
func (*JSONResponse) Handler() fiber.Handler {
return func(c *fiber.Ctx) error {
c.Response().Header.Set("Content-Type", "application/json")
return c.Next()
}
}
func NewJSONResponse() *JSONResponse {
return &JSONResponse{}
}

View File

@ -0,0 +1,140 @@
package middleware
import (
"fmt"
"net/http"
"strings"
"github.com/gofiber/fiber/v2"
"leafdev.top/Leaf/leaf-library-3/internal/api/http/response"
"leafdev.top/Leaf/leaf-library-3/internal/base/conf"
userPkg "leafdev.top/Leaf/leaf-library-3/internal/pkg/user"
"leafdev.top/Leaf/leaf-library-3/internal/service/auth"
)
type RBAC struct {
authService *auth.Service
config *conf.Config
}
func (m *RBAC) RoutePermission() fiber.Handler {
return func(c *fiber.Ctx) error {
user, ok := m.authService.GetUserSafe(c)
if !ok {
return response.Ctx(c).Error(nil).Status(http.StatusUnauthorized).Send()
}
if !user.Valid {
return response.Ctx(c).Error(nil).Status(http.StatusUnauthorized).Send()
}
var path = cleanPath(c.Path())
act := strings.ToLower(c.Method())
permissionName := userPkg.Permission(m.config.App.Name + ":" + path + ":" + act)
pass := user.HasPermissions(permissionName)
if !pass {
return response.Ctx(c).
Message(fmt.Sprintf("permission denied, permission name: %s", permissionName)).
Error(nil).
Status(http.StatusForbidden).
Send()
}
return c.Next()
}
}
func (m *RBAC) RequirePermissions(permissions ...string) fiber.Handler {
return func(c *fiber.Ctx) error {
user, ok := m.authService.GetUserSafe(c)
if !ok {
return response.Ctx(c).Error(nil).Status(http.StatusUnauthorized).Send()
}
if !user.Valid {
return response.Ctx(c).Error(nil).Status(http.StatusUnauthorized).Send()
}
var pass = true
var failedPermissionName string
for _, permission := range permissions {
permissionName := userPkg.Permission(m.config.App.Name + ":" + permission)
has := user.HasPermissions(permissionName)
if !has {
failedPermissionName = permissionName.String()
pass = false
break
}
}
if !pass {
return response.Ctx(c).
Message(fmt.Sprintf("permission denied, required permissions: %s, failed permission: %s",
permissions, failedPermissionName)).
Error(nil).
Status(http.StatusForbidden).
Send()
}
return c.Next()
}
}
func (m *RBAC) RequireRoles(roles ...string) fiber.Handler {
return func(c *fiber.Ctx) error {
user, ok := m.authService.GetUserSafe(c)
if !ok {
return response.Ctx(c).Error(nil).Status(http.StatusUnauthorized).Send()
}
if !user.Valid {
return response.Ctx(c).Error(nil).Status(http.StatusUnauthorized).Send()
}
var pass = true
var failedRoleName string
for _, role := range roles {
roleName := userPkg.Role(m.config.App.Name + ":" + role)
pass = user.HasRoles(roleName)
if !pass {
failedRoleName = roleName.String()
break
}
}
if !pass {
return response.Ctx(c).
Message(fmt.Sprintf("permission denied, required roles: %s, failed role %s", roles, failedRoleName)).
Error(nil).
Status(http.StatusForbidden).
Send()
}
return c.Next()
}
}
func NewRBAC(authService *auth.Service, config *conf.Config) *RBAC {
return &RBAC{
authService: authService,
config: config,
}
}
func cleanPath(path string) string {
// 如果第一个字符是 /,则删掉
if path[0] == '/' {
path = path[1:]
}
// 将所有的 / 转为 :
return strings.ReplaceAll(path, "/", ":")
}

View File

@ -0,0 +1,25 @@
package middleware
import (
"github.com/gofiber/contrib/fiberzap/v2"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
)
type Logger struct {
logger *zap.Logger
}
func NewLogger(logger *zap.Logger) *Logger {
return &Logger{
logger: logger,
}
}
func (l *Logger) Handler() fiber.Handler {
var config = fiberzap.Config{
Logger: l.logger,
}
return fiberzap.New(config)
}

View File

@ -0,0 +1,58 @@
package http
import (
"github.com/gofiber/fiber/v2"
"github.com/google/wire"
"leafdev.top/Leaf/leaf-library-3/internal/api/http/middleware"
v1 "leafdev.top/Leaf/leaf-library-3/internal/api/http/v1"
"leafdev.top/Leaf/leaf-library-3/internal/base/conf"
"leafdev.top/Leaf/leaf-library-3/internal/base/logger"
"leafdev.top/Leaf/leaf-library-3/internal/service/auth"
)
type IMiddleware interface {
Handler() fiber.Handler
}
type Middleware struct {
Logger IMiddleware
Auth IMiddleware
JSONResponse IMiddleware
RBAC *middleware.RBAC
}
type Handlers struct {
User *v1.UserController
}
func NewHandler(
user *v1.UserController,
) *Handlers {
return &Handlers{
User: user,
}
}
func NewMiddleware(config *conf.Config, logger *logger.Logger, authService *auth.Service) *Middleware {
return &Middleware{
Logger: middleware.NewLogger(logger.Logger),
Auth: middleware.NewAuth(config, authService),
JSONResponse: middleware.NewJSONResponse(),
RBAC: middleware.NewRBAC(authService, config),
}
}
var ProviderSet = wire.NewSet(
// Init Middleware
middleware.NewAuth,
middleware.NewLogger,
middleware.NewJSONResponse,
middleware.NewRBAC,
NewMiddleware,
// Init Controller
v1.NewUserController,
// Init Handler
NewHandler,
)

View File

@ -0,0 +1,134 @@
package response
import (
"github.com/gofiber/fiber/v2"
"net/http"
)
type Body 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 *Body
httpStatus int
ctx *fiber.Ctx
}
func Ctx(c *fiber.Ctx) *HttpResponse {
return &HttpResponse{
body: &Body{
Wrap: true,
},
ctx: c,
httpStatus: 0,
}
}
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() error {
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 403 or 401 but not have message
if r.httpStatus == http.StatusForbidden || r.httpStatus == http.StatusUnauthorized {
if r.body.Message == "" {
r.Message("Unauthorized")
}
}
if r.body.Wrap {
return r.ctx.Status(r.httpStatus).JSON(r.body)
}
return r.ctx.Status(r.httpStatus).JSON(r.body.Data)
}
//
//func ResponseMessage(c *gin.Context, code int, message string, data interface{}) {
// c.JSON(code, &Body{
// Message: message,
// Data: data,
// })
// c.Abort()
//}
//
//func ResponseError(c *gin.Context, code int, err error) {
// c.JSON(code, &Body{
// Error: err.Error(),
// })
// c.Abort()
//}
//
//func Response(c *gin.Context, code int, data interface{}) {
// c.JSON(code, &Body{
// Data: data,
// })
// c.Abort()
//}

View File

@ -0,0 +1,43 @@
package v1
import (
"net/http"
"github.com/gofiber/fiber/v2"
"leafdev.top/Leaf/leaf-library-3/internal/api/http/response"
"leafdev.top/Leaf/leaf-library-3/internal/schema"
"leafdev.top/Leaf/leaf-library-3/internal/service/auth"
)
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} response.Body{data=schema.CurrentUserResponse}
// @Failure 400 {object} response.Body
// @Router /api/v1/ping [get]
func (u *UserController) Test(c *fiber.Ctx) error {
user := u.authService.GetUser(c)
var currentUserResponse = &schema.CurrentUserResponse{
IP: c.IP(),
Valid: user.Valid,
UserEmail: user.Token.Email,
UserId: user.Token.Sub,
UserName: user.Token.Name,
}
return response.Ctx(c).Status(http.StatusOK).Data(currentUserResponse).Send()
}

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

@ -0,0 +1,57 @@
package base
import (
"github.com/milvus-io/milvus-sdk-go/v2/client"
"gorm.io/gorm"
"leafdev.top/Leaf/leaf-library-3/internal/api"
"leafdev.top/Leaf/leaf-library-3/internal/base/conf"
"leafdev.top/Leaf/leaf-library-3/internal/base/logger"
"leafdev.top/Leaf/leaf-library-3/internal/base/redis"
"leafdev.top/Leaf/leaf-library-3/internal/base/s3"
"leafdev.top/Leaf/leaf-library-3/internal/base/server"
"leafdev.top/Leaf/leaf-library-3/internal/batch"
"leafdev.top/Leaf/leaf-library-3/internal/dao"
"leafdev.top/Leaf/leaf-library-3/internal/service"
)
type Application struct {
Config *conf.Config
Logger *logger.Logger
Api *api.Api
HttpServer *server.HttpServer
GORM *gorm.DB
DAO *dao.Query
Service *service.Service
Redis *redis.Redis
Batch *batch.Batch
S3 *s3.S3
Milvus client.Client
}
func NewApplication(
config *conf.Config,
httpServer *server.HttpServer,
api *api.Api,
logger *logger.Logger,
services *service.Service,
redis *redis.Redis,
batch *batch.Batch,
s3 *s3.S3,
gorm *gorm.DB,
dao *dao.Query,
milvus client.Client,
) *Application {
return &Application{
Config: config,
HttpServer: httpServer,
Api: api,
Logger: logger,
Service: services,
Redis: redis,
Batch: batch,
S3: s3,
GORM: gorm,
DAO: dao,
Milvus: milvus,
}
}

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

@ -0,0 +1,116 @@
package conf
// Config 配置文件不能有下划线或横线,否则不能解析
type Config struct {
App *App `yaml:"app"`
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"`
Milvus *Milvus `yaml:"milvus"`
ThirdParty *ThirdParty `yaml:"third_party" mapstructure:"third_party"`
}
type App struct {
Name string `yaml:"name"`
AllowedAudiences []string `yaml:"allowed_audiences" mapstructure:"allowed_audiences"`
}
type ThirdParty struct {
OpenAIApiKey string `yaml:"openai_api_key" mapstructure:"openai_api_key"`
}
type Http struct {
Host string `yaml:"host" mapstructure:"host"`
Port int `yaml:"port" mapstructure:"port"`
Url string `yaml:"url" mapstructure:"url"`
Cors struct {
Enabled bool `yaml:"enabled" mapstructure:"enabled"`
AllowedOrigins []string `yaml:"allow_origins" mapstructure:"allow_origins"`
AllowMethods []string `yaml:"allow_methods" mapstructure:"allow_methods"`
AllowHeaders []string `yaml:"allow_headers" mapstructure:"allow_headers"`
AllowCredentials bool `yaml:"allow_credentials" mapstructure:"allow_credentials"`
ExposeHeaders []string `yaml:"expose_headers" mapstructure:"expose_headers"`
MaxAge int `yaml:"max_age" mapstructure:"max_age"`
} `yaml:"cors" mapstructure:"cors"`
}
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"`
SSLMode string `yaml:"sslmode"`
TimeZone string `yaml:"timezone"`
}
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"`
GroupId string `yaml:"group_id" mapstructure:"group_id"`
Username string `yaml:"username" mapstructure:"username"`
Password string `yaml:"password" mapstructure:"password"`
MainTopic string `yaml:"main_topic" mapstructure:"main_topic"`
WorkerTopic string `yaml:"worker_topic" mapstructure:"worker_topic"`
}
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"`
}
type Grpc struct {
Address string `yaml:"address" mapstructure:"address"`
AddressGateway string `yaml:"address_gateway" mapstructure:"address_gateway"`
}

View File

@ -0,0 +1,36 @@
package conf
import (
"os"
"leafdev.top/Leaf/leaf-library-3/configs"
)
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,70 @@
package conf
import (
"os"
"path/filepath"
"strings"
"github.com/spf13/viper"
)
// depth 是配置文件的搜索深度
var depth = 8
func getConfigPath() string {
var path string
if os.Getenv("CONFIG") != "" {
path = os.Getenv("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 NewConfig() *Config {
var path = getConfigPath()
createConfigIfNotExists(path)
if path == "" {
panic("config file not found, created on app root.")
} else {
println("config file found:", path)
}
c := &Config{}
v := viper.New()
v.SetConfigType("yaml")
v.SetConfigFile(path)
if err := v.ReadInConfig(); err != nil {
panic(err)
}
if err := v.Unmarshal(c); err != nil {
panic(err)
}
return c
}

View File

@ -0,0 +1,9 @@
package conf
import (
"leafdev.top/Leaf/leaf-library-3/configs"
)
func (c *Config) GetRBACModel() string {
return string(configs.RBACModel)
}

View File

@ -0,0 +1,36 @@
package logger
import (
"go.uber.org/zap"
"leafdev.top/Leaf/leaf-library-3/internal/base/conf"
)
type Logger struct {
Sugar *zap.SugaredLogger
Logger *zap.Logger
}
func NewZapLogger(config *conf.Config) *Logger {
var logger *zap.Logger
var err error
if config.Debug.Enabled {
logger, err = zap.NewDevelopment(zap.AddCallerSkip(1))
} else {
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,30 @@
package milvus
import (
"context"
"github.com/milvus-io/milvus-sdk-go/v2/client"
"leafdev.top/Leaf/leaf-library-3/internal/base/conf"
"leafdev.top/Leaf/leaf-library-3/internal/base/logger"
"strconv"
)
func NewService(config *conf.Config, logger *logger.Logger) client.Client {
var address = config.Milvus.Host + ":" + strconv.Itoa(config.Milvus.Port)
logger.Sugar.Infof("Connecting 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("Milvus connected!")
if err != nil {
panic(err)
}
return c
}

View File

@ -0,0 +1,36 @@
package orm
import (
"fmt"
"gorm.io/driver/postgres"
"leafdev.top/Leaf/leaf-library-3/internal/base/conf"
"leafdev.top/Leaf/leaf-library-3/internal/base/logger"
"gorm.io/gorm"
"moul.io/zapgorm2"
)
func NewGORM(
config *conf.Config,
logger *logger.Logger,
) *gorm.DB {
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=%s TimeZone=%s",
config.Database.Host, config.Database.User, config.Database.Password, config.Database.Name, config.Database.Port, config.Database.SSLMode, config.Database.TimeZone)
gormConfig := &gorm.Config{}
if !config.Debug.Enabled {
zapGormLogger := zapgorm2.New(logger.Logger)
zapGormLogger.SetAsDefault()
gormConfig.Logger = zapGormLogger
}
db, err := gorm.Open(postgres.Open(dsn), gormConfig)
if err != nil {
panic(err)
}
return db
}

View File

@ -0,0 +1,39 @@
package redis
import (
"context"
"fmt"
"leafdev.top/Leaf/leaf-library-3/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 (
"leafdev.top/Leaf/leaf-library-3/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,
}
}

View File

@ -0,0 +1,111 @@
package server
import (
"net/http"
"strings"
"github.com/ansrivas/fiberprometheus/v2"
"github.com/bytedance/sonic"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/recover"
httpApi "leafdev.top/Leaf/leaf-library-3/internal/api/http"
"leafdev.top/Leaf/leaf-library-3/internal/api/http/response"
"leafdev.top/Leaf/leaf-library-3/internal/base/conf"
"leafdev.top/Leaf/leaf-library-3/internal/base/logger"
"leafdev.top/Leaf/leaf-library-3/internal/consts"
"leafdev.top/Leaf/leaf-library-3/internal/router"
"leafdev.top/Leaf/leaf-library-3/internal/service/auth"
)
type HttpServer struct {
config *conf.Config
Fiber *fiber.App
apiRouter *router.Api
swaggerRouter *router.SwaggerRouter
middleware *httpApi.Middleware
authService *auth.Service
}
// NewHTTPServer new http server.
func NewHTTPServer(
config *conf.Config,
apiRouter *router.Api,
swaggerRouter *router.SwaggerRouter,
middleware *httpApi.Middleware,
logger *logger.Logger,
) *HttpServer {
app := fiber.New(fiber.Config{
JSONEncoder: sonic.Marshal,
JSONDecoder: sonic.Unmarshal,
ErrorHandler: func(ctx *fiber.Ctx, err error) error {
logger.Sugar.Errorf("fiber error: %s", err)
return response.Ctx(ctx).Status(fiber.StatusInternalServerError).Error(consts.ErrInternalServerError).Send()
},
})
app.Use(recover.New())
app.Use(middleware.Logger.Handler())
app.Use(middleware.JSONResponse.Handler())
return &HttpServer{
config: config,
Fiber: app,
apiRouter: apiRouter,
swaggerRouter: swaggerRouter,
middleware: middleware,
}
}
func (hs *HttpServer) AllowAllCors() {
if hs.config.Http.Cors.Enabled {
var config = cors.Config{
AllowOrigins: strings.Join(hs.config.Http.Cors.AllowedOrigins, ","),
AllowMethods: strings.Join(hs.config.Http.Cors.AllowMethods, ","),
AllowHeaders: strings.Join(hs.config.Http.Cors.AllowHeaders, ","),
AllowCredentials: hs.config.Http.Cors.AllowCredentials,
ExposeHeaders: strings.Join(hs.config.Http.Cors.ExposeHeaders, ","),
MaxAge: hs.config.Http.Cors.MaxAge,
}
hs.Fiber.Use(cors.New(config))
}
}
func (hs *HttpServer) BizRouter() *fiber.App {
hs.AllowAllCors()
rootGroup := hs.Fiber.Group("")
// Swagger
hs.swaggerRouter.Register(rootGroup)
// 注册路由
hs.apiRouter.V1(rootGroup)
// 404 Route
hs.Fiber.Use(func(ctx *fiber.Ctx) error {
return response.Ctx(ctx).Status(fiber.StatusNotFound).Send()
})
return hs.Fiber
}
func (hs *HttpServer) MetricRouter() *fiber.App {
app := fiber.New()
app.Use(recover.New())
metricGroup := app.Group("")
prometheus := fiberprometheus.New(hs.config.App.Name)
prometheus.RegisterAt(app, "/metrics")
metricGroup.Use(prometheus.Middleware)
metricGroup.Get("/healthz", func(ctx *fiber.Ctx) error {
return ctx.Status(http.StatusOK).SendString("OK")
})
return app
}

View File

@ -0,0 +1,16 @@
package batch
import (
"leafdev.top/Leaf/leaf-library-3/internal/base/logger"
)
type Batch struct {
logger *logger.Logger
}
func NewBatch(
logger *logger.Logger,
) *Batch {
//base.NewApplication()
return &Batch{logger}
}

31
internal/consts/auth.go Normal file
View File

@ -0,0 +1,31 @@
package consts
import (
"errors"
"leafdev.top/Leaf/leaf-library-3/internal/pkg/user"
)
const (
AuthHeader = "Authorization"
AuthPrefix = "Bearer"
// AnonymousUser 调试模式下的用户
AnonymousUser user.Id = "anonymous"
AuthMiddlewareKey = "auth.user"
AuthAssistantShareMiddlewareKey = "auth.assistant.share"
)
var (
ErrNotValidToken = errors.New("JWT not valid")
ErrJWTFormatError = errors.New("JWT format error")
ErrNotBearerType = errors.New("not bearer token")
ErrEmptyResponse = errors.New("empty response")
ErrTokenError = errors.New("token type error")
ErrUnauthorized = errors.New("unauthorized")
ErrAudienceNotAllowed = errors.New("audience not allowed")
ErrNotYourResource = errors.New("this resource not yours")
ErrPermissionDenied = errors.New("permission denied")
)

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

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

9
internal/consts/rbac.go Normal file
View File

@ -0,0 +1,9 @@
package consts
import (
"leafdev.top/Leaf/leaf-library-3/internal/pkg/user"
)
const (
RoleSuperAdmin user.Role = "super-admin"
)

View File

@ -0,0 +1,7 @@
package consts
import "errors"
var (
ErrInternalServerError = errors.New("there was a server error, but we have logged this request for further investigation")
)

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"
"leafdev.top/Leaf/leaf-library-3/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
}

View File

@ -0,0 +1,15 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS users
(
id BIGSERIAL PRIMARY KEY,
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
);
-- +goose Down
DROP TABLE IF EXISTS users;

View File

@ -0,0 +1,12 @@
package migrations
import (
"embed"
"leafdev.top/Leaf/leaf-library-3/internal/base/conf"
)
//go:embed *.sql
var MigrationFS embed.FS
var Config *conf.Config

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

@ -0,0 +1,15 @@
package entity
import (
"time"
"leafdev.top/Leaf/leaf-library-3/internal/schema"
)
// 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 "users"
}

114
internal/pkg/user/user.go Normal file
View File

@ -0,0 +1,114 @@
package user
import (
"slices"
"time"
)
type Token struct {
Aud string `json:"aud"`
Iss string `json:"iss"`
Iat float64 `json:"iat"`
Exp float64 `json:"exp"`
Sub Id `json:"sub" mapstructure:"-"`
Scopes []string `json:"scopes"`
Roles []Role `json:"roles,omitempty"`
Permissions []Permission `json:"permissions"`
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 Token
Id Id
Valid bool
}
type Role string
func (r Role) String() string {
return string(r)
}
type Permission string
type Id string
func (u *User) GetId() Id {
return u.Token.Sub
}
func (u *User) GetName() string {
return u.Token.Name
}
func (u *User) GetEmail() string {
return u.Token.Email
}
func (u *User) GetPhone() string {
return u.Token.Phone
}
func (u *User) GetRoles() []Role {
return u.Token.Roles
}
func (u *User) GetPermissions() []Permission {
return u.Token.Permissions
}
func (u *User) GetAvatar() string {
return u.Token.Avatar
}
func (u *User) GetUuid() string {
return u.Token.Uuid
}
func (u *User) HasRoles(roles ...Role) bool {
if len(roles) == 0 {
return true
}
for _, role := range roles {
pass := slices.Contains(u.Token.Roles, role)
if !pass {
return false
}
}
return true
}
func (u *User) HasPermissions(permissions ...Permission) bool {
if len(permissions) == 0 {
return true
}
for _, p := range permissions {
pass := slices.Contains(u.Token.Permissions, p)
if !pass {
return false
}
}
return true
}
func (u Id) String() string {
return string(u)
}
func (up Permission) String() string {
return string(up)
}

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

@ -0,0 +1,48 @@
package router
import (
"github.com/gofiber/fiber/v2"
"leafdev.top/Leaf/leaf-library-3/internal/api/http"
)
// 两种方法都可以
//type Api struct {
// User *v1.UserController
//}
type Api struct {
HttpHandler *http.Handlers
Middleware *http.Middleware
}
func NewApiRoute(
//User *v1.UserController,
HttpHandler *http.Handlers,
Middleware *http.Middleware,
) *Api {
//return &Api{
// User,
//}
return &Api{
HttpHandler,
Middleware,
}
}
func (a *Api) V1(r fiber.Router) {
auth := r.Group("/api/v1")
{
// 要求认证
auth.Use(a.Middleware.Auth.Handler())
// RoutePermission 为权限验证
r.Get("/ping", a.Middleware.RBAC.RoutePermission(), a.HttpHandler.User.Test)
}
guest := r.Group("/api/v1")
{
guest.Get("/guest_ping", a.HttpHandler.User.Test)
}
}

View File

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

View File

@ -0,0 +1,19 @@
package router
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/swagger"
_ "leafdev.top/Leaf/leaf-library-3/docs"
)
type SwaggerRouter struct {
//config *conf.Config
}
func NewSwaggerRoute() *SwaggerRouter {
return &SwaggerRouter{}
}
func (a *SwaggerRouter) Register(r fiber.Router) {
r.Get("/swagger/*", swagger.HandlerDefault)
}

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

@ -0,0 +1,23 @@
package schema
import (
"strconv"
)
type EntityId uint
func (i EntityId) String() string {
return strconv.FormatUint(uint64(i), 10)
}
func (i EntityId) Uint() uint {
return uint(i)
}
func (i EntityId) Uint64() uint64 {
return uint64(i)
}
func (i EntityId) Int64() int64 {
return int64(i)
}

12
internal/schema/jwt.go Normal file
View File

@ -0,0 +1,12 @@
package schema
type JWTTokenTypes string
const (
JWTAccessToken JWTTokenTypes = "access_token"
JWTIDToken JWTTokenTypes = "id_token"
)
func (jwtTokenTypes JWTTokenTypes) String() string {
return string(jwtTokenTypes)
}

22
internal/schema/stream.go Normal file
View File

@ -0,0 +1,22 @@
package schema
import "encoding/json"
type EventMessage interface {
JSON() ([]byte, error)
}
type ProcessPostRequest struct {
EventMessage
PostId string `json:"post_id"`
Content string `json:"content"`
}
func (p *ProcessPostRequest) JSON() ([]byte, error) {
return json.Marshal(p)
}
type ProcessPostResult struct {
PostId string `json:"post_id"`
Keywords []string `json:"keywords"`
}

View File

@ -0,0 +1,11 @@
package schema
import "leafdev.top/Leaf/leaf-library-3/internal/pkg/user"
type CurrentUserResponse struct {
IP string `json:"ip"`
Valid bool `json:"valid"`
UserEmail string `json:"userEmail"`
UserId user.Id `json:"userId"`
UserName string `json:"userName"`
}

View File

@ -0,0 +1,117 @@
package auth
import (
"context"
"github.com/gofiber/fiber/v2"
"github.com/mitchellh/mapstructure"
"leafdev.top/Leaf/leaf-library-3/internal/consts"
"leafdev.top/Leaf/leaf-library-3/internal/pkg/user"
"leafdev.top/Leaf/leaf-library-3/internal/schema"
)
func (a *Service) AuthFromToken(tokenType schema.JWTTokenTypes, token string) (*user.User, error) {
if a.config.Debug.Enabled {
return a.parseUserJWT(tokenType, "")
}
return a.parseUserJWT(tokenType, token)
}
func (a *Service) GetUserFromIdToken(idToken string) (*user.User, error) {
return a.parseUserJWT(schema.JWTIDToken, idToken)
}
func (a *Service) GetUser(ctx *fiber.Ctx) *user.User {
userCtx := ctx.Locals(consts.AuthMiddlewareKey)
u, ok := userCtx.(*user.User)
u.Id = u.Token.Sub
if !ok {
panic("User context is not valid")
}
return u
}
func (a *Service) GetCtx(ctx context.Context) *user.User {
userCtx := ctx.Value(consts.AuthMiddlewareKey)
u, ok := userCtx.(*user.User)
u.Id = u.Token.Sub
if !ok {
panic("User context is not valid")
}
return u
}
func (a *Service) GetUserSafe(ctx *fiber.Ctx) (*user.User, bool) {
userCtx := ctx.Locals(consts.AuthMiddlewareKey)
u, ok := userCtx.(*user.User)
u.Id = u.Token.Sub
return u, ok
}
func (a *Service) GetCtxSafe(ctx context.Context) (*user.User, bool) {
userCtx := ctx.Value(consts.AuthMiddlewareKey)
u, ok := userCtx.(*user.User)
u.Id = u.Token.Sub
return u, ok
}
func (a *Service) SetUser(ctx context.Context, user *user.User) context.Context {
return context.WithValue(ctx, consts.AuthMiddlewareKey, user)
}
func (a *Service) parseUserJWT(tokenType schema.JWTTokenTypes, jwtToken string) (*user.User, error) {
var sub = consts.AnonymousUser
var jwtIdToken = new(user.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
}
sub = user.Id(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,25 @@
package auth
import (
"leafdev.top/Leaf/leaf-library-3/internal/base/conf"
"leafdev.top/Leaf/leaf-library-3/internal/base/logger"
"leafdev.top/Leaf/leaf-library-3/internal/service/jwks"
)
type Service struct {
config *conf.Config
jwks *jwks.JWKS
logger *logger.Logger
}
func NewService(
config *conf.Config,
jwks *jwks.JWKS,
logger *logger.Logger,
) *Service {
return &Service{
config: config,
jwks: jwks,
logger: logger,
}
}

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,58 @@
package jwks
import (
"errors"
"leafdev.top/Leaf/leaf-library-3/internal/base/conf"
"leafdev.top/Leaf/leaf-library-3/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())
} else {
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,38 @@
package service
import (
"leafdev.top/Leaf/leaf-library-3/internal/base/logger"
"leafdev.top/Leaf/leaf-library-3/internal/service/auth"
"leafdev.top/Leaf/leaf-library-3/internal/service/jwks"
"leafdev.top/Leaf/leaf-library-3/internal/service/stream"
"github.com/google/wire"
)
type Service struct {
logger *logger.Logger
Jwks *jwks.JWKS
Auth *auth.Service
Stream *stream.Service
}
var Provide = wire.NewSet(
jwks.NewJWKS,
auth.NewService,
stream.NewService,
NewService,
)
func NewService(
logger *logger.Logger,
jwks *jwks.JWKS,
auth *auth.Service,
stream *stream.Service,
) *Service {
return &Service{
logger,
jwks,
auth,
stream,
}
}

View File

@ -0,0 +1,68 @@
package stream
import (
"context"
"fmt"
"github.com/segmentio/kafka-go"
"time"
)
//func (s *Service) Listen(topic string, handler HandlerFunc) error {
// conn, err := s.dial(topic)
// if err != nil {
// return err
// }
//
// batch := conn.ReadBatch(10e3, 1e6) // fetch 10KB min, 1MB max
//
// b := make([]byte, 10e3) // 10KB max per message
// for {
// n, err := batch.Read(b)
// if err != nil {
// return err
// }
// handler(b[:n])
// }
//}
func (s *Service) Consumer(topic string, groupId string) *kafka.Reader {
var r = kafka.ReaderConfig{
Brokers: s.config.Kafka.BootstrapServers,
GroupID: groupId,
GroupTopics: nil,
Topic: topic,
CommitInterval: time.Second,
//StartOffset: kafka., // 仅对新创建的消费者组生效从头开始消费工作中可能更常用从最新的开始消费kafka.LastOffset
Logger: nil,
ErrorLogger: nil,
IsolationLevel: 0,
MaxAttempts: 0,
OffsetOutOfRangeError: false,
MaxBytes: 10e6, // 10MB
}
if s.config.Kafka.Username != "" && s.config.Kafka.Password != "" {
r.Dialer = &kafka.Dialer{
Timeout: 10 * time.Second,
DualStack: true,
SASLMechanism: s.auth(),
}
}
return kafka.NewReader(r)
}
type HandlerFunc func([]byte)
// ReadMessage 消费消息
func (s *Service) ReadMessage(ctx context.Context, topic string, groupId string) {
for {
if msg, err := s.Consumer(topic, groupId).ReadMessage(ctx); err != nil {
fmt.Println(fmt.Sprintf("读kafka失败err:%v", err))
continue
} else {
fmt.Println(fmt.Sprintf("topic=%s,partition=%d,offset=%d,key=%s,value=%s", msg.Topic, msg.Partition, msg.Offset, msg.Key, msg.Value))
}
}
}

View File

@ -0,0 +1,13 @@
package stream
import (
"github.com/segmentio/kafka-go/sasl/plain"
)
func (s *Service) auth() plain.Mechanism {
mechanism := plain.Mechanism{
Username: s.config.Kafka.Username,
Password: s.config.Kafka.Password,
}
return mechanism
}

View File

@ -0,0 +1,120 @@
package stream
import (
"context"
"time"
"github.com/segmentio/kafka-go"
"leafdev.top/Leaf/leaf-library-3/internal/schema"
)
//var connections = map[string]*kafka.Conn{}
//func (s *Service) dial(topic string) (*kafka.Conn, error) {
//
// // 如果topic 存在于 connections 则直接返回
// if conn, ok := connections[topic]; ok {
// return conn, nil
// }
//
// ctx := context.Background()
//
// conn, err := kafka.DialLeader(ctx, "tcp", s.config.Kafka.BootstrapServers[0], s.topic(topic), 0)
// if err != nil {
// return nil, err
// }
// //err = conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
// //if err != nil {
// // return conn, err
// //}
// //
// //// set read deadline
// //err = conn.SetReadDeadline(time.Now().Add(10 * time.Second))
// //if err != nil {
// // return conn, err
// //}
//
// connections[topic] = conn
//
// return conn, nil
//}
//
//func (s *Service) Publish(topic string, message ...[]byte) error {
// conn, err := s.dial(topic)
// if err != nil {
// return err
// }
//
// msg := make([]kafka.Message, len(message))
// for i, v := range message {
// msg[i] = kafka.Message{Value: v}
// }
//
// _, err = conn.WriteMessages(msg...)
//
// return err
//}
func (s *Service) Producer(topic string) *kafka.Writer {
var w = &kafka.Writer{
Addr: kafka.TCP(s.config.Kafka.BootstrapServers...),
Topic: topic,
Balancer: &kafka.Hash{}, // 用于对key进行hash决定消息发送到哪个分区
MaxAttempts: 0,
WriteBackoffMin: 0,
WriteBackoffMax: 0,
BatchSize: 0,
BatchBytes: 0,
BatchTimeout: 0,
ReadTimeout: 0,
//WriteTimeout: time.Second, // kafka有时候可能负载很高写不进去那么超时后可以放弃写入用于可以丢消息的场景
RequiredAcks: kafka.RequireAll, // 不需要任何节点确认就返回
Async: true,
Completion: nil,
Compression: 0,
Logger: nil,
ErrorLogger: nil,
Transport: nil,
AllowAutoTopicCreation: false, // 第一次发消息的时候如果topic不存在就自动创建topic工作中禁止使用
}
if s.config.Kafka.Username != "" && s.config.Kafka.Password != "" {
w.Transport = &kafka.Transport{
SASL: s.auth(),
}
}
return w
}
func (s *Service) SendMessage(ctx context.Context, topic string, data []byte) error {
msg := kafka.Message{
Partition: 0,
Offset: 0,
HighWaterMark: 0,
//Key: key,
Value: data,
Time: time.Time{},
}
err := s.Producer(topic).WriteMessages(ctx, msg)
return err
}
func (s *Service) SendEvent(ctx context.Context, topic string, data schema.EventMessage) error {
j, err := data.JSON()
if err != nil {
return err
}
msg := kafka.Message{
Partition: 0,
Offset: 0,
HighWaterMark: 0,
Value: j,
Time: time.Time{},
}
err = s.Producer(topic).WriteMessages(ctx, msg)
return err
}

View File

@ -0,0 +1,15 @@
package stream
import (
"leafdev.top/Leaf/leaf-library-3/internal/base/conf"
)
type Service struct {
config *conf.Config
}
func NewService(config *conf.Config) *Service {
return &Service{
config,
}
}

18
main.go Normal file
View File

@ -0,0 +1,18 @@
package main
import (
"leafdev.top/Leaf/leaf-library-3/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
}
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
package template.document;
option go_package = "gen/api/v1";
service DocumentService {
rpc ListDocuments(ListDocumentsRequest) returns (ListDocumentsResponse);
}
message ListDocumentsRequest {
string Message = 1;
}
message ListDocumentsResponse {
string Message = 1;
}

View File

@ -0,0 +1,57 @@
{
"swagger": "2.0",
"info": {
"title": "proto/api/v1/document_service.proto",
"version": "version not set"
},
"tags": [
{
"name": "DocumentService"
}
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {},
"definitions": {
"documentListDocumentsResponse": {
"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,217 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.34.2
// protoc (unknown)
// source: proto/api/v1/document_service.proto
package v1
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_proto_api_v1_document_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_proto_api_v1_document_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_proto_api_v1_document_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_proto_api_v1_document_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_proto_api_v1_document_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_proto_api_v1_document_service_proto_rawDescGZIP(), []int{1}
}
func (x *ListDocumentsResponse) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
var File_proto_api_v1_document_service_proto protoreflect.FileDescriptor
var file_proto_api_v1_document_service_proto_rawDesc = []byte{
0x0a, 0x23, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x64,
0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x2e,
0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 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, 0x75, 0x0a,
0x0f, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x12, 0x62, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
0x73, 0x12, 0x27, 0x2e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x2e, 0x64, 0x6f, 0x63,
0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65,
0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x74, 0x65, 0x6d,
0x70, 0x6c, 0x61, 0x74, 0x65, 0x2e, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4c,
0x69, 0x73, 0x74, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0c, 0x5a, 0x0a, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f,
0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_proto_api_v1_document_service_proto_rawDescOnce sync.Once
file_proto_api_v1_document_service_proto_rawDescData = file_proto_api_v1_document_service_proto_rawDesc
)
func file_proto_api_v1_document_service_proto_rawDescGZIP() []byte {
file_proto_api_v1_document_service_proto_rawDescOnce.Do(func() {
file_proto_api_v1_document_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_api_v1_document_service_proto_rawDescData)
})
return file_proto_api_v1_document_service_proto_rawDescData
}
var file_proto_api_v1_document_service_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_proto_api_v1_document_service_proto_goTypes = []any{
(*ListDocumentsRequest)(nil), // 0: template.document.ListDocumentsRequest
(*ListDocumentsResponse)(nil), // 1: template.document.ListDocumentsResponse
}
var file_proto_api_v1_document_service_proto_depIdxs = []int32{
0, // 0: template.document.DocumentService.ListDocuments:input_type -> template.document.ListDocumentsRequest
1, // 1: template.document.DocumentService.ListDocuments:output_type -> template.document.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_proto_api_v1_document_service_proto_init() }
func file_proto_api_v1_document_service_proto_init() {
if File_proto_api_v1_document_service_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_proto_api_v1_document_service_proto_msgTypes[0].Exporter = func(v any, i int) any {
switch v := v.(*ListDocumentsRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proto_api_v1_document_service_proto_msgTypes[1].Exporter = func(v any, i int) any {
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_proto_api_v1_document_service_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_proto_api_v1_document_service_proto_goTypes,
DependencyIndexes: file_proto_api_v1_document_service_proto_depIdxs,
MessageInfos: file_proto_api_v1_document_service_proto_msgTypes,
}.Build()
File_proto_api_v1_document_service_proto = out.File
file_proto_api_v1_document_service_proto_rawDesc = nil
file_proto_api_v1_document_service_proto_goTypes = nil
file_proto_api_v1_document_service_proto_depIdxs = nil
}

View File

@ -0,0 +1,164 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: proto/api/v1/document_service.proto
/*
Package v1 is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package v1
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, "/template.document.DocumentService/ListDocuments", runtime.WithHTTPPathPattern("/template.document.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, "/template.document.DocumentService/ListDocuments", runtime.WithHTTPPathPattern("/template.document.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{"template.document.DocumentService", "ListDocuments"}, ""))
)
var (
forward_DocumentService_ListDocuments_0 = runtime.ForwardResponseMessage
)

View File

@ -0,0 +1,121 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc (unknown)
// source: proto/api/v1/document_service.proto
package v1
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.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
DocumentService_ListDocuments_FullMethodName = "/template.document.DocumentService/ListDocuments"
)
// 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) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListDocumentsResponse)
err := c.cc.Invoke(ctx, DocumentService_ListDocuments_FullMethodName, in, out, cOpts...)
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.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedDocumentServiceServer struct{}
func (UnimplementedDocumentServiceServer) ListDocuments(context.Context, *ListDocumentsRequest) (*ListDocumentsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListDocuments not implemented")
}
func (UnimplementedDocumentServiceServer) mustEmbedUnimplementedDocumentServiceServer() {}
func (UnimplementedDocumentServiceServer) testEmbeddedByValue() {}
// 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) {
// If the following call pancis, it indicates UnimplementedDocumentServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
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_FullMethodName,
}
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: "template.document.DocumentService",
HandlerType: (*DocumentServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "ListDocuments",
Handler: _DocumentService_ListDocuments_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "proto/api/v1/document_service.proto",
}