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