From a0c36c1606807932e182ebf391900b296adb23aa Mon Sep 17 00:00:00 2001 From: Paul Pan Date: Sat, 15 Jul 2023 16:19:49 +0800 Subject: [PATCH] feat: refactor to use DI --- .github/workflows/docker-image.yml | 32 ---- .github/workflows/golangci-lint.yml | 29 ---- Dockerfile.runner | 11 -- Dockerfile.server | 12 -- Makefile | 17 +-- VERSION | 2 +- cmd/common.go | 14 -- cmd/runner/main.go | 34 ----- cmd/server/main.go | 34 ----- cmd/woj/woj.go | 104 +++++++++++++ go.mod | 8 +- go.sum | 12 +- internal/api/consumer/handler.go | 13 +- internal/api/debug/handler.go | 11 +- internal/api/problem/createVersion.go | 5 +- internal/api/problem/details.go | 3 +- internal/api/problem/handler.go | 28 ++-- internal/api/problem/update.go | 5 +- internal/api/problem/upload.go | 3 +- internal/api/runner/handler.go | 15 +- internal/api/status/handler.go | 18 ++- internal/api/status/queryByVersion.go | 3 +- internal/api/submission/create.go | 5 +- internal/api/submission/handler.go | 26 ++-- internal/api/submission/rejudge.go | 3 +- internal/api/user/create.go | 4 +- internal/api/user/handler.go | 22 +-- internal/api/user/login.go | 4 +- internal/api/user/logout.go | 4 +- internal/api/user/profile.go | 5 +- internal/app/runner/runner.go | 21 ++- internal/app/server/server.go | 51 +++---- internal/global/global.go | 15 -- internal/global/jwt.go | 23 --- internal/global/repo.go | 7 - internal/global/setup.go | 40 ----- internal/misc/config/conf.go | 48 ++++++ internal/misc/log/zap.go | 67 +++++++++ internal/{global => model}/config.go | 2 +- internal/model/jwt.go | 12 ++ internal/{global => model}/router.go | 5 +- internal/repo/cache/redis.go | 59 ++++++++ internal/repo/db/pg.go | 139 ++++++++++++++++++ internal/repo/postgresql/postgresql.go | 113 -------------- internal/repo/redis/redis.go | 39 ----- internal/service/jwt/service.go | 25 ---- internal/service/problem/create.go | 2 +- internal/service/problem/createVersion.go | 2 +- internal/service/problem/query.go | 2 +- internal/service/problem/queryFuzz.go | 4 +- .../service/problem/queryLatestVersion.go | 2 +- internal/service/problem/queryVersion.go | 2 +- internal/service/problem/service.go | 23 ++- internal/service/problem/update.go | 2 +- internal/service/problem/updateVersion.go | 2 +- internal/service/runner/service.go | 20 ++- internal/service/status/create.go | 4 +- internal/service/status/query.go | 8 +- internal/service/status/service.go | 23 ++- internal/service/storage/service.go | 49 +++--- internal/service/submission/create.go | 2 +- internal/service/submission/query.go | 4 +- internal/service/submission/service.go | 23 ++- internal/service/task/service.go | 39 +++-- internal/service/user/create.go | 2 +- internal/service/user/login.go | 2 +- internal/service/user/profile.go | 2 +- internal/service/user/service.go | 29 ++-- internal/service/user/version.go | 2 +- internal/{service => web}/jwt/middleware.go | 4 +- internal/web/jwt/service.go | 50 +++++++ internal/{service => web}/jwt/token.go | 17 +-- internal/{pkg => web}/metrics/middleware.go | 10 +- internal/{pkg => web}/metrics/prometheus.go | 45 ++++-- internal/{ => web}/router/.gitignore | 0 internal/{ => web}/router/api.go | 9 +- internal/{ => web}/router/router.go | 61 ++++++-- internal/web/web.go | 1 + 78 files changed, 887 insertions(+), 712 deletions(-) delete mode 100644 .github/workflows/docker-image.yml delete mode 100644 .github/workflows/golangci-lint.yml delete mode 100644 Dockerfile.runner delete mode 100644 Dockerfile.server delete mode 100644 cmd/runner/main.go delete mode 100644 cmd/server/main.go create mode 100644 cmd/woj/woj.go delete mode 100644 internal/global/global.go delete mode 100644 internal/global/jwt.go delete mode 100644 internal/global/repo.go delete mode 100644 internal/global/setup.go create mode 100644 internal/misc/config/conf.go create mode 100644 internal/misc/log/zap.go rename internal/{global => model}/config.go (98%) create mode 100644 internal/model/jwt.go rename internal/{global => model}/router.go (53%) create mode 100644 internal/repo/cache/redis.go create mode 100644 internal/repo/db/pg.go delete mode 100644 internal/repo/postgresql/postgresql.go delete mode 100644 internal/repo/redis/redis.go delete mode 100644 internal/service/jwt/service.go rename internal/{service => web}/jwt/middleware.go (88%) create mode 100644 internal/web/jwt/service.go rename internal/{service => web}/jwt/token.go (75%) rename internal/{pkg => web}/metrics/middleware.go (74%) rename internal/{pkg => web}/metrics/prometheus.go (52%) rename internal/{ => web}/router/.gitignore (100%) rename internal/{ => web}/router/api.go (82%) rename internal/{ => web}/router/router.go (53%) create mode 100644 internal/web/web.go diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml deleted file mode 100644 index e8a654c..0000000 --- a/.github/workflows/docker-image.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Docker Image - -on: - push: - branches: [ "master", "develop" ] - tags: [ "v*" ] - pull_request: - branches: [ "master", "develop" ] - - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Login to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Server Meta - id: server_meta - uses: docker/metadata-action@v4 - with: - images: panpaul/woj-server - - name: Build and Push the Server Image - uses: docker/build-push-action@v3 - with: - push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.server_meta.outputs.tags }} - file: ./Dockerfile.server - labels: ${{ steps.server_meta.outputs.labels }} diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml deleted file mode 100644 index fd08158..0000000 --- a/.github/workflows/golangci-lint.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Lint - -on: - push: - branches: [ "master", "develop" ] - tags: [ "v*" ] - pull_request: - branches: [ "master", "develop" ] - -permissions: - contents: read - pull-requests: read - -jobs: - golangci: - name: lint - runs-on: ubuntu-latest - steps: - - uses: actions/setup-go@v3 - with: - go-version: 1.19 - - uses: actions/checkout@v3 - - name: Generate Swagger Docs - run: make swagger - - name: golangci-lint - uses: golangci/golangci-lint-action@v3 - with: - version: latest - only-new-issues: true diff --git a/Dockerfile.runner b/Dockerfile.runner deleted file mode 100644 index af56a80..0000000 --- a/Dockerfile.runner +++ /dev/null @@ -1,11 +0,0 @@ -FROM golang:latest AS builder -WORKDIR /builder -COPY . /builder -RUN make runner - -FROM alpine:latest -WORKDIR /app -RUN apk --no-cache add tzdata ca-certificates libc6-compat -COPY --from=builder /builder/server /app -COPY --from=builder /builder/resource/runner /app/resource/runner -ENTRYPOINT ["/app/runner"] diff --git a/Dockerfile.server b/Dockerfile.server deleted file mode 100644 index a66ed21..0000000 --- a/Dockerfile.server +++ /dev/null @@ -1,12 +0,0 @@ -FROM golang:latest AS builder -WORKDIR /builder -COPY . /builder -RUN make server - -FROM alpine:latest -WORKDIR /app -RUN apk --no-cache add tzdata ca-certificates libc6-compat -COPY --from=builder /builder/server /app -COPY --from=builder /builder/resource/frontend /app/resource/frontend -EXPOSE 8000 -ENTRYPOINT ["/app/server"] diff --git a/Makefile b/Makefile index 91c4ff3..c3a55b9 100644 --- a/Makefile +++ b/Makefile @@ -1,36 +1,31 @@ GO := go LDFLAGS += -X cmd.BuildTime=$(shell date -u '+%Y-%m-%d-%I-%M-%S') -LDFLAGS += -X cmd.Version=$(shell cat VERSION)+$(shell git rev-parse HEAD) +LDFLAGS += -X cmd.Version=$(shell cat VERSION)+$(shell git rev-parse --short HEAD) LDFLAGS += -s -w GOBUILD := $(GO) build -ldflags '$(LDFLAGS)' GOBIN := $(shell go env GOPATH)/bin -.PHONY: all server runner build clean dep swagger fmt +.PHONY: all build clean dep swagger fmt default: all all: clean build -server: swagger dep - $(GOBUILD) -o server ./cmd/server - -runner: swagger dep - $(GOBUILD) -o runner ./cmd/runner - -build: runner server +build: swagger dep + $(GOBUILD) -o woj ./cmd/woj clean: rm -f runner rm -f server dep: - go mod tidy && go mod download + go mod download swagger: go install github.com/swaggo/swag/cmd/swag@latest - $(GOBIN)/swag init -g internal/router/api.go -o internal/router/docs + $(GOBIN)/swag init -g internal/web/router/api.go -o internal/web/router/docs fmt: go fmt ./... diff --git a/VERSION b/VERSION index afaf360..1cc5f65 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0 \ No newline at end of file +1.1.0 \ No newline at end of file diff --git a/cmd/common.go b/cmd/common.go index 12fd6f7..319af42 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -1,10 +1,8 @@ package cmd import ( - "git.0x7f.app/WOJ/woj-server/internal/global" "github.com/urfave/cli/v2" "log" - "math/rand" "time" ) @@ -61,15 +59,3 @@ func getBuildTime() time.Time { } return build } - -func CommonSetup(c *cli.Context) *global.Global { - rand.Seed(time.Now().Unix()) - - g := new(global.Global) - g.SetupConfig(c.String("config")) - g.SetupZap() - - g.Log.Info("starting...") - - return g -} diff --git a/cmd/runner/main.go b/cmd/runner/main.go deleted file mode 100644 index 1828242..0000000 --- a/cmd/runner/main.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "git.0x7f.app/WOJ/woj-server/cmd" - "git.0x7f.app/WOJ/woj-server/internal/app/runner" - "github.com/urfave/cli/v2" - "log" - "os" -) - -func main() { - a := cmd.App - a.Usage = "woj-runner" - a.Commands = []*cli.Command{ - { - Name: "run", - Aliases: []string{"r"}, - Usage: "start the runner", - Action: run, - }, - } - - err := a.Run(os.Args) - if err != nil { - log.Fatal(err) - } -} - -func run(c *cli.Context) error { - g := cmd.CommonSetup(c) - defer func() { _ = g.Log.Sync() }() - - return runner.RunRunner(g) -} diff --git a/cmd/server/main.go b/cmd/server/main.go deleted file mode 100644 index 7875d74..0000000 --- a/cmd/server/main.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "git.0x7f.app/WOJ/woj-server/cmd" - "git.0x7f.app/WOJ/woj-server/internal/app/server" - "github.com/urfave/cli/v2" - "log" - "os" -) - -func main() { - a := cmd.App - a.Usage = "woj-server" - a.Commands = []*cli.Command{ - { - Name: "run", - Aliases: []string{"r"}, - Usage: "start the server", - Action: run, - }, - } - - err := a.Run(os.Args) - if err != nil { - log.Fatal(err) - } -} - -func run(c *cli.Context) error { - g := cmd.CommonSetup(c) - defer func() { _ = g.Log.Sync() }() - - return server.RunServer(g) -} diff --git a/cmd/woj/woj.go b/cmd/woj/woj.go new file mode 100644 index 0000000..ffe2418 --- /dev/null +++ b/cmd/woj/woj.go @@ -0,0 +1,104 @@ +package main + +import ( + "git.0x7f.app/WOJ/woj-server/cmd" + appRunner "git.0x7f.app/WOJ/woj-server/internal/app/runner" + appServer "git.0x7f.app/WOJ/woj-server/internal/app/server" + "git.0x7f.app/WOJ/woj-server/internal/misc/config" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" + "git.0x7f.app/WOJ/woj-server/internal/repo/cache" + "git.0x7f.app/WOJ/woj-server/internal/repo/db" + "git.0x7f.app/WOJ/woj-server/internal/service/problem" + "git.0x7f.app/WOJ/woj-server/internal/service/runner" + "git.0x7f.app/WOJ/woj-server/internal/service/status" + "git.0x7f.app/WOJ/woj-server/internal/service/storage" + "git.0x7f.app/WOJ/woj-server/internal/service/submission" + "git.0x7f.app/WOJ/woj-server/internal/service/task" + "git.0x7f.app/WOJ/woj-server/internal/service/user" + "git.0x7f.app/WOJ/woj-server/internal/web/jwt" + "git.0x7f.app/WOJ/woj-server/internal/web/metrics" + "git.0x7f.app/WOJ/woj-server/internal/web/router" + "github.com/samber/do" + "github.com/urfave/cli/v2" + slog "log" + "os" +) + +func main() { + a := cmd.App + a.Usage = "woj-server" + a.Commands = []*cli.Command{ + { + Name: "web", + Aliases: []string{"w"}, + Usage: "start web api server", + Action: runServer, + }, + { + Name: "runner", + Aliases: []string{"r"}, + Usage: "start runner", + Action: runRunner, + }, + } + + err := a.Run(os.Args) + if err != nil { + slog.Fatal(err) + } +} + +func prepareServices(c *cli.Context) *do.Injector { + injector := do.New() + + // cli context + do.ProvideValue(injector, c) + + { // basic services + do.Provide(injector, config.NewService) + do.Provide(injector, log.NewService) + } + + { // repo services + do.Provide(injector, db.NewService) + do.Provide(injector, cache.NewService) + } + + { // web helper services + do.Provide(injector, metrics.NewService) + do.Provide(injector, jwt.NewService) + do.Provide(injector, router.NewService) + } + + { // core services + do.Provide(injector, problem.NewService) + do.Provide(injector, runner.NewService) + do.Provide(injector, status.NewService) + do.Provide(injector, storage.NewService) + do.Provide(injector, submission.NewService) + do.Provide(injector, task.NewService) + do.Provide(injector, user.NewService) + } + + return injector +} + +func runServer(c *cli.Context) error { + injector := prepareServices(c) + + logger := do.MustInvoke[log.Service](injector) + defer func() { _ = logger.GetRawLogger().Sync() }() + logger.GetRawLogger().Info("starting...") + + return appServer.RunServer(injector) +} + +func runRunner(c *cli.Context) error { + injector := prepareServices(c) + + logger := do.MustInvoke[log.Service](injector) + defer func() { _ = logger.GetRawLogger().Sync() }() + logger.GetRawLogger().Info("starting...") + + return appRunner.RunRunner(injector) +} diff --git a/go.mod b/go.mod index 493e431..e2f168a 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,19 @@ module git.0x7f.app/WOJ/woj-server -go 1.19 +go 1.20 require ( github.com/gin-contrib/cors v1.4.0 github.com/gin-contrib/pprof v1.4.0 github.com/gin-contrib/zap v0.1.0 github.com/gin-gonic/gin v1.9.1 - github.com/go-redis/redis/v8 v8.11.5 github.com/golang-jwt/jwt/v4 v4.5.0 github.com/hibiken/asynq v0.24.1 github.com/jackc/pgtype v1.14.0 github.com/minio/minio-go/v7 v7.0.60 github.com/prometheus/client_golang v1.16.0 + github.com/redis/go-redis/v9 v9.0.5 + github.com/samber/do v1.6.0 github.com/swaggo/files v1.0.1 github.com/swaggo/gin-swagger v1.6.0 github.com/swaggo/swag v1.16.1 @@ -37,7 +38,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonpointer v0.20.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/spec v0.20.9 // indirect github.com/go-openapi/swag v0.22.4 // indirect @@ -70,7 +71,6 @@ require ( github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.11.0 // indirect - github.com/redis/go-redis/v9 v9.0.5 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/rs/xid v1.5.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect diff --git a/go.sum b/go.sum index 87868c3..be4d803 100644 --- a/go.sum +++ b/go.sum @@ -33,7 +33,6 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cu github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gin-contrib/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g= @@ -55,8 +54,9 @@ github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= +github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= @@ -78,8 +78,6 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91 github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= -github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= -github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= @@ -216,9 +214,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= @@ -251,6 +246,8 @@ github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OK github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/samber/do v1.6.0 h1:Jy/N++BXINDB6lAx5wBlbpHlUdl0FKpLWgGEV9YWqaU= +github.com/samber/do v1.6.0/go.mod h1:DWqBvumy8dyb2vEnYZE7D7zaVEB64J45B0NjTlY/M4k= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -439,7 +436,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= 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/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 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 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/internal/api/consumer/handler.go b/internal/api/consumer/handler.go index fef1ebb..8daeac4 100644 --- a/internal/api/consumer/handler.go +++ b/internal/api/consumer/handler.go @@ -2,11 +2,12 @@ package consumer import ( "context" - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" "git.0x7f.app/WOJ/woj-server/internal/service/problem" "git.0x7f.app/WOJ/woj-server/internal/service/status" "git.0x7f.app/WOJ/woj-server/internal/service/task" "github.com/hibiken/asynq" + "github.com/samber/do" "go.uber.org/zap" ) @@ -24,12 +25,12 @@ type handler struct { taskService task.Service } -func NewConsumer(g *global.Global) Handler { +func NewConsumer(i *do.Injector) Handler { hnd := &handler{ - log: g.Log, - problemService: problem.NewService(g), - statusService: status.NewService(g), - taskService: task.NewService(g), + log: do.MustInvoke[log.Service](i).GetLogger("api.consumer"), + problemService: do.MustInvoke[problem.Service](i), + statusService: do.MustInvoke[status.Service](i), + taskService: do.MustInvoke[task.Service](i), } return hnd diff --git a/internal/api/debug/handler.go b/internal/api/debug/handler.go index 87fadab..50796fd 100644 --- a/internal/api/debug/handler.go +++ b/internal/api/debug/handler.go @@ -1,8 +1,9 @@ package debug import ( - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" "github.com/gin-gonic/gin" + "github.com/samber/do" "go.uber.org/zap" ) @@ -16,7 +17,9 @@ type handler struct { log *zap.Logger } -func RouteRegister(g *global.Global, group *gin.RouterGroup) { - app := &handler{g.Log} - group.GET("/random", app.randomString) +func RouteRegister(rg *gin.RouterGroup, i *do.Injector) { + app := &handler{} + app.log = do.MustInvoke[log.Service](i).GetLogger("api.debug") + + rg.GET("/random", app.randomString) } diff --git a/internal/api/problem/createVersion.go b/internal/api/problem/createVersion.go index 1d3ee6c..c315414 100644 --- a/internal/api/problem/createVersion.go +++ b/internal/api/problem/createVersion.go @@ -2,7 +2,6 @@ package problem import ( "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" "git.0x7f.app/WOJ/woj-server/internal/model" "git.0x7f.app/WOJ/woj-server/internal/service/problem" "github.com/gin-gonic/gin" @@ -30,9 +29,9 @@ func (h *handler) CreateVersion(c *gin.Context) { return } - // uid := claim.(*global.Claim).UID + // uid := claim.(*model.Claim).UID - role := claim.(*global.Claim).Role + role := claim.(*model.Claim).Role req := new(createVersionRequest) if err := c.ShouldBind(req); err != nil { diff --git a/internal/api/problem/details.go b/internal/api/problem/details.go index 8b02d70..c2055bf 100644 --- a/internal/api/problem/details.go +++ b/internal/api/problem/details.go @@ -2,7 +2,6 @@ package problem import ( "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" "git.0x7f.app/WOJ/woj-server/internal/model" "github.com/gin-gonic/gin" ) @@ -28,7 +27,7 @@ func (h *handler) Details(c *gin.Context) { } claim, exist := c.Get("claim") - shouldEnable := !exist || claim.(*global.Claim).Role < model.RoleAdmin + shouldEnable := !exist || claim.(*model.Claim).Role < model.RoleAdmin p, status := h.problemService.Query(req.Pid, true, shouldEnable) if status != e.Success { diff --git a/internal/api/problem/handler.go b/internal/api/problem/handler.go index 75bc3fd..af91ec6 100644 --- a/internal/api/problem/handler.go +++ b/internal/api/problem/handler.go @@ -1,11 +1,13 @@ package problem import ( - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" "git.0x7f.app/WOJ/woj-server/internal/service/problem" "git.0x7f.app/WOJ/woj-server/internal/service/storage" "git.0x7f.app/WOJ/woj-server/internal/service/task" + "git.0x7f.app/WOJ/woj-server/internal/web/jwt" "github.com/gin-gonic/gin" + "github.com/samber/do" "go.uber.org/zap" ) @@ -20,24 +22,24 @@ type Handler interface { type handler struct { log *zap.Logger - jwtService global.JwtService + jwtService jwt.Service problemService problem.Service taskService task.Service storageService storage.Service } -func RouteRegister(g *global.Global, group *gin.RouterGroup) { +func RouteRegister(rg *gin.RouterGroup, i *do.Injector) { app := &handler{ - log: g.Log, - jwtService: g.Jwt, - problemService: problem.NewService(g), - taskService: task.NewService(g), - storageService: storage.NewService(g), + log: do.MustInvoke[log.Service](i).GetLogger("api.problem"), + jwtService: do.MustInvoke[jwt.Service](i), + problemService: do.MustInvoke[problem.Service](i), + taskService: do.MustInvoke[task.Service](i), + storageService: do.MustInvoke[storage.Service](i), } - group.POST("/search", app.Search) - group.POST("/details", app.jwtService.Handler(false), app.Details) - group.POST("/update", app.jwtService.Handler(true), app.Update) - group.POST("/upload", app.jwtService.Handler(true), app.Upload) - group.POST("/create_version", app.jwtService.Handler(true), app.CreateVersion) + rg.POST("/search", app.Search) + rg.POST("/details", app.jwtService.Handler(false), app.Details) + rg.POST("/update", app.jwtService.Handler(true), app.Update) + rg.POST("/upload", app.jwtService.Handler(true), app.Upload) + rg.POST("/create_version", app.jwtService.Handler(true), app.CreateVersion) } diff --git a/internal/api/problem/update.go b/internal/api/problem/update.go index 82e86df..82b3e65 100644 --- a/internal/api/problem/update.go +++ b/internal/api/problem/update.go @@ -2,7 +2,6 @@ package problem import ( "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" "git.0x7f.app/WOJ/woj-server/internal/model" "git.0x7f.app/WOJ/woj-server/internal/service/problem" "github.com/gin-gonic/gin" @@ -34,8 +33,8 @@ func (h *handler) Update(c *gin.Context) { return } - uid := claim.(*global.Claim).UID - role := claim.(*global.Claim).Role + uid := claim.(*model.Claim).UID + role := claim.(*model.Claim).Role if role < model.RoleAdmin { e.Pong(c, e.UserUnauthorized, nil) return diff --git a/internal/api/problem/upload.go b/internal/api/problem/upload.go index 6a0aaf8..c0483be 100644 --- a/internal/api/problem/upload.go +++ b/internal/api/problem/upload.go @@ -2,7 +2,6 @@ package problem import ( "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" "git.0x7f.app/WOJ/woj-server/internal/model" "git.0x7f.app/WOJ/woj-server/pkg/utils" "github.com/gin-gonic/gin" @@ -23,7 +22,7 @@ func (h *handler) Upload(c *gin.Context) { return } - role := claim.(*global.Claim).Role + role := claim.(*model.Claim).Role if role < model.RoleAdmin { e.Pong(c, e.UserUnauthorized, nil) return diff --git a/internal/api/runner/handler.go b/internal/api/runner/handler.go index 164301f..009c151 100644 --- a/internal/api/runner/handler.go +++ b/internal/api/runner/handler.go @@ -4,11 +4,12 @@ import ( "context" "errors" "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" "git.0x7f.app/WOJ/woj-server/internal/service/runner" "git.0x7f.app/WOJ/woj-server/internal/service/storage" "git.0x7f.app/WOJ/woj-server/internal/service/task" "github.com/hibiken/asynq" + "github.com/samber/do" "go.uber.org/zap" ) @@ -26,17 +27,17 @@ type handler struct { storageService storage.Service } -func NewRunner(g *global.Global) (Handler, error) { +func NewRunner(i *do.Injector) (Handler, error) { hnd := &handler{ - log: g.Log, - runnerService: runner.NewService(g), - taskService: task.NewService(g), - storageService: storage.NewService(g), + log: do.MustInvoke[log.Service](i).GetLogger("api.runner"), + runnerService: do.MustInvoke[runner.Service](i), + taskService: do.MustInvoke[task.Service](i), + storageService: do.MustInvoke[storage.Service](i), } status := hnd.runnerService.EnsureDeps(false) if status != e.Success { - g.Log.Error("failed to ensure runner dependencies", zap.String("status", status.String())) + hnd.log.Error("failed to ensure runner dependencies", zap.String("status", status.String())) return nil, errors.New("failed to ensure dependencies") } diff --git a/internal/api/status/handler.go b/internal/api/status/handler.go index e96d965..eb31276 100644 --- a/internal/api/status/handler.go +++ b/internal/api/status/handler.go @@ -1,9 +1,11 @@ package status import ( - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" "git.0x7f.app/WOJ/woj-server/internal/service/status" + "git.0x7f.app/WOJ/woj-server/internal/web/jwt" "github.com/gin-gonic/gin" + "github.com/samber/do" "go.uber.org/zap" ) @@ -17,16 +19,16 @@ type Handler interface { type handler struct { log *zap.Logger statusService status.Service - jwtService global.JwtService + jwtService jwt.Service } -func RouteRegister(g *global.Global, group *gin.RouterGroup) { +func RouteRegister(rg *gin.RouterGroup, i *do.Injector) { app := &handler{ - log: g.Log, - statusService: status.NewService(g), - jwtService: g.Jwt, + log: do.MustInvoke[log.Service](i).GetLogger("api.status"), + statusService: do.MustInvoke[status.Service](i), + jwtService: do.MustInvoke[jwt.Service](i), } - group.POST("/query", app.Query) - group.POST("/query/problem_version", app.jwtService.Handler(true), app.QueryByProblemVersion) + rg.POST("/query", app.Query) + rg.POST("/query/problem_version", app.jwtService.Handler(true), app.QueryByProblemVersion) } diff --git a/internal/api/status/queryByVersion.go b/internal/api/status/queryByVersion.go index 9723789..741bdea 100644 --- a/internal/api/status/queryByVersion.go +++ b/internal/api/status/queryByVersion.go @@ -2,7 +2,6 @@ package status import ( "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" "git.0x7f.app/WOJ/woj-server/internal/model" "github.com/gin-gonic/gin" ) @@ -31,7 +30,7 @@ func (h *handler) QueryByProblemVersion(c *gin.Context) { return } - role := claim.(*global.Claim).Role + role := claim.(*model.Claim).Role req := new(queryByVersionRequest) diff --git a/internal/api/submission/create.go b/internal/api/submission/create.go index 6020f50..3b4a677 100644 --- a/internal/api/submission/create.go +++ b/internal/api/submission/create.go @@ -2,7 +2,6 @@ package submission import ( "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" "git.0x7f.app/WOJ/woj-server/internal/model" "git.0x7f.app/WOJ/woj-server/internal/service/submission" "github.com/gin-gonic/gin" @@ -32,9 +31,9 @@ func (h *handler) Create(c *gin.Context) { return } - uid := claim.(*global.Claim).UID + uid := claim.(*model.Claim).UID - role := claim.(*global.Claim).Role + role := claim.(*model.Claim).Role req := new(createRequest) if err := c.ShouldBind(req); err != nil { diff --git a/internal/api/submission/handler.go b/internal/api/submission/handler.go index 306093e..6cc1de3 100644 --- a/internal/api/submission/handler.go +++ b/internal/api/submission/handler.go @@ -1,12 +1,14 @@ package submission import ( - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" "git.0x7f.app/WOJ/woj-server/internal/service/problem" "git.0x7f.app/WOJ/woj-server/internal/service/status" "git.0x7f.app/WOJ/woj-server/internal/service/submission" "git.0x7f.app/WOJ/woj-server/internal/service/task" + "git.0x7f.app/WOJ/woj-server/internal/web/jwt" "github.com/gin-gonic/gin" + "github.com/samber/do" "go.uber.org/zap" ) @@ -20,24 +22,24 @@ type Handler interface { type handler struct { log *zap.Logger - jwtService global.JwtService + jwtService jwt.Service problemService problem.Service statusService status.Service submissionService submission.Service taskService task.Service } -func RouteRegister(g *global.Global, group *gin.RouterGroup) { +func RouteRegister(rg *gin.RouterGroup, i *do.Injector) { app := &handler{ - log: g.Log, - jwtService: g.Jwt, - problemService: problem.NewService(g), - statusService: status.NewService(g), - submissionService: submission.NewService(g), - taskService: task.NewService(g), + log: do.MustInvoke[log.Service](i).GetLogger("api.submission"), + jwtService: do.MustInvoke[jwt.Service](i), + problemService: do.MustInvoke[problem.Service](i), + statusService: do.MustInvoke[status.Service](i), + submissionService: do.MustInvoke[submission.Service](i), + taskService: do.MustInvoke[task.Service](i), } - group.POST("/create", app.jwtService.Handler(true), app.Create) - group.POST("/query", app.Query) - group.POST("/rejudge", app.jwtService.Handler(true), app.Rejudge) + rg.POST("/create", app.jwtService.Handler(true), app.Create) + rg.POST("/query", app.Query) + rg.POST("/rejudge", app.jwtService.Handler(true), app.Rejudge) } diff --git a/internal/api/submission/rejudge.go b/internal/api/submission/rejudge.go index 09c8964..47cfc6a 100644 --- a/internal/api/submission/rejudge.go +++ b/internal/api/submission/rejudge.go @@ -2,7 +2,6 @@ package submission import ( "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" "git.0x7f.app/WOJ/woj-server/internal/model" "github.com/gin-gonic/gin" ) @@ -27,7 +26,7 @@ func (h *handler) Rejudge(c *gin.Context) { return } - role := claim.(*global.Claim).Role + role := claim.(*model.Claim).Role req := new(rejudgeRequest) if err := c.ShouldBind(req); err != nil { diff --git a/internal/api/user/create.go b/internal/api/user/create.go index 2110edc..1e61a29 100644 --- a/internal/api/user/create.go +++ b/internal/api/user/create.go @@ -2,7 +2,7 @@ package user import ( "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/model" "git.0x7f.app/WOJ/woj-server/internal/service/user" "github.com/gin-gonic/gin" ) @@ -48,7 +48,7 @@ func (h *handler) Create(c *gin.Context) { return } - claim := &global.Claim{ + claim := &model.Claim{ UID: u.ID, Role: u.Role, Version: version, diff --git a/internal/api/user/handler.go b/internal/api/user/handler.go index 9f4589a..a686c28 100644 --- a/internal/api/user/handler.go +++ b/internal/api/user/handler.go @@ -1,9 +1,11 @@ package user import ( - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" "git.0x7f.app/WOJ/woj-server/internal/service/user" + "git.0x7f.app/WOJ/woj-server/internal/web/jwt" "github.com/gin-gonic/gin" + "github.com/samber/do" "go.uber.org/zap" ) @@ -18,19 +20,19 @@ type Handler interface { type handler struct { log *zap.Logger - jwtService global.JwtService + jwtService jwt.Service userService user.Service } -func RouteRegister(g *global.Global, group *gin.RouterGroup) { +func RouteRegister(rg *gin.RouterGroup, i *do.Injector) { app := &handler{ - log: g.Log, - jwtService: g.Jwt, - userService: user.NewService(g), + log: do.MustInvoke[log.Service](i).GetLogger("api.user"), + jwtService: do.MustInvoke[jwt.Service](i), + userService: do.MustInvoke[user.Service](i), } - group.POST("/create", app.Create) - group.POST("/login", app.Login) - group.POST("/logout", app.jwtService.Handler(true), app.Logout) - group.POST("/profile", app.jwtService.Handler(true), app.Profile) + rg.POST("/create", app.Create) + rg.POST("/login", app.Login) + rg.POST("/logout", app.jwtService.Handler(true), app.Logout) + rg.POST("/profile", app.jwtService.Handler(true), app.Profile) } diff --git a/internal/api/user/login.go b/internal/api/user/login.go index 689fd50..9525783 100644 --- a/internal/api/user/login.go +++ b/internal/api/user/login.go @@ -2,7 +2,7 @@ package user import ( "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/model" "git.0x7f.app/WOJ/woj-server/internal/service/user" "github.com/gin-gonic/gin" ) @@ -46,7 +46,7 @@ func (h *handler) Login(c *gin.Context) { e.Pong(c, status, nil) return } - claim := &global.Claim{ + claim := &model.Claim{ UID: u.ID, Role: u.Role, Version: version, diff --git a/internal/api/user/logout.go b/internal/api/user/logout.go index f5ee83d..7e6d00e 100644 --- a/internal/api/user/logout.go +++ b/internal/api/user/logout.go @@ -2,7 +2,7 @@ package user import ( "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/model" "github.com/gin-gonic/gin" ) @@ -21,6 +21,6 @@ func (h *handler) Logout(c *gin.Context) { return } - _, status := h.userService.IncrVersion(claim.(*global.Claim).UID) + _, status := h.userService.IncrVersion(claim.(*model.Claim).UID) e.Pong(c, status, nil) } diff --git a/internal/api/user/profile.go b/internal/api/user/profile.go index 5ba5b4b..3abe353 100644 --- a/internal/api/user/profile.go +++ b/internal/api/user/profile.go @@ -2,7 +2,6 @@ package user import ( "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" "git.0x7f.app/WOJ/woj-server/internal/model" "github.com/gin-gonic/gin" ) @@ -29,8 +28,8 @@ func (h *handler) Profile(c *gin.Context) { return } - uid := claim.(*global.Claim).UID - role := claim.(*global.Claim).Role + uid := claim.(*model.Claim).UID + role := claim.(*model.Claim).Role req := new(profileRequest) diff --git a/internal/app/runner/runner.go b/internal/app/runner/runner.go index d40d93b..09436e4 100644 --- a/internal/app/runner/runner.go +++ b/internal/app/runner/runner.go @@ -2,17 +2,22 @@ package runner import ( "git.0x7f.app/WOJ/woj-server/internal/api/runner" - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/misc/config" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" "git.0x7f.app/WOJ/woj-server/internal/model" "git.0x7f.app/WOJ/woj-server/pkg/utils" "git.0x7f.app/WOJ/woj-server/pkg/zapasynq" "github.com/hibiken/asynq" + "github.com/samber/do" "go.uber.org/zap" "runtime" ) -func RunRunner(g *global.Global) error { - hnd, err := runner.NewRunner(g) +func RunRunner(i *do.Injector) error { + conf := do.MustInvoke[config.Service](i).GetConfig() + rlog := do.MustInvoke[log.Service](i).GetLogger("app.runner") + + hnd, err := runner.NewRunner(i) if err != nil { return err } @@ -23,19 +28,19 @@ func RunRunner(g *global.Global) error { srv := asynq.NewServer( asynq.RedisClientOpt{ - Addr: g.Conf.Redis.Address, - Password: g.Conf.Redis.Password, - DB: g.Conf.Redis.QueueDb, + Addr: conf.Redis.Address, + Password: conf.Redis.Password, + DB: conf.Redis.QueueDb, }, asynq.Config{ Concurrency: utils.If(runtime.NumCPU() > 1, runtime.NumCPU()-1, 1), - Logger: zapasynq.New(g.Log), + Logger: zapasynq.New(rlog), Queues: map[string]int{model.QueueRunner: 1}, }, ) if err := srv.Run(mux); err != nil { - g.Log.Warn("could not run server", zap.Error(err)) + rlog.Warn("could not run server", zap.Error(err)) return err } diff --git a/internal/app/server/server.go b/internal/app/server/server.go index 692ff48..910fc9c 100644 --- a/internal/app/server/server.go +++ b/internal/app/server/server.go @@ -4,15 +4,15 @@ import ( "context" "fmt" "git.0x7f.app/WOJ/woj-server/internal/api/consumer" - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/misc/config" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" "git.0x7f.app/WOJ/woj-server/internal/model" - "git.0x7f.app/WOJ/woj-server/internal/repo/postgresql" - "git.0x7f.app/WOJ/woj-server/internal/repo/redis" - "git.0x7f.app/WOJ/woj-server/internal/router" - "git.0x7f.app/WOJ/woj-server/internal/service/jwt" + "git.0x7f.app/WOJ/woj-server/internal/repo/db" + "git.0x7f.app/WOJ/woj-server/internal/web/router" "git.0x7f.app/WOJ/woj-server/pkg/utils" "git.0x7f.app/WOJ/woj-server/pkg/zapasynq" "github.com/hibiken/asynq" + "github.com/samber/do" "go.uber.org/zap" "net/http" "os" @@ -22,23 +22,14 @@ import ( "time" ) -func RunServer(g *global.Global) error { - // Setup Database - g.Db = new(postgresql.Repo) - g.Db.Setup(g) +func RunServer(i *do.Injector) error { // Prepare Router + conf := do.MustInvoke[config.Service](i).GetConfig() + slog := do.MustInvoke[log.Service](i).GetLogger("app.server") - // Setup Redis - g.Redis = new(redis.Repo) - g.Redis.Setup(g) - - // Setup JWT - g.Jwt = jwt.NewJwtService(g) - - // Prepare Router - routers := router.InitRouters(g) + routers := do.MustInvoke[router.Service](i).GetRouter() // Create Server - addr := fmt.Sprintf("%s:%d", g.Conf.WebServer.Address, g.Conf.WebServer.Port) + addr := fmt.Sprintf("%s:%d", conf.WebServer.Address, conf.WebServer.Port) server := &http.Server{ Addr: addr, Handler: routers, @@ -47,56 +38,56 @@ func RunServer(g *global.Global) error { // Run Server go func() { if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { - g.Log.Fatal("ListenAndServe Failed", zap.Error(err)) + slog.Fatal("ListenAndServe Failed", zap.Error(err)) } }() // Create Queue queueMux := asynq.NewServeMux() { - handler := consumer.NewConsumer(g) + handler := consumer.NewConsumer(i) queueMux.HandleFunc(model.TypeProblemUpdate, handler.ProblemUpdate) queueMux.HandleFunc(model.TypeSubmitUpdate, handler.SubmitUpdate) } queueSrv := asynq.NewServer( asynq.RedisClientOpt{ - Addr: g.Conf.Redis.Address, - Password: g.Conf.Redis.Password, - DB: g.Conf.Redis.QueueDb, + Addr: conf.Redis.Address, + Password: conf.Redis.Password, + DB: conf.Redis.QueueDb, }, asynq.Config{ Concurrency: utils.If(runtime.NumCPU() > 1, runtime.NumCPU()-1, 1), - Logger: zapasynq.New(g.Log), + Logger: zapasynq.New(slog), Queues: map[string]int{model.QueueServer: 1}, }, ) // Run Queue if err := queueSrv.Start(queueMux); err != nil { - g.Log.Fatal("queueSrv.Start Failed", zap.Error(err)) + slog.Fatal("queueSrv.Start Failed", zap.Error(err)) } // Handle SIGINT and SIGTERM. quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit - g.Log.Info("Shutting down server ...") + slog.Info("Shutting down server ...") // Graceful Shutdown Server ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() err := server.Shutdown(ctx) if err != nil { - g.Log.Warn("Server Shutdown Failed", zap.Error(err)) + slog.Warn("Server Shutdown Failed", zap.Error(err)) } // Graceful Shutdown Queue queueSrv.Shutdown() // Graceful Shutdown Database - err = g.Db.Close() + err = do.MustInvoke[db.Service](i).Close() if err != nil { - g.Log.Warn("Database Close Failed", zap.Error(err)) + slog.Warn("Database Close Failed", zap.Error(err)) } return err diff --git a/internal/global/global.go b/internal/global/global.go deleted file mode 100644 index fe69039..0000000 --- a/internal/global/global.go +++ /dev/null @@ -1,15 +0,0 @@ -package global - -import ( - "git.0x7f.app/WOJ/woj-server/internal/pkg/metrics" - "go.uber.org/zap" -) - -type Global struct { - Log *zap.Logger - Conf *Config - Stat *metrics.Metrics - Db Repo - Redis Repo - Jwt JwtService -} diff --git a/internal/global/jwt.go b/internal/global/jwt.go deleted file mode 100644 index c596f43..0000000 --- a/internal/global/jwt.go +++ /dev/null @@ -1,23 +0,0 @@ -package global - -import ( - "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/model" - "github.com/gin-gonic/gin" - "github.com/golang-jwt/jwt/v4" -) - -type Claim struct { - UID uint `json:"id"` - Role model.Role `json:"role"` - Version int64 `json:"version"` - jwt.RegisteredClaims -} - -type JwtService interface { - ParseToken(tokenText string) (*Claim, e.Status) - SignClaim(claim *Claim) (string, e.Status) - Validate(claim *Claim) bool - - Handler(forced bool) gin.HandlerFunc -} diff --git a/internal/global/repo.go b/internal/global/repo.go deleted file mode 100644 index 89b8b28..0000000 --- a/internal/global/repo.go +++ /dev/null @@ -1,7 +0,0 @@ -package global - -type Repo interface { - Setup(*Global) - Get() interface{} - Close() error -} diff --git a/internal/global/setup.go b/internal/global/setup.go deleted file mode 100644 index 4ea2f5c..0000000 --- a/internal/global/setup.go +++ /dev/null @@ -1,40 +0,0 @@ -package global - -import ( - "git.0x7f.app/WOJ/woj-server/pkg/utils" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" - "gopkg.in/yaml.v3" - "log" -) - -func (g *Global) SetupZap() { - cfg := zap.Config{ - Level: zap.NewAtomicLevelAt( - utils.If(g.Conf.Development, zapcore.DebugLevel, zapcore.InfoLevel), - ), - Development: g.Conf.Development, - Encoding: "console", // or json - EncoderConfig: zap.NewDevelopmentEncoderConfig(), - OutputPaths: []string{"stdout"}, - ErrorOutputPaths: []string{"stderr"}, - } - - var err error - g.Log, err = cfg.Build() - if err != nil { - log.Fatalf("Failed to setup Zap: %s\n", err.Error()) - } -} - -func (g *Global) SetupConfig(configFile string) { - data, err := utils.FileRead(configFile) - if err != nil { - log.Fatalf("Failed to setup config: %s\n", err.Error()) - } - - err = yaml.Unmarshal(data, &g.Conf) - if err != nil { - log.Fatalf("Failed to setup config: %s\n", err.Error()) - } -} diff --git a/internal/misc/config/conf.go b/internal/misc/config/conf.go new file mode 100644 index 0000000..d213782 --- /dev/null +++ b/internal/misc/config/conf.go @@ -0,0 +1,48 @@ +package config + +import ( + "git.0x7f.app/WOJ/woj-server/internal/model" + "git.0x7f.app/WOJ/woj-server/pkg/utils" + "github.com/samber/do" + "github.com/urfave/cli/v2" + "gopkg.in/yaml.v3" + "log" +) + +var _ Service = (*service)(nil) + +type Service interface { + GetConfig() *model.Config + HealthCheck() error +} + +func NewService(i *do.Injector) (Service, error) { + cliCtx := do.MustInvoke[*cli.Context](i) + + data, err := utils.FileRead(cliCtx.String("config")) + if err != nil { + log.Printf("Failed to setup config: %s\n", err.Error()) + return nil, err + } + + srv := &service{} + err = yaml.Unmarshal(data, &srv.conf) + if err != nil { + log.Printf("Failed to setup config: %s\n", err.Error()) + return nil, err + } + + return srv, nil +} + +type service struct { + conf model.Config +} + +func (s *service) GetConfig() *model.Config { + return &s.conf +} + +func (s *service) HealthCheck() error { + return nil +} diff --git a/internal/misc/log/zap.go b/internal/misc/log/zap.go new file mode 100644 index 0000000..689ca55 --- /dev/null +++ b/internal/misc/log/zap.go @@ -0,0 +1,67 @@ +package log + +import ( + "git.0x7f.app/WOJ/woj-server/internal/misc/config" + "git.0x7f.app/WOJ/woj-server/pkg/utils" + "github.com/samber/do" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "log" +) + +var _ Service = (*service)(nil) + +type Service interface { + GetRawLogger() *zap.Logger + GetLogger(domain string) *zap.Logger + HealthCheck() error +} + +func NewService(i *do.Injector) (Service, error) { + srv := &service{} + srv.confService = do.MustInvoke[config.Service](i) + + c := srv.confService.GetConfig() + cfg := zap.Config{ + Level: zap.NewAtomicLevelAt(utils.If( + c.Development, + zapcore.DebugLevel, + zapcore.InfoLevel, + )), + Development: c.Development, + Encoding: "console", // or json + EncoderConfig: utils.If( + c.Development, + zap.NewDevelopmentEncoderConfig(), + zap.NewProductionEncoderConfig(), + ), + OutputPaths: []string{"stdout"}, + ErrorOutputPaths: []string{"stderr"}, + } + + var err error + srv.logger, err = cfg.Build() + if err != nil { + log.Printf("Failed to setup Zap: %s\n", err.Error()) + return nil, err + } + + return srv, nil +} + +type service struct { + confService config.Service + logger *zap.Logger +} + +func (s *service) GetRawLogger() *zap.Logger { + return s.logger +} + +func (s *service) GetLogger(domain string) *zap.Logger { + return s.logger.Named(domain) +} + +func (s *service) HealthCheck() error { + return nil +} diff --git a/internal/global/config.go b/internal/model/config.go similarity index 98% rename from internal/global/config.go rename to internal/model/config.go index 0eaaa19..09712a1 100644 --- a/internal/global/config.go +++ b/internal/model/config.go @@ -1,4 +1,4 @@ -package global +package model type ConfigWebServer struct { Address string `yaml:"Address"` diff --git a/internal/model/jwt.go b/internal/model/jwt.go new file mode 100644 index 0000000..655446f --- /dev/null +++ b/internal/model/jwt.go @@ -0,0 +1,12 @@ +package model + +import ( + "github.com/golang-jwt/jwt/v4" +) + +type Claim struct { + UID uint `json:"id"` + Role Role `json:"role"` + Version int64 `json:"version"` + jwt.RegisteredClaims +} diff --git a/internal/global/router.go b/internal/model/router.go similarity index 53% rename from internal/global/router.go rename to internal/model/router.go index 8ee2f78..a419b4f 100644 --- a/internal/global/router.go +++ b/internal/model/router.go @@ -1,11 +1,12 @@ -package global +package model import ( "github.com/gin-gonic/gin" + "github.com/samber/do" ) type EndpointInfo struct { Version string Path string - Register func(g *Global, group *gin.RouterGroup) + Register func(rg *gin.RouterGroup, i *do.Injector) } diff --git a/internal/repo/cache/redis.go b/internal/repo/cache/redis.go new file mode 100644 index 0000000..935452a --- /dev/null +++ b/internal/repo/cache/redis.go @@ -0,0 +1,59 @@ +package cache + +import ( + "context" + "git.0x7f.app/WOJ/woj-server/internal/misc/config" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" + "github.com/redis/go-redis/v9" + "github.com/samber/do" + "go.uber.org/zap" +) + +var _ Service = (*service)(nil) + +type Service interface { + Get() *redis.Client + Close() error + HealthCheck() error +} + +func NewService(i *do.Injector) (Service, error) { + srv := &service{} + srv.log = do.MustInvoke[log.Service](i).GetLogger("redis") + + conf := do.MustInvoke[config.Service](i).GetConfig() + srv.setup(conf.Redis.Address, conf.Redis.Password, conf.Redis.Db) + return srv, srv.err +} + +type service struct { + log *zap.Logger + client *redis.Client + err error +} + +func (s *service) Get() *redis.Client { + return s.client +} + +func (s *service) Close() error { + return s.client.Close() +} + +func (s *service) HealthCheck() error { + return s.err +} + +func (s *service) setup(addr string, password string, db int) { + s.client = redis.NewClient(&redis.Options{ + Addr: addr, + Password: password, + DB: db, + }) + + _, s.err = s.client.Ping(context.Background()).Result() + if s.err != nil { + s.log.Error("Redis ping failed", zap.Error(s.err)) + return + } +} diff --git a/internal/repo/db/pg.go b/internal/repo/db/pg.go new file mode 100644 index 0000000..75bca9e --- /dev/null +++ b/internal/repo/db/pg.go @@ -0,0 +1,139 @@ +package db + +import ( + "database/sql" + "errors" + "fmt" + "git.0x7f.app/WOJ/woj-server/internal/misc/config" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" + "git.0x7f.app/WOJ/woj-server/internal/model" + "github.com/samber/do" + "go.uber.org/zap" + "gorm.io/driver/postgres" + "gorm.io/gorm" + "gorm.io/gorm/schema" + "moul.io/zapgorm2" + "time" +) + +var _ Service = (*service)(nil) + +type Service interface { + Get() *gorm.DB + Close() error + HealthCheck() error +} + +func NewService(i *do.Injector) (Service, error) { + srv := &service{} + srv.log = do.MustInvoke[log.Service](i).GetLogger("postgresql") + + conf := do.MustInvoke[config.Service](i).GetConfig() + srv.setup(conf) + return srv, srv.err +} + +type service struct { + log *zap.Logger + db *gorm.DB + err error +} + +func (s *service) Get() *gorm.DB { + return s.db +} + +func (s *service) Close() error { + var db *sql.DB + db, s.err = s.db.DB() + if s.err != nil { + return s.err + } + + s.err = db.Close() + return s.err +} + +func (s *service) HealthCheck() error { + return s.err +} + +func (s *service) setup(conf *model.Config) { + s.log.Info("Connecting to database...") + + logger := zapgorm2.New(s.log) + logger.IgnoreRecordNotFoundError = true + + dsn := fmt.Sprintf( + // TODO: timezone as config + "user=%s password=%s dbname=%s host=%s port=%d sslmode=disable TimeZone=Asia/Shanghai", + conf.Database.User, + conf.Database.Password, + conf.Database.Database, + conf.Database.Host, + conf.Database.Port, + ) + + s.db, s.err = gorm.Open( + postgres.Open(dsn), + &gorm.Config{ + NamingStrategy: schema.NamingStrategy{ + SingularTable: true, + TablePrefix: conf.Database.Prefix, + }, + PrepareStmt: true, + Logger: logger, + }, + ) + + if s.err != nil { + s.log.Error("Failed to connect to database", zap.Error(s.err)) + return + } + + var db *sql.DB + db, s.err = s.checkAlive(3) + if s.err != nil { + s.log.Error("Database is not alive", zap.Error(s.err)) + return + } + + db.SetMaxOpenConns(conf.Database.MaxOpenConns) + db.SetMaxIdleConns(conf.Database.MaxIdleConns) + db.SetConnMaxLifetime(time.Duration(conf.Database.ConnMaxLifetime) * time.Minute) + + s.migrateDatabase() +} + +func (s *service) migrateDatabase() { + s.log.Info("Auto Migrating database...") + + _ = s.db.AutoMigrate(&model.User{}) + _ = s.db.AutoMigrate(&model.Problem{}) + _ = s.db.AutoMigrate(&model.ProblemVersion{}) + _ = s.db.AutoMigrate(&model.Submission{}) + _ = s.db.AutoMigrate(&model.Status{}) +} + +func (s *service) checkAlive(retry int) (*sql.DB, error) { + if retry <= 0 { + return nil, errors.New("all retries are used up. failed to connect to database") + } + + db, err := s.db.DB() + if err != nil { + s.log.Warn("failed to get sql.DB instance", zap.Error(err)) + time.Sleep(5 * time.Second) + return s.checkAlive(retry - 1) + } + + err = db.Ping() + if err != nil { + s.log.Warn("failed to ping database", zap.Error(err)) + time.Sleep(5 * time.Second) + return s.checkAlive(retry - 1) + } + + s.log.Info("database connect established") + return db, nil +} diff --git a/internal/repo/postgresql/postgresql.go b/internal/repo/postgresql/postgresql.go deleted file mode 100644 index 4b4ddf9..0000000 --- a/internal/repo/postgresql/postgresql.go +++ /dev/null @@ -1,113 +0,0 @@ -package postgresql - -import ( - "database/sql" - "errors" - "fmt" - "git.0x7f.app/WOJ/woj-server/internal/global" - "git.0x7f.app/WOJ/woj-server/internal/model" - "go.uber.org/zap" - "gorm.io/driver/postgres" - "gorm.io/gorm" - "gorm.io/gorm/schema" - "moul.io/zapgorm2" - "time" -) - -var _ global.Repo = (*Repo)(nil) - -type Repo struct { - db *gorm.DB - log *zap.Logger -} - -func (r *Repo) Get() interface{} { - return r.db -} - -func (r *Repo) Close() error { - db, err := r.db.DB() - if err != nil { - return err - } - return db.Close() -} - -func (r *Repo) Setup(g *global.Global) { - r.log = g.Log - - r.log.Info("Connecting to database...") - - logger := zapgorm2.New(r.log) - logger.IgnoreRecordNotFoundError = true - - dsn := fmt.Sprintf("user=%s password=%s dbname=%s host=%s port=%d sslmode=disable TimeZone=Asia/Shanghai", - g.Conf.Database.User, - g.Conf.Database.Password, - g.Conf.Database.Database, - g.Conf.Database.Host, - g.Conf.Database.Port) - - var err error - r.db, err = gorm.Open( - postgres.Open(dsn), - &gorm.Config{ - NamingStrategy: schema.NamingStrategy{ - SingularTable: true, - TablePrefix: g.Conf.Database.Prefix, - }, - PrepareStmt: true, - Logger: logger, - }) - - if err != nil { - r.log.Fatal("Failed to connect to database", zap.Error(err)) - return - } - - db, err := r.checkAlive(3) - if err != nil { - r.log.Fatal("Database is not alive", zap.Error(err)) - return - } - - db.SetMaxOpenConns(g.Conf.Database.MaxOpenConns) - db.SetMaxIdleConns(g.Conf.Database.MaxIdleConns) - db.SetConnMaxLifetime(time.Duration(g.Conf.Database.ConnMaxLifetime) * time.Minute) - - r.migrateDatabase() -} - -func (r *Repo) migrateDatabase() { - r.log.Info("Auto Migrating database...") - - _ = r.db.AutoMigrate(&model.User{}) - _ = r.db.AutoMigrate(&model.Problem{}) - _ = r.db.AutoMigrate(&model.ProblemVersion{}) - _ = r.db.AutoMigrate(&model.Submission{}) - _ = r.db.AutoMigrate(&model.Status{}) -} - -// checkAlive deprecated -func (r *Repo) checkAlive(retry int) (*sql.DB, error) { - if retry <= 0 { - return nil, errors.New("all retries are used up. failed to connect to database") - } - - db, err := r.db.DB() - if err != nil { - r.log.Warn("failed to get sql.DB instance", zap.Error(err)) - time.Sleep(5 * time.Second) - return r.checkAlive(retry - 1) - } - - err = db.Ping() - if err != nil { - r.log.Warn("failed to ping database", zap.Error(err)) - time.Sleep(5 * time.Second) - return r.checkAlive(retry - 1) - } - - r.log.Info("database connect established") - return db, nil -} diff --git a/internal/repo/redis/redis.go b/internal/repo/redis/redis.go deleted file mode 100644 index 3c574a9..0000000 --- a/internal/repo/redis/redis.go +++ /dev/null @@ -1,39 +0,0 @@ -package redis - -import ( - "context" - "git.0x7f.app/WOJ/woj-server/internal/global" - "github.com/go-redis/redis/v8" - "go.uber.org/zap" -) - -var _ global.Repo = (*Repo)(nil) - -type Repo struct { - client *redis.Client - log *zap.Logger -} - -func (r *Repo) Setup(g *global.Global) { - r.log = g.Log - - r.client = redis.NewClient(&redis.Options{ - Addr: g.Conf.Redis.Address, - Password: g.Conf.Redis.Password, - DB: g.Conf.Redis.Db, - }) - - _, err := r.client.Ping(context.Background()).Result() - if err != nil { - r.log.Fatal("Redis ping failed", zap.Error(err)) - return - } -} - -func (r *Repo) Get() interface{} { - return r.client -} - -func (r *Repo) Close() error { - return r.client.Close() -} diff --git a/internal/service/jwt/service.go b/internal/service/jwt/service.go deleted file mode 100644 index fea3877..0000000 --- a/internal/service/jwt/service.go +++ /dev/null @@ -1,25 +0,0 @@ -package jwt - -import ( - "git.0x7f.app/WOJ/woj-server/internal/global" - "github.com/go-redis/redis/v8" - "go.uber.org/zap" -) - -var _ global.JwtService = (*service)(nil) - -type service struct { - log *zap.Logger - redis *redis.Client - SigningKey []byte - ExpireHour int -} - -func NewJwtService(g *global.Global) global.JwtService { - return &service{ - log: g.Log, - redis: g.Redis.Get().(*redis.Client), - SigningKey: []byte(g.Conf.WebServer.JwtSigningKey), - ExpireHour: g.Conf.WebServer.JwtExpireHour, - } -} diff --git a/internal/service/problem/create.go b/internal/service/problem/create.go index deb3f36..41568e1 100644 --- a/internal/service/problem/create.go +++ b/internal/service/problem/create.go @@ -21,7 +21,7 @@ func (s *service) Create(data *CreateData) (*model.Problem, e.Status) { IsEnabled: data.IsEnabled, } - err := s.db.Create(problem).Error + err := s.db.Get().Create(problem).Error if err != nil { s.log.Warn("DatabaseError", zap.Error(err), zap.Any("problem", problem)) return nil, e.DatabaseError diff --git a/internal/service/problem/createVersion.go b/internal/service/problem/createVersion.go index 6a27ca2..15b6a28 100644 --- a/internal/service/problem/createVersion.go +++ b/internal/service/problem/createVersion.go @@ -19,7 +19,7 @@ func (s *service) CreateVersion(data *CreateVersionData) (*model.ProblemVersion, StorageKey: data.StorageKey, } - err := s.db.Create(problemVersion).Error + err := s.db.Get().Create(problemVersion).Error if err != nil { s.log.Warn("DatabaseError", zap.Error(err), zap.Any("problemVersion", problemVersion)) return nil, e.DatabaseError diff --git a/internal/service/problem/query.go b/internal/service/problem/query.go index bde3235..75a24f5 100644 --- a/internal/service/problem/query.go +++ b/internal/service/problem/query.go @@ -12,7 +12,7 @@ import ( func (s *service) Query(pid uint, associations bool, shouldEnable bool) (*model.Problem, e.Status) { problem := new(model.Problem) - query := s.db + query := s.db.Get() if associations { query = query.Preload(clause.Associations) } diff --git a/internal/service/problem/queryFuzz.go b/internal/service/problem/queryFuzz.go index 98451a2..20f0149 100644 --- a/internal/service/problem/queryFuzz.go +++ b/internal/service/problem/queryFuzz.go @@ -10,7 +10,7 @@ import ( func (s *service) QueryFuzz(search string, associations bool, shouldEnable bool) ([]*model.Problem, e.Status) { problems := make([]*model.Problem, 0) - query := s.db + query := s.db.Get() if associations { query = query.Preload(clause.Associations) } @@ -18,7 +18,7 @@ func (s *service) QueryFuzz(search string, associations bool, shouldEnable bool) query = query.Where("is_enabled = true") } query = query. - Where(s.db.Where("title LIKE ?", "%"+search+"%"). + Where(s.db.Get().Where("title LIKE ?", "%"+search+"%"). Or("statement LIKE ?", "%"+search+"%")) err := query.Find(&problems).Error if err != nil { diff --git a/internal/service/problem/queryLatestVersion.go b/internal/service/problem/queryLatestVersion.go index 9b7e172..b040abd 100644 --- a/internal/service/problem/queryLatestVersion.go +++ b/internal/service/problem/queryLatestVersion.go @@ -14,7 +14,7 @@ func (s *service) QueryLatestVersion(pid uint) (*model.ProblemVersion, e.Status) IsEnabled: true, } - err := s.db. + err := s.db.Get(). Where(problemVersion). Last(&problemVersion).Error if errors.Is(err, gorm.ErrRecordNotFound) { diff --git a/internal/service/problem/queryVersion.go b/internal/service/problem/queryVersion.go index 5fb68a5..6ff6e59 100644 --- a/internal/service/problem/queryVersion.go +++ b/internal/service/problem/queryVersion.go @@ -11,7 +11,7 @@ import ( func (s *service) QueryVersion(pvid uint, shouldEnable bool) (*model.ProblemVersion, e.Status) { problemVersion := new(model.ProblemVersion) - err := s.db.First(&problemVersion, pvid).Error + err := s.db.Get().First(&problemVersion, pvid).Error if errors.Is(err, gorm.ErrRecordNotFound) { return nil, e.ProblemVersionNotFound } diff --git a/internal/service/problem/service.go b/internal/service/problem/service.go index 48168aa..f4be009 100644 --- a/internal/service/problem/service.go +++ b/internal/service/problem/service.go @@ -2,10 +2,11 @@ package problem import ( "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" "git.0x7f.app/WOJ/woj-server/internal/model" + "git.0x7f.app/WOJ/woj-server/internal/repo/db" + "github.com/samber/do" "go.uber.org/zap" - "gorm.io/gorm" ) var _ Service = (*service)(nil) @@ -20,16 +21,22 @@ type Service interface { UpdateVersion(pvid uint, values interface{}) e.Status QueryVersion(pvid uint, shouldEnable bool) (*model.ProblemVersion, e.Status) QueryLatestVersion(pid uint) (*model.ProblemVersion, e.Status) + + HealthCheck() error +} + +func NewService(i *do.Injector) (Service, error) { + return &service{ + log: do.MustInvoke[log.Service](i).GetLogger("problem"), + db: do.MustInvoke[db.Service](i), + }, nil } type service struct { log *zap.Logger - db *gorm.DB + db db.Service } -func NewService(g *global.Global) Service { - return &service{ - log: g.Log, - db: g.Db.Get().(*gorm.DB), - } +func (s *service) HealthCheck() error { + return nil } diff --git a/internal/service/problem/update.go b/internal/service/problem/update.go index a498bda..fddc578 100644 --- a/internal/service/problem/update.go +++ b/internal/service/problem/update.go @@ -7,7 +7,7 @@ import ( ) func (s *service) Update(problem *model.Problem) (*model.Problem, e.Status) { - err := s.db.Save(problem).Error + err := s.db.Get().Save(problem).Error if err != nil { s.log.Warn("DatabaseError", zap.Error(err), zap.Any("problem", problem)) return nil, e.DatabaseError diff --git a/internal/service/problem/updateVersion.go b/internal/service/problem/updateVersion.go index 80709df..9442daf 100644 --- a/internal/service/problem/updateVersion.go +++ b/internal/service/problem/updateVersion.go @@ -7,7 +7,7 @@ import ( ) func (s *service) UpdateVersion(pvid uint, values interface{}) e.Status { - err := s.db.Model(&model.ProblemVersion{}).Where("id = ?", pvid).Updates(values).Error + err := s.db.Get().Model(&model.ProblemVersion{}).Where("id = ?", pvid).Updates(values).Error if err != nil { s.log.Warn("DatabaseError", zap.Error(err), zap.Any("pvid", pvid), zap.Any("values", values)) return e.DatabaseError diff --git a/internal/service/runner/service.go b/internal/service/runner/service.go index 0ecd8ae..820c214 100644 --- a/internal/service/runner/service.go +++ b/internal/service/runner/service.go @@ -2,7 +2,9 @@ package runner import ( "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/misc/config" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" + "github.com/samber/do" "go.uber.org/zap" ) @@ -23,6 +25,15 @@ type Service interface { ParseConfig(version uint, skipCheck bool) (Config, error) // ProblemExists check if problem exists ProblemExists(version uint) bool + + HealthCheck() error +} + +func NewService(i *do.Injector) (Service, error) { + return &service{ + log: do.MustInvoke[log.Service](i).GetLogger("runner"), + verbose: do.MustInvoke[config.Service](i).GetConfig().Development, + }, nil } type service struct { @@ -30,9 +41,6 @@ type service struct { verbose bool } -func NewService(g *global.Global) Service { - return &service{ - log: g.Log, - verbose: g.Conf.Development, - } +func (s *service) HealthCheck() error { + return nil } diff --git a/internal/service/status/create.go b/internal/service/status/create.go index 40288e3..e933dba 100644 --- a/internal/service/status/create.go +++ b/internal/service/status/create.go @@ -14,7 +14,7 @@ type CreateData struct { Point int32 } -func (s service) Create(data *CreateData) (*model.Status, e.Status) { +func (s *service) Create(data *CreateData) (*model.Status, e.Status) { status := &model.Status{ SubmissionID: data.SubmissionID, ProblemVersionID: data.ProblemVersionID, @@ -26,7 +26,7 @@ func (s service) Create(data *CreateData) (*model.Status, e.Status) { IsEnabled: true, } - err := s.db.Create(status).Error + err := s.db.Get().Create(status).Error if err != nil { s.log.Warn("DatabaseError", zap.Error(err), zap.Any("status", status)) return nil, e.DatabaseError diff --git a/internal/service/status/query.go b/internal/service/status/query.go index 3e2b3c7..96d9e32 100644 --- a/internal/service/status/query.go +++ b/internal/service/status/query.go @@ -9,14 +9,14 @@ import ( "gorm.io/gorm/clause" ) -func (s service) Query(sid uint, associations bool) (*model.Status, e.Status) { +func (s *service) Query(sid uint, associations bool) (*model.Status, e.Status) { status := &model.Status{ SubmissionID: sid, IsEnabled: true, } - query := s.db + query := s.db.Get() if associations { query = query.Preload(clause.Associations) } @@ -36,14 +36,14 @@ func (s service) Query(sid uint, associations bool) (*model.Status, e.Status) { return status, e.Success } -func (s service) QueryByVersion(pvid uint, offset int, limit int) ([]*model.Status, e.Status) { +func (s *service) QueryByVersion(pvid uint, offset int, limit int) ([]*model.Status, e.Status) { var statuses []*model.Status status := &model.Status{ ProblemVersionID: pvid, IsEnabled: true, } - err := s.db.Preload(clause.Associations). + err := s.db.Get().Preload(clause.Associations). Where(status). Limit(limit). Offset(offset). diff --git a/internal/service/status/service.go b/internal/service/status/service.go index cc4f1b0..b255d98 100644 --- a/internal/service/status/service.go +++ b/internal/service/status/service.go @@ -2,10 +2,11 @@ package status import ( "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" "git.0x7f.app/WOJ/woj-server/internal/model" + "git.0x7f.app/WOJ/woj-server/internal/repo/db" + "github.com/samber/do" "go.uber.org/zap" - "gorm.io/gorm" ) var _ Service = (*service)(nil) @@ -14,16 +15,22 @@ type Service interface { Create(data *CreateData) (*model.Status, e.Status) Query(sid uint, associations bool) (*model.Status, e.Status) QueryByVersion(pvid uint, offset int, limit int) ([]*model.Status, e.Status) + + HealthCheck() error +} + +func NewService(i *do.Injector) (Service, error) { + return &service{ + log: do.MustInvoke[log.Service](i).GetLogger("status"), + db: do.MustInvoke[db.Service](i), + }, nil } type service struct { log *zap.Logger - db *gorm.DB + db db.Service } -func NewService(g *global.Global) Service { - return &service{ - log: g.Log, - db: g.Db.Get().(*gorm.DB), - } +func (s *service) HealthCheck() error { + return nil } diff --git a/internal/service/storage/service.go b/internal/service/storage/service.go index bdda06b..9805a7e 100644 --- a/internal/service/storage/service.go +++ b/internal/service/storage/service.go @@ -2,9 +2,11 @@ package storage import ( "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/misc/config" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" + "github.com/samber/do" "go.uber.org/zap" "time" ) @@ -14,6 +16,33 @@ var _ Service = (*service)(nil) type Service interface { Upload(objectName string, expiry time.Duration) (string, e.Status) Get(objectName string, expiry time.Duration) (string, e.Status) + + HealthCheck() error +} + +func NewService(i *do.Injector) (Service, error) { + conf := do.MustInvoke[config.Service](i).GetConfig() + + srv := &service{ + log: do.MustInvoke[log.Service](i).GetLogger("storage"), + bucket: conf.Storage.Bucket, + } + + var err error + srv.client, err = minio.New( + conf.Storage.Endpoint, + &minio.Options{ + Creds: credentials.NewStaticV4(conf.Storage.AccessKey, conf.Storage.SecretKey, ""), + Secure: conf.Storage.UseSSL, + }, + ) + + if err != nil { + srv.log.Error("failed to create minio client", zap.Error(err)) + return nil, err + } + + return srv, nil } type service struct { @@ -22,20 +51,6 @@ type service struct { bucket string } -func NewService(g *global.Global) Service { - minioClient, err := minio.New(g.Conf.Storage.Endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(g.Conf.Storage.AccessKey, g.Conf.Storage.SecretKey, ""), - Secure: g.Conf.Storage.UseSSL, - }) - - if err != nil { - g.Log.Fatal("failed to create minio client", zap.Error(err)) - return nil - } - - return &service{ - log: g.Log, - client: minioClient, - bucket: g.Conf.Storage.Bucket, - } +func (s *service) HealthCheck() error { + return nil } diff --git a/internal/service/submission/create.go b/internal/service/submission/create.go index ba1a05c..e58480e 100644 --- a/internal/service/submission/create.go +++ b/internal/service/submission/create.go @@ -21,7 +21,7 @@ func (s *service) Create(data *CreateData) (*model.Submission, e.Status) { Code: data.Code, } - err := s.db.Create(submission).Error + err := s.db.Get().Create(submission).Error if err != nil { s.log.Warn("DatabaseError", zap.Error(err), zap.Any("submission", submission)) return nil, e.DatabaseError diff --git a/internal/service/submission/query.go b/internal/service/submission/query.go index f40dc6a..391b67c 100644 --- a/internal/service/submission/query.go +++ b/internal/service/submission/query.go @@ -17,7 +17,7 @@ func (s *service) Query(pid uint, uid uint, offset int, limit int) ([]*model.Sub UserID: uid, } - err := s.db.Preload(clause.Associations). + err := s.db.Get().Preload(clause.Associations). Where(submission). Limit(limit). Offset(offset). @@ -37,7 +37,7 @@ func (s *service) Query(pid uint, uid uint, offset int, limit int) ([]*model.Sub func (s *service) QueryBySid(sid uint, associations bool) (*model.Submission, e.Status) { submission := new(model.Submission) - query := s.db + query := s.db.Get() if associations { query = query.Preload(clause.Associations) } diff --git a/internal/service/submission/service.go b/internal/service/submission/service.go index baede48..3ea8043 100644 --- a/internal/service/submission/service.go +++ b/internal/service/submission/service.go @@ -2,10 +2,11 @@ package submission import ( "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" "git.0x7f.app/WOJ/woj-server/internal/model" + "git.0x7f.app/WOJ/woj-server/internal/repo/db" + "github.com/samber/do" "go.uber.org/zap" - "gorm.io/gorm" ) var _ Service = (*service)(nil) @@ -14,16 +15,22 @@ type Service interface { Create(data *CreateData) (*model.Submission, e.Status) Query(pid uint, uid uint, offset int, limit int) ([]*model.Submission, e.Status) QueryBySid(sid uint, associations bool) (*model.Submission, e.Status) + + HealthCheck() error +} + +func NewService(i *do.Injector) (Service, error) { + return &service{ + log: do.MustInvoke[log.Service](i).GetLogger("submission"), + db: do.MustInvoke[db.Service](i), + }, nil } type service struct { log *zap.Logger - db *gorm.DB + db db.Service } -func NewService(g *global.Global) Service { - return &service{ - log: g.Log, - db: g.Db.Get().(*gorm.DB), - } +func (s *service) HealthCheck() error { + return nil } diff --git a/internal/service/task/service.go b/internal/service/task/service.go index 0629021..91838fc 100644 --- a/internal/service/task/service.go +++ b/internal/service/task/service.go @@ -1,11 +1,14 @@ package task import ( + "errors" "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/misc/config" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" "git.0x7f.app/WOJ/woj-server/internal/model" "git.0x7f.app/WOJ/woj-server/internal/service/runner" "github.com/hibiken/asynq" + "github.com/samber/do" "go.uber.org/zap" ) @@ -18,6 +21,24 @@ type Service interface { SubmitUpdate(data *model.SubmitUpdatePayload, ctx runner.JudgeStatus) (string, e.Status) GetTaskInfo(string, string) (*asynq.TaskInfo, e.Status) + + HealthCheck() error +} + +func NewService(i *do.Injector) (Service, error) { + conf := do.MustInvoke[config.Service](i).GetConfig() + + redisOpt := asynq.RedisClientOpt{ + Addr: conf.Redis.Address, + Password: conf.Redis.Password, + DB: conf.Redis.QueueDb, + } + + return &service{ + log: do.MustInvoke[log.Service](i).GetLogger("task"), + queue: asynq.NewClient(redisOpt), + inspector: asynq.NewInspector(redisOpt), + }, nil } type service struct { @@ -26,15 +47,13 @@ type service struct { inspector *asynq.Inspector } -func NewService(g *global.Global) Service { - redisOpt := asynq.RedisClientOpt{ - Addr: g.Conf.Redis.Address, - Password: g.Conf.Redis.Password, - DB: g.Conf.Redis.QueueDb, +func (s *service) HealthCheck() error { + servers, err := s.inspector.Servers() + if err != nil { + return err } - return &service{ - log: g.Log, - queue: asynq.NewClient(redisOpt), - inspector: asynq.NewInspector(redisOpt), + if len(servers) == 0 { + return errors.New("no asynq server found") } + return nil } diff --git a/internal/service/user/create.go b/internal/service/user/create.go index b4e815f..6a92c1e 100644 --- a/internal/service/user/create.go +++ b/internal/service/user/create.go @@ -29,7 +29,7 @@ func (s *service) Create(data *CreateData) (*model.User, e.Status) { IsEnabled: true, } - err = s.db.Create(user).Error + err = s.db.Get().Create(user).Error if err != nil && strings.Contains(err.Error(), "duplicate key") { return nil, e.UserDuplicated } diff --git a/internal/service/user/login.go b/internal/service/user/login.go index 2d5b44c..f64c4d9 100644 --- a/internal/service/user/login.go +++ b/internal/service/user/login.go @@ -17,7 +17,7 @@ type LoginData struct { func (s *service) Login(data *LoginData) (*model.User, e.Status) { user := &model.User{UserName: data.UserName} - err := s.db.Where(user).First(&user).Error + err := s.db.Get().Where(user).First(&user).Error if errors.Is(err, gorm.ErrRecordNotFound) { return nil, e.UserNotFound } diff --git a/internal/service/user/profile.go b/internal/service/user/profile.go index 06b25dc..1d9554e 100644 --- a/internal/service/user/profile.go +++ b/internal/service/user/profile.go @@ -11,7 +11,7 @@ import ( func (s *service) Profile(uid uint) (*model.User, e.Status) { user := new(model.User) - err := s.db.First(&user, uid).Error + err := s.db.Get().First(&user, uid).Error if errors.Is(err, gorm.ErrRecordNotFound) { return nil, e.UserNotFound } diff --git a/internal/service/user/service.go b/internal/service/user/service.go index eabbc0c..cb62e24 100644 --- a/internal/service/user/service.go +++ b/internal/service/user/service.go @@ -2,11 +2,12 @@ package user import ( "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" "git.0x7f.app/WOJ/woj-server/internal/model" - "github.com/go-redis/redis/v8" + "git.0x7f.app/WOJ/woj-server/internal/repo/cache" + "git.0x7f.app/WOJ/woj-server/internal/repo/db" + "github.com/samber/do" "go.uber.org/zap" - "gorm.io/gorm" ) var _ Service = (*service)(nil) @@ -16,18 +17,24 @@ type Service interface { Login(data *LoginData) (*model.User, e.Status) IncrVersion(uid uint) (int64, e.Status) Profile(uid uint) (*model.User, e.Status) + + HealthCheck() error +} + +func NewService(i *do.Injector) (Service, error) { + return &service{ + log: do.MustInvoke[log.Service](i).GetLogger("user"), + db: do.MustInvoke[db.Service](i), + cache: do.MustInvoke[cache.Service](i), + }, nil } type service struct { log *zap.Logger - db *gorm.DB - redis *redis.Client + db db.Service + cache cache.Service } -func NewService(g *global.Global) Service { - return &service{ - log: g.Log, - db: g.Db.Get().(*gorm.DB), - redis: g.Redis.Get().(*redis.Client), - } +func (s *service) HealthCheck() error { + return nil } diff --git a/internal/service/user/version.go b/internal/service/user/version.go index b773993..402e315 100644 --- a/internal/service/user/version.go +++ b/internal/service/user/version.go @@ -8,7 +8,7 @@ import ( ) func (s *service) IncrVersion(uid uint) (int64, e.Status) { - version, err := s.redis.Incr(context.Background(), fmt.Sprintf("Version:%d", uid)).Result() + version, err := s.cache.Get().Incr(context.Background(), fmt.Sprintf("Version:%d", uid)).Result() if err != nil { s.log.Warn("RedisError", zap.Error(err), zap.Any("uid", uid)) return -1, e.RedisError diff --git a/internal/service/jwt/middleware.go b/internal/web/jwt/middleware.go similarity index 88% rename from internal/service/jwt/middleware.go rename to internal/web/jwt/middleware.go index 5168b87..f9f5f1b 100644 --- a/internal/service/jwt/middleware.go +++ b/internal/web/jwt/middleware.go @@ -2,14 +2,14 @@ package jwt import ( "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/model" "github.com/gin-gonic/gin" "strings" ) func (s *service) Handler(forced bool) gin.HandlerFunc { return func(c *gin.Context) { - claim, status := func() (*global.Claim, e.Status) { + claim, status := func() (*model.Claim, e.Status) { const tokenPrefix = "bearer " tokenHeader := c.GetHeader("Authorization") if tokenHeader == "" || !strings.HasPrefix(strings.ToLower(tokenHeader), tokenPrefix) { diff --git a/internal/web/jwt/service.go b/internal/web/jwt/service.go new file mode 100644 index 0000000..09ba74a --- /dev/null +++ b/internal/web/jwt/service.go @@ -0,0 +1,50 @@ +package jwt + +import ( + "git.0x7f.app/WOJ/woj-server/internal/e" + "git.0x7f.app/WOJ/woj-server/internal/misc/config" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" + "git.0x7f.app/WOJ/woj-server/internal/model" + "git.0x7f.app/WOJ/woj-server/internal/repo/cache" + "github.com/gin-gonic/gin" + "github.com/samber/do" + "go.uber.org/zap" +) + +var _ Service = (*service)(nil) + +type Service interface { + ParseToken(tokenText string) (*model.Claim, e.Status) + SignClaim(claim *model.Claim) (string, e.Status) + Validate(claim *model.Claim) bool + + Handler(forced bool) gin.HandlerFunc + + HealthCheck() error +} + +func NewService(i *do.Injector) (Service, error) { + srv := &service{} + srv.log = do.MustInvoke[log.Service](i).GetLogger("jwt") + srv.cacheService = do.MustInvoke[cache.Service](i) // .Get().(*redis.Client) + + conf := do.MustInvoke[config.Service](i).GetConfig() + srv.SigningKey = []byte(conf.WebServer.JwtSigningKey) + srv.ExpireHour = conf.WebServer.JwtExpireHour + + return srv, srv.err +} + +type service struct { + cacheService cache.Service + log *zap.Logger + + SigningKey []byte + ExpireHour int + + err error +} + +func (s *service) HealthCheck() error { + return s.err +} diff --git a/internal/service/jwt/token.go b/internal/web/jwt/token.go similarity index 75% rename from internal/service/jwt/token.go rename to internal/web/jwt/token.go index f83b061..e67f066 100644 --- a/internal/service/jwt/token.go +++ b/internal/web/jwt/token.go @@ -4,21 +4,21 @@ import ( "context" "fmt" "git.0x7f.app/WOJ/woj-server/internal/e" - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/model" "git.0x7f.app/WOJ/woj-server/pkg/utils" "github.com/golang-jwt/jwt/v4" "go.uber.org/zap" "time" ) -func (s *service) ParseToken(tokenText string) (*global.Claim, e.Status) { +func (s *service) ParseToken(tokenText string) (*model.Claim, e.Status) { if tokenText == "" { return nil, e.TokenEmpty } token, err := jwt.ParseWithClaims( tokenText, - new(global.Claim), + new(model.Claim), func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) @@ -41,20 +41,19 @@ func (s *service) ParseToken(tokenText string) (*global.Claim, e.Status) { } if token.Valid { - c := token.Claims.(*global.Claim) + c := token.Claims.(*model.Claim) return c, e.Success } return nil, e.TokenInvalid } -func (s *service) SignClaim(claim *global.Claim) (string, e.Status) { +func (s *service) SignClaim(claim *model.Claim) (string, e.Status) { now := time.Now() claim.IssuedAt = jwt.NewNumericDate(now) claim.ExpiresAt = jwt.NewNumericDate(now.Add(time.Duration(s.ExpireHour) * time.Hour)) claim.ID = utils.RandomString(16) - // TODO: use per-user claim.Version to tracker invalidation claim.NotBefore = jwt.NewNumericDate(time.Now()) token := jwt.NewWithClaims(jwt.SigningMethodHS512, claim) @@ -66,10 +65,10 @@ func (s *service) SignClaim(claim *global.Claim) (string, e.Status) { return ss, e.Success } -func (s *service) Validate(claim *global.Claim) bool { - curVersion, err := s.redis.Get(context.Background(), fmt.Sprintf("Version:%d", claim.UID)).Int64() +func (s *service) Validate(claim *model.Claim) bool { + curVersion, err := s.cacheService.Get().Get(context.Background(), fmt.Sprintf("Version:%d", claim.UID)).Int64() if err != nil { - s.log.Debug("redis.Get error", zap.Error(err)) + s.log.Debug("cache.Get error", zap.Error(err)) return false } return curVersion == claim.Version diff --git a/internal/pkg/metrics/middleware.go b/internal/web/metrics/middleware.go similarity index 74% rename from internal/pkg/metrics/middleware.go rename to internal/web/metrics/middleware.go index fd36433..36edd35 100644 --- a/internal/pkg/metrics/middleware.go +++ b/internal/web/metrics/middleware.go @@ -9,16 +9,16 @@ import ( "time" ) -func (m *Metrics) SetLogPaths(paths []string) { - m.logPaths = paths +func (s *service) SetLogPaths(paths []string) { + s.logPaths = paths } -func (m *Metrics) Handler() gin.HandlerFunc { +func (s *service) Handler() gin.HandlerFunc { return func(c *gin.Context) { url := c.Request.URL.String() method := c.Request.Method - if !utils.Contains(m.logPaths, func(path string) bool { return strings.HasPrefix(url, path) }) { + if !utils.Contains(s.logPaths, func(path string) bool { return strings.HasPrefix(url, path) }) { c.Next() return } @@ -38,6 +38,6 @@ func (m *Metrics) Handler() gin.HandlerFunc { success = false } - m.Record(method, url, success, status, err, elapsed) + s.Record(method, url, success, status, err, elapsed) } } diff --git a/internal/pkg/metrics/prometheus.go b/internal/web/metrics/prometheus.go similarity index 52% rename from internal/pkg/metrics/prometheus.go rename to internal/web/metrics/prometheus.go index 7bee127..77d8166 100644 --- a/internal/pkg/metrics/prometheus.go +++ b/internal/web/metrics/prometheus.go @@ -2,11 +2,32 @@ package metrics import ( "git.0x7f.app/WOJ/woj-server/internal/e" + "git.0x7f.app/WOJ/woj-server/internal/misc/config" "git.0x7f.app/WOJ/woj-server/internal/pkg/cast" + "github.com/gin-gonic/gin" "github.com/prometheus/client_golang/prometheus" + "github.com/samber/do" ) -type Metrics struct { +var _ Service = (*service)(nil) + +type Service interface { + Record(method, url string, success bool, httpCode int, errCode e.Status, elapsed float64) + SetLogPaths(paths []string) + Handler() gin.HandlerFunc + HealthCheck() error +} + +func NewService(i *do.Injector) (Service, error) { + srv := &service{} + + conf := do.MustInvoke[config.Service](i).GetConfig() + srv.setup(conf.Metrics.Namespace, conf.Metrics.Subsystem) + + return srv, nil +} + +type service struct { namespace string subsystem string @@ -16,11 +37,15 @@ type Metrics struct { logPaths []string } -func (m *Metrics) Setup(namespace string, subsystem string) { - m.namespace = namespace - m.subsystem = subsystem +func (s *service) HealthCheck() error { + return nil +} - m.counter = prometheus.NewCounterVec( +func (s *service) setup(namespace string, subsystem string) { + s.namespace = namespace + s.subsystem = subsystem + + s.counter = prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: namespace, Subsystem: subsystem, @@ -30,7 +55,7 @@ func (m *Metrics) Setup(namespace string, subsystem string) { []string{"method", "url"}, ) - m.hist = prometheus.NewHistogramVec( + s.hist = prometheus.NewHistogramVec( prometheus.HistogramOpts{ Namespace: namespace, Subsystem: subsystem, @@ -41,16 +66,16 @@ func (m *Metrics) Setup(namespace string, subsystem string) { []string{"method", "url", "success", "http_code", "err_code"}, ) - prometheus.MustRegister(m.counter, m.hist) + prometheus.MustRegister(s.counter, s.hist) } -func (m *Metrics) Record(method, url string, success bool, httpCode int, errCode e.Status, elapsed float64) { - m.counter.With(prometheus.Labels{ +func (s *service) Record(method, url string, success bool, httpCode int, errCode e.Status, elapsed float64) { + s.counter.With(prometheus.Labels{ "method": method, "url": url, }).Inc() - m.hist.With(prometheus.Labels{ + s.hist.With(prometheus.Labels{ "method": method, "url": url, "success": cast.ToString(success), diff --git a/internal/router/.gitignore b/internal/web/router/.gitignore similarity index 100% rename from internal/router/.gitignore rename to internal/web/router/.gitignore diff --git a/internal/router/api.go b/internal/web/router/api.go similarity index 82% rename from internal/router/api.go rename to internal/web/router/api.go index b7f4070..222cdbd 100644 --- a/internal/router/api.go +++ b/internal/web/router/api.go @@ -6,8 +6,9 @@ import ( "git.0x7f.app/WOJ/woj-server/internal/api/status" "git.0x7f.app/WOJ/woj-server/internal/api/submission" "git.0x7f.app/WOJ/woj-server/internal/api/user" - "git.0x7f.app/WOJ/woj-server/internal/global" + "git.0x7f.app/WOJ/woj-server/internal/model" "github.com/gin-gonic/gin" + "github.com/samber/do" ) // @title OJ Server API Documentation @@ -16,14 +17,14 @@ import ( // @securityDefinitions.apikey Authentication // @in header // @name Authorization -func setupApi(g *global.Global, root *gin.RouterGroup) { +func (s *service) setupApi(root *gin.RouterGroup, injector *do.Injector) { for _, v := range endpoints { group := root.Group(v.Version).Group(v.Path) - v.Register(g, group) + v.Register(group, injector) } } -var endpoints = []global.EndpointInfo{ +var endpoints = []model.EndpointInfo{ {Version: "", Path: "/debug", Register: debug.RouteRegister}, {Version: "/v1", Path: "/user", Register: user.RouteRegister}, {Version: "/v1", Path: "/problem", Register: problem.RouteRegister}, diff --git a/internal/router/router.go b/internal/web/router/router.go similarity index 53% rename from internal/router/router.go rename to internal/web/router/router.go index c5cf55e..6f9fcc1 100644 --- a/internal/router/router.go +++ b/internal/web/router/router.go @@ -1,37 +1,74 @@ package router import ( - "git.0x7f.app/WOJ/woj-server/internal/global" - "git.0x7f.app/WOJ/woj-server/internal/pkg/metrics" - _ "git.0x7f.app/WOJ/woj-server/internal/router/docs" + "git.0x7f.app/WOJ/woj-server/internal/misc/config" + "git.0x7f.app/WOJ/woj-server/internal/misc/log" + "git.0x7f.app/WOJ/woj-server/internal/model" + "git.0x7f.app/WOJ/woj-server/internal/web/metrics" + _ "git.0x7f.app/WOJ/woj-server/internal/web/router/docs" "git.0x7f.app/WOJ/woj-server/pkg/utils" "github.com/gin-contrib/cors" "github.com/gin-contrib/pprof" ginZap "github.com/gin-contrib/zap" "github.com/gin-gonic/gin" "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/samber/do" swaggerFiles "github.com/swaggo/files" "github.com/swaggo/gin-swagger" "net/http" "time" ) -func InitRouters(g *global.Global) *gin.Engine { - gin.SetMode(utils.If(g.Conf.Development, gin.DebugMode, gin.ReleaseMode)) +var _ Service = (*service)(nil) + +type Service interface { + GetRouter() *gin.Engine + HealthCheck() error +} + +func NewService(i *do.Injector) (Service, error) { + srv := &service{} + srv.metric = do.MustInvoke[metrics.Service](i) + srv.logger = do.MustInvoke[log.Service](i) + + conf := do.MustInvoke[config.Service](i).GetConfig() + srv.initRouters(conf, i) + + return srv, srv.err +} + +type service struct { + metric metrics.Service + logger log.Service + engine *gin.Engine + err error +} + +func (s *service) GetRouter() *gin.Engine { + return s.engine +} + +func (s *service) HealthCheck() error { + return s.err +} + +func (s *service) initRouters(conf *model.Config, injector *do.Injector) *gin.Engine { + gin.SetMode(utils.If(conf.Development, gin.DebugMode, gin.ReleaseMode)) r := gin.New() r.MaxMultipartMemory = 8 << 20 // Logger middleware and debug - if g.Conf.Development { + if conf.Development { // Gin's default logger is pretty enough r.Use(gin.Logger()) r.Use(gin.Recovery()) // add prof pprof.Register(r) } else { - r.Use(ginZap.Ginzap(g.Log, time.RFC3339, false)) - r.Use(ginZap.RecoveryWithZap(g.Log, true)) + ginLog := s.logger.GetLogger("gin") + r.Use(ginZap.Ginzap(ginLog, time.RFC3339, false)) + r.Use(ginZap.RecoveryWithZap(ginLog, true)) } // CORS middleware @@ -43,10 +80,8 @@ func InitRouters(g *global.Global) *gin.Engine { })) // Prometheus middleware - g.Stat = new(metrics.Metrics) - g.Stat.Setup(g.Conf.Metrics.Namespace, g.Conf.Metrics.Subsystem) - g.Stat.SetLogPaths([]string{"/api"}) - r.Use(g.Stat.Handler()) + s.metric.SetLogPaths([]string{"/api"}) + r.Use(s.metric.Handler()) // metrics r.GET("/metrics", gin.WrapH(promhttp.Handler())) @@ -68,7 +103,7 @@ func InitRouters(g *global.Global) *gin.Engine { // api api := r.Group("/api/") - setupApi(g, api) + s.setupApi(api, injector) // static files r.Static("/static", "./resource/frontend/static") diff --git a/internal/web/web.go b/internal/web/web.go new file mode 100644 index 0000000..efb3895 --- /dev/null +++ b/internal/web/web.go @@ -0,0 +1 @@ +package web