From 56e83b0725a53253b80c935e9ef14367a83f85cc Mon Sep 17 00:00:00 2001 From: Yi-Ting Shih Date: Sun, 7 Dec 2025 03:28:07 +0800 Subject: [PATCH] Init: setup uptrace and swagger --- .gitignore | 1 + Dockerfile | 7 +++ Makefile | 2 +- cmds/serve.go | 98 ++++++++++++++++++-------------- docker-compose.yml | 12 +++- docker/postgres/uptrace.sql | 4 ++ docs/docs.go | 43 +++++++++++++- docs/swagger.json | 43 +++++++++++++- docs/swagger.yaml | 27 ++++++++- go.mod | 47 +++++++++++---- go.sum | 98 ++++++++++++++++++++++++-------- go.work.sum | 29 ++++++++++ handlers/api/getImages.go | 27 +++++++++ handlers/api/handlers.go | 8 +++ implements/bunDatabase.go | 4 ++ implements/minioObjectStorage.go | 4 ++ interfaces/database.go | 4 ++ interfaces/objectStorage.go | 4 ++ middlewares/accessLog.go | 16 ++++++ middlewares/bunrouterOtel.go | 48 ++++++++++++++++ middlewares/cors.go | 30 ++++++++++ middlewares/errorHandler.go | 88 ++++++++++++++++++++++++++++ models/alias.go | 22 +++++++ models/image.go | 15 +++++ utils/getRemoteAddr.go | 23 ++++++++ utils/initDB.go | 1 + utils/initMinIO.go | 24 ++++++++ utils/success.go | 14 +++++ 28 files changed, 662 insertions(+), 81 deletions(-) create mode 100644 Dockerfile create mode 100644 docker/postgres/uptrace.sql create mode 100644 handlers/api/getImages.go create mode 100644 handlers/api/handlers.go create mode 100644 implements/bunDatabase.go create mode 100644 implements/minioObjectStorage.go create mode 100644 interfaces/database.go create mode 100644 interfaces/objectStorage.go create mode 100644 middlewares/accessLog.go create mode 100644 middlewares/bunrouterOtel.go create mode 100644 middlewares/cors.go create mode 100644 middlewares/errorHandler.go create mode 100644 models/alias.go create mode 100644 models/image.go create mode 100644 utils/getRemoteAddr.go create mode 100644 utils/initDB.go create mode 100644 utils/initMinIO.go create mode 100644 utils/success.go diff --git a/.gitignore b/.gitignore index e34d8c3..9d7dfe2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ backend +.env diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0186a38 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM docker.io/library/debian:13-slim + +ADD backend /work/backend +WORKDIR /work + +ENTRYPOINT ["/work/backend"] +CMD ["serve"] diff --git a/Makefile b/Makefile index 8f7a1d6..2170da7 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ GO_ENV += CGO_ENABLED=0 SOURCE := $(shell find . -type f -name '*.go') TARGET := backend -all: swagger docker install +all: swagger docker docker: $(TARGET) docker compose up -d --force-recreate --build backend diff --git a/cmds/serve.go b/cmds/serve.go index 150d8e8..81d8274 100644 --- a/cmds/serve.go +++ b/cmds/serve.go @@ -1,24 +1,27 @@ package cmds import ( - "database/sql" "log" "net/http" + "gitea.konchin.com/go2025/backend/handlers/api" + "gitea.konchin.com/go2025/backend/middlewares" "gitea.konchin.com/go2025/backend/tracing" "github.com/spf13/cobra" "github.com/spf13/viper" - "go.uber.org/zap" + httpSwagger "github.com/swaggo/http-swagger" + "github.com/uptrace/bunrouter" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" _ "gitea.konchin.com/go2025/backend/docs" ) -// @title Golang 2025 Final Project -// @version 0.0.1 -// @termsOfService http://swagger.io/terms +// @title Golang 2025 Final Project +// @version 0.0.1 +// @termsOfService http://swagger.io/terms // -// @license.name 0BSD -// @basePath / +// @license.name 0BSD +// @basePath / var serveCmd = &cobra.Command{ Use: "serve", Run: func(cmd *cobra.Command, args []string) { @@ -32,38 +35,44 @@ var serveCmd = &cobra.Command{ defer tracing.DeferUptrace(ctx) } - // Initialize DB instance - sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN( - viper.GetString("pg-connection-string")))) - bunDB := bun.NewDB(sqldb, pgdialect.New()) - bunDB.AddQueryHook(bunotel.NewQueryHook(bunotel.WithDBName("backend"))) + /* + // Initialize DB instance + sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN( + viper.GetString("pg-connection-string")))) + bunDB := bun.NewDB(sqldb, pgdialect.New()) + bunDB.AddQueryHook(bunotel.NewQueryHook(bunotel.WithDBName("backend"))) - // Initialize MinIO instance - mc, err := minio.New(viper.GetString("minio-host"), &minio.Options{ - Creds: credentials.NewStaticV4( - viper.GetString("minio-accesskey"), - viper.GetString("minio-secretkey"), - "", - ), - Secure: viper.GetBool("minio-usessl"), - }) - if err != nil { - tracing.Logger.Ctx(ctx). - Error("failed to create minio client", - zap.Error(err)) - panic(err) - } + // Initialize MinIO instance + mc, err := minio.New(viper.GetString("minio-host"), &minio.Options{ + Creds: credentials.NewStaticV4( + viper.GetString("minio-accesskey"), + viper.GetString("minio-secretkey"), + "", + ), + Secure: viper.GetBool("minio-usessl"), + }) + if err != nil { + tracing.Logger.Ctx(ctx). + Error("failed to create minio client", + zap.Error(err)) + panic(err) + } - if err := utils.InitMinIO(ctx, mc); err != nil { - tracing.Logger.Ctx(ctx). - Error("failed to minio init", - zap.Error(err)) - panic(err) - } + if err := utils.InitMinIO(ctx, mc); err != nil { + tracing.Logger.Ctx(ctx). + Error("failed to minio init", + zap.Error(err)) + panic(err) + } - // Initialize custom interfaces - db := implements.NewBunDatabase(bunDB) - s3 := implements.NewMinIOObjectStorage(mc) + // Initialize custom interfaces + db := implements.NewBunDatabase(bunDB) + s3 := implements.NewMinIOObjectStorage(mc) + + */ + + // Initialize handlers + apis := api.NewHandlers() // Initialize backend router router := bunrouter.New() @@ -73,6 +82,9 @@ var serveCmd = &cobra.Command{ Use(middlewares.AccessLog). Use(middlewares.CORSHandler) + apiGroup := backend.NewGroup("/api") + apiGroup.GET("/images", apis.GetImages) + if viper.GetBool("swagger") { backend.GET("/swagger/*any", bunrouter.HTTPHandlerFunc( @@ -88,6 +100,8 @@ var serveCmd = &cobra.Command{ func init() { serveCmd.Flags(). String("port", "8080", "Port to listen on") + serveCmd.Flags(). + String("cors-origin", "", "CORS origin") serveCmd.Flags(). Bool("zap-production", true, "Toggle production log format") @@ -99,15 +113,15 @@ func init() { "postgres://go2025:go2025@pg:5432/go2025?sslmode=disable", "Postgres connection string") - backendCmd.Flags(). + serveCmd.Flags(). String("minio-host", "minio:9000", "MinIO host[:port]") - backendCmd.Flags(). + serveCmd.Flags(). String("minio-bucket", "go2025", "MinIO bucket") - backendCmd.Flags(). + serveCmd.Flags(). String("minio-accesskey", "poop", "MinIO accesskey") - backendCmd.Flags(). - String("minio-secretkey", "poop", "MinIO secretkey") - backendCmd.Flags(). + serveCmd.Flags(). + String("minio-secretkey", "poop114514", "MinIO secretkey") + serveCmd.Flags(). Bool("minio-usessl", false, "MinIO usessl") serveCmd.Flags(). diff --git a/docker-compose.yml b/docker-compose.yml index e59bfbd..6f2d9f1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,11 +12,19 @@ services: - postgres:/var/lib/postgresql/data restart: unless-stopped + redis: + image: library/redis:8.2.1-alpine3.22 + ports: + - 6379:6379 + volumes: + - redis:/data + restart: unless-stopped + minio: image: minio/minio:RELEASE.2025-07-23T15-54-02Z environment: MINIO_ROOT_USER: poop - MINIO_ROOT_PASSWORD: poop + MINIO_ROOT_PASSWORD: poop114514 command: ["server", "/data", "--console-address", ":9001"] ports: - 9000:9000 @@ -46,6 +54,7 @@ services: volumes: - ./docker/uptrace/config.yml:/etc/uptrace/config.yml depends_on: + - redis - postgres - clickhouse restart: unless-stopped @@ -53,7 +62,6 @@ services: backend: build: context: . - target: backend_runtime env_file: - path: ./.env ports: diff --git a/docker/postgres/uptrace.sql b/docker/postgres/uptrace.sql new file mode 100644 index 0000000..ce47e26 --- /dev/null +++ b/docker/postgres/uptrace.sql @@ -0,0 +1,4 @@ +CREATE USER uptrace; +CREATE DATABASE uptrace; +ALTER DATABASE uptrace OWNER TO uptrace; +ALTER USER uptrace ENCRYPTED PASSWORD 'uptrace'; diff --git a/docs/docs.go b/docs/docs.go index 38188f4..a9b40c8 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -18,7 +18,48 @@ const docTemplate = `{ }, "host": "{{.Host}}", "basePath": "{{.BasePath}}", - "paths": {} + "paths": { + "/api/images": { + "get": { + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.getImagesOutputImage" + } + }, + "400": { + "description": "Bad Request" + }, + "401": { + "description": "Unauthorized" + } + } + } + } + }, + "definitions": { + "api.getImagesOutputImage": { + "type": "object", + "properties": { + "aliases": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "integer" + }, + "uploaded_at": { + "type": "integer" + }, + "uploaded_user_id": { + "type": "string" + } + } + } + } }` // SwaggerInfo holds exported Swagger Info so clients can modify it diff --git a/docs/swagger.json b/docs/swagger.json index 14e1fdf..99f04b3 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -10,5 +10,46 @@ "version": "0.0.1" }, "basePath": "/", - "paths": {} + "paths": { + "/api/images": { + "get": { + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.getImagesOutputImage" + } + }, + "400": { + "description": "Bad Request" + }, + "401": { + "description": "Unauthorized" + } + } + } + } + }, + "definitions": { + "api.getImagesOutputImage": { + "type": "object", + "properties": { + "aliases": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "integer" + }, + "uploaded_at": { + "type": "integer" + }, + "uploaded_user_id": { + "type": "string" + } + } + } + } } \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml index a1c3528..ea7c45b 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,4 +1,18 @@ basePath: / +definitions: + api.getImagesOutputImage: + properties: + aliases: + items: + type: string + type: array + id: + type: integer + uploaded_at: + type: integer + uploaded_user_id: + type: string + type: object info: contact: {} license: @@ -6,5 +20,16 @@ info: termsOfService: http://swagger.io/terms title: Golang 2025 Final Project version: 0.0.1 -paths: {} +paths: + /api/images: + get: + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.getImagesOutputImage' + "400": + description: Bad Request + "401": + description: Unauthorized swagger: "2.0" diff --git a/go.mod b/go.mod index 5607f7d..8cb1346 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,19 @@ module gitea.konchin.com/go2025/backend go 1.25.4 require ( + github.com/minio/minio-go/v7 v7.0.97 github.com/spf13/cobra v1.10.2 github.com/spf13/viper v1.21.0 + github.com/swaggo/http-swagger v1.3.4 github.com/swaggo/swag v1.16.6 + github.com/uptrace/bun v1.2.16 + github.com/uptrace/bun/dialect/pgdialect v1.2.16 + github.com/uptrace/bun/driver/pgdriver v1.2.16 + github.com/uptrace/bun/extra/bunotel v1.2.16 + github.com/uptrace/bunrouter v1.0.23 github.com/uptrace/opentelemetry-go-extra/otelzap v0.3.2 github.com/uptrace/uptrace-go v1.38.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 go.opentelemetry.io/otel v1.38.0 go.opentelemetry.io/otel/trace v1.38.0 go.uber.org/zap v1.27.1 @@ -15,30 +23,46 @@ require ( require ( github.com/KyleBanks/depth v1.2.1 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/go-ini/ini v1.67.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.6 // indirect - github.com/go-openapi/spec v0.20.4 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/spec v0.20.6 // indirect github.com/go-openapi/swag v0.19.15 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.11 // indirect + github.com/klauspost/crc32 v1.3.0 // indirect github.com/mailru/easyjson v0.7.6 // indirect + github.com/minio/crc64nvme v1.1.0 // indirect + github.com/minio/md5-simd v1.1.2 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/philhofer/fwd v1.2.0 // indirect + github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect + github.com/rs/xid v1.6.0 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe // indirect + github.com/tinylib/msgp v1.3.0 // indirect + github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect + github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 // indirect github.com/uptrace/opentelemetry-go-extra/otelutil v0.3.2 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/runtime v0.63.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 // indirect @@ -54,15 +78,18 @@ require ( go.opentelemetry.io/proto/otlp v1.8.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/mod v0.27.0 // indirect - golang.org/x/net v0.44.0 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.36.0 // indirect - golang.org/x/text v0.29.0 // indirect - golang.org/x/tools v0.36.0 // indirect + golang.org/x/crypto v0.45.0 // indirect + golang.org/x/mod v0.29.0 // indirect + golang.org/x/net v0.47.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/text v0.31.0 // indirect + golang.org/x/tools v0.38.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect google.golang.org/grpc v1.75.1 // indirect google.golang.org/protobuf v1.36.9 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + mellium.im/sasl v0.3.2 // indirect ) diff --git a/go.sum b/go.sum index b15dcd9..4c2f18d 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,5 @@ github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -11,10 +7,16 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/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/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 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.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +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-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -23,10 +25,10 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= -github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= -github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/spec v0.20.6 h1:ich1RQ3WDbfoeTqTAb+5EIxNmpKVJZWBNah9RAT0jIQ= +github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= @@ -42,8 +44,17 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnV github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +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/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/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU= +github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM= +github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw= 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= @@ -55,13 +66,25 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/minio/crc64nvme v1.1.0 h1:e/tAguZ+4cw32D+IO/8GSf5UVr9y+3eJcxZI2WOO/7Q= +github.com/minio/crc64nvme v1.1.0/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= +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.97 h1:lqhREPyfgHTB/ciX8k2r8k0D93WaFqxbJX36UZq5occ= +github.com/minio/minio-go/v7 v7.0.97/go.mod h1:re5VXuo0pwEtoNLsNuSr0RrLfT/MBtohwdaSmPPSRSk= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= +github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= +github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +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.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= @@ -85,16 +108,42 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe h1:K8pHPVoTgxFJt1lXuIzzOX7zZhZFldJQK/CgKx9BFIc= +github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= +github.com/swaggo/http-swagger v1.3.4 h1:q7t/XLx0n15H1Q9/tk3Y9L4n210XzJF5WtnDX64a5ww= +github.com/swaggo/http-swagger v1.3.4/go.mod h1:9dAh0unqMBAlbp1uE2Uc2mQTxNMU/ha4UbucIg1MFkQ= github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI= github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg= +github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww= +github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= +github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= +github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= +github.com/uptrace/bun v1.2.16 h1:QlObi6ZIK5Ao7kAALnh91HWYNZUBbVwye52fmlQM9kc= +github.com/uptrace/bun v1.2.16/go.mod h1:jMoNg2n56ckaawi/O/J92BHaECmrz6IRjuMWqlMaMTM= +github.com/uptrace/bun/dialect/pgdialect v1.2.16 h1:KFNZ0LxAyczKNfK/IJWMyaleO6eI9/Z5tUv3DE1NVL4= +github.com/uptrace/bun/dialect/pgdialect v1.2.16/go.mod h1:IJdMeV4sLfh0LDUZl7TIxLI0LipF1vwTK3hBC7p5qLo= +github.com/uptrace/bun/driver/pgdriver v1.2.16 h1:b1kpXKUxtTSGYow5Vlsb+dKV3z0R7aSAJNfMfKp61ZU= +github.com/uptrace/bun/driver/pgdriver v1.2.16/go.mod h1:H6lUZ9CBfp1X5Vq62YGSV7q96/v94ja9AYFjKvdoTk0= +github.com/uptrace/bun/extra/bunotel v1.2.16 h1:zXNUHjIGfVzWv/H+REwKX05zWV+OGUkmC1X1HjlVr+M= +github.com/uptrace/bun/extra/bunotel v1.2.16/go.mod h1:p8L+qeQOxs6TOBa341F4M5HlwujXMTVL3NA9DEaBybQ= +github.com/uptrace/bunrouter v1.0.23 h1:Bi7NKw3uCQkcA/GUCtDNPq5LE5UdR9pe+UyWbjHB/wU= +github.com/uptrace/bunrouter v1.0.23/go.mod h1:O3jAcl+5qgnF+ejhgkmbceEk0E/mqaK+ADOocdNpY8M= +github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 h1:ZjUj9BLYf9PEqBn8W/OapxhPjVRdC6CsXTdULHsyk5c= +github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2/go.mod h1:O8bHQfyinKwTXKkiKNGmLQS7vRsqRxIQTFZpYpHK3IQ= github.com/uptrace/opentelemetry-go-extra/otelutil v0.3.2 h1:3/aHKUq7qaFMWxyQV0W2ryNgg8x8rVeKVA20KJUkfS0= github.com/uptrace/opentelemetry-go-extra/otelutil v0.3.2/go.mod h1:Zit4b8AQXaXvA68+nzmbyDzqiyFRISyw1JiD5JqUBjw= github.com/uptrace/opentelemetry-go-extra/otelzap v0.3.2 h1:cj/Z6FKTTYBnstI0Lni9PA+k2foounKIPUmj1LBwNiQ= github.com/uptrace/opentelemetry-go-extra/otelzap v0.3.2/go.mod h1:LDaXk90gKEC2nC7JH3Lpnhfu+2V7o/TsqomJJmqA39o= github.com/uptrace/uptrace-go v1.38.0 h1:QdJfyQkaz7HNPbqM9OkaQ2L9jfdf0DpfZJv9em7YIgE= github.com/uptrace/uptrace-go v1.38.0/go.mod h1:SdE9nA+/y+SOIzatuIK2tZeYhoWgrAzAr08kJEquZyM= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= go.opentelemetry.io/contrib/instrumentation/runtime v0.63.0 h1:PeBoRj6af6xMI7qCupwFvTbbnd49V7n5YpG6pg8iDYQ= go.opentelemetry.io/contrib/instrumentation/runtime v0.63.0/go.mod h1:ingqBCtMCe8I4vpz/UVzCW6sxoqgZB37nao91mLQ3Bw= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= @@ -133,25 +182,26 @@ go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= -golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= -golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ= @@ -174,3 +224,5 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +mellium.im/sasl v0.3.2 h1:PT6Xp7ccn9XaXAnJ03FcEjmAn7kK1x7aoXV6F+Vmrl0= +mellium.im/sasl v0.3.2/go.mod h1:NKXDi1zkr+BlMHLQjY3ofYuU4KSPFxknb8mfEu6SveY= diff --git a/go.work.sum b/go.work.sum index da0b2fc..71bc6cd 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,7 +1,36 @@ +cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= +cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0/go.mod h1:Cz6ft6Dkn3Et6l2v2a9/RpN7epQ1GtDlO6lj8bEcOvw= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA= +github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= +github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA= +github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= +go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPxAxnjc2pQTxWNkwfstZ+6H2k= golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4= +golang.org/x/telemetry v0.0.0-20250807160809-1a19826ec488/go.mod h1:fGb/2+tgXXjhjHsTNdVEEMZNWA0quBnfrO+AfoDSAKw= +golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= +golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/handlers/api/getImages.go b/handlers/api/getImages.go new file mode 100644 index 0000000..896edd9 --- /dev/null +++ b/handlers/api/getImages.go @@ -0,0 +1,27 @@ +package api + +import ( + "net/http" + + "gitea.konchin.com/go2025/backend/utils" + "github.com/uptrace/bunrouter" +) + +type getImagesOutputImage struct { + Id int64 `json:"id"` + Uploader string `json:"uploaded_user_id"` + UploadTS int64 `json:"uploaded_at"` + Aliases []string `json:"aliases"` +} + +// GetImages +// +// @success 200 {object} getImagesOutputImage +// @failure 400 +// @failure 401 +// @router /api/images [get] +func (self *Handlers) GetImages( + w http.ResponseWriter, req bunrouter.Request, +) error { + return utils.Success(w) +} diff --git a/handlers/api/handlers.go b/handlers/api/handlers.go new file mode 100644 index 0000000..36dc09c --- /dev/null +++ b/handlers/api/handlers.go @@ -0,0 +1,8 @@ +package api + +type Handlers struct { +} + +func NewHandlers() *Handlers { + return &Handlers{} +} diff --git a/implements/bunDatabase.go b/implements/bunDatabase.go new file mode 100644 index 0000000..ffc7c25 --- /dev/null +++ b/implements/bunDatabase.go @@ -0,0 +1,4 @@ +package implements + +type BunDatabase struct { +} diff --git a/implements/minioObjectStorage.go b/implements/minioObjectStorage.go new file mode 100644 index 0000000..1eb8a73 --- /dev/null +++ b/implements/minioObjectStorage.go @@ -0,0 +1,4 @@ +package implements + +type MinIOObjectStorage struct { +} diff --git a/interfaces/database.go b/interfaces/database.go new file mode 100644 index 0000000..1d3fe42 --- /dev/null +++ b/interfaces/database.go @@ -0,0 +1,4 @@ +package interfaces + +type Database interface { +} diff --git a/interfaces/objectStorage.go b/interfaces/objectStorage.go new file mode 100644 index 0000000..0f57f30 --- /dev/null +++ b/interfaces/objectStorage.go @@ -0,0 +1,4 @@ +package interfaces + +type ObjectStorage interface { +} diff --git a/middlewares/accessLog.go b/middlewares/accessLog.go new file mode 100644 index 0000000..c26bdca --- /dev/null +++ b/middlewares/accessLog.go @@ -0,0 +1,16 @@ +package middlewares + +import ( + "net/http" + + "gitea.konchin.com/go2025/backend/tracing" + "github.com/uptrace/bunrouter" +) + +func AccessLog(next bunrouter.HandlerFunc) bunrouter.HandlerFunc { + return func(w http.ResponseWriter, req bunrouter.Request) error { + tracing.Logger.Ctx(req.Context()). + Info(req.Method + " " + req.Params().Route()) + return next(w, req) + } +} diff --git a/middlewares/bunrouterOtel.go b/middlewares/bunrouterOtel.go new file mode 100644 index 0000000..15d1f7e --- /dev/null +++ b/middlewares/bunrouterOtel.go @@ -0,0 +1,48 @@ +package middlewares + +import ( + "net/http" + + "gitea.konchin.com/go2025/backend/utils" + "github.com/uptrace/bunrouter" + "go.opentelemetry.io/otel/attribute" + semconv "go.opentelemetry.io/otel/semconv/v1.17.0" + "go.opentelemetry.io/otel/trace" +) + +func BunrouterOtel(next bunrouter.HandlerFunc) bunrouter.HandlerFunc { + return func(w http.ResponseWriter, req bunrouter.Request) error { + span := trace.SpanFromContext(req.Context()) + if !span.IsRecording() { + return next(w, req) + } + + params := req.Params() + span.SetName(req.Method + " " + params.Route()) + + paramSlice := params.Slice() + attrs := make([]attribute.KeyValue, 0, 3+len(paramSlice)) + + attrs = append(attrs, semconv.HTTPRouteKey.String(req.Route())) + + if req.URL != nil { + attrs = append(attrs, semconv.HTTPTargetKey.String(req.URL.RequestURI())) + } else { + // This should never occur if the request was generated by the net/http + // package. Fail gracefully, if it does though. + attrs = append(attrs, semconv.HTTPTargetKey.String(req.RequestURI)) + } + + attrs = append(attrs, semconv.HTTPClientIPKey.String( + utils.GetRemoteAddr(req))) + + for _, param := range paramSlice { + attrs = append(attrs, attribute.String("http.route.param."+param.Key, + param.Value)) + } + + span.SetAttributes(attrs...) + + return next(w, req) + } +} diff --git a/middlewares/cors.go b/middlewares/cors.go new file mode 100644 index 0000000..f83caba --- /dev/null +++ b/middlewares/cors.go @@ -0,0 +1,30 @@ +package middlewares + +import ( + "net/http" + + "github.com/spf13/viper" + "github.com/uptrace/bunrouter" +) + +func CORSHandler(next bunrouter.HandlerFunc) bunrouter.HandlerFunc { + return func(w http.ResponseWriter, req bunrouter.Request) error { + origin := viper.GetString("cors-origin") + if origin == "" { + return next(w, req) + } + + w.Header().Set("Access-Control-Allow-Origin", origin) + w.Header().Set("Access-Control-Allow-Credentials", "true") + + if req.Method == http.MethodOptions { + w.Header().Set("Access-Control-Allow-Methods", + "GET,PUT,POST,DELETE,OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", + "authorization,content-type,content-length") + return nil + } + + return next(w, req) + } +} diff --git a/middlewares/errorHandler.go b/middlewares/errorHandler.go new file mode 100644 index 0000000..e9a03ca --- /dev/null +++ b/middlewares/errorHandler.go @@ -0,0 +1,88 @@ +package middlewares + +import ( + "net/http" + + "gitea.konchin.com/go2025/backend/tracing" + "github.com/uptrace/bunrouter" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" + "go.uber.org/zap" +) + +type HTTPError struct { + StatusCode int `json:"code"` + Message string `json:"message"` + OriginError error `json:"-"` + + TraceID string `json:"traceId"` +} + +func (e HTTPError) Error() string { + return e.Message +} + +func NewHTTPError(err error) HTTPError { + return HTTPError{ + StatusCode: http.StatusInternalServerError, + Message: "Internal server error with unknown reason", + OriginError: err, + } +} + +func ErrorHandler(next bunrouter.HandlerFunc) bunrouter.HandlerFunc { + return func(w http.ResponseWriter, req bunrouter.Request) error { + err := next(w, req) + ctx := req.Context() + + var httpErr HTTPError + switch err := err.(type) { + case nil: + return nil + + case HTTPError: + httpErr = err + + default: + tracing.Logger.Ctx(ctx). + Error("unhandled error", + zap.Error(err)) + httpErr = NewHTTPError(err) + } + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + span := trace.SpanFromContext(ctx) + httpErr.TraceID = span.SpanContext().TraceID().String() + span.SetAttributes( + attribute.Int("http.response.status_code", httpErr.StatusCode), + ) + + if 500 <= httpErr.StatusCode && httpErr.StatusCode <= 599 { + span.SetStatus(codes.Error, err.Error()) + if httpErr.OriginError == nil { + tracing.Logger.Ctx(ctx). + Error(httpErr.Message) + } else { + tracing.Logger.Ctx(ctx). + Error(httpErr.Message, + zap.Error(httpErr.OriginError)) + } + } else { + span.SetStatus(codes.Ok, "") + if httpErr.OriginError == nil { + tracing.Logger.Ctx(ctx). + Warn(httpErr.Message) + } else { + tracing.Logger.Ctx(ctx). + Warn(httpErr.Message, + zap.Error(httpErr.OriginError)) + } + } + + w.WriteHeader(httpErr.StatusCode) + _ = bunrouter.JSON(w, httpErr) + + return err + } +} diff --git a/models/alias.go b/models/alias.go new file mode 100644 index 0000000..fd7b0c1 --- /dev/null +++ b/models/alias.go @@ -0,0 +1,22 @@ +package models + +import "github.com/uptrace/bun" + +type Alias struct { + bun.BaseModel `bun:"table:alias"` + + Id int64 `bun:"id,pk,autoincrement"` + Name string `bun:"name,notnull"` + + Images []Image `bun:"m2m:alias_image,join:Alias=Image"` +} + +type AliasImage struct { + bun.BaseModel `bun:"table:alias_image"` + + AliasId int64 `bun:"alias_id,pk"` + ImageId int64 `bun:"image_id,pk"` + + Alias *Alias `bun:"belongs-to,join:alias_id=id"` + Image *Image `bun:"belongs-to,join:image_id=id"` +} diff --git a/models/image.go b/models/image.go new file mode 100644 index 0000000..394f501 --- /dev/null +++ b/models/image.go @@ -0,0 +1,15 @@ +package models + +import ( + "time" + + "github.com/uptrace/bun" +) + +type Image struct { + bun.BaseModel `bun:"table:image"` + + Id int64 `bun:"id,pk,autoincrement"` + Uploader string `bun:"uploader"` + UploadTS time.Time `bun:"upload_timestamp"` +} diff --git a/utils/getRemoteAddr.go b/utils/getRemoteAddr.go new file mode 100644 index 0000000..d556a22 --- /dev/null +++ b/utils/getRemoteAddr.go @@ -0,0 +1,23 @@ +package utils + +import ( + "net" + + "github.com/uptrace/bunrouter" +) + +func GetRemoteAddr(req bunrouter.Request) string { + xForwardedFor := req.Header.Get("X-Forwarded-For") + xRealIP := req.Header.Get("X-Real-IP") + + if xForwardedFor != "" { + return xForwardedFor + } + + if xRealIP != "" { + return xRealIP + } + + host, _, _ := net.SplitHostPort(req.RemoteAddr) + return host +} diff --git a/utils/initDB.go b/utils/initDB.go new file mode 100644 index 0000000..d4b585b --- /dev/null +++ b/utils/initDB.go @@ -0,0 +1 @@ +package utils diff --git a/utils/initMinIO.go b/utils/initMinIO.go new file mode 100644 index 0000000..f1f4c03 --- /dev/null +++ b/utils/initMinIO.go @@ -0,0 +1,24 @@ +package utils + +import ( + "context" + "os" + + "github.com/minio/minio-go/v7" +) + +func InitMinIO(ctx context.Context, mc *minio.Client) error { + MINIO_BUCKET := os.Getenv("MINIO_BUCKET") + + err := mc.MakeBucket(ctx, MINIO_BUCKET, minio.MakeBucketOptions{ + Region: "us-east-1", + }) + if err != nil { + exists, errBucketExists := mc.BucketExists(ctx, MINIO_BUCKET) + if errBucketExists != nil || !exists { + return errBucketExists + } + } + + return nil +} diff --git a/utils/success.go b/utils/success.go new file mode 100644 index 0000000..6fd2f3b --- /dev/null +++ b/utils/success.go @@ -0,0 +1,14 @@ +package utils + +import ( + "io" + "net/http" +) + +func Success(w http.ResponseWriter) error { + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "text/plain") + _, err := io.WriteString(w, + `{"code":200, "message": "success"}`+"\n") + return err +}