diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..e07de46
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,8 @@
+# top
+/woj
+
+# runner
+resource/runner/.mark.image
+resource/runner/problem/*
+resource/runner/tmp/*
+resource/runner/user/*
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/.gitignore b/.gitignore
index 8826357..dc4048b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,96 +1,3 @@
-### Project
-/tmp
-/server
-/runner
-my.secrets
-
-### JetBrains template
-# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
-# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
-
-# User-specific stuff
-.idea/**/workspace.xml
-.idea/**/tasks.xml
-.idea/**/usage.statistics.xml
-.idea/**/dictionaries
-.idea/**/shelf
-
-# Generated files
-.idea/**/contentModel.xml
-
-# Sensitive or high-churn files
-.idea/**/dataSources/
-.idea/**/dataSources.ids
-.idea/**/dataSources.local.xml
-.idea/**/sqlDataSources.xml
-.idea/**/dynamic.xml
-.idea/**/uiDesigner.xml
-.idea/**/dbnavigator.xml
-
-# Gradle
-.idea/**/gradle.xml
-.idea/**/libraries
-
-# Gradle and Maven with auto-import
-# When using Gradle or Maven with auto-import, you should exclude module files,
-# since they will be recreated, and may cause churn. Uncomment if using
-# auto-import.
-# .idea/artifacts
-# .idea/compiler.xml
-# .idea/jarRepositories.xml
-# .idea/modules.xml
-# .idea/*.iml
-# .idea/modules
-# *.iml
-# *.ipr
-
-# CMake
-cmake-build-*/
-
-# Mongo Explorer plugin
-.idea/**/mongoSettings.xml
-
-# File-based project format
-*.iws
-
-# IntelliJ
-out/
-
-# mpeltonen/sbt-idea plugin
-.idea_modules/
-
-# JIRA plugin
-atlassian-ide-plugin.xml
-
-# Cursive Clojure plugin
-.idea/replstate.xml
-
-# Crashlytics plugin (for Android Studio and IntelliJ)
-com_crashlytics_export_strings.xml
-crashlytics.properties
-crashlytics-build.properties
-fabric.properties
-
-# Editor-based Rest Client
-.idea/httpRequests
-
-# Android studio 3.1+ serialized cache file
-.idea/caches/build_file_checksums.ser
-
-### Go template
-# Binaries for programs and plugins
-*.exe
-*.exe~
-*.dll
-*.so
-*.dylib
-
-# Test binary, built with `go test -c`
-*.test
-
-# Output of the go coverage tool, specifically when used with LiteIDE
-*.out
-
-# Dependency directories (remove the comment below to include it)
-# vendor/
-
+/woj
+/config.yaml
+/dsn.txt
diff --git a/.idea/jsonSchemas.xml b/.idea/jsonSchemas.xml
new file mode 100644
index 0000000..3120128
--- /dev/null
+++ b/.idea/jsonSchemas.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/swagger-settings.xml b/.idea/swagger-settings.xml
new file mode 100644
index 0000000..01d844c
--- /dev/null
+++ b/.idea/swagger-settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
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 af08537..ce25cb9 100644
--- a/Makefile
+++ b/Makefile
@@ -1,36 +1,37 @@
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)
+PKG_BASE := $(shell head -n 1 go.mod | awk '{print $$2}')
+BUILD_TIME := $(shell date -u '+%Y%m%d-%I%M%S')
+VERSION := $(shell cat VERSION)
+GIT_COMMIT := $(shell git rev-parse HEAD)
+
+LDFLAGS += -X $(PKG_BASE)/cmd.BuildTime=$(BUILD_TIME)
+LDFLAGS += -X $(PKG_BASE)/cmd.Version=$(VERSION)
+LDFLAGS += -X $(PKG_BASE)/cmd.GitCommit=$(GIT_COMMIT)
+LDFLAGS += -X $(PKG_BASE)/cmd.SentryDSN=$(shell cat dsn.txt)
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: dep
- $(GOBUILD) -o runner ./cmd/runner
-
-build: runner server
+build: swagger dep
+ $(GOBUILD) -o woj ./cmd/woj
clean:
- rm -f runner
- rm -f server
+ rm -f woj
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 -d .,./internal/e,./internal/model --pdl 1 -o internal/web/router/docs
fmt:
go fmt ./...
diff --git a/Runner.Dockerfile b/Runner.Dockerfile
new file mode 100644
index 0000000..b58e46a
--- /dev/null
+++ b/Runner.Dockerfile
@@ -0,0 +1,35 @@
+# builder
+FROM docker.io/library/golang:alpine AS builder
+
+ENV GOPROXY=https://goproxy.cn
+WORKDIR /builder
+
+RUN apk add --no-cache git make
+RUN go install github.com/swaggo/swag/cmd/swag@latest
+
+COPY go.mod /builder/go.mod
+COPY go.sum /builder/go.sum
+RUN go mod download
+
+COPY . /builder
+RUN make build
+
+
+# main image
+FROM quay.io/podman/stable
+
+# pkill
+RUN yum -y install jq procps-ng && yum -y clean all && rm -rf /var/cache
+
+WORKDIR /app
+
+# prepare images
+COPY --from=builder /builder/resource/runner /app/resource/runner
+RUN bash -c "cd /app/resource/runner/scripts && ./prepare_images.sh save"
+
+# sources
+COPY --from=builder /builder/config.docker.yaml /app
+COPY --from=builder /builder/docker-entrypoint.sh /app
+COPY --from=builder /builder/woj /app
+
+ENTRYPOINT ["/app/docker-entrypoint.sh"]
diff --git a/Server.Dockerfile b/Server.Dockerfile
new file mode 100644
index 0000000..e82eeae
--- /dev/null
+++ b/Server.Dockerfile
@@ -0,0 +1,29 @@
+# builder
+FROM docker.io/library/golang:alpine AS builder
+
+ENV GOPROXY=https://goproxy.cn
+WORKDIR /builder
+
+RUN apk add --no-cache git make
+RUN go install github.com/swaggo/swag/cmd/swag@latest
+
+COPY go.mod /builder/go.mod
+COPY go.sum /builder/go.sum
+RUN go mod download
+
+COPY . /builder
+RUN make build
+
+
+# main image
+FROM docker.io/library/alpine
+
+WORKDIR /app
+RUN apk --no-cache add tzdata ca-certificates libc6-compat bash
+
+COPY --from=builder /builder/config.docker.yaml /app
+COPY --from=builder /builder/docker-entrypoint.sh /app
+COPY --from=builder /builder/resource/frontend /app/resource/frontend
+COPY --from=builder /builder/woj /app
+
+ENTRYPOINT ["/app/docker-entrypoint.sh"]
diff --git a/VERSION b/VERSION
index afaf360..867e524 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.0.0
\ No newline at end of file
+1.2.0
\ No newline at end of file
diff --git a/build_image.sh b/build_image.sh
new file mode 100755
index 0000000..5d104a0
--- /dev/null
+++ b/build_image.sh
@@ -0,0 +1,61 @@
+#!/usr/bin/env bash
+
+. resource/runner/scripts/common.sh
+
+# version
+VERSION="$(cat VERSION)"
+log_info "VERSION: $VERSION"
+
+function build_base() {
+ log_info "[+] Building Base Images"
+ pushd resource/runner || exit 1
+ $DOCKER build -t git.0x7f.app/woj/ubuntu-full:latest -f scripts/ubuntu-full.Dockerfile . ||
+ (log_error "Build Full Image failed" && exit 1)
+ $DOCKER build -t git.0x7f.app/woj/ubuntu-run:latest -f scripts/ubuntu-run.Dockerfile . ||
+ (log_error "Build Tiny Image failed" && exit 1)
+ popd
+}
+
+function push_base() {
+ log_info "[+] Pushing Base Images"
+ $DOCKER push "git.0x7f.app/woj/ubuntu-full:latest"
+ $DOCKER push "git.0x7f.app/woj/ubuntu-run:latest"
+}
+
+function build_server() {
+ log_info "[+] Building Server"
+ $DOCKER build -t "git.0x7f.app/woj/woj-server:latest" -f Server.Dockerfile . ||
+ (log_error "[!] Failed to build Server" && exit 1)
+}
+
+function build_runner() {
+ log_info "[+] Building Runner"
+ $DOCKER build \
+ --cap-add=sys_admin,mknod \
+ --device=/dev/fuse \
+ --security-opt label=disable \
+ -t "git.0x7f.app/woj/woj-runner:latest" \
+ -f Runner.Dockerfile . ||
+ (log_error "[!] Failed to build Runner" && exit 1)
+}
+
+function push_server() {
+ log_info "[+] Pushing Server Images"
+ $DOCKER push "git.0x7f.app/woj/woj-server:latest"
+ $DOCKER tag "git.0x7f.app/woj/woj-server:latest" "git.0x7f.app/woj/woj-server:$VERSION"
+ $DOCKER push "git.0x7f.app/woj/woj-server:$VERSION"
+}
+
+function push_runner() {
+ log_info "[+] Pushing Runner Images"
+ $DOCKER push "git.0x7f.app/woj/woj-runner:latest"
+ $DOCKER tag "git.0x7f.app/woj/woj-runner:latest" "git.0x7f.app/woj/woj-runner:$VERSION"
+ $DOCKER push "git.0x7f.app/woj/woj-runner:$VERSION"
+}
+
+build_base
+push_base
+build_server
+push_server
+build_runner
+push_runner
diff --git a/cmd/common.go b/cmd/common.go
index 377871f..0560068 100644
--- a/cmd/common.go
+++ b/cmd/common.go
@@ -1,10 +1,9 @@
package cmd
import (
- "github.com/WHUPRJ/woj-server/internal/global"
+ "github.com/getsentry/sentry-go"
"github.com/urfave/cli/v2"
"log"
- "math/rand"
"time"
)
@@ -34,27 +33,39 @@ var App = &cli.App{
EnvVars: []string{"APP_CONFIG"},
},
},
+ After: cleanupSentry,
}
var (
BuildTime string
Version string
+ GitCommit string
+ SentryDSN string
)
func init() {
if BuildTime == "" {
- BuildTime = "2022-09-06-01-00-00"
+ // First Commit
+ BuildTime = "20220907-153437"
}
App.Compiled = getBuildTime()
if Version == "" {
- Version = "0.0.0+None"
+ Version = "0.0.0"
}
App.Version = Version
+
+ if GitCommit == "" {
+ GitCommit = "out-of-tree"
+ }
+
+ if SentryDSN != "" {
+ setupSentry()
+ }
}
func getBuildTime() time.Time {
- build, err := time.Parse("2006-01-02-15-04-05", BuildTime)
+ build, err := time.Parse("20060102-150405", BuildTime)
if err != nil {
log.Printf("failed to parse build time: %v", err)
build = time.Now()
@@ -62,14 +73,22 @@ 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
+func setupSentry() {
+ err := sentry.Init(sentry.ClientOptions{
+ Dsn: SentryDSN,
+ EnableTracing: true,
+ TracesSampleRate: 1.0,
+ SendDefaultPII: true,
+ Release: GitCommit,
+ })
+ if err != nil {
+ log.Fatalf("sentry.Init: %s", err)
+ }
+}
+
+func cleanupSentry(*cli.Context) error {
+ if SentryDSN != "" {
+ sentry.Flush(time.Second * 2)
+ }
+ return nil
}
diff --git a/cmd/runner/main.go b/cmd/runner/main.go
deleted file mode 100644
index aa7f406..0000000
--- a/cmd/runner/main.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package main
-
-import (
- "github.com/WHUPRJ/woj-server/cmd"
- "github.com/WHUPRJ/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 4b23945..0000000
--- a/cmd/server/main.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package main
-
-import (
- "github.com/WHUPRJ/woj-server/cmd"
- "github.com/WHUPRJ/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..c4d7100
--- /dev/null
+++ b/cmd/woj/woj.go
@@ -0,0 +1,115 @@
+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/getsentry/sentry-go"
+ "github.com/samber/do"
+ "github.com/urfave/cli/v2"
+ slog "log"
+ "os"
+ "time"
+)
+
+func main() {
+ a := cmd.App
+ a.Usage = "woj-server"
+ a.Commands = []*cli.Command{
+ {
+ Name: "server",
+ Aliases: []string{"s"},
+ Usage: "start web api server",
+ Action: wrap(appServer.RunServer),
+ },
+ {
+ Name: "migrate",
+ Aliases: []string{"m"},
+ Usage: "migrate database",
+ Action: wrap(appServer.RunServerMigrate),
+ },
+ {
+ Name: "runner",
+ Aliases: []string{"r"},
+ Usage: "start runner",
+ Action: wrap(appRunner.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 wrap(f func(i *do.Injector) error) func(*cli.Context) error {
+ return func(c *cli.Context) error {
+ defer func() {
+ if cmd.SentryDSN != "" {
+ // only recover when sentry is enabled
+ if r := recover(); r != nil {
+ sentry.CaptureException(r.(error))
+ sentry.Flush(time.Second * 2)
+ slog.Printf("Panic Captured: %v", r)
+ }
+ }
+ }()
+
+ injector := prepareServices(c)
+
+ logger := do.MustInvoke[log.Service](injector)
+ defer func() { _ = logger.GetRawLogger().Sync() }()
+ logger.GetRawLogger().Info("starting...")
+
+ return f(injector)
+ }
+}
diff --git a/config.docker.yaml b/config.docker.yaml
new file mode 100644
index 0000000..2eecdd0
--- /dev/null
+++ b/config.docker.yaml
@@ -0,0 +1,37 @@
+WebServer:
+ Address: ${WEB_SERVER_ADDRESS}
+ Port: ${WEB_SERVER_PORT}
+ JwtSigningKey: ${WEB_SERVER_JWT_SIGNING_KEY}
+ JwtExpireHour: ${WEB_SERVER_JWT_EXPIRE_HOUR}
+
+Redis:
+ Db: ${REDIS_DB}
+ QueueDb: ${REDIS_QUEUE_DB}
+ Address: ${REDIS_ADDRESS}
+ Port: ${REDIS_PORT}
+ Password: ${REDIS_PASSWORD}
+
+Database:
+ Host: ${DATABASE_HOST}
+ Port: ${DATABASE_PORT}
+ User: ${DATABASE_USER}
+ Password: ${DATABASE_PASSWORD}
+ Database: ${DATABASE_NAME}
+ Prefix: ${DATABASE_PREFIX}
+ MaxOpenConns: ${DATABASE_MAX_OPEN_CONNS}
+ MaxIdleConns: ${DATABASE_MAX_IDLE_CONNS}
+ ConnMaxLifetime: ${DATABASE_CONN_MAX_LIFETIME}
+ TimeZone: ${DATABASE_TIMEZONE}
+
+Storage:
+ Endpoint: ${STORAGE_ENDPOINT}
+ UseSSL: ${STORAGE_USE_SSL}
+ AccessKey: ${STORAGE_ACCESS_KEY}
+ SecretKey: ${STORAGE_SECRET_KEY}
+ Bucket: ${STORAGE_BUCKET}
+
+Metrics:
+ Namespace: ${METRICS_NAMESPACE}
+ Subsystem: ${METRICS_SUBSYSTEM}
+
+Development: ${DEVELOPMENT}
diff --git a/config.yaml b/config.yaml
deleted file mode 100644
index 206536b..0000000
--- a/config.yaml
+++ /dev/null
@@ -1,35 +0,0 @@
-WebServer:
- Address: 0.0.0.0
- Port: 8000
- JwtSigningKey: 'rq67SdQIRABhHq40'
- JwtExpireHour: 12
-
-Redis:
- Db: 0
- QueueDb: 1
- Address: '127.0.0.1:6379'
- Password: ''
-
-Database:
- Host: '127.0.0.1'
- Port: 5432
- User: 'dev'
- Password: 'password'
- Database: 'dev'
- Prefix: 'oj_'
- MaxOpenConns: 100
- MaxIdleConns: 60
- ConnMaxLifetime: 60
-
-Storage:
- Endpoint: '127.0.0.1:9000'
- UseSSL: false
- AccessKey: 'EHd5Zj56QrTivhFI'
- SecretKey: 'FUHy4RW1mn0Kbr5pibDZ6R2F9116FZKY'
- Bucket: 'woj'
-
-Metrics:
- Namespace: 'OJ'
- Subsystem: 'server'
-
-Development: true
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..92641ad
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,103 @@
+services:
+ server:
+ image: git.0x7f.app/woj/woj-server:1.1.0
+ restart: unless-stopped
+ healthcheck:
+ test: [ "CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1:8000/health" ]
+ interval: 5s
+ command: server
+ environment:
+ - REDIS_ADDRESS=cache
+ - DATABASE_HOST=db
+ - DATABASE_USER=dev
+ - DATABASE_PASSWORD=password
+ - DATABASE_NAME=dev
+ - STORAGE_ENDPOINT=storage:9000
+ - STORAGE_ACCESS_KEY=access_key
+ - STORAGE_SECRET_KEY=secret_key
+ - STORAGE_BUCKET=woj
+ - DEVELOPMENT=true
+ volumes:
+ - /etc/timezone:/etc/timezone:ro
+ - /etc/localtime:/etc/localtime:ro
+ depends_on:
+ runner:
+ condition: service_started
+ storage:
+ condition: service_healthy
+ cache:
+ condition: service_healthy
+ db:
+ condition: service_healthy
+ ports:
+ - "8000:8000"
+
+ runner:
+ image: git.0x7f.app/woj/woj-runner:1.1.0
+ restart: unless-stopped
+ command: runner
+ security_opt:
+ - "label=disable"
+ cap_add:
+ - SYS_ADMIN
+ - MKNOD
+ devices:
+ - "/dev/fuse"
+ environment:
+ - REDIS_ADDRESS=cache
+ - STORAGE_ENDPOINT=storage:9000
+ - STORAGE_ACCESS_KEY=access_key
+ - STORAGE_SECRET_KEY=secret_key
+ - STORAGE_BUCKET=woj
+ - DEVELOPMENT=true
+ volumes:
+ - runner:/app/resource/runner/user
+ - /etc/timezone:/etc/timezone:ro
+ - /etc/localtime:/etc/localtime:ro
+ depends_on:
+ storage:
+ condition: service_healthy
+ cache:
+ condition: service_healthy
+
+ storage:
+ image: quay.io/minio/minio:latest
+ restart: unless-stopped
+ healthcheck:
+ test: [ "CMD", "curl", "-f", "http://127.0.0.1:9000/minio/health/live" ]
+ interval: 5s
+ entrypoint: sh
+ command: -c 'mkdir -p /data/woj && minio server /data'
+ environment:
+ MINIO_ROOT_USER: "access_key"
+ MINIO_ROOT_PASSWORD: "secret_key"
+ volumes:
+ - storage:/data
+
+ cache:
+ image: docker.io/library/redis:alpine
+ restart: unless-stopped
+ healthcheck:
+ test: [ "CMD", "redis-cli", "ping" ]
+ interval: 5s
+ volumes:
+ - cache:/data
+
+ db:
+ image: docker.io/library/postgres:alpine
+ restart: unless-stopped
+ healthcheck:
+ test: [ "CMD", "pg_isready", "-U", "dev" ]
+ interval: 5s
+ environment:
+ - POSTGRES_USER=dev
+ - POSTGRES_PASSWORD=password
+ - POSTGRES_DB=dev
+ volumes:
+ - db:/var/lib/postgresql/data
+
+volumes:
+ runner:
+ storage:
+ cache:
+ db:
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
new file mode 100755
index 0000000..870c55d
--- /dev/null
+++ b/docker-entrypoint.sh
@@ -0,0 +1,86 @@
+#!/bin/bash
+set -eo pipefail
+
+COLOR_RED="\e[0;31m"
+COLOR_GREEN="\e[0;32m"
+COLOR_YELLOW="\e[0;33m"
+COLOR_NONE="\e[0m"
+function log_info() { echo -e "${COLOR_GREEN}$*${COLOR_NONE}" 1>&2; }
+function log_warn() { echo -e "${COLOR_YELLOW}$*${COLOR_NONE}" 1>&2; }
+function log_error() { echo -e "${COLOR_RED}$*${COLOR_NONE}" 1>&2; }
+
+log_info "extracting env vars"
+
+function check_env() {
+ # $1 -> var name
+ # $2 -> default value
+ # $3 -> quota or not
+ val=$(eval "echo -n \${$1}")
+ if test -z "$val"; then
+ log_warn "Environment variable $1 is not set, using default value \"$2\""
+ export "$1"="$2"
+ else
+ log_info "Using $1=$val"
+ fi
+
+ if "$3"; then
+ val=$(eval "echo -n \${$1}")
+ # shellcheck disable=SC2140
+ export "$1"="'$val'"
+ fi
+
+ # echo -n ">>>>> "
+ # eval "echo -n \${$1}"
+ # echo " <<<<<"
+}
+
+check_env "WEB_SERVER_ADDRESS" "0.0.0.0" true
+check_env "WEB_SERVER_PORT" 8000 false
+check_env "WEB_SERVER_JWT_SIGNING_KEY" "$(head -n 10 /dev/urandom | md5sum | cut -c 1-32)" true
+check_env "WEB_SERVER_JWT_EXPIRE_HOUR" 12 false
+
+check_env "REDIS_DB" 0 false
+check_env "REDIS_QUEUE_DB" 1 false
+check_env "REDIS_ADDRESS" "redis" true
+check_env "REDIS_PORT" 6379 false
+check_env "REDIS_PASSWORD" "" true
+
+check_env "DATABASE_HOST" "postgres" true
+check_env "DATABASE_PORT" 5432 false
+check_env "DATABASE_USER" "dev" true
+check_env "DATABASE_PASSWORD" "password" true
+check_env "DATABASE_NAME" "dev" true
+check_env "DATABASE_PREFIX" "oj_" true
+check_env "DATABASE_MAX_OPEN_CONNS" 100 false
+check_env "DATABASE_MAX_IDLE_CONNS" 60 false
+check_env "DATABASE_CONN_MAX_LIFETIME" 60 false
+check_env "DATABASE_TIMEZONE" "Asia/Shanghai" true
+
+check_env "STORAGE_ENDPOINT" "minio:9000" true
+check_env "STORAGE_USE_SSL" "false" false
+check_env "STORAGE_ACCESS_KEY" "access_key" true
+check_env "STORAGE_SECRET_KEY" "secret_key" true
+check_env "STORAGE_BUCKET" "woj" true
+
+check_env "METRICS_NAMESPACE" "woj" true
+check_env "METRICS_SUBSYSTEM" "server" true
+
+check_env "DEVELOPMENT" false false
+
+rm -f /tmp/tmp.yaml
+(
+ echo "cat </app/config.yaml"
+ cat /app/config.docker.yaml
+ echo "EOF"
+) >/tmp/tmp.yaml
+
+if [ -f '/app/config.yaml' ]; then
+ log_info "config.yaml already exists, skip"
+else
+ log_info "creating config.yaml"
+ . /tmp/tmp.yaml || (log_error "failed to create config.yaml" && exit 1)
+fi
+
+log_info "starting woj"
+#cat /app/config.yaml
+exec /app/woj "$@"
diff --git a/go.mod b/go.mod
index b42a8c2..1c2a5fd 100644
--- a/go.mod
+++ b/go.mod
@@ -1,90 +1,96 @@
-module github.com/WHUPRJ/woj-server
+module git.0x7f.app/WOJ/woj-server
-go 1.19
+go 1.20
require (
- github.com/gin-contrib/cors v1.4.0
+ github.com/TheZeroSlave/zapsentry v1.20.0
+ github.com/getsentry/sentry-go v0.25.0
+ github.com/gin-contrib/cors v1.5.0
github.com/gin-contrib/pprof v1.4.0
- github.com/gin-contrib/zap v0.0.2
- github.com/gin-gonic/gin v1.8.1
- github.com/go-redis/redis/v8 v8.11.5
- github.com/golang-jwt/jwt/v4 v4.4.2
- github.com/hibiken/asynq v0.23.0
- github.com/jackc/pgtype v1.11.0
- github.com/minio/minio-go/v7 v7.0.42
- github.com/prometheus/client_golang v1.13.0
- github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a
- github.com/swaggo/gin-swagger v1.5.3
- github.com/swaggo/swag v1.8.5
- github.com/urfave/cli/v2 v2.14.1
- go.uber.org/zap v1.23.0
- golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
- golang.org/x/text v0.3.7
+ github.com/gin-contrib/zap v0.2.0
+ github.com/gin-gonic/gin v1.9.1
+ 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.66
+ github.com/prometheus/client_golang v1.17.0
+ github.com/redis/go-redis/v9 v9.3.1
+ 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.2
+ github.com/urfave/cli/v2 v2.26.0
+ go.uber.org/zap v1.26.0
+ golang.org/x/crypto v0.17.0
+ golang.org/x/text v0.14.0
gopkg.in/yaml.v3 v3.0.1
- gorm.io/driver/postgres v1.3.9
- gorm.io/gorm v1.23.8
- moul.io/zapgorm2 v1.1.3
+ gorm.io/driver/postgres v1.5.4
+ gorm.io/gorm v1.25.5
+ moul.io/zapgorm2 v1.3.0
)
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/beorn7/perks v1.0.1 // indirect
- github.com/cespare/xxhash/v2 v2.1.2 // indirect
- github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
+ github.com/bytedance/sonic v1.10.2 // indirect
+ github.com/cespare/xxhash/v2 v2.2.0 // indirect
+ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
+ github.com/chenzhuoyu/iasm v0.9.1 // indirect
+ github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
- github.com/dustin/go-humanize v1.0.0 // indirect
+ github.com/dustin/go-humanize v1.0.1 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // 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/swag v0.19.15 // indirect
- github.com/go-playground/locales v0.14.0 // indirect
- github.com/go-playground/universal-translator v0.18.0 // indirect
- github.com/go-playground/validator/v10 v10.10.0 // indirect
- github.com/goccy/go-json v0.9.7 // indirect
- github.com/golang/protobuf v1.5.2 // indirect
- github.com/google/uuid v1.3.0 // indirect
- github.com/jackc/chunkreader/v2 v2.0.1 // indirect
- github.com/jackc/pgconn v1.12.1 // indirect
+ github.com/go-openapi/jsonpointer v0.20.2 // indirect
+ github.com/go-openapi/jsonreference v0.20.3 // indirect
+ github.com/go-openapi/spec v0.20.12 // indirect
+ github.com/go-openapi/swag v0.22.5 // indirect
+ github.com/go-playground/locales v0.14.1 // indirect
+ github.com/go-playground/universal-translator v0.18.1 // indirect
+ github.com/go-playground/validator/v10 v10.16.0 // indirect
+ github.com/goccy/go-json v0.10.2 // indirect
+ github.com/golang/protobuf v1.5.3 // indirect
+ github.com/google/go-cmp v0.6.0 // indirect
+ github.com/google/uuid v1.5.0 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
- github.com/jackc/pgproto3/v2 v2.3.0 // indirect
- github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
- github.com/jackc/pgx/v4 v4.16.1 // indirect
+ github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
+ github.com/jackc/pgx/v4 v4.18.1 // indirect
+ github.com/jackc/pgx/v5 v5.5.1 // indirect
+ github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
- github.com/jinzhu/now v1.1.4 // indirect
+ github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
- github.com/klauspost/compress v1.15.9 // indirect
- github.com/klauspost/cpuid/v2 v2.1.0 // indirect
- github.com/leodido/go-urn v1.2.1 // indirect
- github.com/mailru/easyjson v0.7.6 // indirect
- github.com/mattn/go-isatty v0.0.14 // indirect
- github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
+ github.com/klauspost/compress v1.17.4 // indirect
+ github.com/klauspost/cpuid/v2 v2.2.6 // indirect
+ github.com/leodido/go-urn v1.2.4 // indirect
+ github.com/mailru/easyjson v0.7.7 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
- github.com/minio/sha256-simd v1.0.0 // indirect
+ github.com/minio/sha256-simd v1.0.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
- github.com/pelletier/go-toml/v2 v2.0.1 // indirect
- github.com/prometheus/client_model v0.2.0 // indirect
- github.com/prometheus/common v0.37.0 // indirect
- github.com/prometheus/procfs v0.8.0 // indirect
+ github.com/pelletier/go-toml/v2 v2.1.1 // indirect
+ github.com/prometheus/client_model v0.5.0 // indirect
+ github.com/prometheus/common v0.45.0 // indirect
+ github.com/prometheus/procfs v0.12.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
- github.com/rs/xid v1.4.0 // indirect
+ github.com/rs/xid v1.5.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
- github.com/sirupsen/logrus v1.9.0 // indirect
- github.com/spf13/cast v1.5.0 // indirect
- github.com/ugorji/go/codec v1.2.7 // indirect
- github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
- go.uber.org/atomic v1.7.0 // indirect
- go.uber.org/multierr v1.7.0 // indirect
- golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
- golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect
- golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
- golang.org/x/tools v0.1.10 // indirect
- google.golang.org/protobuf v1.28.1 // indirect
- gopkg.in/ini.v1 v1.66.6 // indirect
- gopkg.in/yaml.v2 v2.4.0 // indirect
+ github.com/sirupsen/logrus v1.9.3 // indirect
+ github.com/spf13/cast v1.6.0 // indirect
+ github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+ github.com/ugorji/go/codec v1.2.12 // indirect
+ github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect
+ go.uber.org/multierr v1.11.0 // indirect
+ golang.org/x/arch v0.6.0 // indirect
+ golang.org/x/net v0.19.0 // indirect
+ golang.org/x/sync v0.5.0 // indirect
+ golang.org/x/sys v0.15.0 // indirect
+ golang.org/x/time v0.5.0 // indirect
+ golang.org/x/tools v0.16.1 // indirect
+ google.golang.org/protobuf v1.31.0 // indirect
+ gopkg.in/ini.v1 v1.67.0 // indirect
)
diff --git a/go.sum b/go.sum
index 28e388a..aa3ba5a 100644
--- a/go.sum
+++ b/go.sum
@@ -1,74 +1,34 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
-cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
-cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
-cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
-cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
-cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
-cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
-cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
-cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
-cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
-cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
-cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
-cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
-cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
-cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
-cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
-cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
-cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
-cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
-cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
-cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
-cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
-cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
-cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
-cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
-cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
-cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
-cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
-cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
-dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
-github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
-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/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
-github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
+github.com/TheZeroSlave/zapsentry v1.20.0 h1:PP7Qb2OPue+E87NqvEq4xcT50Y7BXKYHdGwb0GmsrpE=
+github.com/TheZeroSlave/zapsentry v1.20.0/go.mod h1:D1YMfSuu6xnkhwFXxrronesmsiyDhIqo+86I3Ok+r64=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
-github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
+github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
+github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
+github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
+github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
+github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
+github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
+github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
+github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
+github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
+github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
+github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
+github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
+github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
+github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
+github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
-github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
+github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -76,137 +36,72 @@ 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/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
-github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
-github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
-github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/gin-contrib/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g=
-github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURUfmBoMlcs=
+github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
+github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
+github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
+github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
+github.com/getsentry/sentry-go v0.25.0 h1:q6Eo+hS+yoJlTO3uu/azhQadsD8V+jQn2D8VvX1eOyI=
+github.com/getsentry/sentry-go v0.25.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
+github.com/gin-contrib/cors v1.5.0 h1:DgGKV7DDoOn36DFkNtbHrjoRiT5ExCe+PC9/xp7aKvk=
+github.com/gin-contrib/cors v1.5.0/go.mod h1:TvU7MAZ3EwrPLI2ztzTt3tqgvBCq+wn8WpZmfADjupI=
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
-github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
github.com/gin-contrib/pprof v1.4.0 h1:XxiBSf5jWZ5i16lNOPbMTVdgHBdhfGRD5PZ1LWazzvg=
github.com/gin-contrib/pprof v1.4.0/go.mod h1:RrehPJasUVBPK6yTUwOl8/NP6i0vbUgmxtis+Z5KE90=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
-github.com/gin-contrib/zap v0.0.2 h1:VnIucI+kUsxgzmcrX0gMk19a2I12KirTxi+ufuT2xZk=
-github.com/gin-contrib/zap v0.0.2/go.mod h1:2vZj8gTuOYOfottCirxZr9gNM/Q1yk2iSVn15SUVG5A=
-github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
-github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
+github.com/gin-contrib/zap v0.2.0 h1:HLvt3rZXyC8XC+s2lHzMFow3UDqiEbfrBWJyHHS6L8A=
+github.com/gin-contrib/zap v0.2.0/go.mod h1:eqfbe9ZmI+GgTZF6nRiC2ZwDeM4DK1Viwc8OxTCphh0=
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
-github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
+github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
+github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
-github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
-github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
-github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
-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/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=
-github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
+github.com/go-openapi/jsonpointer v0.20.1 h1:MkK4VEIEZMj4wT9PmjaUmGflVBr9nvud4Q4UVFbDoBE=
+github.com/go-openapi/jsonpointer v0.20.1/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
+github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
+github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
+github.com/go-openapi/jsonreference v0.20.3 h1:EjGcjTW8pD1mRis6+w/gmoBdqv5+RbE9B85D1NgDOVQ=
+github.com/go-openapi/jsonreference v0.20.3/go.mod h1:FviDZ46i9ivh810gqzFLl5NttD5q3tSlMLqLr6okedM=
+github.com/go-openapi/spec v0.20.12 h1:cgSLbrsmziAP2iais+Vz7kSazwZ8rsUZd6TUzdDgkVI=
+github.com/go-openapi/spec v0.20.12/go.mod h1:iSCgnBcwbMW9SfzJb8iYynXvcY6C/QFrI7otzF7xGM4=
+github.com/go-openapi/swag v0.22.5 h1:fVS63IE3M0lsuWRzuom3RLwUMVI2peDH01s6M70ugys=
+github.com/go-openapi/swag v0.22.5/go.mod h1:Gl91UqO+btAM0plGGxHqJcQZ1ZTy6jbmridBTsDy8A0=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
-github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
-github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
+github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
-github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
-github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
+github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
-github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
-github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
+github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
-github.com/go-redis/redis/v8 v8.11.2/go.mod h1:DLomh7y2e3ggQXQLd1YgmvIfecPJoFl7WU5SOQ/r06M=
-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-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
+github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
-github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
+github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
+github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
-github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
-github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
-github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
-github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
-github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
-github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
-github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
-github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
+github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
+github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
-github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
-github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
-github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
-github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hibiken/asynq v0.23.0 h1:kmKkNFgqiXBatC8oz94Mer6uvKoGn4STlIVDV5wnKyE=
-github.com/hibiken/asynq v0.23.0/go.mod h1:K70jPVx+CAmmQrXot7Dru0D52EO7ob4BIun3ri5z1Qw=
-github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
+github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/hibiken/asynq v0.24.1 h1:+5iIEAyA9K/lcSPvx3qoPtsKJeKI5u9aOIvUmSsazEw=
+github.com/hibiken/asynq v0.24.1/go.mod h1:u5qVeSbrnfT+vtG5Mq8ZPzQu/BmCKMHvTGb91uy9Tts=
+github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
@@ -217,16 +112,16 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
-github.com/jackc/pgconn v1.12.1 h1:rsDFzIpRk7xT4B8FufgpCCeyjdNpKyghZeSefViE5W8=
-github.com/jackc/pgconn v1.12.1/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono=
+github.com/jackc/pgconn v1.14.0 h1:vrbA9Ud87g6JdFWkHTJXppVce58qPIdP7N8y0Ml/A7Q=
+github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
-github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
+github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
@@ -234,184 +129,145 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
-github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y=
-github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
-github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
+github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0=
+github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
+github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
+github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
+github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
-github.com/jackc/pgtype v1.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs=
-github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
+github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw=
+github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
-github.com/jackc/pgx/v4 v4.16.1 h1:JzTglcal01DrghUqt+PmzWsZx/Yh7SC/CTQmSBMTd0Y=
-github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ=
+github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0=
+github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE=
+github.com/jackc/pgx/v5 v5.5.1 h1:5I9etrGkLrN+2XPCsi6XLlV5DITbSL/xBZdmAxFcXPI=
+github.com/jackc/pgx/v5 v5.5.1/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
-github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0=
+github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
+github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
-github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
+github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
-github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
-github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
-github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
-github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
-github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
-github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
+github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
+github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
-github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
-github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0=
-github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
+github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
+github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
-github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
+github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
+github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-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/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
-github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
-github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
+github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
+github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
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.42 h1:fP56plNR/Tkw/+Xczw9NL5TGxe5gJDvgd8LidNR3BEI=
-github.com/minio/minio-go/v7 v7.0.42/go.mod h1:nCrRzjoSUQh8hgKKtu3Y708OLvRLtuASMg2/nvmbarw=
-github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
-github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
+github.com/minio/minio-go/v7 v7.0.66 h1:bnTOXOHjOqv/gcMuiVbN9o2ngRItvqE774dG9nq0Dzw=
+github.com/minio/minio-go/v7 v7.0.66/go.mod h1:DHAgmyQEGdW3Cif0UooKOyrT3Vxs82zNdV6tkKhRtbs=
+github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
+github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
-github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg=
-github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
-github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
-github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
-github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
-github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U=
-github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
-github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
-github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
-github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
-github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
+github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
+github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
+github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
-github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
-github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
-github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
-github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
-github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
-github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
-github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
-github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
-github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
-github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
-github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
-github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
-github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
-github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
-github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
-github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
-github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
-github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
+github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
+github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
+github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
+github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
+github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
+github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
+github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
+github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
+github.com/redis/go-redis/v9 v9.0.3/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
+github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0=
+github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
+github.com/redis/go-redis/v9 v9.3.1 h1:KqdY8U+3X6z+iACvumCNxnoluToB+9Me+TvyFa21Mds=
+github.com/redis/go-redis/v9 v9.3.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
-github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
+github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
-github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY=
-github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
+github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
+github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
-github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
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 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
-github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
-github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
-github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
-github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
-github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
-github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
+github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
+github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -419,66 +275,58 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a h1:kAe4YSu0O0UFn1DowNo2MY5p6xzqtJ/wQ7LZynSvGaY=
-github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w=
-github.com/swaggo/gin-swagger v1.5.3 h1:8mWmHLolIbrhJJTflsaFoZzRBYVmEE7JZGIq08EiC0Q=
-github.com/swaggo/gin-swagger v1.5.3/go.mod h1:3XJKSfHjDMB5dBo/0rrTXidPmgLeqsX89Yp4uA50HpI=
-github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ=
-github.com/swaggo/swag v1.8.5 h1:7NgtfXsXE+jrcOwRyiftGKW7Ppydj7tZiVenuRf1fE4=
-github.com/swaggo/swag v1.8.5/go.mod h1:jMLeXOOmYyjk8PvHTsXBdrubsNd9gUJTTCzL5iBnseg=
-github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
+github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
+github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
+github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
+github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04=
+github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E=
+github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
+github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
-github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
-github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
-github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
-github.com/urfave/cli/v2 v2.14.1 h1:0Sx+C9404t2+DPuIJ3UpZFOEFhNG3wPxMj7uZHyZKFA=
-github.com/urfave/cli/v2 v2.14.1/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
-github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
-github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
-github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
+github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+github.com/urfave/cli/v2 v2.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI=
+github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
+github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI=
+github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
-go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
-go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
-go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI=
-go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
-go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
+go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
+go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
-go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
-go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
-go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
-go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
-golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
+go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
+golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc=
+golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -486,356 +334,120 @@ golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWP
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
-golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
-golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
-golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
-golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
-golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
-golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
+golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
+golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
+golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
-golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
-golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
-golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
-golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
-golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
-golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
+golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
+golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
-golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
+golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y=
-golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
+golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
-golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
-golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
-golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
-golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
-golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
+golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
-google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
-google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
-google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
-google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
-google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
-google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
-google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
-google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
-google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
-google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
-google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
-google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
-google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
-google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
-google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
-google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
-google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
+google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
-gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI=
-gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gorm.io/driver/postgres v1.3.9 h1:lWGiVt5CijhQAg0PWB7Od1RNcBw/jS4d2cAScBcSDXg=
-gorm.io/driver/postgres v1.3.9/go.mod h1:qw/FeqjxmYqW5dBcYNBsnhQULIApQdk7YuuDPktVi1U=
-gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
-gorm.io/gorm v1.23.7/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
-gorm.io/gorm v1.23.8 h1:h8sGJ+biDgBA1AD1Ha9gFCx7h8npU7AsLdlkX0n2TpE=
-gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+gorm.io/driver/postgres v1.5.4 h1:Iyrp9Meh3GmbSuyIAGyjkN+n9K+GHX9b9MqsTL4EJCo=
+gorm.io/driver/postgres v1.5.4/go.mod h1:Bgo89+h0CRcdA33Y6frlaHHVuTdOf87pmyzwW9C/BH0=
+gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
+gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
+gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-moul.io/zapgorm2 v1.1.3 h1:PP9224dk0l2f56KE1anr3vcS2HzKV9PusKUE6UT9ncI=
-moul.io/zapgorm2 v1.1.3/go.mod h1:HTO6sXgHhQD0s2D9HA4xcnJ+qxFRFwsCUxIeFDnKtq0=
-rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
-rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+moul.io/zapgorm2 v1.3.0 h1:+CzUTMIcnafd0d/BvBce8T4uPn6DQnpIrz64cyixlkk=
+moul.io/zapgorm2 v1.3.0/go.mod h1:nPVy6U9goFKHR4s+zfSo1xVFaoU7Qgd5DoCdOfzoCqs=
+nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/internal/api/consumer/handler.go b/internal/api/consumer/handler.go
index 3c8b840..8daeac4 100644
--- a/internal/api/consumer/handler.go
+++ b/internal/api/consumer/handler.go
@@ -2,11 +2,12 @@ package consumer
import (
"context"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/service/problem"
- "github.com/WHUPRJ/woj-server/internal/service/status"
- "github.com/WHUPRJ/woj-server/internal/service/task"
+ "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/consumer/problemUpdate.go b/internal/api/consumer/problemUpdate.go
index d5187d0..f7c6cbb 100644
--- a/internal/api/consumer/problemUpdate.go
+++ b/internal/api/consumer/problemUpdate.go
@@ -4,8 +4,8 @@ import (
"context"
"encoding/json"
"fmt"
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"github.com/hibiken/asynq"
"github.com/jackc/pgtype"
"go.uber.org/zap"
diff --git a/internal/api/consumer/submitUpdate.go b/internal/api/consumer/submitUpdate.go
index 8b9c64b..9c2d4c5 100644
--- a/internal/api/consumer/submitUpdate.go
+++ b/internal/api/consumer/submitUpdate.go
@@ -4,9 +4,9 @@ import (
"context"
"encoding/json"
"fmt"
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
- "github.com/WHUPRJ/woj-server/internal/service/status"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/service/status"
"github.com/hibiken/asynq"
"go.uber.org/zap"
)
diff --git a/internal/api/debug/handler.go b/internal/api/debug/handler.go
index 23ae47e..5d930e2 100644
--- a/internal/api/debug/handler.go
+++ b/internal/api/debug/handler.go
@@ -1,22 +1,25 @@
package debug
import (
- "github.com/WHUPRJ/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"
)
var _ Handler = (*handler)(nil)
type Handler interface {
- randomString(c *gin.Context)
+ RandomString(c *gin.Context)
}
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/debug/random.go b/internal/api/debug/random.go
index 25b839f..f174930 100644
--- a/internal/api/debug/random.go
+++ b/internal/api/debug/random.go
@@ -1,20 +1,20 @@
package debug
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/pkg/utils"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/pkg/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
-// randomString
-// @Summary random string
-// @Description generate random string with length = 32
+// RandomString
+// @Summary generate random string
+// @Description Generate random string with length = 32.
// @Tags debug
// @Produce json
-// @Response 200 {object} e.Response "random string"
+// @Response 200 {object} e.Response[string] "random string"
// @Router /debug/random [get]
-func (h *handler) randomString(c *gin.Context) {
+func (h *handler) RandomString(c *gin.Context) {
str := utils.RandomString(32)
h.log.Info("random string", zap.String("str", str))
e.Pong(c, e.Success, str)
diff --git a/internal/api/problem/createVersion.go b/internal/api/problem/create_version.go
similarity index 54%
rename from internal/api/problem/createVersion.go
rename to internal/api/problem/create_version.go
index 2456abc..6ab028e 100644
--- a/internal/api/problem/createVersion.go
+++ b/internal/api/problem/create_version.go
@@ -1,10 +1,9 @@
package problem
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/model"
- "github.com/WHUPRJ/woj-server/internal/service/problem"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/service/problem"
"github.com/gin-gonic/gin"
)
@@ -14,54 +13,61 @@ type createVersionRequest struct {
}
// CreateVersion
-// @Summary create a problem version
-// @Description create a problem version
+// @Summary [admin] create a problem version
+// @Description Create a problem version associated with `pid`.
+// @Tags problem,admin
// @Accept application/x-www-form-urlencoded
// @Produce json
// @Param pid formData int true "problem id"
-// @Param storage_key formData string true "storage key"
-// @Response 200 {object} e.Response ""
+// @Param storage_key formData string true "storage key, zip file containing problem data"
+// @Response 200 {object} e.Response[any] "nothing"
// @Security Authentication
// @Router /v1/problem/create_version [post]
func (h *handler) CreateVersion(c *gin.Context) {
claim, exist := c.Get("claim")
if !exist {
- e.Pong(c, e.UserUnauthenticated, nil)
+ e.Pong[any](c, e.UserUnauthenticated, nil)
return
}
- // uid := claim.(*global.Claim).UID
-
- role := claim.(*global.Claim).Role
req := new(createVersionRequest)
-
if err := c.ShouldBind(req); err != nil {
e.Pong(c, e.InvalidParameter, err.Error())
return
}
- // guest can not submit
+ // only admin can create problem version
+ role := claim.(*model.Claim).Role
if role < model.RoleAdmin {
- e.Pong(c, e.UserUnauthorized, nil)
+ e.Pong[any](c, e.UserUnauthorized, nil)
return
}
- // TODO: check pid exist
+ // make sure problem exists
+ _, status := h.problemService.Query(req.ProblemID, false, false)
+ if status != e.Success {
+ e.Pong[any](c, status, nil)
+ return
+ }
+ // create problem version
createVersionData := &problem.CreateVersionData{
ProblemID: req.ProblemID,
StorageKey: req.StorageKey,
}
pv, status := h.problemService.CreateVersion(createVersionData)
if status != e.Success {
- e.Pong(c, status, nil)
+ e.Pong[any](c, status, nil)
return
}
+ // enqueue task: runner build problem
payload := &model.ProblemBuildPayload{
ProblemVersionID: pv.ID,
StorageKey: pv.StorageKey,
}
_, status = h.taskService.ProblemBuild(payload)
- e.Pong(c, status, nil)
+ e.Pong[any](c, status, nil)
+
+ // TODO: if failed, delete problem version
}
diff --git a/internal/api/problem/details.go b/internal/api/problem/details.go
index 3df21e6..337e287 100644
--- a/internal/api/problem/details.go
+++ b/internal/api/problem/details.go
@@ -1,9 +1,8 @@
package problem
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"github.com/gin-gonic/gin"
)
@@ -11,37 +10,44 @@ type detailsRequest struct {
Pid uint `form:"pid"`
}
+type problemDetailsResponse struct {
+ Problem *model.Problem `json:"problem"`
+ Context interface{} `json:"context"`
+}
+
// Details
// @Summary get details of a problem
-// @Description get details of a problem
+// @Description Get details of a problem.
+// @Tags problem
// @Accept application/x-www-form-urlencoded
// @Produce json
// @Param pid formData int true "problem id"
-// @Response 200 {object} e.Response "problem details"
+// @Response 200 {object} e.Response[problemDetailsResponse] "problem details"
// @Router /v1/problem/details [post]
func (h *handler) Details(c *gin.Context) {
req := new(detailsRequest)
-
if err := c.ShouldBind(req); err != nil {
e.Pong(c, e.InvalidParameter, err.Error())
return
}
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 {
- e.Pong(c, status, nil)
+ e.Pong[any](c, status, nil)
return
}
pv, status := h.problemService.QueryLatestVersion(req.Pid)
if status != e.Success {
- e.Pong(c, status, nil)
+ e.Pong[any](c, status, nil)
+ return
}
- e.Pong(c, e.Success, gin.H{
- "problem": p,
- "context": pv.Context.Get(),
+
+ e.Pong(c, e.Success, problemDetailsResponse{
+ Problem: p,
+ Context: pv.Context.Get(),
})
}
diff --git a/internal/api/problem/handler.go b/internal/api/problem/handler.go
index 53bdda2..797b1b1 100644
--- a/internal/api/problem/handler.go
+++ b/internal/api/problem/handler.go
@@ -1,11 +1,13 @@
package problem
import (
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/service/problem"
- "github.com/WHUPRJ/woj-server/internal/service/storage"
- "github.com/WHUPRJ/woj-server/internal/service/task"
+ "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"
)
@@ -16,28 +18,29 @@ type Handler interface {
Search(c *gin.Context)
Update(c *gin.Context)
Upload(c *gin.Context)
+ CreateVersion(c *gin.Context)
}
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/search.go b/internal/api/problem/search.go
index 9baeab1..1b64f69 100644
--- a/internal/api/problem/search.go
+++ b/internal/api/problem/search.go
@@ -1,7 +1,8 @@
package problem
import (
- "github.com/WHUPRJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ _ "git.0x7f.app/WOJ/woj-server/internal/model" // swag requires this
"github.com/gin-gonic/gin"
)
@@ -10,16 +11,16 @@ type searchRequest struct {
}
// Search
-// @Summary get detail of a problem
-// @Description get detail of a problem
+// @Summary search for problems
+// @Description Search for problems based on keywords. If the keyword is empty, return all problems.
+// @Tags problem
// @Accept application/x-www-form-urlencoded
// @Produce json
-// @Param search formData string false "word search"
-// @Response 200 {object} e.Response "problemset"
+// @Param search formData string false "keyword"
+// @Response 200 {object} e.Response[[]model.Problem] "problems found"
// @Router /v1/problem/search [post]
func (h *handler) Search(c *gin.Context) {
req := new(searchRequest)
-
if err := c.ShouldBind(req); err != nil {
e.Pong(c, e.InvalidParameter, err.Error())
return
diff --git a/internal/api/problem/update.go b/internal/api/problem/update.go
index 6905b16..8909108 100644
--- a/internal/api/problem/update.go
+++ b/internal/api/problem/update.go
@@ -1,10 +1,10 @@
package problem
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/model"
- "github.com/WHUPRJ/woj-server/internal/service/problem"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/service/problem"
+ "git.0x7f.app/WOJ/woj-server/pkg/utils"
"github.com/gin-gonic/gin"
)
@@ -16,28 +16,31 @@ type updateRequest struct {
}
// Update
-// @Summary create or update a problem
-// @Description create or update a problem
+// @Summary [admin] create or update a problem
+// @Description Create or update a problem.
+// @Tags problem,admin
// @Accept application/x-www-form-urlencoded
// @Produce json
// @Param pid formData int false "problem id, 0 for create"
// @Param title formData string true "title"
// @Param statement formData string true "statement"
// @Param is_enabled formData bool false "is enabled"
-// @Response 200 {object} e.Response "problem info without provider information"
+// @Response 200 {object} e.Response[model.Problem] "problem info without provider information"
// @Security Authentication
// @Router /v1/problem/update [post]
func (h *handler) Update(c *gin.Context) {
claim, exist := c.Get("claim")
if !exist {
- e.Pong(c, e.UserUnauthenticated, nil)
+ e.Pong[any](c, e.UserUnauthenticated, nil)
return
}
- uid := claim.(*global.Claim).UID
- role := claim.(*global.Claim).Role
+ uid := claim.(*model.Claim).UID
+ role := claim.(*model.Claim).Role
+
+ // only admin can modify problem
if role < model.RoleAdmin {
- e.Pong(c, e.UserUnauthorized, nil)
+ e.Pong[any](c, e.UserUnauthorized, nil)
return
}
@@ -48,6 +51,7 @@ func (h *handler) Update(c *gin.Context) {
}
if req.Pid == 0 {
+ // create problem
createData := &problem.CreateData{
Title: req.Title,
Statement: req.Statement,
@@ -58,18 +62,24 @@ func (h *handler) Update(c *gin.Context) {
e.Pong(c, status, p)
return
} else {
+ // update problem
+
+ // check if problem exists
p, status := h.problemService.Query(req.Pid, true, false)
if status != e.Success {
- e.Pong(c, status, nil)
- return
- }
- if p.ProviderID != uid {
- e.Pong(c, e.UserUnauthorized, nil)
+ e.Pong[any](c, status, nil)
return
}
- p.Title = req.Title
- p.Statement = req.Statement
+ // check if user is the provider of the problem
+ if p.ProviderID != uid {
+ e.Pong[any](c, e.UserUnauthorized, nil)
+ return
+ }
+
+ // update problem
+ p.Title = utils.If(req.Title != "", req.Title, p.Title)
+ p.Statement = utils.If(req.Statement != "", req.Statement, p.Statement)
p.IsEnabled = req.IsEnabled
p, status = h.problemService.Update(p)
diff --git a/internal/api/problem/upload.go b/internal/api/problem/upload.go
index a83edc3..20d2333 100644
--- a/internal/api/problem/upload.go
+++ b/internal/api/problem/upload.go
@@ -1,44 +1,47 @@
package problem
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/model"
- "github.com/WHUPRJ/woj-server/pkg/utils"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/pkg/utils"
"github.com/gin-gonic/gin"
"time"
)
+type uploadResponse struct {
+ Key string `json:"key"`
+ URL string `json:"url"`
+}
+
// Upload
-// @Summary get upload url
-// @Description get upload url
+// @Summary [admin] get upload url
+// @Description Retrieve a pre-signed upload URL from the object storage
+// @Tags problem,admin
// @Produce json
-// @Response 200 {object} e.Response "upload url and key"
+// @Response 200 {object} e.Response[uploadResponse] "upload url and key"
// @Security Authentication
// @Router /v1/problem/upload [post]
func (h *handler) Upload(c *gin.Context) {
claim, exist := c.Get("claim")
if !exist {
- e.Pong(c, e.UserUnauthenticated, nil)
+ e.Pong[any](c, e.UserUnauthenticated, nil)
return
}
- role := claim.(*global.Claim).Role
+ // only admin can upload
+ role := claim.(*model.Claim).Role
if role < model.RoleAdmin {
- e.Pong(c, e.UserUnauthorized, nil)
+ e.Pong[any](c, e.UserUnauthorized, nil)
return
}
+ // generate random key
key := utils.RandomString(16)
url, status := h.storageService.Upload(key, time.Second*60*60)
-
if status != e.Success {
- e.Pong(c, status, nil)
+ e.Pong[any](c, status, nil)
return
}
- e.Pong(c, e.Success, gin.H{
- "key": key,
- "url": url,
- })
+ e.Pong(c, e.Success, uploadResponse{Key: key, URL: url})
}
diff --git a/internal/api/runner/build.go b/internal/api/runner/build.go
index 58d9463..d6e0227 100644
--- a/internal/api/runner/build.go
+++ b/internal/api/runner/build.go
@@ -4,8 +4,8 @@ import (
"context"
"encoding/json"
"fmt"
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"github.com/hibiken/asynq"
"go.uber.org/zap"
"time"
@@ -31,6 +31,7 @@ func (h *handler) Build(_ context.Context, t *asynq.Task) error {
}
for i := range config.Languages {
+ // do not store in db
config.Languages[i].Type = ""
config.Languages[i].Script = ""
config.Languages[i].Cmp = ""
diff --git a/internal/api/runner/handler.go b/internal/api/runner/handler.go
index ad89428..009c151 100644
--- a/internal/api/runner/handler.go
+++ b/internal/api/runner/handler.go
@@ -3,12 +3,13 @@ package runner
import (
"context"
"errors"
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/service/runner"
- "github.com/WHUPRJ/woj-server/internal/service/storage"
- "github.com/WHUPRJ/woj-server/internal/service/task"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "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/runner/judge.go b/internal/api/runner/judge.go
index 1a8e563..fe33486 100644
--- a/internal/api/runner/judge.go
+++ b/internal/api/runner/judge.go
@@ -4,10 +4,10 @@ import (
"context"
"encoding/json"
"fmt"
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
- "github.com/WHUPRJ/woj-server/internal/service/runner"
- "github.com/WHUPRJ/woj-server/pkg/utils"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/service/runner"
+ "git.0x7f.app/WOJ/woj-server/pkg/utils"
"github.com/hibiken/asynq"
"go.uber.org/zap"
"path/filepath"
@@ -63,7 +63,7 @@ func (h *handler) Judge(_ context.Context, t *asynq.Task) error {
// 5. run and judge
result, point, status := h.runnerService.RunAndJudge(p.ProblemVersionID, user, p.Submission.Language, &config)
- return utils.If(status != e.Success, e.InternalError, e.Success).(e.Status), point, result
+ return utils.If(status != e.Success, e.InternalError, e.Success), point, result
}()
h.taskService.SubmitUpdate(&model.SubmitUpdatePayload{
diff --git a/internal/api/status/handler.go b/internal/api/status/handler.go
index 1b85a77..42b9aff 100644
--- a/internal/api/status/handler.go
+++ b/internal/api/status/handler.go
@@ -1,9 +1,12 @@
package status
import (
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/service/status"
+ "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/service/submission"
+ "git.0x7f.app/WOJ/woj-server/internal/web/jwt"
"github.com/gin-gonic/gin"
+ "github.com/samber/do"
"go.uber.org/zap"
)
@@ -11,22 +14,26 @@ var _ Handler = (*handler)(nil)
type Handler interface {
Query(c *gin.Context)
+ QueryBySubmissionID(c *gin.Context)
QueryByProblemVersion(c *gin.Context)
}
type handler struct {
- log *zap.Logger
- statusService status.Service
- jwtService global.JwtService
+ log *zap.Logger
+ statusService status.Service
+ submissionService submission.Service
+ 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"),
+ submissionService: do.MustInvoke[submission.Service](i),
+ 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.jwtService.Handler(true), app.Query)
+ rg.POST("/query/submission", app.jwtService.Handler(true), app.QueryBySubmissionID)
+ rg.POST("/query/version", app.jwtService.Handler(true), app.QueryByProblemVersion)
}
diff --git a/internal/api/status/query.go b/internal/api/status/query.go
index 13faf0f..a686269 100644
--- a/internal/api/status/query.go
+++ b/internal/api/status/query.go
@@ -1,31 +1,75 @@
package status
import (
- "github.com/WHUPRJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/pkg/utils"
"github.com/gin-gonic/gin"
)
type queryRequest struct {
- SubmissionID uint `form:"sid" binding:"required"`
+ Pid uint `form:"pid"`
+ Uid uint `form:"uid"`
+ Offset int `form:"offset"`
+ Limit int `form:"limit" binding:"required"`
+}
+
+type queryResponse struct {
+ Submission model.Submission `json:"submission"`
+ Point int32 `json:"point"`
}
// Query
-// @Summary query submissions by via submission id
-// @Description query submissions by via submission id
+// @Summary query status via problem id or user id
+// @Description Batch query judgement status based on either the question or user.
+// @Tags status
// @Accept application/x-www-form-urlencoded
// @Produce json
-// @Param sid formData uint true "submission id"
-// @Response 200 {object} e.Response "model.status"
+// @Param pid formData uint false "problem id"
+// @Param uid formData uint false "user id"
+// @Param offset formData int false "start position"
+// @Param limit formData int true "limit number of records"
+// @Response 200 {object} e.Response[[]queryResponse] "queryResponse"
// @Router /v1/status/query [post]
func (h *handler) Query(c *gin.Context) {
- req := new(queryRequest)
+ claim, exist := c.Get("claim")
+ if !exist {
+ e.Pong[any](c, e.UserUnauthenticated, nil)
+ return
+ }
+ req := new(queryRequest)
if err := c.ShouldBind(req); err != nil {
e.Pong(c, e.InvalidParameter, err.Error())
return
}
- status, eStatus := h.statusService.Query(req.SubmissionID, true)
+ if req.Pid == 0 && req.Uid == 0 {
+ e.Pong[any](c, e.InvalidParameter, nil)
+ return
+ }
- e.Pong(c, eStatus, status)
+ submissions, status := h.submissionService.Query(req.Pid, req.Uid, req.Offset, req.Limit)
+
+ uid := claim.(*model.Claim).UID
+ role := claim.(*model.Claim).Role
+ var response []*queryResponse
+
+ for _, submission := range submissions {
+ cur, _ := h.statusService.Query(submission.ID, false)
+ point := utils.If(cur == nil, -1, cur.Point)
+ resp := &queryResponse{
+ Submission: *submission,
+ Point: point,
+ }
+
+ if role < model.RoleAdmin || uid != submission.UserID {
+ // strip out code
+ resp.Submission.Code = ""
+ }
+
+ response = append(response, resp)
+ }
+
+ e.Pong(c, status, response)
}
diff --git a/internal/api/status/queryByVersion.go b/internal/api/status/queryByVersion.go
deleted file mode 100644
index 3a738c0..0000000
--- a/internal/api/status/queryByVersion.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package status
-
-import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/model"
- "github.com/gin-gonic/gin"
-)
-
-type queryByVersionRequest struct {
- ProblemVersionID uint `form:"pvid" binding:"required"`
- Offset int `form:"offset"`
- Limit int `form:"limit" binding:"required"`
-}
-
-// QueryByProblemVersion
-// @Summary query submissions by problem version (admin only)
-// @Description query submissions by problem version (admin only)
-// @Accept application/x-www-form-urlencoded
-// @Produce json
-// @Param pvid formData uint true "problem version id"
-// @Param offset formData int true "start position"
-// @Param limit formData int true "limit number of records"
-// @Response 200 {object} e.Response "[]*model.status"
-// @Router /v1/status/query/problem_version [post]
-func (h *handler) QueryByProblemVersion(c *gin.Context) {
-
- claim, exist := c.Get("claim")
- if !exist {
- e.Pong(c, e.UserUnauthenticated, nil)
- return
- }
-
- role := claim.(*global.Claim).Role
-
- req := new(queryByVersionRequest)
-
- if err := c.ShouldBind(req); err != nil {
- e.Pong(c, e.InvalidParameter, err.Error())
- return
- }
-
- if role < model.RoleAdmin {
- e.Pong(c, e.UserUnauthorized, nil)
- return
- }
-
- statuses, eStatus := h.statusService.QueryByVersion(req.ProblemVersionID, req.Offset, req.Limit)
-
- e.Pong(c, eStatus, statuses)
-}
diff --git a/internal/api/status/query_one.go b/internal/api/status/query_one.go
new file mode 100644
index 0000000..b0e9834
--- /dev/null
+++ b/internal/api/status/query_one.go
@@ -0,0 +1,50 @@
+package status
+
+import (
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
+ "github.com/gin-gonic/gin"
+)
+
+type queryOneRequest struct {
+ SubmissionID uint `form:"sid" binding:"required"`
+}
+
+// QueryBySubmissionID
+// @Summary query status via submission id
+// @Description Query the detailed results of the judgement based on the submission ID.
+// @Tags status
+// @Accept application/x-www-form-urlencoded
+// @Produce json
+// @Param sid formData uint true "submission id"
+// @Response 200 {object} e.Response[model.Status] "submission status"
+// @Router /v1/status/query/submission [post]
+func (h *handler) QueryBySubmissionID(c *gin.Context) {
+ claim, exist := c.Get("claim")
+ if !exist {
+ e.Pong[any](c, e.UserUnauthenticated, nil)
+ return
+ }
+
+ req := new(queryOneRequest)
+ if err := c.ShouldBind(req); err != nil {
+ e.Pong(c, e.InvalidParameter, err.Error())
+ return
+ }
+
+ // query status
+ submitStatus, status := h.statusService.Query(req.SubmissionID, true)
+
+ // check permission
+ role := claim.(*model.Claim).Role
+ if role >= model.RoleAdmin || submitStatus.Submission.UserID == claim.(*model.Claim).UID {
+ // full status
+ e.Pong(c, status, submitStatus)
+ return
+ } else {
+ // strip out code
+ submitStatus.Submission.Code = ""
+ e.Pong(c, status, submitStatus)
+ return
+ }
+}
diff --git a/internal/api/status/query_version.go b/internal/api/status/query_version.go
new file mode 100644
index 0000000..73e1aab
--- /dev/null
+++ b/internal/api/status/query_version.go
@@ -0,0 +1,49 @@
+package status
+
+import (
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
+ "github.com/gin-gonic/gin"
+)
+
+type queryByVersionRequest struct {
+ ProblemVersionID uint `form:"pvid" binding:"required"`
+ Offset int `form:"offset"`
+ Limit int `form:"limit" binding:"required"`
+}
+
+// QueryByProblemVersion
+// @Summary [admin] query status by problem version
+// @Description Retrieve all judgement results corresponding to the problem version.
+// @Tags status,admin
+// @Accept application/x-www-form-urlencoded
+// @Produce json
+// @Param pvid formData uint true "problem version"
+// @Param offset formData int false "start position"
+// @Param limit formData int true "max number of results"
+// @Response 200 {object} e.Response[[]model.Status] "submission status array"
+// @Security Authentication
+// @Router /v1/status/query/version [post]
+func (h *handler) QueryByProblemVersion(c *gin.Context) {
+ claim, exist := c.Get("claim")
+ if !exist {
+ e.Pong[any](c, e.UserUnauthenticated, nil)
+ return
+ }
+
+ req := new(queryByVersionRequest)
+ if err := c.ShouldBind(req); err != nil {
+ e.Pong(c, e.InvalidParameter, err.Error())
+ return
+ }
+
+ // check permission
+ role := claim.(*model.Claim).Role
+ if role < model.RoleAdmin {
+ e.Pong[any](c, e.UserUnauthorized, nil)
+ return
+ }
+
+ submitStatus, status := h.statusService.QueryByVersion(req.ProblemVersionID, req.Offset, req.Limit)
+ e.Pong(c, status, submitStatus)
+}
diff --git a/internal/api/submission/create.go b/internal/api/submission/create.go
index 5743285..70dcb8a 100644
--- a/internal/api/submission/create.go
+++ b/internal/api/submission/create.go
@@ -1,10 +1,9 @@
package submission
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/model"
- "github.com/WHUPRJ/woj-server/internal/service/submission"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/service/submission"
"github.com/gin-gonic/gin"
)
@@ -15,63 +14,66 @@ type createRequest struct {
}
// Create
-// @Summary create a submission
-// @Description create a submission
+// @Summary submit for judgement
+// @Description Submit the code for judgement.
+// @Tags submission
// @Accept application/x-www-form-urlencoded
// @Produce json
// @Param pid formData int true "problem id"
// @Param language formData string true "language"
// @Param code formData string true "code"
-// @Response 200 {object} e.Response ""
+// @Response 200 {object} e.Response[uint] "submission id"
// @Security Authentication
// @Router /v1/submission/create [post]
func (h *handler) Create(c *gin.Context) {
claim, exist := c.Get("claim")
if !exist {
- e.Pong(c, e.UserUnauthenticated, nil)
+ e.Pong[any](c, e.UserUnauthenticated, nil)
return
}
- uid := claim.(*global.Claim).UID
+ uid := claim.(*model.Claim).UID
+ role := claim.(*model.Claim).Role
+
+ // guest can not submit
+ if role < model.RoleGeneral {
+ e.Pong[any](c, e.UserUnauthorized, nil)
+ return
+ }
- role := claim.(*global.Claim).Role
req := new(createRequest)
-
if err := c.ShouldBind(req); err != nil {
e.Pong(c, e.InvalidParameter, err.Error())
return
}
- // guest can not submit
- if role < model.RoleGeneral {
- e.Pong(c, e.UserUnauthorized, nil)
- return
- }
-
+ // create submission
createData := &submission.CreateData{
ProblemID: req.Pid,
UserID: uid,
Language: req.Language,
Code: req.Code,
}
- s, status := h.submissionService.Create(createData)
+ res, status := h.submissionService.Create(createData)
if status != e.Success {
- e.Pong(c, status, nil)
+ e.Pong[any](c, status, nil)
return
}
+ // query latest version
pv, status := h.problemService.QueryLatestVersion(req.Pid)
if status != e.Success {
- e.Pong(c, status, nil)
+ e.Pong[any](c, status, nil)
return
}
+ // submit judge
payload := &model.SubmitJudgePayload{
ProblemVersionID: pv.ID,
StorageKey: pv.StorageKey,
- Submission: *s,
+ Submission: *res,
}
_, status = h.taskService.SubmitJudge(payload)
- e.Pong(c, status, nil)
+ e.Pong[any](c, status, res.ID)
}
diff --git a/internal/api/submission/handler.go b/internal/api/submission/handler.go
index a23aff5..68e2860 100644
--- a/internal/api/submission/handler.go
+++ b/internal/api/submission/handler.go
@@ -1,12 +1,14 @@
package submission
import (
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/service/problem"
- "github.com/WHUPRJ/woj-server/internal/service/status"
- "github.com/WHUPRJ/woj-server/internal/service/submission"
- "github.com/WHUPRJ/woj-server/internal/service/task"
+ "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"
)
@@ -14,30 +16,28 @@ var _ Handler = (*handler)(nil)
type Handler interface {
Create(c *gin.Context)
- Query(c *gin.Context)
Rejudge(c *gin.Context)
}
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("/rejudge", app.jwtService.Handler(true), app.Rejudge)
}
diff --git a/internal/api/submission/query.go b/internal/api/submission/query.go
deleted file mode 100644
index e3e6126..0000000
--- a/internal/api/submission/query.go
+++ /dev/null
@@ -1,71 +0,0 @@
-package submission
-
-import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
- "github.com/gin-gonic/gin"
-)
-
-type queryRequest struct {
- Pid uint `form:"pid"`
- Uid uint `form:"uid"`
- Offset int `form:"offset"`
- Limit int `form:"limit" binding:"required"`
-}
-
-type queryResponse struct {
- Submission model.Submission `json:"submission"`
- Point int32 `json:"point"`
-}
-
-// Query
-// @Summary Query submissions
-// @Description Query submissions
-// @Accept application/x-www-form-urlencoded
-// @Produce json
-// @Param pid formData uint true "problem id"
-// @Param uid formData uint true "user id"
-// @Param offset formData int true "start position"
-// @Param limit formData int true "limit number of records"
-// @Response 200 {object} e.Response "queryResponse"
-// @Router /v1/submission/query [post]
-
-func (h *handler) Query(c *gin.Context) {
- req := new(queryRequest)
-
- if err := c.ShouldBind(req); err != nil {
- e.Pong(c, e.InvalidParameter, err.Error())
- return
- }
-
- if req.Pid == 0 && req.Uid == 0 {
- e.Pong(c, e.InvalidParameter, nil)
- return
- }
-
- submissions, status := h.submissionService.Query(req.Pid, req.Uid, req.Offset, req.Limit)
-
- var response []*queryResponse
-
- for _, submission := range submissions {
- currentStatus, _ := h.statusService.Query(submission.ID, false)
- var currentPoint int32
-
- if currentStatus == nil {
- currentPoint = -1
- } else {
- currentPoint = currentStatus.Point
- }
-
- newResponse := &queryResponse{
- Submission: *submission,
- Point: currentPoint,
- }
-
- newResponse.Submission.Code = ""
-
- response = append(response, newResponse)
- }
-
- e.Pong(c, status, response)
-}
diff --git a/internal/api/submission/rejudge.go b/internal/api/submission/rejudge.go
index 7d87250..08a7df5 100644
--- a/internal/api/submission/rejudge.go
+++ b/internal/api/submission/rejudge.go
@@ -1,9 +1,8 @@
package submission
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"github.com/gin-gonic/gin"
)
@@ -12,52 +11,55 @@ type rejudgeRequest struct {
}
// Rejudge
-// @Summary rejudge a submission
-// @Description rejudge a submission
+// @Summary [admin] rejudge a specific submission
+// @Description rejudge a specific submission
+// @Tags submission,admin
// @Accept application/x-www-form-urlencoded
// @Produce json
-// @Param sid formData int true "submission id"
-// @Response 200 {object} e.Response ""
+// @Param sid formData int true "submission id"
+// @Response 200 {object} e.Response[any] "nothing"
// @Security Authentication
// @Router /v1/submission/rejudge [post]
func (h *handler) Rejudge(c *gin.Context) {
claim, exist := c.Get("claim")
if !exist {
- e.Pong(c, e.UserUnauthenticated, nil)
+ e.Pong[any](c, e.UserUnauthenticated, nil)
return
}
- role := claim.(*global.Claim).Role
req := new(rejudgeRequest)
-
if err := c.ShouldBind(req); err != nil {
e.Pong(c, e.InvalidParameter, err.Error())
return
}
// only admin can rejudge
+ role := claim.(*model.Claim).Role
if role < model.RoleAdmin {
- e.Pong(c, e.UserUnauthorized, nil)
+ e.Pong[any](c, e.UserUnauthorized, nil)
return
}
+ // query submission
s, status := h.submissionService.QueryBySid(req.Sid, false)
if status != e.Success {
- e.Pong(c, status, nil)
+ e.Pong[any](c, status, nil)
return
}
+ // query latest problem version
pv, status := h.problemService.QueryLatestVersion(s.ProblemID)
if status != e.Success {
- e.Pong(c, status, nil)
+ e.Pong[any](c, status, nil)
return
}
+ // submit judge
_, status = h.taskService.SubmitJudge(&model.SubmitJudgePayload{
ProblemVersionID: pv.ID,
StorageKey: pv.StorageKey,
Submission: *s,
})
- e.Pong(c, status, nil)
+ e.Pong[any](c, status, nil)
}
diff --git a/internal/api/user/create.go b/internal/api/user/create.go
index ba224e1..e8d5148 100644
--- a/internal/api/user/create.go
+++ b/internal/api/user/create.go
@@ -1,9 +1,9 @@
package user
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/service/user"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/service/user"
"github.com/gin-gonic/gin"
)
@@ -16,21 +16,22 @@ type createRequest struct {
// Create
// @Summary create a new user
// @Description create a new user
+// @Tags user
// @Accept application/x-www-form-urlencoded
// @Produce json
// @Param username formData string true "username"
// @Param nickname formData string true "nickname"
// @Param password formData string true "password"
-// @Response 200 {object} e.Response "jwt token"
+// @Response 200 {object} e.Response[string] "jwt token"
// @Router /v1/user/create [post]
func (h *handler) Create(c *gin.Context) {
req := new(createRequest)
-
if err := c.ShouldBind(req); err != nil {
e.Pong(c, e.InvalidParameter, err.Error())
return
}
+ // create user
createData := &user.CreateData{
UserName: req.UserName,
Password: req.Password,
@@ -38,17 +39,19 @@ func (h *handler) Create(c *gin.Context) {
}
u, status := h.userService.Create(createData)
if status != e.Success {
- e.Pong(c, status, nil)
+ e.Pong[any](c, status, nil)
return
}
+ // update version in cache
version, status := h.userService.IncrVersion(u.ID)
if status != e.Success {
- e.Pong(c, status, nil)
+ e.Pong[any](c, status, nil)
return
}
- claim := &global.Claim{
+ // sign jwt token
+ 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 70785b1..a686c28 100644
--- a/internal/api/user/handler.go
+++ b/internal/api/user/handler.go
@@ -1,9 +1,11 @@
package user
import (
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/service/user"
+ "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 fdd3243..42c6ed8 100644
--- a/internal/api/user/login.go
+++ b/internal/api/user/login.go
@@ -1,9 +1,9 @@
package user
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/service/user"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/service/user"
"github.com/gin-gonic/gin"
)
@@ -12,20 +12,25 @@ type loginRequest struct {
Password string `form:"password" binding:"required"`
}
+type loginResponse struct {
+ Token string `json:"token"`
+ NickName string `json:"nickname"`
+}
+
// Login
// @Summary login
// @Description login and return token
+// @Tags user
// @Accept application/x-www-form-urlencoded
// @Produce json
// @Param username formData string true "username"
// @Param password formData string true "password"
-// @Response 200 {object} e.Response "jwt token and user nickname"
+// @Response 200 {object} e.Response[loginResponse] "jwt token and user's nickname"
// @Router /v1/user/login [post]
func (h *handler) Login(c *gin.Context) {
req := new(loginRequest)
-
if err := c.ShouldBind(req); err != nil {
- e.Pong(c, e.InvalidParameter, nil)
+ e.Pong(c, e.InvalidParameter, err.Error())
return
}
@@ -36,24 +41,21 @@ func (h *handler) Login(c *gin.Context) {
}
u, status := h.userService.Login(loginData)
if status != e.Success {
- e.Pong(c, status, nil)
+ e.Pong[any](c, status, nil)
return
}
// sign and return token
version, status := h.userService.IncrVersion(u.ID)
if status != e.Success {
- e.Pong(c, status, nil)
+ e.Pong[any](c, status, nil)
return
}
- claim := &global.Claim{
+ claim := &model.Claim{
UID: u.ID,
Role: u.Role,
Version: version,
}
token, status := h.jwtService.SignClaim(claim)
- e.Pong(c, status, gin.H{
- "token": token,
- "nickname": u.NickName,
- })
+ e.Pong(c, status, loginResponse{Token: token, NickName: u.NickName})
}
diff --git a/internal/api/user/logout.go b/internal/api/user/logout.go
index f9a288c..0132c1f 100644
--- a/internal/api/user/logout.go
+++ b/internal/api/user/logout.go
@@ -1,26 +1,27 @@
package user
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"github.com/gin-gonic/gin"
)
// Logout
// @Summary logout
// @Description logout
+// @Tags user
// @Accept application/x-www-form-urlencoded
// @Produce json
-// @Response 200 {object} e.Response "nil"
+// @Response 200 {object} e.Response[any] "nothing"
// @Security Authentication
// @Router /v1/user/logout [post]
func (h *handler) Logout(c *gin.Context) {
claim, exist := c.Get("claim")
if !exist {
- e.Pong(c, e.UserUnauthenticated, nil)
+ e.Pong[any](c, e.UserUnauthenticated, nil)
return
}
- _, status := h.userService.IncrVersion(claim.(*global.Claim).UID)
- e.Pong(c, status, nil)
+ _, status := h.userService.IncrVersion(claim.(*model.Claim).UID)
+ e.Pong[any](c, status, nil)
}
diff --git a/internal/api/user/profile.go b/internal/api/user/profile.go
index bc23f9e..72e798b 100644
--- a/internal/api/user/profile.go
+++ b/internal/api/user/profile.go
@@ -1,9 +1,9 @@
package user
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/pkg/utils"
"github.com/gin-gonic/gin"
)
@@ -14,41 +14,39 @@ type profileRequest struct {
// Profile
// @Summary profile
// @Description fetch user profile
+// @Tags user
// @Accept application/x-www-form-urlencoded
// @Produce json
// @Param uid formData int false "user id"
-// @Response 200 {object} e.Response "user info"
+// @Response 200 {object} e.Response[model.User] "user info"
// @Security Authentication
// @Router /v1/user/profile [post]
func (h *handler) Profile(c *gin.Context) {
- // TODO: create a new struct for profile (user info & solve info)
-
claim, exist := c.Get("claim")
if !exist {
- e.Pong(c, e.UserUnauthenticated, nil)
+ e.Pong[any](c, e.UserUnauthenticated, nil)
return
}
- uid := claim.(*global.Claim).UID
- role := claim.(*global.Claim).Role
+ uid := claim.(*model.Claim).UID
+ role := claim.(*model.Claim).Role
req := new(profileRequest)
-
if err := c.ShouldBind(req); err != nil {
- e.Pong(c, e.InvalidParameter, nil)
+ e.Pong(c, e.InvalidParameter, err.Error())
return
}
- if req.UID == 0 {
- req.UID = uid
- } else if req.UID != uid && role < model.RoleGeneral {
- e.Pong(c, e.UserUnauthorized, nil)
+ user, status := h.userService.Profile(utils.If(req.UID == 0, uid, req.UID))
+ if status != e.Success {
+ e.Pong[any](c, status, nil)
return
}
- user, status := h.userService.Profile(req.UID)
-
- // TODO: >= admin can see is_enable
+ if role < model.RoleAdmin && user.ID != uid {
+ e.Pong[any](c, e.UserUnauthorized, nil)
+ return
+ }
e.Pong(c, status, user)
}
diff --git a/internal/app/runner/runner.go b/internal/app/runner/runner.go
index a57aa36..4884196 100644
--- a/internal/app/runner/runner.go
+++ b/internal/app/runner/runner.go
@@ -1,18 +1,24 @@
package runner
import (
- "github.com/WHUPRJ/woj-server/internal/api/runner"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/model"
- "github.com/WHUPRJ/woj-server/pkg/utils"
- "github.com/WHUPRJ/woj-server/pkg/zapasynq"
+ "fmt"
+ "git.0x7f.app/WOJ/woj-server/internal/api/runner"
+ "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 +29,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: fmt.Sprintf("%s:%d", conf.Redis.Address, conf.Redis.Port),
+ Password: conf.Redis.Password,
+ DB: conf.Redis.QueueDb,
},
asynq.Config{
- Concurrency: utils.If(runtime.NumCPU() > 1, runtime.NumCPU()-1, 1).(int),
- Logger: zapasynq.New(g.Log),
+ Concurrency: utils.If(runtime.NumCPU() > 1, runtime.NumCPU()-1, 1),
+ 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 c46b94a..163823c 100644
--- a/internal/app/server/server.go
+++ b/internal/app/server/server.go
@@ -2,17 +2,18 @@ package server
import (
"context"
+ "errors"
"fmt"
- "github.com/WHUPRJ/woj-server/internal/api/consumer"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/model"
- "github.com/WHUPRJ/woj-server/internal/repo/postgresql"
- "github.com/WHUPRJ/woj-server/internal/repo/redis"
- "github.com/WHUPRJ/woj-server/internal/router"
- "github.com/WHUPRJ/woj-server/internal/service/jwt"
- "github.com/WHUPRJ/woj-server/pkg/utils"
- "github.com/WHUPRJ/woj-server/pkg/zapasynq"
+ "git.0x7f.app/WOJ/woj-server/internal/api/consumer"
+ "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/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 +23,27 @@ import (
"time"
)
-func RunServer(g *global.Global) error {
- // Setup Database
- g.Db = new(postgresql.Repo)
- g.Db.Setup(g)
+func RunServerMigrate(i *do.Injector) error {
+ slog := do.MustInvoke[log.Service](i).GetLogger("app.server")
- // Setup Redis
- g.Redis = new(redis.Repo)
- g.Redis.Setup(g)
+ // Migrate and shutdown database
+ err := do.MustInvoke[db.Service](i).Close()
+ if err != nil {
+ slog.Warn("Database Close Failed", zap.Error(err))
+ }
- // Setup JWT
- g.Jwt = jwt.NewJwtService(g)
+ return err
+}
+
+func RunServer(i *do.Injector) error {
+ conf := do.MustInvoke[config.Service](i).GetConfig()
+ slog := do.MustInvoke[log.Service](i).GetLogger("app.server")
// 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,
@@ -46,57 +51,57 @@ 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))
+ if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
+ 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: fmt.Sprintf("%s:%d", conf.Redis.Address, conf.Redis.Port),
+ Password: conf.Redis.Password,
+ DB: conf.Redis.QueueDb,
},
asynq.Config{
- Concurrency: utils.If(runtime.NumCPU() > 1, runtime.NumCPU()-1, 1).(int),
- Logger: zapasynq.New(g.Log),
+ Concurrency: utils.If(runtime.NumCPU() > 1, runtime.NumCPU()-1, 1),
+ 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/e/resp.go b/internal/e/resp.go
index 4ea2ae5..cd07d7f 100644
--- a/internal/e/resp.go
+++ b/internal/e/resp.go
@@ -1,35 +1,26 @@
package e
import (
- "github.com/WHUPRJ/woj-server/pkg/utils"
+ "git.0x7f.app/WOJ/woj-server/pkg/utils"
"github.com/gin-gonic/gin"
"net/http"
)
-type Response struct {
- Code int `json:"code"`
- Msg string `json:"msg"`
- Body interface{} `json:"body"`
+type Response[T any] struct {
+ Code int `json:"code"`
+ Msg string `json:"msg"`
+ Body T `json:"body"`
}
-func Wrap(status Status, body interface{}) interface{} {
- return Response{
+func wrap[T any](status Status, body T) Response[interface{}] {
+ return Response[interface{}]{
Code: int(status),
Msg: status.String(),
- Body: utils.If(status == Success, body, nil),
+ Body: utils.If[interface{}](status == Success, body, nil),
}
}
-func Pong(c *gin.Context, status Status, body interface{}) {
+func Pong[T any](c *gin.Context, status Status, body T) {
c.Set("err", status)
- c.JSON(http.StatusOK, Wrap(status, body))
-}
-
-type Endpoint func(*gin.Context) (Status, interface{})
-
-func PongWrapper(handler Endpoint) func(*gin.Context) {
- return func(c *gin.Context) {
- status, body := handler(c)
- Pong(c, status, body)
- }
+ c.JSON(http.StatusOK, wrap(status, body))
}
diff --git a/internal/global/global.go b/internal/global/global.go
deleted file mode 100644
index 609895a..0000000
--- a/internal/global/global.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package global
-
-import (
- "github.com/WHUPRJ/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 b39faf2..0000000
--- a/internal/global/jwt.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package global
-
-import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/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 d04d91c..0000000
--- a/internal/global/setup.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package global
-
-import (
- "github.com/WHUPRJ/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).(zapcore.Level),
- ),
- 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..a9fd97a
--- /dev/null
+++ b/internal/misc/log/zap.go
@@ -0,0 +1,91 @@
+package log
+
+import (
+ "git.0x7f.app/WOJ/woj-server/cmd"
+ "git.0x7f.app/WOJ/woj-server/internal/misc/config"
+ "git.0x7f.app/WOJ/woj-server/pkg/utils"
+ "github.com/TheZeroSlave/zapsentry"
+ "github.com/getsentry/sentry-go"
+ "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
+ }
+
+ if cmd.SentryDSN != "" {
+ srv.logger = attachSentry(srv.logger)
+ }
+
+ return srv, nil
+}
+
+func attachSentry(log *zap.Logger) *zap.Logger {
+ cfg := zapsentry.Configuration{
+ Level: zapcore.ErrorLevel,
+ EnableBreadcrumbs: true,
+ BreadcrumbLevel: zapcore.InfoLevel,
+ }
+
+ core, err := zapsentry.NewCore(cfg, zapsentry.NewSentryClientFromClient(sentry.CurrentHub().Client()))
+ if err != nil {
+ log.Warn("failed to init zap", zap.Error(err))
+ return log
+ }
+
+ log = zapsentry.AttachCoreToLogger(core, log)
+ return log.With(zapsentry.NewScope())
+}
+
+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/model/Task.go b/internal/model/Task.go
index fc8a529..9b2cc05 100644
--- a/internal/model/Task.go
+++ b/internal/model/Task.go
@@ -1,7 +1,7 @@
package model
import (
- "github.com/WHUPRJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
)
const (
diff --git a/internal/global/config.go b/internal/model/config.go
similarity index 94%
rename from internal/global/config.go
rename to internal/model/config.go
index 0eaaa19..ddf1393 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"`
@@ -11,6 +11,7 @@ type ConfigRedis struct {
Db int `yaml:"Db"`
QueueDb int `yaml:"QueueDb"`
Address string `yaml:"Address"`
+ Port int `yaml:"Port"`
Password string `yaml:"Password"`
}
@@ -24,6 +25,7 @@ type ConfigDatabase struct {
MaxOpenConns int `yaml:"MaxOpenConns"`
MaxIdleConns int `yaml:"MaxIdleConns"`
ConnMaxLifetime int `yaml:"ConnMaxLifetime"`
+ TimeZone string `yaml:"TimeZone"`
}
type ConfigStorage struct {
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/pkg/cast/cast.go b/internal/pkg/cast/cast.go
index d73466d..e9eb272 100644
--- a/internal/pkg/cast/cast.go
+++ b/internal/pkg/cast/cast.go
@@ -2,7 +2,7 @@ package cast
import (
"fmt"
- "github.com/WHUPRJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
"strconv"
)
diff --git a/internal/pkg/metrics/prometheus.go b/internal/pkg/metrics/prometheus.go
deleted file mode 100644
index 8426604..0000000
--- a/internal/pkg/metrics/prometheus.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package metrics
-
-import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/pkg/cast"
- "github.com/prometheus/client_golang/prometheus"
-)
-
-type Metrics struct {
- namespace string
- subsystem string
-
- counter *prometheus.CounterVec
- hist *prometheus.HistogramVec
-
- logPaths []string
-}
-
-func (m *Metrics) Setup(namespace string, subsystem string) {
- m.namespace = namespace
- m.subsystem = subsystem
-
- m.counter = prometheus.NewCounterVec(
- prometheus.CounterOpts{
- Namespace: namespace,
- Subsystem: subsystem,
- Name: "requests_total",
- Help: "Total number of requests",
- },
- []string{"method", "url"},
- )
-
- m.hist = prometheus.NewHistogramVec(
- prometheus.HistogramOpts{
- Namespace: namespace,
- Subsystem: subsystem,
- Name: "requests_details",
- Help: "Details of each request",
- Buckets: []float64{0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 25, 50, 100, 250, 500, 1000},
- },
- []string{"method", "url", "success", "http_code", "err_code"},
- )
-
- prometheus.MustRegister(m.counter, m.hist)
-}
-
-func (m *Metrics) Record(method, url string, success bool, httpCode int, errCode e.Status, elapsed float64) {
- m.counter.With(prometheus.Labels{
- "method": method,
- "url": url,
- }).Inc()
-
- m.hist.With(prometheus.Labels{
- "method": method,
- "url": url,
- "success": cast.ToString(success),
- "http_code": cast.ToString(httpCode),
- "err_code": cast.ToString(errCode),
- }).Observe(elapsed)
-}
diff --git a/internal/repo/cache/redis.go b/internal/repo/cache/redis.go
new file mode 100644
index 0000000..a18f748
--- /dev/null
+++ b/internal/repo/cache/redis.go
@@ -0,0 +1,60 @@
+package cache
+
+import (
+ "context"
+ "fmt"
+ "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(fmt.Sprintf("%s:%d", conf.Redis.Address, conf.Redis.Port), 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..3d0b39d
--- /dev/null
+++ b/internal/repo/db/pg.go
@@ -0,0 +1,165 @@
+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"
+ "git.0x7f.app/WOJ/woj-server/pkg/utils"
+ "github.com/samber/do"
+ "go.uber.org/zap"
+ "gorm.io/driver/postgres"
+ "gorm.io/gorm"
+ "gorm.io/gorm/schema"
+ "hash/fnv"
+ "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
+
+ tz := utils.If(conf.Database.TimeZone == "", "Asia/Shanghai", conf.Database.TimeZone)
+ dsn := fmt.Sprintf(
+ "user=%s password=%s dbname=%s host=%s port=%d sslmode=disable TimeZone=%s",
+ conf.Database.User,
+ conf.Database.Password,
+ conf.Database.Database,
+ conf.Database.Host,
+ conf.Database.Port,
+ tz,
+ )
+
+ 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...")
+
+ // Running AutoMigrate concurrently on the same model fails with various race conditions
+ // https://github.com/go-gorm/gorm/pull/6680
+ // https://github.com/go-gorm/postgres/pull/224
+
+ // Obtain a lock to prevent concurrent AutoMigrate
+
+ lockID := func(s string) int64 {
+ h := fnv.New64a()
+ _, err := h.Write([]byte(s))
+ return utils.If(err != nil, int64(0x4242AA55), int64(h.Sum64()))
+ }("gorm:migrator")
+
+ s.err = s.db.Exec("SELECT pg_advisory_lock(?)", lockID).Error
+ if s.err != nil {
+ s.log.Error("Failed to obtain lock", zap.Error(s.err))
+ return
+ }
+
+ _ = 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{})
+
+ s.err = s.db.Exec("SELECT pg_advisory_unlock(?)", lockID).Error
+ if s.err != nil {
+ s.log.Error("Failed to release lock", zap.Error(s.err))
+ }
+}
+
+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 4a5ae90..0000000
--- a/internal/repo/postgresql/postgresql.go
+++ /dev/null
@@ -1,113 +0,0 @@
-package postgresql
-
-import (
- "database/sql"
- "errors"
- "fmt"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/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 d23da71..0000000
--- a/internal/repo/redis/redis.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package redis
-
-import (
- "context"
- "github.com/WHUPRJ/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/router/router.go b/internal/router/router.go
deleted file mode 100644
index a5669f2..0000000
--- a/internal/router/router.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package router
-
-import (
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/pkg/metrics"
- _ "github.com/WHUPRJ/woj-server/internal/router/docs"
- "github.com/WHUPRJ/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"
- 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).(string))
-
- r := gin.New()
- r.MaxMultipartMemory = 8 << 20
-
- // Logger middleware and debug
- if g.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))
- }
-
- // CORS middleware
- r.Use(cors.New(cors.Config{
- AllowAllOrigins: true,
- AllowMethods: []string{"GET", "POST", "PUT", "OPTIONS"},
- AllowHeaders: []string{"Origin", "Content-Length", "Content-Type"},
- AllowCredentials: true,
- }))
-
- // 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())
-
- // metrics
- r.GET("/metrics", gin.WrapH(promhttp.Handler()))
-
- // swagger
- r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
-
- // health
- r.GET("/health", func(c *gin.Context) {
- resp := &struct {
- Timestamp time.Time `json:"timestamp"`
- Status string `json:"status"`
- }{
- Timestamp: time.Now(),
- Status: "ok",
- }
- c.JSON(http.StatusOK, resp)
- })
-
- // api
- api := r.Group("/api/")
- setupApi(g, api)
-
- // static files
- r.Static("/static", "./resource/frontend/static")
- r.StaticFile("/", "./resource/frontend/index.html")
- r.NoRoute(func(c *gin.Context) {
- c.File("./resource/frontend/index.html")
- })
-
- return r
-}
diff --git a/internal/service/jwt/service.go b/internal/service/jwt/service.go
deleted file mode 100644
index 3cb372d..0000000
--- a/internal/service/jwt/service.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package jwt
-
-import (
- "github.com/WHUPRJ/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 d79c4dd..41568e1 100644
--- a/internal/service/problem/create.go
+++ b/internal/service/problem/create.go
@@ -1,8 +1,8 @@
package problem
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"go.uber.org/zap"
)
@@ -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 b63a9d0..15b6a28 100644
--- a/internal/service/problem/createVersion.go
+++ b/internal/service/problem/createVersion.go
@@ -1,8 +1,8 @@
package problem
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"github.com/jackc/pgtype"
"go.uber.org/zap"
)
@@ -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 aa10be5..75a24f5 100644
--- a/internal/service/problem/query.go
+++ b/internal/service/problem/query.go
@@ -2,8 +2,8 @@ package problem
import (
"errors"
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"go.uber.org/zap"
"gorm.io/gorm"
"gorm.io/gorm/clause"
@@ -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 a461e37..20f0149 100644
--- a/internal/service/problem/queryFuzz.go
+++ b/internal/service/problem/queryFuzz.go
@@ -1,8 +1,8 @@
package problem
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"go.uber.org/zap"
"gorm.io/gorm/clause"
)
@@ -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 55f0fc8..b040abd 100644
--- a/internal/service/problem/queryLatestVersion.go
+++ b/internal/service/problem/queryLatestVersion.go
@@ -2,8 +2,8 @@ package problem
import (
"errors"
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"go.uber.org/zap"
"gorm.io/gorm"
)
@@ -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 9ca08ae..6ff6e59 100644
--- a/internal/service/problem/queryVersion.go
+++ b/internal/service/problem/queryVersion.go
@@ -2,8 +2,8 @@ package problem
import (
"errors"
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"go.uber.org/zap"
"gorm.io/gorm"
)
@@ -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 0dd05af..f4be009 100644
--- a/internal/service/problem/service.go
+++ b/internal/service/problem/service.go
@@ -1,11 +1,12 @@
package problem
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "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 026b934..fddc578 100644
--- a/internal/service/problem/update.go
+++ b/internal/service/problem/update.go
@@ -1,13 +1,13 @@
package problem
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"go.uber.org/zap"
)
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 de8230c..9442daf 100644
--- a/internal/service/problem/updateVersion.go
+++ b/internal/service/problem/updateVersion.go
@@ -1,13 +1,13 @@
package problem
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"go.uber.org/zap"
)
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/common.go b/internal/service/runner/common.go
index 24eba47..76da6c5 100644
--- a/internal/service/runner/common.go
+++ b/internal/service/runner/common.go
@@ -2,8 +2,8 @@ package runner
import (
"fmt"
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/pkg/utils"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/pkg/utils"
"go.uber.org/zap"
"os"
"os/exec"
@@ -36,6 +36,10 @@ func (s *service) execute(script string, args ...string) error {
p := filepath.Join(ScriptsDir, script)
cmd := exec.Command(p, args...)
cmd.Dir = ScriptsDir
+ if s.verbose {
+ cmd.Stdout = os.Stderr
+ cmd.Stderr = os.Stderr
+ }
return cmd.Run()
}
diff --git a/internal/service/runner/compile.go b/internal/service/runner/compile.go
index 62ad6c0..bc129f3 100644
--- a/internal/service/runner/compile.go
+++ b/internal/service/runner/compile.go
@@ -2,8 +2,8 @@ package runner
import (
"fmt"
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/pkg/utils"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/pkg/utils"
"os"
"path/filepath"
)
@@ -16,14 +16,14 @@ func (s *service) Compile(version uint, user string, lang string) (JudgeStatus,
log := filepath.Join(UserDir, user, fmt.Sprintf("%s.compile.log", user))
msg, err := utils.FileRead(log)
- msg = utils.If(err == nil, msg, nil).([]byte)
+ msg = utils.If(err == nil, msg, nil)
msgText := string(msg)
if !utils.FileExist(target) || utils.FileEmpty(target) {
return JudgeStatus{
Message: "compile failed",
Tasks: []TaskStatus{{Verdict: VerdictCompileError, Message: msgText}}},
- utils.If(status == e.Success, e.RunnerUserCompileFailed, status).(e.Status)
+ utils.If(status == e.Success, e.RunnerUserCompileFailed, status)
}
return JudgeStatus{}, e.Success
diff --git a/internal/service/runner/config.go b/internal/service/runner/config.go
index 94131c0..844ffca 100644
--- a/internal/service/runner/config.go
+++ b/internal/service/runner/config.go
@@ -16,9 +16,9 @@ type Config struct {
} `json:"Runtime"`
Languages []struct {
Lang string `json:"Lang"`
- Type string `json:"Type"`
- Script string `json:"Script"`
- Cmp string `json:"Cmp"`
+ Type string `json:"Type,omitempty"`
+ Script string `json:"Script,omitempty"`
+ Cmp string `json:"Cmp,omitempty"`
} `json:"Languages"`
Tasks []struct {
Id int `json:"Id"`
diff --git a/internal/service/runner/deps.go b/internal/service/runner/deps.go
index 96940f5..28f7849 100644
--- a/internal/service/runner/deps.go
+++ b/internal/service/runner/deps.go
@@ -1,8 +1,8 @@
package runner
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/pkg/utils"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/pkg/utils"
"go.uber.org/zap"
"os"
"os/exec"
@@ -10,7 +10,7 @@ import (
)
func (s *service) EnsureDeps(force bool) e.Status {
- mark := filepath.Join(Prefix, ".mark.docker")
+ mark := filepath.Join(Prefix, ".mark.image")
if force {
_ = os.Remove(mark)
@@ -18,9 +18,13 @@ func (s *service) EnsureDeps(force bool) e.Status {
return e.Success
}
- script := filepath.Join(ScriptsDir, "prepare_container.sh")
+ script := filepath.Join(ScriptsDir, "prepare_images.sh")
cmd := exec.Command(script)
cmd.Dir = ScriptsDir
+ if s.verbose {
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ }
err := cmd.Run()
if err != nil {
s.log.Warn("prebuild docker images failed", zap.Error(err))
diff --git a/internal/service/runner/newProblem.go b/internal/service/runner/newProblem.go
index 79f969f..d723604 100644
--- a/internal/service/runner/newProblem.go
+++ b/internal/service/runner/newProblem.go
@@ -2,10 +2,10 @@ package runner
import (
"fmt"
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/pkg/down"
- "github.com/WHUPRJ/woj-server/pkg/unzip"
- "github.com/WHUPRJ/woj-server/pkg/utils"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/pkg/down"
+ "git.0x7f.app/WOJ/woj-server/pkg/unzip"
+ "git.0x7f.app/WOJ/woj-server/pkg/utils"
"go.uber.org/zap"
"os"
"path/filepath"
diff --git a/internal/service/runner/runAndJudge.go b/internal/service/runner/runAndJudge.go
index c093c22..f0cf9ba 100644
--- a/internal/service/runner/runAndJudge.go
+++ b/internal/service/runner/runAndJudge.go
@@ -1,6 +1,6 @@
package runner
-import "github.com/WHUPRJ/woj-server/internal/e"
+import "git.0x7f.app/WOJ/woj-server/internal/e"
func (s *service) RunAndJudge(version uint, user string, lang string, config *Config) (JudgeStatus, int32, e.Status) {
// run user program
diff --git a/internal/service/runner/service.go b/internal/service/runner/service.go
index d9322ac..820c214 100644
--- a/internal/service/runner/service.go
+++ b/internal/service/runner/service.go
@@ -1,8 +1,10 @@
package runner
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
+ "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"
+ "github.com/samber/do"
"go.uber.org/zap"
)
@@ -23,14 +25,22 @@ 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 {
- log *zap.Logger
+ log *zap.Logger
+ verbose bool
}
-func NewService(g *global.Global) Service {
- return &service{
- log: g.Log,
- }
+func (s *service) HealthCheck() error {
+ return nil
}
diff --git a/internal/service/runner/status.go b/internal/service/runner/status.go
index 6336b95..cf0bdac 100644
--- a/internal/service/runner/status.go
+++ b/internal/service/runner/status.go
@@ -5,7 +5,7 @@ import (
"encoding/json"
"encoding/xml"
"fmt"
- "github.com/WHUPRJ/woj-server/pkg/utils"
+ "git.0x7f.app/WOJ/woj-server/pkg/utils"
"golang.org/x/text/encoding/charmap"
"io"
"path/filepath"
diff --git a/internal/service/status/create.go b/internal/service/status/create.go
index 4c863d6..e933dba 100644
--- a/internal/service/status/create.go
+++ b/internal/service/status/create.go
@@ -1,8 +1,8 @@
package status
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"github.com/jackc/pgtype"
"go.uber.org/zap"
)
@@ -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 dd474b5..96d9e32 100644
--- a/internal/service/status/query.go
+++ b/internal/service/status/query.go
@@ -2,21 +2,21 @@ package status
import (
"errors"
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"go.uber.org/zap"
"gorm.io/gorm"
"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 70a95b8..b255d98 100644
--- a/internal/service/status/service.go
+++ b/internal/service/status/service.go
@@ -1,11 +1,12 @@
package status
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "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/get.go b/internal/service/storage/get.go
index c65f3cd..e6b7132 100644
--- a/internal/service/storage/get.go
+++ b/internal/service/storage/get.go
@@ -2,7 +2,7 @@ package storage
import (
"context"
- "github.com/WHUPRJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
"go.uber.org/zap"
"net/url"
"time"
diff --git a/internal/service/storage/service.go b/internal/service/storage/service.go
index 633b336..9805a7e 100644
--- a/internal/service/storage/service.go
+++ b/internal/service/storage/service.go
@@ -1,10 +1,12 @@
package storage
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
+ "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"
"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/storage/upload.go b/internal/service/storage/upload.go
index 69e7627..56f9ba5 100644
--- a/internal/service/storage/upload.go
+++ b/internal/service/storage/upload.go
@@ -2,7 +2,7 @@ package storage
import (
"context"
- "github.com/WHUPRJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
"go.uber.org/zap"
"time"
)
diff --git a/internal/service/submission/create.go b/internal/service/submission/create.go
index 2c919d6..e58480e 100644
--- a/internal/service/submission/create.go
+++ b/internal/service/submission/create.go
@@ -1,8 +1,8 @@
package submission
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"go.uber.org/zap"
)
@@ -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 7dc6e7f..391b67c 100644
--- a/internal/service/submission/query.go
+++ b/internal/service/submission/query.go
@@ -2,8 +2,8 @@ package submission
import (
"errors"
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"go.uber.org/zap"
"gorm.io/gorm"
"gorm.io/gorm/clause"
@@ -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 b5e8095..3ea8043 100644
--- a/internal/service/submission/service.go
+++ b/internal/service/submission/service.go
@@ -1,11 +1,12 @@
package submission
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "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/common.go b/internal/service/task/common.go
index e88844c..561c298 100644
--- a/internal/service/task/common.go
+++ b/internal/service/task/common.go
@@ -1,7 +1,7 @@
package task
import (
- "github.com/WHUPRJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
"github.com/hibiken/asynq"
"go.uber.org/zap"
)
diff --git a/internal/service/task/problem.go b/internal/service/task/problem.go
index 327b003..878c29d 100644
--- a/internal/service/task/problem.go
+++ b/internal/service/task/problem.go
@@ -2,8 +2,8 @@ package task
import (
"encoding/json"
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"go.uber.org/zap"
)
diff --git a/internal/service/task/service.go b/internal/service/task/service.go
index f1931bd..a08e689 100644
--- a/internal/service/task/service.go
+++ b/internal/service/task/service.go
@@ -1,11 +1,15 @@
package task
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/model"
- "github.com/WHUPRJ/woj-server/internal/service/runner"
+ "errors"
+ "fmt"
+ "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/service/runner"
"github.com/hibiken/asynq"
+ "github.com/samber/do"
"go.uber.org/zap"
)
@@ -18,6 +22,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: fmt.Sprintf("%s:%d", conf.Redis.Address, conf.Redis.Port),
+ 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 +48,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/task/submit.go b/internal/service/task/submit.go
index c588c6b..3f534aa 100644
--- a/internal/service/task/submit.go
+++ b/internal/service/task/submit.go
@@ -2,9 +2,9 @@ package task
import (
"encoding/json"
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
- "github.com/WHUPRJ/woj-server/internal/service/runner"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/service/runner"
"go.uber.org/zap"
)
diff --git a/internal/service/user/create.go b/internal/service/user/create.go
index f91cd53..6a92c1e 100644
--- a/internal/service/user/create.go
+++ b/internal/service/user/create.go
@@ -1,8 +1,8 @@
package user
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"go.uber.org/zap"
"golang.org/x/crypto/bcrypt"
"strings"
@@ -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 d80acbd..f64c4d9 100644
--- a/internal/service/user/login.go
+++ b/internal/service/user/login.go
@@ -2,8 +2,8 @@ package user
import (
"errors"
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"go.uber.org/zap"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
@@ -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 7cd706c..1d9554e 100644
--- a/internal/service/user/profile.go
+++ b/internal/service/user/profile.go
@@ -2,8 +2,8 @@ package user
import (
"errors"
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/model"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/model"
"go.uber.org/zap"
"gorm.io/gorm"
)
@@ -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 4baeffd..cb62e24 100644
--- a/internal/service/user/service.go
+++ b/internal/service/user/service.go
@@ -1,12 +1,13 @@
package user
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/internal/model"
- "github.com/go-redis/redis/v8"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "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"
+ "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 8303af8..402e315 100644
--- a/internal/service/user/version.go
+++ b/internal/service/user/version.go
@@ -3,12 +3,12 @@ package user
import (
"context"
"fmt"
- "github.com/WHUPRJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
"go.uber.org/zap"
)
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 61%
rename from internal/service/jwt/middleware.go
rename to internal/web/jwt/middleware.go
index 2d2eddc..e4195e6 100644
--- a/internal/service/jwt/middleware.go
+++ b/internal/web/jwt/middleware.go
@@ -1,22 +1,27 @@
package jwt
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "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) {
- const tokenPrefix = "bearer "
+ claim, status := func() (*model.Claim, e.Status) {
tokenHeader := c.GetHeader("Authorization")
- if tokenHeader == "" || !strings.HasPrefix(strings.ToLower(tokenHeader), tokenPrefix) {
+ if tokenHeader == "" {
return nil, e.TokenEmpty
}
- token := tokenHeader[len(tokenPrefix):]
+ token := tokenHeader
+ const tokenPrefix = "bearer "
+ if strings.HasPrefix(strings.ToLower(tokenHeader), tokenPrefix) {
+ // don't force "bearer" prefix
+ token = tokenHeader[len(tokenPrefix):]
+ }
+
claim, status := s.ParseToken(token)
if status != e.Success {
return nil, status
@@ -32,7 +37,7 @@ func (s *service) Handler(forced bool) gin.HandlerFunc {
c.Set("claim", claim)
}
if forced && status != e.Success {
- e.Pong(c, status, nil)
+ e.Pong[any](c, status, nil)
c.Abort()
} else {
c.Next()
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 64%
rename from internal/service/jwt/token.go
rename to internal/web/jwt/token.go
index 876757e..3d788ba 100644
--- a/internal/service/jwt/token.go
+++ b/internal/web/jwt/token.go
@@ -2,23 +2,24 @@ package jwt
import (
"context"
+ "errors"
"fmt"
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/internal/global"
- "github.com/WHUPRJ/woj-server/pkg/utils"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "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"])
@@ -26,7 +27,8 @@ func (s *service) ParseToken(tokenText string) (*global.Claim, e.Status) {
return s.SigningKey, nil
})
- if ve, ok := err.(*jwt.ValidationError); ok {
+ var ve *jwt.ValidationError
+ if errors.As(err, &ve) {
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
return nil, e.TokenMalformed
} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
@@ -35,26 +37,22 @@ func (s *service) ParseToken(tokenText string) (*global.Claim, e.Status) {
} else {
return nil, e.TokenInvalid
}
- } else if err != nil {
- s.log.Warn("JWT Token Parse Error", zap.Error(err))
- return nil, e.TokenUnknown
}
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 +64,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 65%
rename from internal/pkg/metrics/middleware.go
rename to internal/web/metrics/middleware.go
index e1fd20e..36edd35 100644
--- a/internal/pkg/metrics/middleware.go
+++ b/internal/web/metrics/middleware.go
@@ -1,24 +1,24 @@
package metrics
import (
- "github.com/WHUPRJ/woj-server/internal/e"
- "github.com/WHUPRJ/woj-server/pkg/utils"
+ "git.0x7f.app/WOJ/woj-server/internal/e"
+ "git.0x7f.app/WOJ/woj-server/pkg/utils"
"github.com/gin-gonic/gin"
"net/http"
"strings"
"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/web/metrics/prometheus.go b/internal/web/metrics/prometheus.go
new file mode 100644
index 0000000..77d8166
--- /dev/null
+++ b/internal/web/metrics/prometheus.go
@@ -0,0 +1,85 @@
+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"
+)
+
+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
+
+ counter *prometheus.CounterVec
+ hist *prometheus.HistogramVec
+
+ logPaths []string
+}
+
+func (s *service) HealthCheck() error {
+ return nil
+}
+
+func (s *service) setup(namespace string, subsystem string) {
+ s.namespace = namespace
+ s.subsystem = subsystem
+
+ s.counter = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Namespace: namespace,
+ Subsystem: subsystem,
+ Name: "requests_total",
+ Help: "Total number of requests",
+ },
+ []string{"method", "url"},
+ )
+
+ s.hist = prometheus.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Namespace: namespace,
+ Subsystem: subsystem,
+ Name: "requests_details",
+ Help: "Details of each request",
+ Buckets: []float64{0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 25, 50, 100, 250, 500, 1000},
+ },
+ []string{"method", "url", "success", "http_code", "err_code"},
+ )
+
+ prometheus.MustRegister(s.counter, s.hist)
+}
+
+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()
+
+ s.hist.With(prometheus.Labels{
+ "method": method,
+ "url": url,
+ "success": cast.ToString(success),
+ "http_code": cast.ToString(httpCode),
+ "err_code": cast.ToString(errCode),
+ }).Observe(elapsed)
+}
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 53%
rename from internal/router/api.go
rename to internal/web/router/api.go
index ce7fa19..2173aad 100644
--- a/internal/router/api.go
+++ b/internal/web/router/api.go
@@ -1,29 +1,30 @@
package router
import (
- "github.com/WHUPRJ/woj-server/internal/api/debug"
- "github.com/WHUPRJ/woj-server/internal/api/problem"
- "github.com/WHUPRJ/woj-server/internal/api/status"
- "github.com/WHUPRJ/woj-server/internal/api/submission"
- "github.com/WHUPRJ/woj-server/internal/api/user"
- "github.com/WHUPRJ/woj-server/internal/global"
+ "git.0x7f.app/WOJ/woj-server/internal/api/debug"
+ "git.0x7f.app/WOJ/woj-server/internal/api/problem"
+ "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/model"
"github.com/gin-gonic/gin"
+ "github.com/samber/do"
)
-// @title OJ Server API Documentation
-// @version 1.0
+// @title WOJ Server API Documentation
+// @version 1.2.0
// @BasePath /api
// @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/web/router/router.go b/internal/web/router/router.go
new file mode 100644
index 0000000..691ad9e
--- /dev/null
+++ b/internal/web/router/router.go
@@ -0,0 +1,128 @@
+package router
+
+import (
+ "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"
+ sentrygin "github.com/getsentry/sentry-go/gin"
+ "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"
+)
+
+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.engine = 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[string](conf.Development, gin.DebugMode, gin.ReleaseMode))
+
+ r := gin.New()
+ r.MaxMultipartMemory = 8 << 20
+
+ // Sentry middleware
+ r.Use(sentrygin.New(sentrygin.Options{Repanic: true}))
+
+ // Logger middleware and debug
+ if conf.Development {
+ // Gin's default logger is pretty enough
+ r.Use(gin.Logger())
+ r.Use(gin.Recovery())
+ // add prof
+ pprof.Register(r)
+ } else {
+ ginLog := s.logger.GetLogger("gin")
+ r.Use(ginZap.Ginzap(ginLog, time.RFC3339, false))
+ r.Use(ginZap.RecoveryWithZap(ginLog, true))
+ }
+
+ // CORS middleware
+ r.Use(cors.New(cors.Config{
+ AllowAllOrigins: true,
+ AllowMethods: []string{"GET", "POST", "PUT", "OPTIONS"},
+ AllowHeaders: []string{"Authorization", "Origin", "Content-Length", "Content-Type"},
+ AllowCredentials: true,
+ }))
+
+ // Prometheus middleware
+ s.metric.SetLogPaths([]string{"/api"})
+ r.Use(s.metric.Handler())
+
+ // metrics
+ r.GET("/metrics", gin.WrapH(promhttp.Handler()))
+
+ // swagger
+ r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
+
+ // health
+ r.GET("/health", func(c *gin.Context) {
+ var err bool
+ for _, v := range injector.HealthCheck() {
+ if v != nil {
+ err = true
+ break
+ }
+ }
+
+ resp := &struct {
+ Timestamp time.Time `json:"timestamp"`
+ Status string `json:"status"`
+ }{
+ Timestamp: time.Now(),
+ Status: utils.If(err, "unhealthy", "healthy"),
+ }
+ c.JSON(utils.If(err, http.StatusServiceUnavailable, http.StatusOK), resp)
+ })
+
+ // api
+ api := r.Group("/api/")
+ s.setupApi(api, injector)
+
+ // static files
+ r.Static("/static", "./resource/frontend/static")
+ r.StaticFile("/", "./resource/frontend/index.html")
+ r.NoRoute(func(c *gin.Context) {
+ c.File("./resource/frontend/index.html")
+ })
+
+ return r
+}
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
diff --git a/pkg/down/down.go b/pkg/down/down.go
index d4b30b9..1f88818 100644
--- a/pkg/down/down.go
+++ b/pkg/down/down.go
@@ -2,7 +2,7 @@ package down
import (
"fmt"
- "github.com/WHUPRJ/woj-server/pkg/utils"
+ "git.0x7f.app/WOJ/woj-server/pkg/utils"
"io"
"net/http"
"os"
diff --git a/pkg/utils/file.go b/pkg/utils/file.go
index 9ae7939..a96076d 100644
--- a/pkg/utils/file.go
+++ b/pkg/utils/file.go
@@ -20,7 +20,7 @@ func FileWrite(filePath string, content []byte) error {
func FileExist(filePath string) bool {
_, err := os.Stat(filePath)
- return If(err == nil || os.IsExist(err), true, false).(bool)
+ return If(err == nil || os.IsExist(err), true, false)
}
func FileEmpty(filePath string) bool {
@@ -35,5 +35,5 @@ func FileTouch(filePath string) bool {
base := filepath.Dir(filePath)
_ = os.MkdirAll(base, 0755)
_, err := os.OpenFile(filePath, os.O_RDONLY|os.O_CREATE, 0644)
- return If(err == nil, true, false).(bool)
+ return If(err == nil, true, false)
}
diff --git a/pkg/utils/if.go b/pkg/utils/if.go
index 6b70ac7..cfc607f 100644
--- a/pkg/utils/if.go
+++ b/pkg/utils/if.go
@@ -1,6 +1,6 @@
package utils
-func If(condition bool, trueVal, falseVal interface{}) interface{} {
+func If[T any](condition bool, trueVal, falseVal T) T {
if condition {
return trueVal
} else {
diff --git a/resource/deploy/cache.yaml b/resource/deploy/cache.yaml
new file mode 100644
index 0000000..e58407d
--- /dev/null
+++ b/resource/deploy/cache.yaml
@@ -0,0 +1,69 @@
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ namespace: woj
+ name: cache-pvc
+ labels:
+ app: cache
+spec:
+ accessModes:
+ - ReadWriteOnce
+ storageClassName: local-path
+ resources:
+ requests:
+ storage: 2Gi
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ namespace: woj
+ name: cache-deployment
+ labels:
+ app: cache
+spec:
+ selector:
+ matchLabels:
+ app: cache
+ template:
+ metadata:
+ namespace: woj
+ name: cache-pod
+ labels:
+ app: cache
+ spec:
+ containers:
+ - name: cache
+ image: docker.io/bitnami/redis:7.2
+ imagePullPolicy: IfNotPresent
+ ports:
+ - containerPort: 6379
+ env:
+ - name: REDIS_PASSWORD
+ valueFrom:
+ configMapKeyRef:
+ name: shared-config
+ key: REDIS_PASSWORD
+ volumeMounts:
+ - name: cache-vol
+ mountPath: /data
+ volumes:
+ - name: cache-vol
+ persistentVolumeClaim:
+ claimName: cache-pvc
+---
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: woj
+ name: cache-service
+ labels:
+ app: cache
+spec:
+ type: ClusterIP
+ selector:
+ app: cache
+ ports:
+ - protocol: TCP
+ port: 6379
+ targetPort: 6379
\ No newline at end of file
diff --git a/resource/deploy/config.yaml b/resource/deploy/config.yaml
new file mode 100644
index 0000000..a934596
--- /dev/null
+++ b/resource/deploy/config.yaml
@@ -0,0 +1,12 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ namespace: woj
+ name: shared-config
+data:
+ POSTGRES_USER: "woj"
+ POSTGRES_PASSWORD: "A_VeRy-S3cUr3^PaSsWoRd"
+ POSTGRES_DB: "woj"
+ MINIO_ROOT_USER: "A_VeRy_CoMpLeX_AcCeSs_KeY"
+ MINIO_ROOT_PASSWORD: "A_VeRy_CoMpLeX_ScReT_KeY"
+ REDIS_PASSWORD: "YeT_An0tHeR_VeRy-S3cUr3^PaSsWoRd"
diff --git a/resource/deploy/db.yaml b/resource/deploy/db.yaml
new file mode 100644
index 0000000..ee11abb
--- /dev/null
+++ b/resource/deploy/db.yaml
@@ -0,0 +1,79 @@
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ namespace: woj
+ name: db-pvc
+ labels:
+ app: db
+spec:
+ accessModes:
+ - ReadWriteOnce
+ storageClassName: local-path
+ resources:
+ requests:
+ storage: 5Gi
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ namespace: woj
+ name: db-deployment
+ labels:
+ app: db
+spec:
+ selector:
+ matchLabels:
+ app: db
+ template:
+ metadata:
+ namespace: woj
+ name: db-pod
+ labels:
+ app: db
+ spec:
+ containers:
+ - name: db
+ image: docker.io/library/postgres:16-alpine
+ imagePullPolicy: IfNotPresent
+ ports:
+ - containerPort: 5432
+ env:
+ - name: POSTGRES_USER
+ valueFrom:
+ configMapKeyRef:
+ name: shared-config
+ key: POSTGRES_USER
+ - name: POSTGRES_PASSWORD
+ valueFrom:
+ configMapKeyRef:
+ name: shared-config
+ key: POSTGRES_PASSWORD
+ - name: POSTGRES_DB
+ valueFrom:
+ configMapKeyRef:
+ name: shared-config
+ key: POSTGRES_DB
+ volumeMounts:
+ - name: db-vol
+ mountPath: /var/lib/postgresql/data
+ volumes:
+ - name: db-vol
+ persistentVolumeClaim:
+ claimName: db-pvc
+---
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: woj
+ name: db-service
+ labels:
+ app: db
+spec:
+ type: ClusterIP
+ selector:
+ app: db
+ ports:
+ - protocol: TCP
+ port: 5432
+ targetPort: 5432
diff --git a/resource/deploy/runner.yaml b/resource/deploy/runner.yaml
new file mode 100644
index 0000000..76feac8
--- /dev/null
+++ b/resource/deploy/runner.yaml
@@ -0,0 +1,72 @@
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ namespace: woj
+ name: runner-pvc
+ labels:
+ app: runner
+spec:
+ accessModes:
+ - ReadWriteOnce
+ storageClassName: local-path
+ resources:
+ requests:
+ storage: 5Gi
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ namespace: woj
+ name: runner-deployment
+ labels:
+ app: runner
+spec:
+ replicas: 3
+ selector:
+ matchLabels:
+ app: runner
+ template:
+ metadata:
+ namespace: woj
+ name: runner-pod
+ labels:
+ app: runner
+ spec:
+ containers:
+ - name: runner
+ image: git.0x7f.app/woj/woj-runner:1.1.0
+ imagePullPolicy: IfNotPresent
+ args:
+ - runner
+ env:
+ - name: REDIS_ADDRESS
+ value: "cache-service.woj.svc.cluster.local"
+ - name: REDIS_PASSWORD
+ valueFrom:
+ configMapKeyRef:
+ name: shared-config
+ key: REDIS_PASSWORD
+ - name: STORAGE_ENDPOINT
+ value: "storage-service.woj.svc.cluster.local:9000"
+ - name: STORAGE_ACCESS_KEY
+ valueFrom:
+ configMapKeyRef:
+ name: shared-config
+ key: MINIO_ROOT_USER
+ - name: STORAGE_SECRET_KEY
+ valueFrom:
+ configMapKeyRef:
+ name: shared-config
+ key: MINIO_ROOT_PASSWORD
+ - name: STORAGE_BUCKET
+ value: "woj"
+ securityContext:
+ privileged: true
+ volumeMounts:
+ - name: runner-vol
+ mountPath: /app/resource/runner/user
+ volumes:
+ - name: runner-vol
+ persistentVolumeClaim:
+ claimName: runner-pvc
diff --git a/resource/deploy/server.yaml b/resource/deploy/server.yaml
new file mode 100644
index 0000000..100f254
--- /dev/null
+++ b/resource/deploy/server.yaml
@@ -0,0 +1,107 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ namespace: woj
+ name: server-deployment
+ labels:
+ app: server
+spec:
+ replicas: 2
+ selector:
+ matchLabels:
+ app: server
+ template:
+ metadata:
+ namespace: woj
+ name: server-pod
+ labels:
+ app: server
+ spec:
+ initContainers:
+ - name: init-server
+ image: git.0x7f.app/woj/woj-server:1.1.0
+ imagePullPolicy: IfNotPresent
+ args:
+ - init
+ env:
+ - name: DATABASE_HOST
+ value: "db-service.woj.svc.cluster.local"
+ - name: DATABASE_USER
+ valueFrom:
+ configMapKeyRef:
+ name: shared-config
+ key: POSTGRES_USER
+ - name: DATABASE_PASSWORD
+ valueFrom:
+ configMapKeyRef:
+ name: shared-config
+ key: POSTGRES_PASSWORD
+ - name: DATABASE_NAME
+ valueFrom:
+ configMapKeyRef:
+ name: shared-config
+ key: POSTGRES_DB
+ containers:
+ - name: server
+ image: git.0x7f.app/woj/woj-server:1.1.0
+ imagePullPolicy: IfNotPresent
+ args:
+ - server
+ env:
+ - name: DATABASE_HOST
+ value: "db-service.woj.svc.cluster.local"
+ - name: DATABASE_USER
+ valueFrom:
+ configMapKeyRef:
+ name: shared-config
+ key: POSTGRES_USER
+ - name: DATABASE_PASSWORD
+ valueFrom:
+ configMapKeyRef:
+ name: shared-config
+ key: POSTGRES_PASSWORD
+ - name: DATABASE_NAME
+ valueFrom:
+ configMapKeyRef:
+ name: shared-config
+ key: POSTGRES_DB
+ - name: REDIS_ADDRESS
+ value: "cache-service.woj.svc.cluster.local"
+ - name: REDIS_PASSWORD
+ valueFrom:
+ configMapKeyRef:
+ name: shared-config
+ key: REDIS_PASSWORD
+ - name: STORAGE_ENDPOINT
+ value: "storage-service.woj.svc.cluster.local:9000"
+ - name: STORAGE_ACCESS_KEY
+ valueFrom:
+ configMapKeyRef:
+ name: shared-config
+ key: MINIO_ROOT_USER
+ - name: STORAGE_SECRET_KEY
+ valueFrom:
+ configMapKeyRef:
+ name: shared-config
+ key: MINIO_ROOT_PASSWORD
+ - name: STORAGE_BUCKET
+ value: "woj"
+ ports:
+ - containerPort: 8000
+---
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: woj
+ name: server-service
+ labels:
+ app: server
+spec:
+ type: LoadBalancer
+ selector:
+ app: server
+ ports:
+ - protocol: TCP
+ port: 8000
+ targetPort: 8000
diff --git a/resource/deploy/storage.yaml b/resource/deploy/storage.yaml
new file mode 100644
index 0000000..fc4e9b5
--- /dev/null
+++ b/resource/deploy/storage.yaml
@@ -0,0 +1,80 @@
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ namespace: woj
+ name: storage-pvc
+ labels:
+ app: storage
+spec:
+ accessModes:
+ - ReadWriteOnce
+ storageClassName: local-path
+ resources:
+ requests:
+ storage: 5Gi
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ namespace: woj
+ name: storage-deployment
+ labels:
+ app: storage
+spec:
+ selector:
+ matchLabels:
+ app: storage
+ template:
+ metadata:
+ namespace: woj
+ name: storage-pod
+ labels:
+ app: storage
+ spec:
+ containers:
+ - name: storage
+ image: quay.io/minio/minio:latest
+ imagePullPolicy: Always
+ command:
+ - /bin/bash
+ - -c
+ args:
+ - mkdir -p /data/woj && minio server /data --console-address ":9001"
+ ports:
+ - containerPort: 9000
+ - containerPort: 9001
+ env:
+ - name: MINIO_ROOT_USER
+ valueFrom:
+ configMapKeyRef:
+ name: shared-config
+ key: MINIO_ROOT_USER
+ - name: MINIO_ROOT_PASSWORD
+ valueFrom:
+ configMapKeyRef:
+ name: shared-config
+ key: MINIO_ROOT_PASSWORD
+ volumeMounts:
+ - name: storage-vol
+ mountPath: /data
+ volumes:
+ - name: storage-vol
+ persistentVolumeClaim:
+ claimName: storage-pvc
+---
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: woj
+ name: storage-service
+ labels:
+ app: storage
+spec:
+ type: LoadBalancer
+ selector:
+ app: storage
+ ports:
+ - protocol: TCP
+ port: 9000
+ targetPort: 9000
diff --git a/resource/runner/tmp/.gitkeep b/resource/frontend/.gitignore
similarity index 100%
rename from resource/runner/tmp/.gitkeep
rename to resource/frontend/.gitignore
diff --git a/resource/runner/.gitignore b/resource/runner/.gitignore
index 0cf8d7b..0f3e631 100644
--- a/resource/runner/.gitignore
+++ b/resource/runner/.gitignore
@@ -1,2 +1,4 @@
+# docker image mark
.mark.*
+# other tmp files
*.zip
diff --git a/resource/runner/framework/scripts/.gitignore b/resource/runner/framework/scripts/.gitignore
index bf3d440..7b0fd4d 100644
--- a/resource/runner/framework/scripts/.gitignore
+++ b/resource/runner/framework/scripts/.gitignore
@@ -1,2 +1,5 @@
+# bin
/libwoj_sandbox.so
/woj_launcher
+# source
+/woj-sandbox
diff --git a/resource/runner/framework/scripts/setup.sh b/resource/runner/framework/scripts/setup.sh
index b93b328..e97e402 100755
--- a/resource/runner/framework/scripts/setup.sh
+++ b/resource/runner/framework/scripts/setup.sh
@@ -2,7 +2,7 @@
set -x
rm -rf woj-sandbox
-git clone https://github.com/WHUPRJ/woj-sandbox.git >/dev/null 2>&1 || exit 1
+git clone https://git.0x7f.app/WOJ/woj-sandbox.git >/dev/null 2>&1 || exit 1
cd woj-sandbox && ./build_libseccomp.sh || exit 1
mkdir -p build && cd build || exit 1
@@ -12,4 +12,4 @@ make -j || exit 1
cd ../..
cp woj-sandbox/build/libwoj_sandbox.so . || exit 1
cp woj-sandbox/build/woj_launcher . || exit 1
-rm -rf woj-sandbox
+rm -rf woj-sandbox || exit 1
diff --git a/resource/runner/problem/.gitignore b/resource/runner/problem/.gitignore
new file mode 100644
index 0000000..9ec52b8
--- /dev/null
+++ b/resource/runner/problem/.gitignore
@@ -0,0 +1,4 @@
+*
+!.gitignore
+!example
+!example/*
diff --git a/resource/runner/problem/example/README.md b/resource/runner/problem/example/README.md
index ba9485d..39ffa40 100644
--- a/resource/runner/problem/example/README.md
+++ b/resource/runner/problem/example/README.md
@@ -5,6 +5,7 @@
```
.
├── config.json # 题目配置信息
+├── description.md # (not used) 题目描述必须通过 API 提交给系统
├── data # 数据目录
│ ├── input # 输入数据
│ │ ├── (x).input # 第 x 组输入数据
@@ -15,7 +16,7 @@
└── judge # 评测脚本目录
├── c.Makefile # (optional) 自定义评测脚本
├── prebuild.Makefile # (optional) 题目初始化脚本
- └── ...
+ └── ... # 其余可能用到的程序、脚本等
```
## 详细说明
@@ -28,37 +29,31 @@
// 运行时配置
"TimeLimit": 1000, // 时间限制 (ms)
"MemoryLimit": 16, // 内存限制 (MB)
- "NProcLimit": 1 // 进(线)程 限制
+ "NProcLimit": 1 // 进(线)程 限制
},
"Languages": [
+ // c 语言,使用自定义评测脚本,脚本为 ./judge/XYZ.Makefile
{"Lang": "c", "Type": "custom", "Script": "XYZ.Makefile", "Cmp": ""},
+ // c++ 语言,使用默认评测脚本,答案比对方式为 NCMP(testlib)
{"Lang": "cpp", "Type": "default", "Script": "", "Cmp": "NCMP"}
], // 支持的语言
"Tasks": [
// 评测点信息
- {"Id": 1, "Points": 25}, // 第一个评测点,分值 25 分,使用 ./data/1.? 为测试数据
- {"Id": 2, "Points": 25},
- {"Id": 3, "Points": 25},
- {"Id": 4, "Points": 25}
+ {"Id": 1, "Points": 10}, // 第一个评测点,分值 25 分,使用 ./data/{input,output}/1.{input,output} 为测试数据
+ {"Id": 2, "Points": 20},
+ {"Id": 3, "Points": 30},
+ {"Id": 4, "Points": 40}
]
}
```
### 评测脚本
-见注释
-
-
-
-
-
-
-
-
-
-
-
-
+1. 默认评测脚本目前只支持 `c` 语言和 `cpp`,参见 `../../framework/template/default/{c,cpp}.Makefile`
+2. 自定义评测脚本参见 `./judge/XYZ.Makefile`
+3. `prebuild.Makefile`: 题目初始化脚本,用于编译辅助程序、生成数据等。如果存在改文件,系统会在题目分发到判机后自动执行,否则跳过改步骤
+## 注意事项
+1. 打包的时候确保 `config.json`, `data`, `judge` 在根目录下
diff --git a/resource/runner/problem/example/config.json b/resource/runner/problem/example/config.json
index 54dbaff..3e4effc 100644
--- a/resource/runner/problem/example/config.json
+++ b/resource/runner/problem/example/config.json
@@ -9,9 +9,9 @@
{"Lang": "cpp", "Type": "default", "Script": "", "Cmp": "NCMP"}
],
"Tasks": [
- {"Id": 1, "Points": 25},
- {"Id": 2, "Points": 25},
- {"Id": 3, "Points": 25},
- {"Id": 4, "Points": 25}
+ {"Id": 1, "Points": 10},
+ {"Id": 2, "Points": 20},
+ {"Id": 3, "Points": 30},
+ {"Id": 4, "Points": 40}
]
}
\ No newline at end of file
diff --git a/resource/runner/problem/example/description.md b/resource/runner/problem/example/description.md
new file mode 100644
index 0000000..9eadab5
--- /dev/null
+++ b/resource/runner/problem/example/description.md
@@ -0,0 +1,35 @@
+# A+B Problem
+
+## Description
+
+Given two integers `a` and `b`, calculate the sum of them.
+
+## Scope
+
+- $|a|,|b| \le {10}^9$
+
+## I/O Format
+
+### Input Format
+
+Two integers separated by spaces.
+
+### Output Format
+
+One integer.
+
+## Example Cases
+
+### Case 1
+
+#### Input
+
+```
+20 30
+```
+
+#### Output
+
+```
+50
+```
diff --git a/resource/runner/problem/example/judge/XYZ.Makefile b/resource/runner/problem/example/judge/XYZ.Makefile
index d15d183..1264083 100644
--- a/resource/runner/problem/example/judge/XYZ.Makefile
+++ b/resource/runner/problem/example/judge/XYZ.Makefile
@@ -17,6 +17,8 @@ compile:
$(CC) $(CFLAGS) -o $(PREFIX)/user/$(USER_PROG).out $(PREFIX)/user/$(USER_PROG).$(LANG) $(PREFIX)/judge/gadget.c
judge:
- # Rename on *.out.usr or *.judge is not allowed
- sed '/gadgets/d' $(PREFIX)/user/$(TEST_NUM).out.usr > $(PREFIX)/user/$(TEST_NUM).out.usr1
+ # !! Rename on *.out.usr or *.judge is not allowed !!
+ # 自定义评测方式
+ sed '/gadgets/d' $(PREFIX)/user/$(TEST_NUM).out.usr >$(PREFIX)/user/$(TEST_NUM).out.usr1
+ # 评测结果要求符合 testlib 的格式 -> $(TEST_NUM).judge
$(NCMP) $(PREFIX)/data/input/$(TEST_NUM).input $(PREFIX)/user/$(TEST_NUM).out.usr1 $(PREFIX)/data/output/$(TEST_NUM).output $(PREFIX)/user/$(TEST_NUM).judge -appes
diff --git a/resource/runner/problem/example/judge/prebuild.Makefile b/resource/runner/problem/example/judge/prebuild.Makefile
index 39daf6f..c07df39 100644
--- a/resource/runner/problem/example/judge/prebuild.Makefile
+++ b/resource/runner/problem/example/judge/prebuild.Makefile
@@ -1,7 +1,11 @@
include ${TEMPLATE}/c.mk ${TEMPLATE}/Judger.mk
+# 当题目被下载到 runner 后,会自动执行 prebuild 阶段,判题时不会再次执行
+
prebuild:
+ # 生成测试数据生成工具
clang++ -I$(TESTLIB) -Ofast -o $(PREFIX)/judge/gen.out $(PREFIX)/judge/gen.cpp
+ # 生成 2,4 号测试数据
@if [ ! -f $(PREFIX)/data/input/2.input ]; then \
$(PREFIX)/judge/gen.out > $(PREFIX)/data/input/2.input; \
python3 -c "print(sum(map(int, input().split())))" < $(PREFIX)/data/input/2.input > $(PREFIX)/data/output/2.output; \
diff --git a/resource/runner/scripts/common.sh b/resource/runner/scripts/common.sh
index cd875a6..408ed96 100755
--- a/resource/runner/scripts/common.sh
+++ b/resource/runner/scripts/common.sh
@@ -8,3 +8,10 @@ COLOR_NONE="\e[0m"
function log_info() { echo -e "${COLOR_GREEN}$*${COLOR_NONE}" 1>&2; }
function log_warn() { echo -e "${COLOR_YELLOW}$*${COLOR_NONE}" 1>&2; }
function log_error() { echo -e "${COLOR_RED}$*${COLOR_NONE}" 1>&2; }
+
+# Docker or Podman
+DOCKER="podman"
+if [ "$USE_DOCKER" ]; then
+ log_error "docker is deprecated"
+ log_info "Use podman instead"
+fi
diff --git a/resource/runner/scripts/prepare_container.sh b/resource/runner/scripts/prepare_container.sh
deleted file mode 100755
index e02c989..0000000
--- a/resource/runner/scripts/prepare_container.sh
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/usr/bin/env bash
-
-. common.sh
-
-cd "$(dirname "$0")"/../ || exit 1
-
-if [ -f ./.mark.docker ]; then
- log_warn "Docker containers already prepared"
- log_warn "If you want to re-prepare the containers, please remove the file $(pwd)/.mark.docker"
- exit 1
-fi
-
-# Full
-cat <ubuntu-full.Dockerfile
-FROM ubuntu:22.04
-WORKDIR /woj/
-
-# Install dependencies
-RUN apt-get update && apt-get install -y gcc g++ clang make cmake autoconf m4 libtool gperf git parallel python3 && apt-get clean && rm -rf /var/lib/apt/lists
-
-# Copy source code
-RUN mkdir -p /woj/framework && mkdir -p /woj/problem
-COPY framework /woj/framework
-
-# Build
-RUN cd /woj/framework/template && ./setup.sh
-RUN cd /woj/framework/scripts && ./setup.sh
-
-# Environment
-ENV WOJ_LAUNCHER=/woj/framework/scripts/woj_launcher
-ENV WOJ_SANDBOX=/woj/framework/scripts/libwoj_sandbox.so
-ENV TEMPLATE=/woj/framework/template
-ENV TESTLIB=/woj/framework/template/testlib
-ENV PREFIX=/woj/problem
-EOF
-docker build -t woj/ubuntu-full -f ubuntu-full.Dockerfile . || exit 1
-rm ubuntu-full.Dockerfile
-
-# Tiny
-cat <ubuntu-run.Dockerfile
-FROM woj/ubuntu-full:latest AS builder
-FROM ubuntu:22.04
-WORKDIR /woj/problem
-RUN mkdir -p /woj/framework/scripts
-COPY --from=builder /woj/framework/scripts/libwoj_sandbox.so /woj/framework/scripts/
-COPY --from=builder /woj/framework/scripts/woj_launcher /woj/framework/scripts/
-EOF
-docker build -t woj/ubuntu-run -f ubuntu-run.Dockerfile . || exit 1
-rm ubuntu-run.Dockerfile
-
-touch ./.mark.docker
diff --git a/resource/runner/scripts/prepare_images.sh b/resource/runner/scripts/prepare_images.sh
new file mode 100755
index 0000000..f03c696
--- /dev/null
+++ b/resource/runner/scripts/prepare_images.sh
@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+
+. common.sh
+
+cd "$(dirname "$0")"/../ || exit 1
+
+# Check Mark
+if [ -f ./.mark.image ]; then
+ log_warn "Docker images already prepared"
+ log_warn "If you want to re-prepare the images, please remove the file $(pwd)/.mark.image"
+ exit 1
+fi
+
+log_info "Preparing image..."
+log_info "Checking $DOCKER - $($DOCKER --version)"
+
+# Full
+if [ -f ./tmp/ubuntu-full.tar.gz ]; then
+ log_info "Importing Full Image"
+ gzip -d -c ./tmp/ubuntu-full.tar.gz | $DOCKER load || (log_error "Import Full Image failed" && exit 1)
+else
+ log_info "Pulling Full Image"
+ if ! $DOCKER pull git.0x7f.app/woj/ubuntu-full:latest; then
+ log_warn "Pull failed, building from scratch"
+ log_info "Building Full Image"
+ $DOCKER build -t git.0x7f.app/woj/ubuntu-full:latest -f scripts/ubuntu-full.Dockerfile . || (log_error "Build Full Image failed" && exit 1)
+ fi
+fi
+
+# Tiny
+if [ -f ./tmp/ubuntu-tiny.tar.gz ]; then
+ log_info "Importing Tiny Image"
+ gzip -d -c ./tmp/ubuntu-tiny.tar.gz | $DOCKER load || (log_error "Import Tiny Image failed" && exit 1)
+else
+ log_info "Pulling Tiny Image"
+ if ! $DOCKER pull git.0x7f.app/woj/ubuntu-run:latest; then
+ log_warn "Pull failed, building from scratch"
+ log_info "Building Tiny Image"
+ $DOCKER build -t git.0x7f.app/woj/ubuntu-run:latest -f scripts/ubuntu-run.Dockerfile . || (log_error "Build Tiny Image failed" && exit 1)
+ fi
+fi
+
+# Mark
+if [ "$1" == "save" ]; then
+ log_info "Saving Images"
+ $DOCKER save git.0x7f.app/woj/ubuntu-full:latest | gzip -9 >./tmp/ubuntu-full.tar.gz
+ $DOCKER save git.0x7f.app/woj/ubuntu-run:latest | gzip -9 >./tmp/ubuntu-tiny.tar.gz
+else
+ touch ./.mark.image
+fi
+
+log_info "Done"
diff --git a/resource/runner/scripts/problem.sh b/resource/runner/scripts/problem.sh
index ef407a2..a9f2754 100755
--- a/resource/runner/scripts/problem.sh
+++ b/resource/runner/scripts/problem.sh
@@ -9,67 +9,67 @@
# $3: language
# exports: Info_Script, Info_Cmp, Info_Num, Info_Limit_Time, Info_Limit_Memory, Info_Limit_NProc
function get_problem_info() {
- local err
+ local err
- if [ ! -f "$1/problem/$2/config.json" ]; then
- log_error "problem $2 not found"
- return 1
- fi
+ if [ ! -f "$1/problem/$2/config.json" ]; then
+ log_error "problem $2 not found"
+ return 1
+ fi
- parse_language_info "$1" "$2" "$3"
- err=$?
- if [ "$err" -ne 0 ]; then
- return "$err"
- fi
+ parse_language_info "$1" "$2" "$3"
+ err=$?
+ if [ "$err" -ne 0 ]; then
+ return "$err"
+ fi
- parse_limits "$1" "$2"
- err=$?
- if [ "$err" -ne 0 ]; then
- return "$err"
- fi
+ parse_limits "$1" "$2"
+ err=$?
+ if [ "$err" -ne 0 ]; then
+ return "$err"
+ fi
}
function parse_language_info() {
- export Info_Script
- export Info_Cmp
+ export Info_Script
+ export Info_Cmp
- local lang_config
- local lang_type
- local lang_script
+ local lang_config
+ local lang_type
+ local lang_script
- lang_config=$(jq ".Languages[] | select(.Lang == \"$3\")" "$1/problem/$2/config.json")
- if [ -z "$lang_config" ]; then
- log_error "language $3 is not supported"
- return 1
- fi
+ lang_config=$(jq ".Languages[] | select(.Lang == \"$3\")" "$1/problem/$2/config.json")
+ if [ -z "$lang_config" ]; then
+ log_error "language $3 is not supported"
+ return 1
+ fi
- Info_Cmp=$(echo "$lang_config" | jq -r ".Cmp")
+ Info_Cmp=$(echo "$lang_config" | jq -r ".Cmp")
- lang_type=$(echo "$lang_config" | jq -r ".Type")
- lang_script=$(echo "$lang_config" | jq -r ".Script")
+ lang_type=$(echo "$lang_config" | jq -r ".Type")
+ lang_script=$(echo "$lang_config" | jq -r ".Script")
- if [ "$lang_type" == "custom" ]; then
- Info_Script="/woj/problem/judge/$lang_script"
- elif [ "$lang_type" == "default" ]; then
- Info_Script="/woj/framework/template/default/$3.Makefile"
- else
- log_warn "Config file might be corrupted!"
- log_error "Unknown language type: $lang_type"
- return 1
- fi
+ if [ "$lang_type" == "custom" ]; then
+ Info_Script="/woj/problem/judge/$lang_script"
+ elif [ "$lang_type" == "default" ]; then
+ Info_Script="/woj/framework/template/default/$3.Makefile"
+ else
+ log_warn "Config file might be corrupted!"
+ log_error "Unknown language type: $lang_type"
+ return 1
+ fi
}
function parse_limits() {
- export Info_Limit_Time
- export Info_Limit_Memory
- export Info_Limit_NProc
- export Info_Num
+ export Info_Limit_Time
+ export Info_Limit_Memory
+ export Info_Limit_NProc
+ export Info_Num
- local cfg
- cfg="$1/problem/$2/config.json"
+ local cfg
+ cfg="$1/problem/$2/config.json"
- Info_Limit_Time=$(jq ".Runtime.TimeLimit" "$cfg")
- Info_Limit_Memory=$(jq ".Runtime.MemoryLimit" "$cfg")
- Info_Limit_NProc=$(jq ".Runtime.NProcLimit" "$cfg")
- Info_Num=$(jq ".Tasks | length" "$1/problem/$2/config.json")
+ Info_Limit_Time=$(jq ".Runtime.TimeLimit" "$cfg")
+ Info_Limit_Memory=$(jq ".Runtime.MemoryLimit" "$cfg")
+ Info_Limit_NProc=$(jq ".Runtime.NProcLimit" "$cfg")
+ Info_Num=$(jq ".Tasks | length" "$1/problem/$2/config.json")
}
diff --git a/resource/runner/scripts/problem_compile.sh b/resource/runner/scripts/problem_compile.sh
index 8f35ac6..a25ccfc 100755
--- a/resource/runner/scripts/problem_compile.sh
+++ b/resource/runner/scripts/problem_compile.sh
@@ -6,8 +6,8 @@ WORKSPACE=$(cd "$(dirname "$0")"/.. && pwd)
. "$WORKSPACE"/scripts/problem.sh
if [ "$1" == "" ] || [ ! -d "$WORKSPACE/problem/$1" ] || [ "$2" == "" ] || [ ! -d "$WORKSPACE/user/$2" ] || [ -z "$3" ]; then
- log_warn "Usage: $0 "
- exit 1
+ log_warn "Usage: $0 "
+ exit 1
fi
get_problem_info "$WORKSPACE" "$1" "$3"
@@ -20,11 +20,11 @@ rm -f "$EXE_FILE" && touch "$EXE_FILE"
export TIMEOUT=${4:-60}
docker_run \
- -v "$WORKSPACE"/problem/"$1"/judge:/woj/problem/judge:ro \
- -v "$SRC_FILE":/woj/problem/user/"$2"."$3":ro \
- -v "$EXE_FILE":/woj/problem/user/"$2".out \
- -e USER_PROG="$2" \
- -e LANG="$3" \
- woj/ubuntu-full \
- sh -c \
- "cd /woj/problem/user && make -f $Info_Script compile"
+ -v "$WORKSPACE"/problem/"$1"/judge:/woj/problem/judge:ro \
+ -v "$SRC_FILE":/woj/problem/user/"$2"."$3":ro \
+ -v "$EXE_FILE":/woj/problem/user/"$2".out \
+ -e USER_PROG="$2" \
+ -e LANG="$3" \
+ git.0x7f.app/woj/ubuntu-full \
+ sh -c \
+ "cd /woj/problem/user && make -f $Info_Script compile"
diff --git a/resource/runner/scripts/problem_judge.sh b/resource/runner/scripts/problem_judge.sh
index 0142ee2..b262d6f 100755
--- a/resource/runner/scripts/problem_judge.sh
+++ b/resource/runner/scripts/problem_judge.sh
@@ -6,35 +6,35 @@ WORKSPACE=$(cd "$(dirname "$0")"/.. && pwd)
. "$WORKSPACE"/scripts/problem.sh
if [ "$1" == "" ] || [ ! -d "$WORKSPACE/problem/$1" ] || [ "$2" == "" ] || [ ! -d "$WORKSPACE/user/$2" ] || [ -z "$3" ]; then
- log_warn "Usage: $0 "
- exit 1
+ log_warn "Usage: $0 "
+ exit 1
fi
get_problem_info "$WORKSPACE" "$1" "$3"
export TIMEOUT=${4:-60}
for test_num in $(seq "$Info_Num"); do
- std_file="$WORKSPACE/problem/$1/data/output/$test_num.output"
- ans_file="$WORKSPACE/user/$2/$test_num.out.usr"
- jdg_file="$WORKSPACE/user/$2/$test_num.judge"
+ std_file="$WORKSPACE/problem/$1/data/output/$test_num.output"
+ ans_file="$WORKSPACE/user/$2/$test_num.out.usr"
+ jdg_file="$WORKSPACE/user/$2/$test_num.judge"
- if [ ! -f "$std_file" ] || [ ! -f "$ans_file" ]; then
- log_error "Missing test case $test_num"
- exit 1
- fi
+ if [ ! -f "$std_file" ] || [ ! -f "$ans_file" ]; then
+ log_error "Missing test case $test_num"
+ exit 1
+ fi
- log_info "Judging test case $test_num"
+ log_info "Judging test case $test_num"
- touch "$jdg_file"
+ touch "$jdg_file"
- docker_run \
- -v "$WORKSPACE"/problem/"$1"/judge:/woj/problem/judge:ro \
- -v "$WORKSPACE"/problem/"$1"/data:/woj/problem/data:ro \
- -v "$ans_file":/woj/problem/user/"$test_num".out.usr \
- -v "$jdg_file":/woj/problem/user/"$test_num".judge \
- -e TEST_NUM="$test_num" \
- -e CMP="$Info_Cmp" \
- woj/ubuntu-full \
- sh -c \
- "cd /woj/problem/user && make -f $Info_Script judge"
+ docker_run \
+ -v "$WORKSPACE"/problem/"$1"/judge:/woj/problem/judge:ro \
+ -v "$WORKSPACE"/problem/"$1"/data:/woj/problem/data:ro \
+ -v "$ans_file":/woj/problem/user/"$test_num".out.usr \
+ -v "$jdg_file":/woj/problem/user/"$test_num".judge \
+ -e TEST_NUM="$test_num" \
+ -e CMP="$Info_Cmp" \
+ git.0x7f.app/woj/ubuntu-full \
+ sh -c \
+ "cd /woj/problem/user && make -f $Info_Script judge"
done
diff --git a/resource/runner/scripts/problem_prebuild.sh b/resource/runner/scripts/problem_prebuild.sh
index 3b97164..e054688 100755
--- a/resource/runner/scripts/problem_prebuild.sh
+++ b/resource/runner/scripts/problem_prebuild.sh
@@ -5,28 +5,28 @@ WORKSPACE=$(cd "$(dirname "$0")"/.. && pwd)
. "$WORKSPACE"/scripts/common.sh
if [ "$1" == "" ] || [ ! -d "$WORKSPACE/problem/$1" ]; then
- log_warn "Usage: $0 "
- exit 1
+ log_warn "Usage: $0 "
+ exit 1
fi
if [ -f "$WORKSPACE/problem/$1/.mark.prebuild" ]; then
- log_warn "Problem $1 already prebuilt"
- log_warn "If you want to re-prebuild the problem, please remove the file $WORKSPACE/problem/$1/.mark.prebuild"
- exit 0
+ log_warn "Problem $1 already prebuilt"
+ log_warn "If you want to re-prebuild the problem, please remove the file $WORKSPACE/problem/$1/.mark.prebuild"
+ exit 0
fi
if [ ! -f "$WORKSPACE/problem/$1/judge/prebuild.Makefile" ]; then
- log_warn "Problem $1 does not have prebuild scripts"
- log_warn "$WORKSPACE/problem/$1/.mark.prebuild"
- exit 0
+ log_warn "Problem $1 does not have prebuild scripts"
+ log_warn "$WORKSPACE/problem/$1/.mark.prebuild"
+ exit 0
fi
export TIMEOUT=${2:-300}
docker_run \
- -v "$WORKSPACE/problem/$1/data":/woj/problem/data \
- -v "$WORKSPACE/problem/$1/judge":/woj/problem/judge \
- -e PREFIX=/woj/problem \
- woj/ubuntu-full \
- sh -c "cd /woj/problem/judge && make -f prebuild.Makefile prebuild && touch .mark.prebuild"
+ -v "$WORKSPACE/problem/$1/data":/woj/problem/data \
+ -v "$WORKSPACE/problem/$1/judge":/woj/problem/judge \
+ -e PREFIX=/woj/problem \
+ git.0x7f.app/woj/ubuntu-full \
+ sh -c "cd /woj/problem/judge && make -f prebuild.Makefile prebuild && touch .mark.prebuild"
mv "$WORKSPACE/problem/$1/judge/.mark.prebuild" "$WORKSPACE/problem/$1/.mark.prebuild" || exit 1
diff --git a/resource/runner/scripts/problem_run.sh b/resource/runner/scripts/problem_run.sh
index 3fb4ae3..03da64d 100755
--- a/resource/runner/scripts/problem_run.sh
+++ b/resource/runner/scripts/problem_run.sh
@@ -6,20 +6,20 @@ WORKSPACE=$(cd "$(dirname "$0")"/.. && pwd)
. "$WORKSPACE"/scripts/problem.sh
if [ "$1" == "" ] || [ ! -d "$WORKSPACE/problem/$1" ] || [ "$2" == "" ] || [ ! -d "$WORKSPACE/user/$2" ] || [ -z "$3" ]; then
- log_warn "Usage: $0 "
- exit 1
+ log_warn "Usage: $0 "
+ exit 1
fi
if [ ! -f "$WORKSPACE/problem/$1/.mark.prebuild" ]; then
- log_warn "Problem $1 has not been prebuilt"
- log_warn "Please run 'problem_prebuild.sh $1' first"
- exit 1
+ log_warn "Problem $1 has not been prebuilt"
+ log_warn "Please run 'problem_prebuild.sh $1' first"
+ exit 1
fi
if [ ! -f "$WORKSPACE/user/$2/$2.out" ]; then
- log_warn "User $2 has not been compiled"
- log_warn "Please run 'problem_compile.sh ...' first"
- exit 1
+ log_warn "User $2 has not been compiled"
+ log_warn "Please run 'problem_compile.sh ...' first"
+ exit 1
fi
parse_limits "$WORKSPACE" "$1"
@@ -35,37 +35,37 @@ TIMEOUT=$(((LIMIT_TIME + 1000) / 1000 + 4))
log_info "Timeout: $TIMEOUT"
for test_num in $(seq "$Info_Num"); do
- test_case="$WORKSPACE/problem/$1/data/input/$test_num.input"
- exe_file="$WORKSPACE/user/$2/$2.out"
- ans_file="$WORKSPACE/user/$2/$test_num.out.usr"
- ifo_file="$WORKSPACE/user/$2/$test_num.info"
+ test_case="$WORKSPACE/problem/$1/data/input/$test_num.input"
+ exe_file="$WORKSPACE/user/$2/$2.out"
+ ans_file="$WORKSPACE/user/$2/$test_num.out.usr"
+ ifo_file="$WORKSPACE/user/$2/$test_num.info"
- if [ ! -f "$test_case" ]; then
- log_error "Test case $test_num does not exist"
- exit 1
- fi
+ if [ ! -f "$test_case" ]; then
+ log_error "Test case $test_num does not exist"
+ exit 1
+ fi
- log_info "Running test case $test_num"
- rm -f "$ans_file" && touch "$ans_file"
- rm -f "$ifo_file" && touch "$ifo_file"
- docker_run \
- --cpus 1 \
- --network none \
- -v "$test_case":/woj/problem/data/input/"$test_num".input:ro \
- -v "$exe_file":/woj/user/"$2".out:ro \
- -v "$ans_file":/woj/user/"$test_num".out.usr \
- -v "$ifo_file":/woj/user/"$test_num".info \
- woj/ubuntu-run \
- sh -c \
- "cd /woj/user && /woj/framework/scripts/woj_launcher \
- --memory_limit=$Info_Limit_Memory \
- --nproc_limit=$Info_Limit_NProc \
- --time_limit=$Info_Limit_Time \
- --sandbox_path=/woj/framework/scripts/libwoj_sandbox.so \
- --sandbox_template=$3 \
- --sandbox_action=nothing \
- --file_input=/woj/problem/data/input/$test_num.input \
- --file_output=/woj/user/$test_num.out.usr \
- --file_info=/woj/user/$test_num.info \
- --program=/woj/user/$2.out"
+ log_info "Running test case $test_num"
+ rm -f "$ans_file" && touch "$ans_file"
+ rm -f "$ifo_file" && touch "$ifo_file"
+ docker_run \
+ --cpus 1 \
+ --network none \
+ -v "$test_case":/woj/problem/data/input/"$test_num".input:ro \
+ -v "$exe_file":/woj/user/"$2".out:ro \
+ -v "$ans_file":/woj/user/"$test_num".out.usr \
+ -v "$ifo_file":/woj/user/"$test_num".info \
+ git.0x7f.app/woj/ubuntu-run \
+ sh -c \
+ "cd /woj/user && /woj/framework/scripts/woj_launcher \
+ --memory_limit=$Info_Limit_Memory \
+ --nproc_limit=$Info_Limit_NProc \
+ --time_limit=$Info_Limit_Time \
+ --sandbox_path=/woj/framework/scripts/libwoj_sandbox.so \
+ --sandbox_template=$3 \
+ --sandbox_action=nothing \
+ --file_input=/woj/problem/data/input/$test_num.input \
+ --file_output=/woj/user/$test_num.out.usr \
+ --file_info=/woj/user/$test_num.info \
+ --program=/woj/user/$2.out"
done
diff --git a/resource/runner/scripts/run_timeout.sh b/resource/runner/scripts/run_timeout.sh
index 0cafef9..20b64f2 100755
--- a/resource/runner/scripts/run_timeout.sh
+++ b/resource/runner/scripts/run_timeout.sh
@@ -3,14 +3,17 @@
. common.sh
function docker_run() {
- local timeout=${TIMEOUT:-10}
- local log_file=${LOG_FILE:-/dev/stderr}
- log_info "Docker run with timeout $timeout"
- CONTAINER_NAME=$(uuidgen)
- (
- sleep "$timeout"
- docker kill "$CONTAINER_NAME"
- ) &
- docker run --rm --name "$CONTAINER_NAME" "$@" > "$log_file" 2>&1
- pkill -P $$
+ local timeout=${TIMEOUT:-10}
+ local log_file=${LOG_FILE:-"/dev/stderr"}
+ local log_limit=${LOG_LIMIT:-4K}
+ log_info "$DOCKER run with timeout $timeout"
+ CONTAINER_NAME=$(uuidgen)
+ (
+ sleep "$timeout"
+ $DOCKER kill "$CONTAINER_NAME"
+ ) &
+ $DOCKER run --rm --name "$CONTAINER_NAME" "$@" 2>&1 | head -c "$log_limit" >"$log_file"
+ pkill -P $$
+ $DOCKER kill "$CONTAINER_NAME" >/dev/null 2>&1
+ return 0
}
diff --git a/resource/runner/scripts/ubuntu-full.Dockerfile b/resource/runner/scripts/ubuntu-full.Dockerfile
new file mode 100644
index 0000000..a7391da
--- /dev/null
+++ b/resource/runner/scripts/ubuntu-full.Dockerfile
@@ -0,0 +1,20 @@
+FROM docker.io/library/ubuntu:22.04
+WORKDIR /woj/
+
+# Install dependencies
+RUN apt-get update && apt-get upgrade -y && apt-get install -y gcc g++ clang make cmake autoconf m4 libtool gperf git parallel python3 && apt-get clean && rm -rf /var/lib/apt/lists
+
+# Copy source code
+RUN mkdir -p /woj/framework && mkdir -p /woj/problem
+COPY framework /woj/framework
+
+# Build
+RUN cd /woj/framework/template && ./setup.sh
+RUN cd /woj/framework/scripts && ./setup.sh
+
+# Environment
+ENV WOJ_LAUNCHER=/woj/framework/scripts/woj_launcher
+ENV WOJ_SANDBOX=/woj/framework/scripts/libwoj_sandbox.so
+ENV TEMPLATE=/woj/framework/template
+ENV TESTLIB=/woj/framework/template/testlib
+ENV PREFIX=/woj/problem
diff --git a/resource/runner/scripts/ubuntu-run.Dockerfile b/resource/runner/scripts/ubuntu-run.Dockerfile
new file mode 100644
index 0000000..44839ba
--- /dev/null
+++ b/resource/runner/scripts/ubuntu-run.Dockerfile
@@ -0,0 +1,8 @@
+FROM woj/ubuntu-full:latest AS builder
+FROM docker.io/library/ubuntu:22.04
+
+WORKDIR /woj/problem
+RUN mkdir -p /woj/framework/scripts
+
+COPY --from=builder /woj/framework/scripts/libwoj_sandbox.so /woj/framework/scripts/
+COPY --from=builder /woj/framework/scripts/woj_launcher /woj/framework/scripts/
diff --git a/resource/runner/tmp/.gitignore b/resource/runner/tmp/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/resource/runner/tmp/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/resource/runner/user/.gitignore b/resource/runner/user/.gitignore
index 2699330..d6b7ef3 100644
--- a/resource/runner/user/.gitignore
+++ b/resource/runner/user/.gitignore
@@ -1 +1,2 @@
-/test_user
\ No newline at end of file
+*
+!.gitignore