diff --git a/.dockerignore b/.dockerignore index 58ce30a..cb44ba9 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,4 @@ dist data volumes +/front/node_modules diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..84afbe0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +pkg/proto/** linguist-vendored +front/src/proto/** linguist-vendored diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 20ebd96..8f72a78 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,12 +8,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version-file: 'go.mod' @@ -30,7 +30,7 @@ jobs: make setup-release - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v4 + uses: goreleaser/goreleaser-action@v6 with: version: latest args: release @@ -38,13 +38,13 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Login to GitHub Container Registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -57,7 +57,7 @@ jobs: - name: Build and push full client image id: docker_build_full - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v6 with: context: . file: client_env/Dockerfile @@ -72,7 +72,7 @@ jobs: - name: Build and push light client image id: docker_build_light - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v6 with: context: . file: client_env/Dockerfile @@ -87,7 +87,7 @@ jobs: - name: Build and push sage client image id: docker_build_sage - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v6 with: context: . file: client_env/Dockerfile @@ -99,4 +99,3 @@ jobs: tags: | ghcr.io/${{ steps.repo_name.outputs.lowercase }}/neo_env_sage:latest ghcr.io/${{ steps.repo_name.outputs.lowercase }}/neo_env_sage:${{ steps.get_version.outputs.VERSION }} - diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 85d9764..e634fae 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,47 +7,69 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version-file: 'go.mod' - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v6 with: version: latest - args: --config .golangci.yml - name: Run tests run: make test-cov - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 proto: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - uses: bufbuild/buf-setup-action@v1 - uses: bufbuild/buf-lint-action@v1 with: input: proto - config: proto/buf.yaml + + front: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + package_json_file: 'front/package.json' + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version-file: 'front/.nvmrc' + cache: 'pnpm' + cache-dependency-path: 'front/pnpm-lock.yaml' + + - name: Install deps + run: cd front && pnpm i --frozen-lockfile + + - name: run linter + run: make lint-front image: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Expose GitHub Runtime - uses: crazy-max/ghaction-github-runtime@v2 + uses: crazy-max/ghaction-github-runtime@v3 - name: Build && test client image run: | diff --git a/.golangci.yml b/.golangci.yml index 74042a2..8f639aa 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,8 +1,6 @@ run: timeout: 5m tests: true - skip-dirs: - - pkg/apps/.* linters: enable: @@ -13,7 +11,6 @@ linters: - errname - errorlint - exhaustive - - exportloopref - gocritic - gofmt - goimports diff --git a/.goreleaser.yml b/.goreleaser.yml index f82861b..ecceda5 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -44,8 +44,8 @@ archives: - cmd - internal - pkg - - proto/go - monitoring + - front - id: "client_env" format: zip diff --git a/Dockerfile b/Dockerfile index 2cdbdc9..ee39cde 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.21-alpine as build +FROM golang:1.23-alpine as build ENV CGO_ENABLED=0 @@ -6,7 +6,6 @@ WORKDIR /app COPY go.* ./ COPY cmd cmd COPY internal internal -COPY proto/go proto/go COPY pkg pkg RUN --mount=type=cache,target=/root/.cache/go-build \ --mount=type=cache,target=/go/pkg/mod \ @@ -15,8 +14,22 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ -o neo_server \ cmd/server/main.go +FROM node:20-slim AS front-base +ENV PNPM_HOME="/pnpm" +ENV PATH="$PNPM_HOME:$PATH" +RUN corepack enable + +COPY front /app +WORKDIR /app + +FROM front-base AS front-build +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile +RUN pnpm run build + FROM alpine -COPY --from=build /app/neo_server /neo_server +WORKDIR /app +COPY --from=build /app/neo_server neo_server +COPY --from=front-build /app/dist front/dist -CMD ["/neo_server", "--config", "/config.yml"] +CMD ["./neo_server", "--config", "/config.yml"] diff --git a/Makefile b/Makefile index f1b987e..a556955 100644 --- a/Makefile +++ b/Makefile @@ -15,12 +15,16 @@ lint-go: lint-proto: cd proto && buf lint +.PHONY: lint-front +lint-front: + cd front && pnpm lint + .PHONY: lint -lint: lint-go lint-proto +lint: lint-go lint-proto lint-front .PHONY: goimports goimports: - gofancyimports fix --local github.com/c4t-but-s4d/neo -w $(shell find . -type f -name '*.go' -not -path "./proto/*") + gofancyimports fix --local github.com/c4t-but-s4d/neo/v2 -w $(shell find . -type f -name '*.go' -not -path "./pkg/proto/*") .PHONY: test test: @@ -32,6 +36,7 @@ validate: lint test .PHONY: proto proto: cd proto && buf generate + cd front && ./add_ts_ignore.sh .PHONY: test-cov test-cov: diff --git a/client_env/Dockerfile b/client_env/Dockerfile index 967f6d6..8f7d4d5 100644 --- a/client_env/Dockerfile +++ b/client_env/Dockerfile @@ -1,28 +1,32 @@ -FROM golang:1.21 as build +FROM golang:1.23-alpine AS build ENV CGO_ENABLED=0 +RUN apk add upx + WORKDIR /app COPY go.* ./ COPY cmd cmd COPY internal internal -COPY proto/go proto/go COPY pkg pkg RUN --mount=type=cache,target=/root/.cache/go-build \ --mount=type=cache,target=/go/pkg/mod \ go build \ + -trimpath \ -ldflags="-s -w" \ -o client \ - cmd/client/main.go - -RUN --mount=type=cache,target=/root/.cache/go-build \ - --mount=type=cache,target=/go/pkg/mod \ + cmd/client/main.go && \ go build \ + -trimpath \ -ldflags="-s -w" \ -o reaper \ - cmd/reaper/main.go + cmd/reaper/main.go && \ + upx --lzma -9 client && \ + upx --lzma -9 reaper + +FROM ubuntu:24.04 AS image-full -FROM ubuntu:jammy as image-full +ENV PIP_BREAK_SYSTEM_PACKAGES=1 SHELL ["/bin/bash", "-o", "pipefail", "-c"] @@ -67,7 +71,7 @@ RUN chmod +x /entrypoint.sh WORKDIR /work ENTRYPOINT ["/entrypoint.sh"] -FROM image-full as image-full-sage +FROM image-full AS image-full-sage RUN --mount=type=cache,sharing=private,target=/var/cache/apt \ apt-get update && \ @@ -75,7 +79,7 @@ RUN --mount=type=cache,sharing=private,target=/var/cache/apt \ sagemath && \ rm -rf /var/lib/apt/lists/* -FROM alpine:3.16 as image-light +FROM alpine:3.20 AS image-light RUN apk add bash dbus diff --git a/cmd/client/cli/add.go b/cmd/client/cli/add.go index 53b6061..2bd611c 100644 --- a/cmd/client/cli/add.go +++ b/cmd/client/cli/add.go @@ -19,7 +19,7 @@ import ( "github.com/c4t-but-s4d/neo/v2/internal/client" "github.com/c4t-but-s4d/neo/v2/pkg/archive" - epb "github.com/c4t-but-s4d/neo/v2/proto/go/exploits" + epb "github.com/c4t-but-s4d/neo/v2/pkg/proto/exploits" ) type addCLI struct { diff --git a/cmd/client/cli/run.go b/cmd/client/cli/run.go index cf25b38..96c52f5 100644 --- a/cmd/client/cli/run.go +++ b/cmd/client/cli/run.go @@ -17,7 +17,7 @@ const JobsPerCPU = 5 type runCLI struct { *baseCLI run *exploit.Runner - sender *joblogger.RemoteSender + sender joblogger.Sender } func parseJobsFlag(cmd *cobra.Command, name string) int { diff --git a/cmd/client/cli/tail.go b/cmd/client/cli/tail.go index 946384e..112ceeb 100644 --- a/cmd/client/cli/tail.go +++ b/cmd/client/cli/tail.go @@ -8,7 +8,7 @@ import ( "github.com/spf13/cobra" "github.com/c4t-but-s4d/neo/v2/internal/client" - logspb "github.com/c4t-but-s4d/neo/v2/proto/go/logs" + logspb "github.com/c4t-but-s4d/neo/v2/pkg/proto/logs" ) type tailCLI struct { diff --git a/cmd/client/cli/update.go b/cmd/client/cli/update.go index 040b2a3..1c9b99b 100644 --- a/cmd/client/cli/update.go +++ b/cmd/client/cli/update.go @@ -11,8 +11,7 @@ import ( "google.golang.org/protobuf/types/known/durationpb" "github.com/c4t-but-s4d/neo/v2/internal/client" - - epb "github.com/c4t-but-s4d/neo/v2/proto/go/exploits" + epb "github.com/c4t-but-s4d/neo/v2/pkg/proto/exploits" ) type updateCLI struct { diff --git a/cmd/client/cli/utils.go b/cmd/client/cli/utils.go index 8cb9246..46a029b 100644 --- a/cmd/client/cli/utils.go +++ b/cmd/client/cli/utils.go @@ -5,7 +5,7 @@ import ( "github.com/samber/lo" - "github.com/c4t-but-s4d/neo/v2/proto/go/exploits" + "github.com/c4t-but-s4d/neo/v2/pkg/proto/exploits" ) func isBinary(data []byte) bool { diff --git a/cmd/client/cmd/root.go b/cmd/client/cmd/root.go index d0f5812..71e60ab 100644 --- a/cmd/client/cmd/root.go +++ b/cmd/client/cmd/root.go @@ -33,12 +33,10 @@ func init() { rootCmd.PersistentFlags().StringP("config", "c", "client_config.yml", "config file") rootCmd.PersistentFlags().BoolP("verbose", "v", true, "enable debug logging") rootCmd.PersistentFlags().String("host", "127.0.0.1:5005", "server host") - rootCmd.PersistentFlags().String("metrics_host", "127.0.0.1:9091", "pushgateway host") mustBindPersistent(rootCmd, "config") mustBindPersistent(rootCmd, "host") mustBindPersistent(rootCmd, "verbose") - mustBindPersistent(rootCmd, "metrics_host") } func mustBindPersistent(c *cobra.Command, flag string) { diff --git a/cmd/server/main.go b/cmd/server/main.go index 03c749b..15feb35 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -2,8 +2,8 @@ package main import ( "context" + "errors" "fmt" - "net" "net/http" "os/signal" "strings" @@ -20,15 +20,18 @@ import ( "google.golang.org/grpc/reflection" "github.com/c4t-but-s4d/neo/v2/internal/logger" + "github.com/c4t-but-s4d/neo/v2/internal/logstor" "github.com/c4t-but-s4d/neo/v2/internal/server/config" "github.com/c4t-but-s4d/neo/v2/internal/server/exploits" "github.com/c4t-but-s4d/neo/v2/internal/server/fs" logs "github.com/c4t-but-s4d/neo/v2/internal/server/logs" "github.com/c4t-but-s4d/neo/v2/pkg/grpcauth" + "github.com/c4t-but-s4d/neo/v2/pkg/mu" + "github.com/c4t-but-s4d/neo/v2/pkg/neohttp" "github.com/c4t-but-s4d/neo/v2/pkg/neosync" - epb "github.com/c4t-but-s4d/neo/v2/proto/go/exploits" - fspb "github.com/c4t-but-s4d/neo/v2/proto/go/fileserver" - logspb "github.com/c4t-but-s4d/neo/v2/proto/go/logs" + epb "github.com/c4t-but-s4d/neo/v2/pkg/proto/exploits" + fspb "github.com/c4t-but-s4d/neo/v2/pkg/proto/fileserver" + logspb "github.com/c4t-but-s4d/neo/v2/pkg/proto/logs" ) func main() { @@ -56,7 +59,7 @@ func main() { logrus.Fatalf("Failed to create bolt storage: %v", err) } - logStore, err := logs.NewLogStorage(initCtx, cfg.RedisURL) + logStore, err := logstor.NewRedisStorage(initCtx, cfg.RedisURL) if err != nil { logrus.Fatalf("Failed to create log storage: %v", err) } @@ -73,11 +76,6 @@ func main() { } logsServer := logs.New(logStore) - lis, err := net.Listen("tcp", cfg.Addr) - if err != nil { - logrus.Fatalf("Failed to listen: %v", err) - } - var opts []grpc.ServerOption if cfg.GrpcAuthKey != "" { authInterceptor := grpcauth.NewServerInterceptor(cfg.GrpcAuthKey) @@ -91,20 +89,29 @@ func main() { logspb.RegisterServiceServer(s, logsServer) reflection.Register(s) - http.Handle("/metrics", promhttp.Handler()) - go func() { - logrus.Infof("Starting metrics server on %s", viper.GetString("metrics.address")) - if err := http.ListenAndServe(viper.GetString("metrics.address"), http.DefaultServeMux); err != nil { - logrus.Fatalf("Failed to serve metrics: %v", err) - } - }() + httpMux := http.NewServeMux() + httpMux.Handle("/", neohttp.StaticHandler(cfg.StaticDir)) + + muHandler := mu.NewHandler(s, mu.WithHTTPHandler(httpMux)) + httpServer := &http.Server{ + Handler: muHandler, + Addr: cfg.Address, + } + + // Separate server to make it private. + metricsMux := http.NewServeMux() + metricsMux.Handle("/metrics", promhttp.Handler()) + metricsServer := &http.Server{ + Handler: metricsMux, + Addr: cfg.MetricsAddress, + } runCtx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT) defer cancel() wg := sync.WaitGroup{} - wg.Add(3) + wg.Add(4) go func() { defer wg.Done() exploitsServer.HeartBeat(runCtx) @@ -117,11 +124,28 @@ func main() { defer wg.Done() <-runCtx.Done() logrus.Info("Received shutdown signal, stopping server") - s.GracefulStop() + + shutdownCtx, shutdownCancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT) + defer shutdownCancel() + shutdownCtx, shutdownCancel = context.WithTimeout(shutdownCtx, 5*time.Second) + defer shutdownCancel() + + if err := httpServer.Shutdown(shutdownCtx); err != nil { + logrus.Errorf("Failed to shutdown http server: %v", err) + } + if err := metricsServer.Shutdown(shutdownCtx); err != nil { + logrus.Errorf("Failed to shutdown metrics server: %v", err) + } + }() + go func() { + defer wg.Done() + if err := metricsServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + logrus.Fatalf("Failed to serve metrics: %v", err) + } }() - logrus.Infof("Starting server on %s", cfg.Addr) - if err := s.Serve(lis); err != nil { + logrus.Infof("Starting multiproto server on %s", cfg.Address) + if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { logrus.Fatalf("Failed to serve: %v", err) } @@ -149,12 +173,19 @@ func setupConfig() error { viper.MustBindEnv("grpc_auth_key") viper.MustBindEnv("farm.password") viper.MustBindEnv("farm.url") + viper.MustBindEnv("db_path") + viper.MustBindEnv("redis_url") + viper.MustBindEnv("base_dir") viper.SetDefault("config", "server_config.yml") viper.SetDefault("ping_every", time.Second*5) viper.SetDefault("submit_every", time.Second*2) - viper.SetDefault("metrics.address", ":3000") - viper.SetDefault("addr", ":5005") + viper.SetDefault("address", ":5005") + viper.SetDefault("metrics_address", ":3000") + viper.SetDefault("static_dir", "front/dist") + viper.SetDefault("redis_url", "redis://127.0.0.1:6379/0") + viper.SetDefault("db_path", "data/db.db") + viper.SetDefault("base_dir", "data/exploits") return nil } diff --git a/compose.yml b/compose.yml index 1919ee1..af276f1 100644 --- a/compose.yml +++ b/compose.yml @@ -4,10 +4,15 @@ services: volumes: - "./configs/server/config.yml:/config.yml:ro" - "./volumes/data:/data" + extra_hosts: + - "host.docker.internal:host-gateway" environment: NEO_GRPC_AUTH_KEY: ${GRPC_AUTH_KEY} NEO_FARM_PASSWORD: ${FARM_PASSWORD} NEO_FARM_URL: ${FARM_URL} + NEO_DB_PATH: /data/db.db + NEO_REDIS_URL: 'redis://redis:6379/0' + NEO_BASE_DIR: '/data/exploits' ports: - '5005:5005' restart: unless-stopped @@ -18,6 +23,7 @@ services: grafana: image: grafana/grafana-oss:10.0.3 + user: '0' volumes: - ./volumes/grafana:/var/lib/grafana - ./monitoring/cfg/grafana/provisioning:/etc/grafana/provisioning @@ -33,6 +39,7 @@ services: victoria: image: victoriametrics/victoria-metrics:v1.92.1 + user: '0' volumes: - ./monitoring/cfg/prometheus:/etc/prometheus - ./volumes/victoria-metrics:/victoria-metrics-data @@ -42,7 +49,15 @@ services: - "-storageDataPath=/victoria-metrics-data" - "-promscrape.config=/etc/prometheus/config.yml" restart: unless-stopped - cpus: 2 - mem_limit: 4gb + + victoria-proxy: + image: caddy:2.6.1-alpine + restart: unless-stopped + environment: + HTTP_BASIC_AUTH_USER: "${VICTORIA_USER:-admin}" + HTTP_BASIC_AUTH_PASSWORD: "${VICTORIA_PASSWORD:-1234}" + command: "/bin/sh -c 'export HTTP_BASIC_AUTH_PASSWORD_HASH=$(caddy hash-password --plaintext $$HTTP_BASIC_AUTH_PASSWORD) && caddy run --config /etc/caddy/Caddyfile'" ports: - "8428:8428" + volumes: + - "./monitoring/cfg/victoria-proxy:/etc/caddy" diff --git a/configs/client/config.yml b/configs/client/config.yml index 26a0376..d76b093 100644 --- a/configs/client/config.yml +++ b/configs/client/config.yml @@ -1,4 +1,8 @@ host: "localhost:5005" -metrics_host: "localhost:8428/api/v1/import/prometheus" exploit_dir: "exploits" grpc_auth_key: "s3cret_t0ken_pls_d0nt_leak" + +metrics: + url: "localhost:8428/api/v1/import/prometheus" + user: "admin" + password: "1234" \ No newline at end of file diff --git a/configs/server/config.yml b/configs/server/config.yml index 48f3387..86819f5 100644 --- a/configs/server/config.yml +++ b/configs/server/config.yml @@ -1,13 +1,9 @@ -db_path: "data/db.db" -redis_url: 'redis://redis:6379/0' -base_dir: "data/exploits" ping_every: "5s" submit_every: "2s" grpc_auth_key: "s3cret_t0ken_pls_d0nt_leak" farm: - # url: "http://127.0.0.1:5137" - url: "http://192.168.82.252:5137" + url: "http://host.docker.internal:5137" password: "1234" env: diff --git a/front/.eslintrc.cjs b/front/.eslintrc.cjs new file mode 100644 index 0000000..6850255 --- /dev/null +++ b/front/.eslintrc.cjs @@ -0,0 +1,26 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/strict-type-checked", + "plugin:react-hooks/recommended", + "plugin:react/recommended", + "plugin:react/jsx-runtime", + ], + ignorePatterns: ["dist", ".eslintrc.cjs", "src/proto/**/*"], + parser: "@typescript-eslint/parser", + plugins: ["react-refresh"], + rules: { + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true }, + ], + }, + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + project: ["./tsconfig.json", "./tsconfig.node.json"], + tsconfigRootDir: __dirname, + }, +}; diff --git a/front/.gitignore b/front/.gitignore new file mode 100644 index 0000000..29a21ea --- /dev/null +++ b/front/.gitignore @@ -0,0 +1,15 @@ +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/front/.nvmrc b/front/.nvmrc new file mode 100644 index 0000000..bb8c76c --- /dev/null +++ b/front/.nvmrc @@ -0,0 +1 @@ +v22.11.0 diff --git a/front/add_ts_ignore.sh b/front/add_ts_ignore.sh new file mode 100755 index 0000000..6e4f687 --- /dev/null +++ b/front/add_ts_ignore.sh @@ -0,0 +1,5 @@ +#!/bin/bash -e + +for file in $(find src/proto -name '*.ts' -type f); do + (echo "// @ts-nocheck" && cat "${file}") > .kek && mv .kek "${file}" +done \ No newline at end of file diff --git a/front/index.html b/front/index.html new file mode 100644 index 0000000..92b6e10 --- /dev/null +++ b/front/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/front/package.json b/front/package.json new file mode 100644 index 0000000..1faf0f9 --- /dev/null +++ b/front/package.json @@ -0,0 +1,46 @@ +{ + "name": "front", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "@emotion/react": "^11.11.1", + "@emotion/styled": "^11.11.0", + "@fontsource/roboto": "^5.0.8", + "@mui/icons-material": "^5.14.16", + "@mui/material": "^5.14.17", + "@types/react-window": "^1.8.8", + "long": "^5.2.3", + "nice-grpc-common": "^2.0.2", + "nice-grpc-web": "^3.3.2", + "protobufjs": "^7.2.5", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-helmet-async": "^1.3.0", + "react-hook-form": "^7.48.2", + "react-router-dom": "^6.18.0", + "react-window": "^1.8.9", + "react-window-infinite-loader": "^1.0.9" + }, + "devDependencies": { + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@types/react-window-infinite-loader": "^1.0.9", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "@vitejs/plugin-react": "^4.0.3", + "eslint": "^8.45.0", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "typescript": "^5.0.2", + "vite": "^4.4.5" + }, + "packageManager": "pnpm@9.12.3+sha512.cce0f9de9c5a7c95bef944169cc5dfe8741abfb145078c0d508b868056848a87c81e626246cb60967cbd7fd29a6c062ef73ff840d96b3c86c40ac92cf4a813ee" +} diff --git a/front/pnpm-lock.yaml b/front/pnpm-lock.yaml new file mode 100644 index 0000000..e36b273 --- /dev/null +++ b/front/pnpm-lock.yaml @@ -0,0 +1,3651 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@emotion/react': + specifier: ^11.11.1 + version: 11.13.3(@types/react@18.3.12)(react@18.3.1) + '@emotion/styled': + specifier: ^11.11.0 + version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1) + '@fontsource/roboto': + specifier: ^5.0.8 + version: 5.1.0 + '@mui/icons-material': + specifier: ^5.14.16 + version: 5.16.7(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.12)(react@18.3.1) + '@mui/material': + specifier: ^5.14.17 + version: 5.16.7(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/react-window': + specifier: ^1.8.8 + version: 1.8.8 + long: + specifier: ^5.2.3 + version: 5.2.3 + nice-grpc-common: + specifier: ^2.0.2 + version: 2.0.2 + nice-grpc-web: + specifier: ^3.3.2 + version: 3.3.5(ws@8.18.0) + protobufjs: + specifier: ^7.2.5 + version: 7.4.0 + react: + specifier: ^18.2.0 + version: 18.3.1 + react-dom: + specifier: ^18.2.0 + version: 18.3.1(react@18.3.1) + react-helmet-async: + specifier: ^1.3.0 + version: 1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-hook-form: + specifier: ^7.48.2 + version: 7.53.2(react@18.3.1) + react-router-dom: + specifier: ^6.18.0 + version: 6.28.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-window: + specifier: ^1.8.9 + version: 1.8.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-window-infinite-loader: + specifier: ^1.0.9 + version: 1.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + devDependencies: + '@types/react': + specifier: ^18.2.15 + version: 18.3.12 + '@types/react-dom': + specifier: ^18.2.7 + version: 18.3.1 + '@types/react-window-infinite-loader': + specifier: ^1.0.9 + version: 1.0.9 + '@typescript-eslint/eslint-plugin': + specifier: ^6.0.0 + version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3) + '@typescript-eslint/parser': + specifier: ^6.0.0 + version: 6.21.0(eslint@8.57.1)(typescript@5.6.3) + '@vitejs/plugin-react': + specifier: ^4.0.3 + version: 4.3.3(vite@4.5.5(@types/node@22.9.0)) + eslint: + specifier: ^8.45.0 + version: 8.57.1 + eslint-plugin-react: + specifier: ^7.33.2 + version: 7.37.2(eslint@8.57.1) + eslint-plugin-react-hooks: + specifier: ^4.6.0 + version: 4.6.2(eslint@8.57.1) + eslint-plugin-react-refresh: + specifier: ^0.4.3 + version: 0.4.14(eslint@8.57.1) + typescript: + specifier: ^5.0.2 + version: 5.6.3 + vite: + specifier: ^4.4.5 + version: 4.5.5(@types/node@22.9.0) + +packages: + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@babel/code-frame@7.26.2': + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.26.2': + resolution: {integrity: sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.26.0': + resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.26.2': + resolution: {integrity: sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.25.9': + resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.25.9': + resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.26.0': + resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.25.9': + resolution: {integrity: sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.25.9': + resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.25.9': + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.25.9': + resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.26.0': + resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.26.2': + resolution: {integrity: sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.25.9': + resolution: {integrity: sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.25.9': + resolution: {integrity: sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.26.0': + resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.25.9': + resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.25.9': + resolution: {integrity: sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.26.0': + resolution: {integrity: sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==} + engines: {node: '>=6.9.0'} + + '@emotion/babel-plugin@11.12.0': + resolution: {integrity: sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==} + + '@emotion/cache@11.13.1': + resolution: {integrity: sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/is-prop-valid@1.3.1': + resolution: {integrity: sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.13.3': + resolution: {integrity: sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.2': + resolution: {integrity: sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA==} + + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + + '@emotion/styled@11.13.0': + resolution: {integrity: sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==} + peerDependencies: + '@emotion/react': ^11.0.0-rc.0 + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + + '@emotion/use-insertion-effect-with-fallbacks@1.1.0': + resolution: {integrity: sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.1': + resolution: {integrity: sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA==} + + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + + '@esbuild/android-arm64@0.18.20': + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.18.20': + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.18.20': + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.18.20': + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.18.20': + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.18.20': + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.18.20': + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.18.20': + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.18.20': + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.18.20': + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.18.20': + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.18.20': + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.18.20': + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.18.20': + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.18.20': + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.18.20': + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.18.20': + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.18.20': + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.18.20': + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.18.20': + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.18.20': + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.18.20': + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.4.1': + resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@8.57.1': + resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@fontsource/roboto@5.1.0': + resolution: {integrity: sha512-cFRRC1s6RqPygeZ8Uw/acwVHqih8Czjt6Q0MwoUoDe9U3m4dH1HmNDRBZyqlMSFwgNAUKgFImncKdmDHyKpwdg==} + + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@mui/core-downloads-tracker@5.16.7': + resolution: {integrity: sha512-RtsCt4Geed2/v74sbihWzzRs+HsIQCfclHeORh5Ynu2fS4icIKozcSubwuG7vtzq2uW3fOR1zITSP84TNt2GoQ==} + + '@mui/icons-material@5.16.7': + resolution: {integrity: sha512-UrGwDJCXEszbDI7yV047BYU5A28eGJ79keTCP4cc74WyncuVrnurlmIRxaHL8YK+LI1Kzq+/JM52IAkNnv4u+Q==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@mui/material': ^5.0.0 + '@types/react': ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/material@5.16.7': + resolution: {integrity: sha512-cwwVQxBhK60OIOqZOVLFt55t01zmarKJiJUWbk0+8s/Ix5IaUzAShqlJchxsIQ4mSrWqgcKCCXKtIlG5H+/Jmg==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + + '@mui/private-theming@5.16.6': + resolution: {integrity: sha512-rAk+Rh8Clg7Cd7shZhyt2HGTTE5wYKNSJ5sspf28Fqm/PZ69Er9o6KX25g03/FG2dfpg5GCwZh/xOojiTfm3hw==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/styled-engine@5.16.6': + resolution: {integrity: sha512-zaThmS67ZmtHSWToTiHslbI8jwrmITcN93LQaR2lKArbvS7Z3iLkwRoiikNWutx9MBs8Q6okKvbZq1RQYB3v7g==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + + '@mui/system@5.16.7': + resolution: {integrity: sha512-Jncvs/r/d/itkxh7O7opOunTqbbSSzMTHzZkNLM+FjAOg+cYAZHrPDlYe1ZGKUYORwwb2XexlWnpZp0kZ4AHuA==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + + '@mui/types@7.2.19': + resolution: {integrity: sha512-6XpZEM/Q3epK9RN8ENoXuygnqUQxE+siN/6rGRi2iwJPgBUR25mphYQ9ZI87plGh58YoZ5pp40bFvKYOCDJ3tA==} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/utils@5.16.6': + resolution: {integrity: sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@popperjs/core@2.11.8': + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@remix-run/router@1.21.0': + resolution: {integrity: sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==} + engines: {node: '>=14.0.0'} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.6.8': + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.20.6': + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@22.9.0': + resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==} + + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/prop-types@15.7.13': + resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==} + + '@types/react-dom@18.3.1': + resolution: {integrity: sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==} + + '@types/react-transition-group@4.4.11': + resolution: {integrity: sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==} + + '@types/react-window-infinite-loader@1.0.9': + resolution: {integrity: sha512-gEInTjQwURCnDOFyIEK2+fWB5gTjqwx30O62QfxA9stE5aiB6EWkGj4UMhc0axq7/FV++Gs/TGW8FtgEx0S6Tw==} + + '@types/react-window@1.8.8': + resolution: {integrity: sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==} + + '@types/react@18.3.12': + resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==} + + '@types/semver@7.5.8': + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + + '@typescript-eslint/eslint-plugin@6.21.0': + resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@6.21.0': + resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/scope-manager@6.21.0': + resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/type-utils@6.21.0': + resolution: {integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@6.21.0': + resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/typescript-estree@6.21.0': + resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@6.21.0': + resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + + '@typescript-eslint/visitor-keys@6.21.0': + resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@ungap/structured-clone@1.2.0': + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + + '@vitejs/plugin-react@4.3.3': + resolution: {integrity: sha512-NooDe9GpHGqNns1i8XDERg0Vsg5SSYRhRxxyTGogUdkdNt47jal+fbuYi+Yfq6pzRCKXyoPcWisfxE6RIM3GKA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 + + abort-controller-x@0.4.3: + resolution: {integrity: sha512-VtUwTNU8fpMwvWGn4xE93ywbogTYsuT+AUxAXOeelbXuQVIwNmC5YLeho9sH4vZ4ITW8414TTAOG1nW6uIVHCA==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-buffer-byte-length@1.0.1: + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} + + array-includes@3.1.8: + resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.3: + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + engines: {node: '>= 0.4'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.24.2: + resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001679: + resolution: {integrity: sha512-j2YqID/YwpLnKzCmBOS4tlZdWprXm3ZmQLBH9ZBXFOhoxLA46fwyBvx6toCBWBmnuwUY/qB3kEU6gFx8qgCroA==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + cross-spawn@7.0.5: + resolution: {integrity: sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==} + engines: {node: '>= 8'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + data-view-buffer@1.0.1: + resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.1: + resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.0: + resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + engines: {node: '>= 0.4'} + + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + + electron-to-chromium@1.5.55: + resolution: {integrity: sha512-6maZ2ASDOTBtjt9FhqYPRnbvKU5tjG0IN9SztUOWYw2AzNDNpKJYLJmlK0/En4Hs/aiWnB+JZ+gW19PIGszgKg==} + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es-abstract@1.23.3: + resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.2.0: + resolution: {integrity: sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + + es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + + esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-plugin-react-hooks@4.6.2: + resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + + eslint-plugin-react-refresh@0.4.14: + resolution: {integrity: sha512-aXvzCTK7ZBv1e7fahFuR3Z/fyQQSIQ711yPgYRj+Oj64tyTgO4iQIDmYXDBqvSWQ/FA4OSCsXOStlF+noU0/NA==} + peerDependencies: + eslint: '>=7' + + eslint-plugin-react@7.37.2: + resolution: {integrity: sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint@8.57.1: + resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + + for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.0.2: + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} + engines: {node: '>= 0.4'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + + has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + internal-slot@1.0.7: + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + engines: {node: '>= 0.4'} + + invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + + is-array-buffer@3.0.4: + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-async-function@2.0.0: + resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} + engines: {node: '>= 0.4'} + + is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + + is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.1: + resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + engines: {node: '>= 0.4'} + + is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.0.2: + resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + + is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} + + is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + + is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + + is-weakset@2.0.3: + resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} + engines: {node: '>= 0.4'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isomorphic-ws@5.0.0: + resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} + peerDependencies: + ws: '*' + + iterator.prototype@1.1.3: + resolution: {integrity: sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==} + engines: {node: '>= 0.4'} + + js-base64@3.7.7: + resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsesc@3.0.2: + resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + memoize-one@5.2.1: + resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + nice-grpc-common@2.0.2: + resolution: {integrity: sha512-7RNWbls5kAL1QVUOXvBsv1uO0wPQK3lHv+cY1gwkTzirnG1Nop4cBJZubpgziNbaVc/bl9QJcyvsf/NQxa3rjQ==} + + nice-grpc-web@3.3.5: + resolution: {integrity: sha512-+G+WcHUHCXZC/GcZ8OiuT7BSlvIBsujaojcemGIHClq8Dh3z31BzsC1E8bUv++WzQAHq1/8PnBySvcmF1tb90Q==} + + node-releases@2.0.18: + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.3: + resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + engines: {node: '>= 0.4'} + + object.entries@1.1.8: + resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.0: + resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} + engines: {node: '>= 0.4'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + + postcss@8.4.47: + resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + protobufjs@7.4.0: + resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==} + engines: {node: '>=12.0.0'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + + react-helmet-async@1.3.0: + resolution: {integrity: sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==} + peerDependencies: + react: ^16.6.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.6.0 || ^17.0.0 || ^18.0.0 + + react-hook-form@7.53.2: + resolution: {integrity: sha512-YVel6fW5sOeedd1524pltpHX+jgU2u3DSDtXEaBORNdqiNrsX/nUI/iGXONegttg0mJVnfrIkiV0cmTU6Oo2xw==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-refresh@0.14.2: + resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} + engines: {node: '>=0.10.0'} + + react-router-dom@6.28.0: + resolution: {integrity: sha512-kQ7Unsl5YdyOltsPGl31zOjLrDv+m2VcIEcIHqYYD3Lp0UppLjrzcfJqDJwXxFw3TH/yvapbnUvPlAj7Kx5nbg==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + react-router@6.28.0: + resolution: {integrity: sha512-HrYdIFqdrnhDw0PqG/AKjAqEqM7AvxCz0DQ4h2W8k6nqmc5uRBYDag0SBxx9iYz5G8gnuNVLzUe13wl9eAsXXg==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + + react-window-infinite-loader@1.0.9: + resolution: {integrity: sha512-5Hg89IdU4Vrp0RT8kZYKeTIxWZYhNkVXeI1HbKo01Vm/Z7qztDvXljwx16sMzsa9yapRJQW3ODZfMUw38SOWHw==} + engines: {node: '>8.0.0'} + peerDependencies: + react: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 + react-dom: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 + + react-window@1.8.10: + resolution: {integrity: sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==} + engines: {node: '>8.0.0'} + peerDependencies: + react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + reflect.getprototypeof@1.0.6: + resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} + engines: {node: '>= 0.4'} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + regexp.prototype.flags@1.5.3: + resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==} + engines: {node: '>= 0.4'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rollup@3.29.5: + resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-array-concat@1.1.2: + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + engines: {node: '>=0.4'} + + safe-regex-test@1.0.3: + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + shallowequal@1.1.0: + resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + + string.prototype.matchall@4.0.11: + resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.9: + resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.8: + resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-api-utils@1.4.0: + resolution: {integrity: sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + ts-error@1.0.6: + resolution: {integrity: sha512-tLJxacIQUM82IR7JO1UUkKlYuUTmoY9HBJAmNWFzheSlDS5SPMcNIepejHJa4BpPQLAcbRhRf3GDJzyj6rbKvA==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + typed-array-buffer@1.0.2: + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.6: + resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} + engines: {node: '>= 0.4'} + + typescript@5.6.3: + resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + update-browserslist-db@1.1.1: + resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + vite@4.5.5: + resolution: {integrity: sha512-ifW3Lb2sMdX+WU91s3R0FyQlAyLxOzCSCP37ujw0+r5POeHPwe6udWVIElKQq8gk3t7b8rkmvqC6IHBpCff4GQ==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + + which-builtin-type@1.1.4: + resolution: {integrity: sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@babel/code-frame@7.26.2': + dependencies: + '@babel/helper-validator-identifier': 7.25.9 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.26.2': {} + + '@babel/core@7.26.0': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.2 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helpers': 7.26.0 + '@babel/parser': 7.26.2 + '@babel/template': 7.25.9 + '@babel/traverse': 7.25.9 + '@babel/types': 7.26.0 + convert-source-map: 2.0.0 + debug: 4.3.7 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.26.2': + dependencies: + '@babel/parser': 7.26.2 + '@babel/types': 7.26.0 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.0.2 + + '@babel/helper-compilation-targets@7.25.9': + dependencies: + '@babel/compat-data': 7.26.2 + '@babel/helper-validator-option': 7.25.9 + browserslist: 4.24.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-module-imports@7.25.9': + dependencies: + '@babel/traverse': 7.25.9 + '@babel/types': 7.26.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.25.9': {} + + '@babel/helper-string-parser@7.25.9': {} + + '@babel/helper-validator-identifier@7.25.9': {} + + '@babel/helper-validator-option@7.25.9': {} + + '@babel/helpers@7.26.0': + dependencies: + '@babel/template': 7.25.9 + '@babel/types': 7.26.0 + + '@babel/parser@7.26.2': + dependencies: + '@babel/types': 7.26.0 + + '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/runtime@7.26.0': + dependencies: + regenerator-runtime: 0.14.1 + + '@babel/template@7.25.9': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/parser': 7.26.2 + '@babel/types': 7.26.0 + + '@babel/traverse@7.25.9': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.2 + '@babel/parser': 7.26.2 + '@babel/template': 7.25.9 + '@babel/types': 7.26.0 + debug: 4.3.7 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.26.0': + dependencies: + '@babel/helper-string-parser': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + + '@emotion/babel-plugin@11.12.0': + dependencies: + '@babel/helper-module-imports': 7.25.9 + '@babel/runtime': 7.26.0 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.2 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + + '@emotion/cache@11.13.1': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.1 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + + '@emotion/hash@0.9.2': {} + + '@emotion/is-prop-valid@1.3.1': + dependencies: + '@emotion/memoize': 0.9.0 + + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@emotion/babel-plugin': 11.12.0 + '@emotion/cache': 11.13.1 + '@emotion/serialize': 1.3.2 + '@emotion/use-insertion-effect-with-fallbacks': 1.1.0(react@18.3.1) + '@emotion/utils': 1.4.1 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.12 + transitivePeerDependencies: + - supports-color + + '@emotion/serialize@1.3.2': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.1 + csstype: 3.1.3 + + '@emotion/sheet@1.4.0': {} + + '@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@emotion/babel-plugin': 11.12.0 + '@emotion/is-prop-valid': 1.3.1 + '@emotion/react': 11.13.3(@types/react@18.3.12)(react@18.3.1) + '@emotion/serialize': 1.3.2 + '@emotion/use-insertion-effect-with-fallbacks': 1.1.0(react@18.3.1) + '@emotion/utils': 1.4.1 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.12 + transitivePeerDependencies: + - supports-color + + '@emotion/unitless@0.10.0': {} + + '@emotion/use-insertion-effect-with-fallbacks@1.1.0(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@emotion/utils@1.4.1': {} + + '@emotion/weak-memoize@0.4.0': {} + + '@esbuild/android-arm64@0.18.20': + optional: true + + '@esbuild/android-arm@0.18.20': + optional: true + + '@esbuild/android-x64@0.18.20': + optional: true + + '@esbuild/darwin-arm64@0.18.20': + optional: true + + '@esbuild/darwin-x64@0.18.20': + optional: true + + '@esbuild/freebsd-arm64@0.18.20': + optional: true + + '@esbuild/freebsd-x64@0.18.20': + optional: true + + '@esbuild/linux-arm64@0.18.20': + optional: true + + '@esbuild/linux-arm@0.18.20': + optional: true + + '@esbuild/linux-ia32@0.18.20': + optional: true + + '@esbuild/linux-loong64@0.18.20': + optional: true + + '@esbuild/linux-mips64el@0.18.20': + optional: true + + '@esbuild/linux-ppc64@0.18.20': + optional: true + + '@esbuild/linux-riscv64@0.18.20': + optional: true + + '@esbuild/linux-s390x@0.18.20': + optional: true + + '@esbuild/linux-x64@0.18.20': + optional: true + + '@esbuild/netbsd-x64@0.18.20': + optional: true + + '@esbuild/openbsd-x64@0.18.20': + optional: true + + '@esbuild/sunos-x64@0.18.20': + optional: true + + '@esbuild/win32-arm64@0.18.20': + optional: true + + '@esbuild/win32-ia32@0.18.20': + optional: true + + '@esbuild/win32-x64@0.18.20': + optional: true + + '@eslint-community/eslint-utils@4.4.1(eslint@8.57.1)': + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.12.6 + debug: 4.3.7 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.57.1': {} + + '@fontsource/roboto@5.1.0': {} + + '@humanwhocodes/config-array@0.13.0': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.3.7 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@mui/core-downloads-tracker@5.16.7': {} + + '@mui/icons-material@5.16.7(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@mui/material': 5.16.7(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.12 + + '@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@mui/core-downloads-tracker': 5.16.7 + '@mui/system': 5.16.7(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1) + '@mui/types': 7.2.19(@types/react@18.3.12) + '@mui/utils': 5.16.6(@types/react@18.3.12)(react@18.3.1) + '@popperjs/core': 2.11.8 + '@types/react-transition-group': 4.4.11 + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 18.3.1 + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.12)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1) + '@types/react': 18.3.12 + + '@mui/private-theming@5.16.6(@types/react@18.3.12)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@mui/utils': 5.16.6(@types/react@18.3.12)(react@18.3.1) + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.12 + + '@mui/styled-engine@5.16.6(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@emotion/cache': 11.13.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.12)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1) + + '@mui/system@5.16.7(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@mui/private-theming': 5.16.6(@types/react@18.3.12)(react@18.3.1) + '@mui/styled-engine': 5.16.6(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1) + '@mui/types': 7.2.19(@types/react@18.3.12) + '@mui/utils': 5.16.6(@types/react@18.3.12)(react@18.3.1) + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.12)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1) + '@types/react': 18.3.12 + + '@mui/types@7.2.19(@types/react@18.3.12)': + optionalDependencies: + '@types/react': 18.3.12 + + '@mui/utils@5.16.6(@types/react@18.3.12)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@mui/types': 7.2.19(@types/react@18.3.12) + '@types/prop-types': 15.7.13 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 18.3.1 + optionalDependencies: + '@types/react': 18.3.12 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@popperjs/core@2.11.8': {} + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@remix-run/router@1.21.0': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.26.2 + '@babel/types': 7.26.0 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.6 + + '@types/babel__generator@7.6.8': + dependencies: + '@babel/types': 7.26.0 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.26.2 + '@babel/types': 7.26.0 + + '@types/babel__traverse@7.20.6': + dependencies: + '@babel/types': 7.26.0 + + '@types/json-schema@7.0.15': {} + + '@types/node@22.9.0': + dependencies: + undici-types: 6.19.8 + + '@types/parse-json@4.0.2': {} + + '@types/prop-types@15.7.13': {} + + '@types/react-dom@18.3.1': + dependencies: + '@types/react': 18.3.12 + + '@types/react-transition-group@4.4.11': + dependencies: + '@types/react': 18.3.12 + + '@types/react-window-infinite-loader@1.0.9': + dependencies: + '@types/react': 18.3.12 + '@types/react-window': 1.8.8 + + '@types/react-window@1.8.8': + dependencies: + '@types/react': 18.3.12 + + '@types/react@18.3.12': + dependencies: + '@types/prop-types': 15.7.13 + csstype: 3.1.3 + + '@types/semver@7.5.8': {} + + '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.6.3) + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/type-utils': 6.21.0(eslint@8.57.1)(typescript@5.6.3) + '@typescript-eslint/utils': 6.21.0(eslint@8.57.1)(typescript@5.6.3) + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.3.7 + eslint: 8.57.1 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + semver: 7.6.3 + ts-api-utils: 1.4.0(typescript@5.6.3) + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.6.3)': + dependencies: + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.6.3) + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.3.7 + eslint: 8.57.1 + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@6.21.0': + dependencies: + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 + + '@typescript-eslint/type-utils@6.21.0(eslint@8.57.1)(typescript@5.6.3)': + dependencies: + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.6.3) + '@typescript-eslint/utils': 6.21.0(eslint@8.57.1)(typescript@5.6.3) + debug: 4.3.7 + eslint: 8.57.1 + ts-api-utils: 1.4.0(typescript@5.6.3) + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@6.21.0': {} + + '@typescript-eslint/typescript-estree@6.21.0(typescript@5.6.3)': + dependencies: + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.3.7 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.3 + semver: 7.6.3 + ts-api-utils: 1.4.0(typescript@5.6.3) + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@6.21.0(eslint@8.57.1)(typescript@5.6.3)': + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.1) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.6.3) + eslint: 8.57.1 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/visitor-keys@6.21.0': + dependencies: + '@typescript-eslint/types': 6.21.0 + eslint-visitor-keys: 3.4.3 + + '@ungap/structured-clone@1.2.0': {} + + '@vitejs/plugin-react@4.3.3(vite@4.5.5(@types/node@22.9.0))': + dependencies: + '@babel/core': 7.26.0 + '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0) + '@types/babel__core': 7.20.5 + react-refresh: 0.14.2 + vite: 4.5.5(@types/node@22.9.0) + transitivePeerDependencies: + - supports-color + + abort-controller-x@0.4.3: {} + + acorn-jsx@5.3.2(acorn@8.14.0): + dependencies: + acorn: 8.14.0 + + acorn@8.14.0: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + array-buffer-byte-length@1.0.1: + dependencies: + call-bind: 1.0.7 + is-array-buffer: 3.0.4 + + array-includes@3.1.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + get-intrinsic: 1.2.4 + is-string: 1.0.7 + + array-union@2.1.0: {} + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-shim-unscopables: 1.0.2 + + array.prototype.flat@1.3.2: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-shim-unscopables: 1.0.2 + + array.prototype.flatmap@1.3.2: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-shim-unscopables: 1.0.2 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-shim-unscopables: 1.0.2 + + arraybuffer.prototype.slice@1.0.3: + dependencies: + array-buffer-byte-length: 1.0.1 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + is-array-buffer: 3.0.4 + is-shared-array-buffer: 1.0.3 + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.0.0 + + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.26.0 + cosmiconfig: 7.1.0 + resolve: 1.22.8 + + balanced-match@1.0.2: {} + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.24.2: + dependencies: + caniuse-lite: 1.0.30001679 + electron-to-chromium: 1.5.55 + node-releases: 2.0.18 + update-browserslist-db: 1.1.1(browserslist@4.24.2) + + call-bind@1.0.7: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001679: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + convert-source-map@1.9.0: {} + + convert-source-map@2.0.0: {} + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + + cross-spawn@7.0.5: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.1.3: {} + + data-view-buffer@1.0.1: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + data-view-byte-length@1.0.1: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + data-view-byte-offset@1.0.0: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + debug@4.3.7: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.26.0 + csstype: 3.1.3 + + electron-to-chromium@1.5.55: {} + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + es-abstract@1.23.3: + dependencies: + array-buffer-byte-length: 1.0.1 + arraybuffer.prototype.slice: 1.0.3 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + data-view-buffer: 1.0.1 + data-view-byte-length: 1.0.1 + data-view-byte-offset: 1.0.0 + es-define-property: 1.0.0 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-set-tostringtag: 2.0.3 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.4 + get-symbol-description: 1.0.2 + globalthis: 1.0.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + internal-slot: 1.0.7 + is-array-buffer: 3.0.4 + is-callable: 1.2.7 + is-data-view: 1.0.1 + is-negative-zero: 2.0.3 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.3 + is-string: 1.0.7 + is-typed-array: 1.1.13 + is-weakref: 1.0.2 + object-inspect: 1.13.3 + object-keys: 1.1.1 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.3 + safe-array-concat: 1.1.2 + safe-regex-test: 1.0.3 + string.prototype.trim: 1.2.9 + string.prototype.trimend: 1.0.8 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.2 + typed-array-byte-length: 1.0.1 + typed-array-byte-offset: 1.0.2 + typed-array-length: 1.0.6 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.15 + + es-define-property@1.0.0: + dependencies: + get-intrinsic: 1.2.4 + + es-errors@1.3.0: {} + + es-iterator-helpers@1.2.0: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-set-tostringtag: 2.0.3 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + globalthis: 1.0.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + internal-slot: 1.0.7 + iterator.prototype: 1.1.3 + safe-array-concat: 1.1.2 + + es-object-atoms@1.0.0: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.0.3: + dependencies: + get-intrinsic: 1.2.4 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.0.2: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.2.1: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + + esbuild@0.18.20: + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-plugin-react-hooks@4.6.2(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + + eslint-plugin-react-refresh@0.4.14(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + + eslint-plugin-react@7.37.2(eslint@8.57.1): + dependencies: + array-includes: 3.1.8 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.2 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.2.0 + eslint: 8.57.1 + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.8 + object.fromentries: 2.0.8 + object.values: 1.2.0 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.11 + string.prototype.repeat: 1.0.0 + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint@8.57.1: + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.1) + '@eslint-community/regexpp': 4.12.1 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.1 + '@humanwhocodes/config-array': 0.13.0 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.5 + debug: 4.3.7 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + espree@9.6.1: + dependencies: + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) + eslint-visitor-keys: 3.4.3 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.2: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.17.1: + dependencies: + reusify: 1.0.4 + + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-root@1.1.0: {} + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@3.2.0: + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + rimraf: 3.0.2 + + flatted@3.3.1: {} + + for-each@0.3.3: + dependencies: + is-callable: 1.2.7 + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.6: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + functions-have-names: 1.2.3 + + functions-have-names@1.2.3: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.2.4: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + + get-symbol-description@1.0.2: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@11.12.0: {} + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.0.1 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + gopd@1.0.1: + dependencies: + get-intrinsic: 1.2.4 + + graphemer@1.4.0: {} + + has-bigints@1.0.2: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.0 + + has-proto@1.0.3: {} + + has-symbols@1.0.3: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.0.3 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + + ignore@5.3.2: {} + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + internal-slot@1.0.7: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.0.6 + + invariant@2.2.4: + dependencies: + loose-envify: 1.4.0 + + is-array-buffer@3.0.4: + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + + is-arrayish@0.2.1: {} + + is-async-function@2.0.0: + dependencies: + has-tostringtag: 1.0.2 + + is-bigint@1.0.4: + dependencies: + has-bigints: 1.0.2 + + is-boolean-object@1.1.2: + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.15.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.1: + dependencies: + is-typed-array: 1.1.13 + + is-date-object@1.0.5: + dependencies: + has-tostringtag: 1.0.2 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.0.2: + dependencies: + call-bind: 1.0.7 + + is-generator-function@1.0.10: + dependencies: + has-tostringtag: 1.0.2 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.0.7: + dependencies: + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-path-inside@3.0.3: {} + + is-regex@1.1.4: + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.3: + dependencies: + call-bind: 1.0.7 + + is-string@1.0.7: + dependencies: + has-tostringtag: 1.0.2 + + is-symbol@1.0.4: + dependencies: + has-symbols: 1.0.3 + + is-typed-array@1.1.13: + dependencies: + which-typed-array: 1.1.15 + + is-weakmap@2.0.2: {} + + is-weakref@1.0.2: + dependencies: + call-bind: 1.0.7 + + is-weakset@2.0.3: + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + isomorphic-ws@5.0.0(ws@8.18.0): + dependencies: + ws: 8.18.0 + + iterator.prototype@1.1.3: + dependencies: + define-properties: 1.2.1 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + reflect.getprototypeof: 1.0.6 + set-function-name: 2.0.2 + + js-base64@3.7.7: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsesc@3.0.2: {} + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.8 + array.prototype.flat: 1.3.2 + object.assign: 4.1.5 + object.values: 1.2.0 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lines-and-columns@1.2.4: {} + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + long@5.2.3: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + memoize-one@5.2.1: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimatch@9.0.3: + dependencies: + brace-expansion: 2.0.1 + + ms@2.1.3: {} + + nanoid@3.3.7: {} + + natural-compare@1.4.0: {} + + nice-grpc-common@2.0.2: + dependencies: + ts-error: 1.0.6 + + nice-grpc-web@3.3.5(ws@8.18.0): + dependencies: + abort-controller-x: 0.4.3 + isomorphic-ws: 5.0.0(ws@8.18.0) + js-base64: 3.7.7 + nice-grpc-common: 2.0.2 + transitivePeerDependencies: + - ws + + node-releases@2.0.18: {} + + object-assign@4.1.1: {} + + object-inspect@1.13.3: {} + + object-keys@1.1.1: {} + + object.assign@4.1.5: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + has-symbols: 1.0.3 + object-keys: 1.1.1 + + object.entries@1.1.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + + object.values@1.2.0: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.26.2 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-type@4.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + possible-typed-array-names@1.0.0: {} + + postcss@8.4.47: + dependencies: + nanoid: 3.3.7 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + protobufjs@7.4.0: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 22.9.0 + long: 5.2.3 + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-fast-compare@3.2.2: {} + + react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + invariant: 2.2.4 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-fast-compare: 3.2.2 + shallowequal: 1.1.0 + + react-hook-form@7.53.2(react@18.3.1): + dependencies: + react: 18.3.1 + + react-is@16.13.1: {} + + react-is@18.3.1: {} + + react-refresh@0.14.2: {} + + react-router-dom@6.28.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@remix-run/router': 1.21.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 6.28.0(react@18.3.1) + + react-router@6.28.0(react@18.3.1): + dependencies: + '@remix-run/router': 1.21.0 + react: 18.3.1 + + react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-window-infinite-loader@1.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-window@1.8.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + memoize-one: 5.2.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + reflect.getprototypeof@1.0.6: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + globalthis: 1.0.4 + which-builtin-type: 1.1.4 + + regenerator-runtime@0.14.1: {} + + regexp.prototype.flags@1.5.3: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-errors: 1.3.0 + set-function-name: 2.0.2 + + resolve-from@4.0.0: {} + + resolve@1.22.8: + dependencies: + is-core-module: 2.15.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + resolve@2.0.0-next.5: + dependencies: + is-core-module: 2.15.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.0.4: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + rollup@3.29.5: + optionalDependencies: + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-array-concat@1.1.2: + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + isarray: 2.0.5 + + safe-regex-test@1.0.3: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-regex: 1.1.4 + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + semver@6.3.1: {} + + semver@7.6.3: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + shallowequal@1.1.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel@1.0.6: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.3 + + slash@3.0.0: {} + + source-map-js@1.2.1: {} + + source-map@0.5.7: {} + + string.prototype.matchall@4.0.11: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.7 + regexp.prototype.flags: 1.5.3 + set-function-name: 2.0.2 + side-channel: 1.0.6 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.23.3 + + string.prototype.trim@1.2.9: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + + string.prototype.trimend@1.0.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-json-comments@3.1.1: {} + + stylis@4.2.0: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + text-table@0.2.0: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@1.4.0(typescript@5.6.3): + dependencies: + typescript: 5.6.3 + + ts-error@1.0.6: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.20.2: {} + + typed-array-buffer@1.0.2: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-typed-array: 1.1.13 + + typed-array-byte-length@1.0.1: + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + + typed-array-byte-offset@1.0.2: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + + typed-array-length@1.0.6: + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + possible-typed-array-names: 1.0.0 + + typescript@5.6.3: {} + + unbox-primitive@1.0.2: + dependencies: + call-bind: 1.0.7 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + + undici-types@6.19.8: {} + + update-browserslist-db@1.1.1(browserslist@4.24.2): + dependencies: + browserslist: 4.24.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + vite@4.5.5(@types/node@22.9.0): + dependencies: + esbuild: 0.18.20 + postcss: 8.4.47 + rollup: 3.29.5 + optionalDependencies: + '@types/node': 22.9.0 + fsevents: 2.3.3 + + which-boxed-primitive@1.0.2: + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + + which-builtin-type@1.1.4: + dependencies: + function.prototype.name: 1.1.6 + has-tostringtag: 1.0.2 + is-async-function: 2.0.0 + is-date-object: 1.0.5 + is-finalizationregistry: 1.0.2 + is-generator-function: 1.0.10 + is-regex: 1.1.4 + is-weakref: 1.0.2 + isarray: 2.0.5 + which-boxed-primitive: 1.0.2 + which-collection: 1.0.2 + which-typed-array: 1.1.15 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.3 + + which-typed-array@1.1.15: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + wrappy@1.0.2: {} + + ws@8.18.0: {} + + yallist@3.1.1: {} + + yaml@1.10.2: {} + + yocto-queue@0.1.0: {} diff --git a/front/public/logo.png b/front/public/logo.png new file mode 100644 index 0000000..4dda6d4 Binary files /dev/null and b/front/public/logo.png differ diff --git a/front/public/logo_toolbar.png b/front/public/logo_toolbar.png new file mode 100644 index 0000000..1544747 Binary files /dev/null and b/front/public/logo_toolbar.png differ diff --git a/front/src/components/ExploitUpdateForm.tsx b/front/src/components/ExploitUpdateForm.tsx new file mode 100644 index 0000000..dfa3a91 --- /dev/null +++ b/front/src/components/ExploitUpdateForm.tsx @@ -0,0 +1,152 @@ +import { ExploitState } from "@/proto/exploits/api"; +import { useExploitServiceClient } from "@/services/exploits"; +import { formatSeconds, parseDuration } from "@/utils/duration"; +import { Button, Grid, Stack, TextField, Typography } from "@mui/material"; +import { ClientError } from "nice-grpc-common"; +import { SubmitHandler, useForm } from "react-hook-form"; + +interface P { + exploit: ExploitState; + onRefresh: () => void; +} + +interface Inputs { + timeout: string; + interval: string; +} + +export default function ExploitUpdateForm(props: P) { + const { + register, + handleSubmit, + formState: { errors }, + setError, + } = useForm({ + defaultValues: { + timeout: formatSeconds( + props.exploit.config?.timeout?.seconds.toInt() || 0 + ), + interval: formatSeconds( + props.exploit.config?.runEvery?.seconds.toInt() || 0 + ), + }, + }); + + const exploitServiceClient = useExploitServiceClient(); + + const submitState = async (state: ExploitState) => { + try { + await exploitServiceClient.updateExploit({ state }); + } catch (e) { + if (e instanceof ClientError) { + setError("root", { + type: "manual", + message: e.message, + }); + } + } + props.onRefresh(); + }; + + const onSubmit: SubmitHandler = async (inputs: Inputs) => { + const newState = ExploitState.fromJSON(ExploitState.toJSON(props.exploit)); + if (!newState.config) { + return; + } + newState.config.timeout = parseDuration(inputs.timeout); + newState.config.runEvery = parseDuration(inputs.interval); + await submitState(newState); + }; + + const handleDisabledButton = async () => { + const newState = ExploitState.fromJSON(ExploitState.toJSON(props.exploit)); + if (!newState.config) { + return; + } + newState.config.disabled = !newState.config.disabled; + await submitState(newState); + }; + + const validateDuration = (value: string) => { + try { + parseDuration(value); + return true; + } catch (e) { + if (e instanceof Error) { + return e.message; + } else { + return "unknown error"; + } + } + }; + + return ( + <> +
{ + event.preventDefault(); + void handleSubmit(onSubmit)(event); + }} + > + + + + + + + + + {errors.root && ( + + {errors.root.message} + + )} + + + + + + + + +
+ + ); +} diff --git a/front/src/components/ExploitsView.tsx b/front/src/components/ExploitsView.tsx new file mode 100644 index 0000000..cc4ec83 --- /dev/null +++ b/front/src/components/ExploitsView.tsx @@ -0,0 +1,59 @@ +import ExploitUpdateForm from "@/components/ExploitUpdateForm"; +import { ExploitState } from "@/proto/exploits/api"; +import { formatSeconds } from "@/utils/duration"; +import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; +import { + Accordion, + AccordionDetails, + AccordionSummary, + Box, + Typography, +} from "@mui/material"; +import { useState } from "react"; + +interface P { + exploits: ExploitState[]; + onRefresh: () => void; +} + +export default function ExploitsView(props: P) { + const [expanded, setExpanded] = useState(false); + + const handleChange = + (exploit: ExploitState) => + (_event: React.SyntheticEvent, isExpanded: boolean) => { + setExpanded(isExpanded ? exploit.exploitId : false); + }; + + return ( + <> + {props.exploits.map((exploit) => ( + + }> + + {exploit.exploitId} + {" v"} + {exploit.version.toString()} + {" timeout="} + {formatSeconds(exploit.config?.timeout?.seconds.toInt() || 0)} + {" interval="} + {formatSeconds(exploit.config?.runEvery?.seconds.toInt() || 0)} + + + + + + + + + ))} + + ); +} diff --git a/front/src/components/LogsRootView.tsx b/front/src/components/LogsRootView.tsx new file mode 100644 index 0000000..9d841b4 --- /dev/null +++ b/front/src/components/LogsRootView.tsx @@ -0,0 +1,170 @@ +import { ExploitState } from "@/proto/exploits/api"; +import { LogLine } from "@/proto/logs/api"; +import { useRootContext } from "@/routes/rootContext"; +import { useExploitServiceClient } from "@/services/exploits"; +import { useLogsServiceClient } from "@/services/logs"; +import { useWindowDimensions } from "@/utils/window"; +import { + FormControl, + Grid, + InputLabel, + MenuItem, + Select, + SelectChangeEvent, + Stack, + TextField, +} from "@mui/material"; +import { useEffect, useRef, useState } from "react"; +import { unstable_batchedUpdates } from "react-dom"; +import LogsView from "./LogsView"; + +interface S { + exploitID: string; + exploitVersion: string; +} + +interface P { + exploits: ExploitState[]; +} + +export default function LogsRootView(props: P) { + const [state, setState] = useState({ exploitID: "", exploitVersion: "" }); + const [lines, setLines] = useState([]); + const [linesToken, setLinesToken] = useState(""); + const [loading, setLoading] = useState(false); + const [hasMoreLines, setHasMoreLines] = useState(true); + const [linesRequested, setLinesRequested] = useState(false); + + const prevStateRef = useRef(state); + + const exploitServiceClient = useExploitServiceClient(); + const logsServiceClient = useLogsServiceClient(); + + const fetchExploitData = async (exploitID: string) => { + try { + const response = await exploitServiceClient.exploit({ + exploitId: exploitID, + }); + return response.state?.version.toString() || ""; + } catch { + return ""; + } + }; + + const handleExploitIDChange = (event: SelectChangeEvent) => { + const wrapper = async () => { + const exploitVersion = await fetchExploitData(event.target.value); + setState({ exploitID: event.target.value, exploitVersion }); + }; + void wrapper(); + }; + + const handleExploitVersionChange = ( + event: React.ChangeEvent + ) => { + setState({ ...state, exploitVersion: event.target.value }); + }; + + useEffect(() => { + const prevState = prevStateRef.current; + if ( + state.exploitID !== prevState.exploitID || + state.exploitVersion !== prevState.exploitVersion + ) { + unstable_batchedUpdates(() => { + setLines([]); + setLinesToken(""); + setLoading(false); + setHasMoreLines(true); + }); + prevStateRef.current = state; + } + + if ( + !state.exploitID || + !state.exploitVersion || + loading || + !linesRequested + ) { + return; + } + + setLoading(true); + const wrapper = async () => { + const stream = logsServiceClient.searchLogLines({ + exploit: state.exploitID, + version: state.exploitVersion, + lastToken: linesToken, + }); + + const lines: LogLine[] = []; + let nextToken = ""; + for await (const response of stream) { + lines.push(...response.lines); + nextToken = response.lastToken; + } + + unstable_batchedUpdates(() => { + setLines((prevLines) => [...prevLines, ...lines]); + setLinesToken(nextToken); + setLoading(false); + setHasMoreLines(lines.length > 0); + setLinesRequested(false); + }); + }; + void wrapper(); + }, [state, logsServiceClient, loading, linesToken, linesRequested]); + + const streamFormRef = useRef(null); + const { topbarRef } = useRootContext(); + + const { height: windowHeight } = useWindowDimensions(); + const viewSize = + windowHeight - + (topbarRef.current?.clientHeight ?? 0) - + (streamFormRef.current?.clientHeight ?? 0) - + 1; + + return ( + + + + + Exploit + + + + + + + { + setLinesRequested(true); + }} + /> + + + ); +} diff --git a/front/src/components/LogsView.tsx b/front/src/components/LogsView.tsx new file mode 100644 index 0000000..0e9d58e --- /dev/null +++ b/front/src/components/LogsView.tsx @@ -0,0 +1,115 @@ +import { LogLine } from "@/proto/logs/api"; +import { CSSProperties } from "react"; +import { ListOnItemsRenderedProps, VariableSizeList } from "react-window"; +import InfiniteLoader from "react-window-infinite-loader"; + +interface P { + lines: LogLine[]; + viewHeight: number; + hasMoreLines: boolean; + isLoading: boolean; + loadNext: () => void; +} + +export default function LogsView(props: P) { + const mapLevelColor = (level: string) => { + switch (level) { + case "debug": + return "grey"; + case "info": + return "black"; + case "warning": + return "orange"; + case "error": + return "red"; + default: + return "black"; + } + }; + + const lineCounts = props.lines.map((line) => line.message.split("\n").length); + + const isLineLoaded = (index: number) => + !props.hasMoreLines || index < props.lines.length; + + const lineCount = props.hasMoreLines + ? props.lines.length + 1 + : props.lines.length; + + const loadMoreLines = props.isLoading ? () => {} : props.loadNext; + + const LineRow = ({ + index, + style, + }: { + index: number; + style: CSSProperties; + }) => { + if (!isLineLoaded(index)) { + return ( +
+ Loading... +
+ ); + } + + const line = props.lines[index]; + + return ( +
+        [{line.team}] [{line.level}] {line.message}
+      
+ ); + }; + + const lineHeight = (index: number): number => { + if (!isLineLoaded(index)) { + return 16; + } + return lineCounts[index] * 16; + }; + + return ( + + {({ + onItemsRendered, + ref, + }: { + onItemsRendered: (props: ListOnItemsRenderedProps) => unknown; + ref: (ref: unknown) => void; + }) => ( + + {LineRow} + + )} + + ); +} diff --git a/front/src/config.ts b/front/src/config.ts new file mode 100644 index 0000000..9f491b3 --- /dev/null +++ b/front/src/config.ts @@ -0,0 +1,10 @@ +let grpcAddressTemp = "ws://127.0.0.1:5005"; +if (!import.meta.env.DEV) { + if (window.location.protocol === "https:") { + grpcAddressTemp = "wss://" + window.location.host; + } else { + grpcAddressTemp = "ws://" + window.location.host; + } +} + +export const grpcAddress = grpcAddressTemp; diff --git a/front/src/index.css b/front/src/index.css new file mode 100644 index 0000000..1d09d66 --- /dev/null +++ b/front/src/index.css @@ -0,0 +1,8 @@ +body > #root { + max-height: 100vh; + /* overflow: hidden; */ +} + +body { + margin: 0; +} \ No newline at end of file diff --git a/front/src/main.tsx b/front/src/main.tsx new file mode 100644 index 0000000..4d37a37 --- /dev/null +++ b/front/src/main.tsx @@ -0,0 +1,51 @@ +import "@/index.css"; +import ExploitsContainer from "@/routes/exploits"; +import Root from "@/routes/root"; +import theme from "@/theme"; +import "@fontsource/roboto/300.css"; +import "@fontsource/roboto/400.css"; +import "@fontsource/roboto/500.css"; +import "@fontsource/roboto/700.css"; +import { ThemeProvider } from "@mui/material"; +import React from "react"; +import ReactDOM from "react-dom/client"; +import { HelmetProvider } from "react-helmet-async"; +import { createBrowserRouter, RouterProvider } from "react-router-dom"; +import AuthProvider from "./routes/auth"; +import LogsContainer from "./routes/logs"; + +const router = createBrowserRouter([ + { + path: "/", + element: , + children: [ + { + index: true, + element: ( + + + + ), + }, + { + path: "/logs", + element: ( + + + + ), + }, + ], + }, +]); + +// eslint-disable-next-line @typescript-eslint/no-non-null-assertion +ReactDOM.createRoot(document.getElementById("root")!).render( + + + + + + + +); diff --git a/front/src/proto/exploits/api.ts b/front/src/proto/exploits/api.ts new file mode 100644 index 0000000..c64211b --- /dev/null +++ b/front/src/proto/exploits/api.ts @@ -0,0 +1,1963 @@ +// @ts-nocheck +/* eslint-disable */ +import Long from "long"; +import type { CallContext, CallOptions } from "nice-grpc-common"; +import _m0 from "protobufjs/minimal"; +import { FileInfo } from "../fileserver/api"; +import { Duration } from "../google/protobuf/duration"; + +export const protobufPackage = "exploits"; + +export interface TeamBucket { + teams: Map; +} + +export interface TeamBucket_TeamsEntry { + key: string; + value: string; +} + +export interface ExploitConfiguration { + entrypoint: string; + isArchive: boolean; + runEvery: Duration | undefined; + timeout: Duration | undefined; + disabled: boolean; + endless: boolean; +} + +export interface ExploitState { + exploitId: string; + version: Long; + file: FileInfo | undefined; + config: ExploitConfiguration | undefined; +} + +export interface Config { + farmUrl: string; + farmPassword: string; + flagRegexp: string; + pingEvery: Duration | undefined; + submitEvery: Duration | undefined; + environ: string[]; +} + +export interface ServerState { + /** Ping distribution by client */ + clientTeamMap: Map; + exploits: ExploitState[]; + config: Config | undefined; +} + +export interface ServerState_ClientTeamMapEntry { + key: string; + value: TeamBucket | undefined; +} + +export interface PingRequest { + clientId: string; + payload?: + | { $case: "serverInfoRequest"; serverInfoRequest: PingRequest_ServerInfo } + | { $case: "heartbeatRequest"; heartbeatRequest: PingRequest_Heartbeat } + | { $case: "leaveRequest"; leaveRequest: PingRequest_Leave } + | undefined; +} + +export interface PingRequest_ServerInfo { +} + +export interface PingRequest_Heartbeat { + weight: number; +} + +export interface PingRequest_Leave { +} + +export interface PingResponse { + state: ServerState | undefined; +} + +export interface ExploitRequest { + exploitId: string; +} + +export interface ExploitResponse { + state: ExploitState | undefined; +} + +export interface UpdateExploitRequest { + state: ExploitState | undefined; +} + +export interface UpdateExploitResponse { + state: ExploitState | undefined; +} + +export interface BroadcastRequest { + command: string; +} + +export interface BroadcastResponse { +} + +export interface BroadcastSubscribeRequest { +} + +export interface BroadcastSubscribeResponse { + command: string; +} + +export interface SingleRunRequest { + exploitId: string; +} + +export interface SingleRunResponse { +} + +export interface SingleRunSubscribeRequest { +} + +export interface SingleRunSubscribeResponse { + exploitId: string; +} + +function createBaseTeamBucket(): TeamBucket { + return { teams: new Map() }; +} + +export const TeamBucket = { + encode(message: TeamBucket, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + message.teams.forEach((value, key) => { + TeamBucket_TeamsEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).ldelim(); + }); + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): TeamBucket { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTeamBucket(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + const entry1 = TeamBucket_TeamsEntry.decode(reader, reader.uint32()); + if (entry1.value !== undefined) { + message.teams.set(entry1.key, entry1.value); + } + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): TeamBucket { + return { + teams: isObject(object.teams) + ? Object.entries(object.teams).reduce>((acc, [key, value]) => { + acc.set(key, String(value)); + return acc; + }, new Map()) + : new Map(), + }; + }, + + toJSON(message: TeamBucket): unknown { + const obj: any = {}; + if (message.teams?.size) { + obj.teams = {}; + message.teams.forEach((v, k) => { + obj.teams[k] = v; + }); + } + return obj; + }, + + create, I>>(base?: I): TeamBucket { + return TeamBucket.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): TeamBucket { + const message = createBaseTeamBucket(); + message.teams = (() => { + const m = new Map(); + (object.teams as Map ?? new Map()).forEach((value, key) => { + if (value !== undefined) { + m.set(key, globalThis.String(value)); + } + }); + return m; + })(); + return message; + }, +}; + +function createBaseTeamBucket_TeamsEntry(): TeamBucket_TeamsEntry { + return { key: "", value: "" }; +} + +export const TeamBucket_TeamsEntry = { + encode(message: TeamBucket_TeamsEntry, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.key !== "") { + writer.uint32(10).string(message.key); + } + if (message.value !== "") { + writer.uint32(18).string(message.value); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): TeamBucket_TeamsEntry { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTeamBucket_TeamsEntry(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.key = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.value = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): TeamBucket_TeamsEntry { + return { + key: isSet(object.key) ? globalThis.String(object.key) : "", + value: isSet(object.value) ? globalThis.String(object.value) : "", + }; + }, + + toJSON(message: TeamBucket_TeamsEntry): unknown { + const obj: any = {}; + if (message.key !== "") { + obj.key = message.key; + } + if (message.value !== "") { + obj.value = message.value; + } + return obj; + }, + + create, I>>(base?: I): TeamBucket_TeamsEntry { + return TeamBucket_TeamsEntry.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): TeamBucket_TeamsEntry { + const message = createBaseTeamBucket_TeamsEntry(); + message.key = object.key ?? ""; + message.value = object.value ?? ""; + return message; + }, +}; + +function createBaseExploitConfiguration(): ExploitConfiguration { + return { entrypoint: "", isArchive: false, runEvery: undefined, timeout: undefined, disabled: false, endless: false }; +} + +export const ExploitConfiguration = { + encode(message: ExploitConfiguration, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.entrypoint !== "") { + writer.uint32(10).string(message.entrypoint); + } + if (message.isArchive === true) { + writer.uint32(16).bool(message.isArchive); + } + if (message.runEvery !== undefined) { + Duration.encode(message.runEvery, writer.uint32(26).fork()).ldelim(); + } + if (message.timeout !== undefined) { + Duration.encode(message.timeout, writer.uint32(34).fork()).ldelim(); + } + if (message.disabled === true) { + writer.uint32(40).bool(message.disabled); + } + if (message.endless === true) { + writer.uint32(48).bool(message.endless); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ExploitConfiguration { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseExploitConfiguration(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.entrypoint = reader.string(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.isArchive = reader.bool(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.runEvery = Duration.decode(reader, reader.uint32()); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.timeout = Duration.decode(reader, reader.uint32()); + continue; + case 5: + if (tag !== 40) { + break; + } + + message.disabled = reader.bool(); + continue; + case 6: + if (tag !== 48) { + break; + } + + message.endless = reader.bool(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ExploitConfiguration { + return { + entrypoint: isSet(object.entrypoint) ? globalThis.String(object.entrypoint) : "", + isArchive: isSet(object.isArchive) ? globalThis.Boolean(object.isArchive) : false, + runEvery: isSet(object.runEvery) ? Duration.fromJSON(object.runEvery) : undefined, + timeout: isSet(object.timeout) ? Duration.fromJSON(object.timeout) : undefined, + disabled: isSet(object.disabled) ? globalThis.Boolean(object.disabled) : false, + endless: isSet(object.endless) ? globalThis.Boolean(object.endless) : false, + }; + }, + + toJSON(message: ExploitConfiguration): unknown { + const obj: any = {}; + if (message.entrypoint !== "") { + obj.entrypoint = message.entrypoint; + } + if (message.isArchive === true) { + obj.isArchive = message.isArchive; + } + if (message.runEvery !== undefined) { + obj.runEvery = Duration.toJSON(message.runEvery); + } + if (message.timeout !== undefined) { + obj.timeout = Duration.toJSON(message.timeout); + } + if (message.disabled === true) { + obj.disabled = message.disabled; + } + if (message.endless === true) { + obj.endless = message.endless; + } + return obj; + }, + + create, I>>(base?: I): ExploitConfiguration { + return ExploitConfiguration.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ExploitConfiguration { + const message = createBaseExploitConfiguration(); + message.entrypoint = object.entrypoint ?? ""; + message.isArchive = object.isArchive ?? false; + message.runEvery = (object.runEvery !== undefined && object.runEvery !== null) + ? Duration.fromPartial(object.runEvery) + : undefined; + message.timeout = (object.timeout !== undefined && object.timeout !== null) + ? Duration.fromPartial(object.timeout) + : undefined; + message.disabled = object.disabled ?? false; + message.endless = object.endless ?? false; + return message; + }, +}; + +function createBaseExploitState(): ExploitState { + return { exploitId: "", version: Long.ZERO, file: undefined, config: undefined }; +} + +export const ExploitState = { + encode(message: ExploitState, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.exploitId !== "") { + writer.uint32(10).string(message.exploitId); + } + if (!message.version.isZero()) { + writer.uint32(16).int64(message.version); + } + if (message.file !== undefined) { + FileInfo.encode(message.file, writer.uint32(26).fork()).ldelim(); + } + if (message.config !== undefined) { + ExploitConfiguration.encode(message.config, writer.uint32(50).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ExploitState { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseExploitState(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.exploitId = reader.string(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.version = reader.int64() as Long; + continue; + case 3: + if (tag !== 26) { + break; + } + + message.file = FileInfo.decode(reader, reader.uint32()); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.config = ExploitConfiguration.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ExploitState { + return { + exploitId: isSet(object.exploitId) ? globalThis.String(object.exploitId) : "", + version: isSet(object.version) ? Long.fromValue(object.version) : Long.ZERO, + file: isSet(object.file) ? FileInfo.fromJSON(object.file) : undefined, + config: isSet(object.config) ? ExploitConfiguration.fromJSON(object.config) : undefined, + }; + }, + + toJSON(message: ExploitState): unknown { + const obj: any = {}; + if (message.exploitId !== "") { + obj.exploitId = message.exploitId; + } + if (!message.version.isZero()) { + obj.version = (message.version || Long.ZERO).toString(); + } + if (message.file !== undefined) { + obj.file = FileInfo.toJSON(message.file); + } + if (message.config !== undefined) { + obj.config = ExploitConfiguration.toJSON(message.config); + } + return obj; + }, + + create, I>>(base?: I): ExploitState { + return ExploitState.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ExploitState { + const message = createBaseExploitState(); + message.exploitId = object.exploitId ?? ""; + message.version = (object.version !== undefined && object.version !== null) + ? Long.fromValue(object.version) + : Long.ZERO; + message.file = (object.file !== undefined && object.file !== null) ? FileInfo.fromPartial(object.file) : undefined; + message.config = (object.config !== undefined && object.config !== null) + ? ExploitConfiguration.fromPartial(object.config) + : undefined; + return message; + }, +}; + +function createBaseConfig(): Config { + return { farmUrl: "", farmPassword: "", flagRegexp: "", pingEvery: undefined, submitEvery: undefined, environ: [] }; +} + +export const Config = { + encode(message: Config, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.farmUrl !== "") { + writer.uint32(10).string(message.farmUrl); + } + if (message.farmPassword !== "") { + writer.uint32(18).string(message.farmPassword); + } + if (message.flagRegexp !== "") { + writer.uint32(26).string(message.flagRegexp); + } + if (message.pingEvery !== undefined) { + Duration.encode(message.pingEvery, writer.uint32(34).fork()).ldelim(); + } + if (message.submitEvery !== undefined) { + Duration.encode(message.submitEvery, writer.uint32(42).fork()).ldelim(); + } + for (const v of message.environ) { + writer.uint32(50).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Config { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseConfig(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.farmUrl = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.farmPassword = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.flagRegexp = reader.string(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.pingEvery = Duration.decode(reader, reader.uint32()); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.submitEvery = Duration.decode(reader, reader.uint32()); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.environ.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Config { + return { + farmUrl: isSet(object.farmUrl) ? globalThis.String(object.farmUrl) : "", + farmPassword: isSet(object.farmPassword) ? globalThis.String(object.farmPassword) : "", + flagRegexp: isSet(object.flagRegexp) ? globalThis.String(object.flagRegexp) : "", + pingEvery: isSet(object.pingEvery) ? Duration.fromJSON(object.pingEvery) : undefined, + submitEvery: isSet(object.submitEvery) ? Duration.fromJSON(object.submitEvery) : undefined, + environ: globalThis.Array.isArray(object?.environ) ? object.environ.map((e: any) => globalThis.String(e)) : [], + }; + }, + + toJSON(message: Config): unknown { + const obj: any = {}; + if (message.farmUrl !== "") { + obj.farmUrl = message.farmUrl; + } + if (message.farmPassword !== "") { + obj.farmPassword = message.farmPassword; + } + if (message.flagRegexp !== "") { + obj.flagRegexp = message.flagRegexp; + } + if (message.pingEvery !== undefined) { + obj.pingEvery = Duration.toJSON(message.pingEvery); + } + if (message.submitEvery !== undefined) { + obj.submitEvery = Duration.toJSON(message.submitEvery); + } + if (message.environ?.length) { + obj.environ = message.environ; + } + return obj; + }, + + create, I>>(base?: I): Config { + return Config.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Config { + const message = createBaseConfig(); + message.farmUrl = object.farmUrl ?? ""; + message.farmPassword = object.farmPassword ?? ""; + message.flagRegexp = object.flagRegexp ?? ""; + message.pingEvery = (object.pingEvery !== undefined && object.pingEvery !== null) + ? Duration.fromPartial(object.pingEvery) + : undefined; + message.submitEvery = (object.submitEvery !== undefined && object.submitEvery !== null) + ? Duration.fromPartial(object.submitEvery) + : undefined; + message.environ = object.environ?.map((e) => e) || []; + return message; + }, +}; + +function createBaseServerState(): ServerState { + return { clientTeamMap: new Map(), exploits: [], config: undefined }; +} + +export const ServerState = { + encode(message: ServerState, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + message.clientTeamMap.forEach((value, key) => { + ServerState_ClientTeamMapEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).ldelim(); + }); + for (const v of message.exploits) { + ExploitState.encode(v!, writer.uint32(18).fork()).ldelim(); + } + if (message.config !== undefined) { + Config.encode(message.config, writer.uint32(26).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ServerState { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerState(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + const entry1 = ServerState_ClientTeamMapEntry.decode(reader, reader.uint32()); + if (entry1.value !== undefined) { + message.clientTeamMap.set(entry1.key, entry1.value); + } + continue; + case 2: + if (tag !== 18) { + break; + } + + message.exploits.push(ExploitState.decode(reader, reader.uint32())); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.config = Config.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerState { + return { + clientTeamMap: isObject(object.clientTeamMap) + ? Object.entries(object.clientTeamMap).reduce>((acc, [key, value]) => { + acc.set(key, TeamBucket.fromJSON(value)); + return acc; + }, new Map()) + : new Map(), + exploits: globalThis.Array.isArray(object?.exploits) + ? object.exploits.map((e: any) => ExploitState.fromJSON(e)) + : [], + config: isSet(object.config) ? Config.fromJSON(object.config) : undefined, + }; + }, + + toJSON(message: ServerState): unknown { + const obj: any = {}; + if (message.clientTeamMap?.size) { + obj.clientTeamMap = {}; + message.clientTeamMap.forEach((v, k) => { + obj.clientTeamMap[k] = TeamBucket.toJSON(v); + }); + } + if (message.exploits?.length) { + obj.exploits = message.exploits.map((e) => ExploitState.toJSON(e)); + } + if (message.config !== undefined) { + obj.config = Config.toJSON(message.config); + } + return obj; + }, + + create, I>>(base?: I): ServerState { + return ServerState.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ServerState { + const message = createBaseServerState(); + message.clientTeamMap = (() => { + const m = new Map(); + (object.clientTeamMap as Map ?? new Map()).forEach((value, key) => { + if (value !== undefined) { + m.set(key, TeamBucket.fromPartial(value)); + } + }); + return m; + })(); + message.exploits = object.exploits?.map((e) => ExploitState.fromPartial(e)) || []; + message.config = (object.config !== undefined && object.config !== null) + ? Config.fromPartial(object.config) + : undefined; + return message; + }, +}; + +function createBaseServerState_ClientTeamMapEntry(): ServerState_ClientTeamMapEntry { + return { key: "", value: undefined }; +} + +export const ServerState_ClientTeamMapEntry = { + encode(message: ServerState_ClientTeamMapEntry, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.key !== "") { + writer.uint32(10).string(message.key); + } + if (message.value !== undefined) { + TeamBucket.encode(message.value, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ServerState_ClientTeamMapEntry { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerState_ClientTeamMapEntry(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.key = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.value = TeamBucket.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerState_ClientTeamMapEntry { + return { + key: isSet(object.key) ? globalThis.String(object.key) : "", + value: isSet(object.value) ? TeamBucket.fromJSON(object.value) : undefined, + }; + }, + + toJSON(message: ServerState_ClientTeamMapEntry): unknown { + const obj: any = {}; + if (message.key !== "") { + obj.key = message.key; + } + if (message.value !== undefined) { + obj.value = TeamBucket.toJSON(message.value); + } + return obj; + }, + + create, I>>(base?: I): ServerState_ClientTeamMapEntry { + return ServerState_ClientTeamMapEntry.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): ServerState_ClientTeamMapEntry { + const message = createBaseServerState_ClientTeamMapEntry(); + message.key = object.key ?? ""; + message.value = (object.value !== undefined && object.value !== null) + ? TeamBucket.fromPartial(object.value) + : undefined; + return message; + }, +}; + +function createBasePingRequest(): PingRequest { + return { clientId: "", payload: undefined }; +} + +export const PingRequest = { + encode(message: PingRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.clientId !== "") { + writer.uint32(10).string(message.clientId); + } + switch (message.payload?.$case) { + case "serverInfoRequest": + PingRequest_ServerInfo.encode(message.payload.serverInfoRequest, writer.uint32(18).fork()).ldelim(); + break; + case "heartbeatRequest": + PingRequest_Heartbeat.encode(message.payload.heartbeatRequest, writer.uint32(26).fork()).ldelim(); + break; + case "leaveRequest": + PingRequest_Leave.encode(message.payload.leaveRequest, writer.uint32(34).fork()).ldelim(); + break; + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): PingRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePingRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.clientId = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.payload = { + $case: "serverInfoRequest", + serverInfoRequest: PingRequest_ServerInfo.decode(reader, reader.uint32()), + }; + continue; + case 3: + if (tag !== 26) { + break; + } + + message.payload = { + $case: "heartbeatRequest", + heartbeatRequest: PingRequest_Heartbeat.decode(reader, reader.uint32()), + }; + continue; + case 4: + if (tag !== 34) { + break; + } + + message.payload = { $case: "leaveRequest", leaveRequest: PingRequest_Leave.decode(reader, reader.uint32()) }; + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): PingRequest { + return { + clientId: isSet(object.clientId) ? globalThis.String(object.clientId) : "", + payload: isSet(object.serverInfoRequest) + ? { $case: "serverInfoRequest", serverInfoRequest: PingRequest_ServerInfo.fromJSON(object.serverInfoRequest) } + : isSet(object.heartbeatRequest) + ? { $case: "heartbeatRequest", heartbeatRequest: PingRequest_Heartbeat.fromJSON(object.heartbeatRequest) } + : isSet(object.leaveRequest) + ? { $case: "leaveRequest", leaveRequest: PingRequest_Leave.fromJSON(object.leaveRequest) } + : undefined, + }; + }, + + toJSON(message: PingRequest): unknown { + const obj: any = {}; + if (message.clientId !== "") { + obj.clientId = message.clientId; + } + if (message.payload?.$case === "serverInfoRequest") { + obj.serverInfoRequest = PingRequest_ServerInfo.toJSON(message.payload.serverInfoRequest); + } + if (message.payload?.$case === "heartbeatRequest") { + obj.heartbeatRequest = PingRequest_Heartbeat.toJSON(message.payload.heartbeatRequest); + } + if (message.payload?.$case === "leaveRequest") { + obj.leaveRequest = PingRequest_Leave.toJSON(message.payload.leaveRequest); + } + return obj; + }, + + create, I>>(base?: I): PingRequest { + return PingRequest.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): PingRequest { + const message = createBasePingRequest(); + message.clientId = object.clientId ?? ""; + if ( + object.payload?.$case === "serverInfoRequest" && + object.payload?.serverInfoRequest !== undefined && + object.payload?.serverInfoRequest !== null + ) { + message.payload = { + $case: "serverInfoRequest", + serverInfoRequest: PingRequest_ServerInfo.fromPartial(object.payload.serverInfoRequest), + }; + } + if ( + object.payload?.$case === "heartbeatRequest" && + object.payload?.heartbeatRequest !== undefined && + object.payload?.heartbeatRequest !== null + ) { + message.payload = { + $case: "heartbeatRequest", + heartbeatRequest: PingRequest_Heartbeat.fromPartial(object.payload.heartbeatRequest), + }; + } + if ( + object.payload?.$case === "leaveRequest" && + object.payload?.leaveRequest !== undefined && + object.payload?.leaveRequest !== null + ) { + message.payload = { + $case: "leaveRequest", + leaveRequest: PingRequest_Leave.fromPartial(object.payload.leaveRequest), + }; + } + return message; + }, +}; + +function createBasePingRequest_ServerInfo(): PingRequest_ServerInfo { + return {}; +} + +export const PingRequest_ServerInfo = { + encode(_: PingRequest_ServerInfo, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): PingRequest_ServerInfo { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePingRequest_ServerInfo(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(_: any): PingRequest_ServerInfo { + return {}; + }, + + toJSON(_: PingRequest_ServerInfo): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>(base?: I): PingRequest_ServerInfo { + return PingRequest_ServerInfo.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(_: I): PingRequest_ServerInfo { + const message = createBasePingRequest_ServerInfo(); + return message; + }, +}; + +function createBasePingRequest_Heartbeat(): PingRequest_Heartbeat { + return { weight: 0 }; +} + +export const PingRequest_Heartbeat = { + encode(message: PingRequest_Heartbeat, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.weight !== 0) { + writer.uint32(8).int32(message.weight); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): PingRequest_Heartbeat { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePingRequest_Heartbeat(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.weight = reader.int32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): PingRequest_Heartbeat { + return { weight: isSet(object.weight) ? globalThis.Number(object.weight) : 0 }; + }, + + toJSON(message: PingRequest_Heartbeat): unknown { + const obj: any = {}; + if (message.weight !== 0) { + obj.weight = Math.round(message.weight); + } + return obj; + }, + + create, I>>(base?: I): PingRequest_Heartbeat { + return PingRequest_Heartbeat.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): PingRequest_Heartbeat { + const message = createBasePingRequest_Heartbeat(); + message.weight = object.weight ?? 0; + return message; + }, +}; + +function createBasePingRequest_Leave(): PingRequest_Leave { + return {}; +} + +export const PingRequest_Leave = { + encode(_: PingRequest_Leave, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): PingRequest_Leave { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePingRequest_Leave(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(_: any): PingRequest_Leave { + return {}; + }, + + toJSON(_: PingRequest_Leave): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>(base?: I): PingRequest_Leave { + return PingRequest_Leave.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(_: I): PingRequest_Leave { + const message = createBasePingRequest_Leave(); + return message; + }, +}; + +function createBasePingResponse(): PingResponse { + return { state: undefined }; +} + +export const PingResponse = { + encode(message: PingResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.state !== undefined) { + ServerState.encode(message.state, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): PingResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePingResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.state = ServerState.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): PingResponse { + return { state: isSet(object.state) ? ServerState.fromJSON(object.state) : undefined }; + }, + + toJSON(message: PingResponse): unknown { + const obj: any = {}; + if (message.state !== undefined) { + obj.state = ServerState.toJSON(message.state); + } + return obj; + }, + + create, I>>(base?: I): PingResponse { + return PingResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): PingResponse { + const message = createBasePingResponse(); + message.state = (object.state !== undefined && object.state !== null) + ? ServerState.fromPartial(object.state) + : undefined; + return message; + }, +}; + +function createBaseExploitRequest(): ExploitRequest { + return { exploitId: "" }; +} + +export const ExploitRequest = { + encode(message: ExploitRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.exploitId !== "") { + writer.uint32(10).string(message.exploitId); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ExploitRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseExploitRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.exploitId = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ExploitRequest { + return { exploitId: isSet(object.exploitId) ? globalThis.String(object.exploitId) : "" }; + }, + + toJSON(message: ExploitRequest): unknown { + const obj: any = {}; + if (message.exploitId !== "") { + obj.exploitId = message.exploitId; + } + return obj; + }, + + create, I>>(base?: I): ExploitRequest { + return ExploitRequest.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ExploitRequest { + const message = createBaseExploitRequest(); + message.exploitId = object.exploitId ?? ""; + return message; + }, +}; + +function createBaseExploitResponse(): ExploitResponse { + return { state: undefined }; +} + +export const ExploitResponse = { + encode(message: ExploitResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.state !== undefined) { + ExploitState.encode(message.state, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ExploitResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseExploitResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.state = ExploitState.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ExploitResponse { + return { state: isSet(object.state) ? ExploitState.fromJSON(object.state) : undefined }; + }, + + toJSON(message: ExploitResponse): unknown { + const obj: any = {}; + if (message.state !== undefined) { + obj.state = ExploitState.toJSON(message.state); + } + return obj; + }, + + create, I>>(base?: I): ExploitResponse { + return ExploitResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ExploitResponse { + const message = createBaseExploitResponse(); + message.state = (object.state !== undefined && object.state !== null) + ? ExploitState.fromPartial(object.state) + : undefined; + return message; + }, +}; + +function createBaseUpdateExploitRequest(): UpdateExploitRequest { + return { state: undefined }; +} + +export const UpdateExploitRequest = { + encode(message: UpdateExploitRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.state !== undefined) { + ExploitState.encode(message.state, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): UpdateExploitRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseUpdateExploitRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.state = ExploitState.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): UpdateExploitRequest { + return { state: isSet(object.state) ? ExploitState.fromJSON(object.state) : undefined }; + }, + + toJSON(message: UpdateExploitRequest): unknown { + const obj: any = {}; + if (message.state !== undefined) { + obj.state = ExploitState.toJSON(message.state); + } + return obj; + }, + + create, I>>(base?: I): UpdateExploitRequest { + return UpdateExploitRequest.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): UpdateExploitRequest { + const message = createBaseUpdateExploitRequest(); + message.state = (object.state !== undefined && object.state !== null) + ? ExploitState.fromPartial(object.state) + : undefined; + return message; + }, +}; + +function createBaseUpdateExploitResponse(): UpdateExploitResponse { + return { state: undefined }; +} + +export const UpdateExploitResponse = { + encode(message: UpdateExploitResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.state !== undefined) { + ExploitState.encode(message.state, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): UpdateExploitResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseUpdateExploitResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.state = ExploitState.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): UpdateExploitResponse { + return { state: isSet(object.state) ? ExploitState.fromJSON(object.state) : undefined }; + }, + + toJSON(message: UpdateExploitResponse): unknown { + const obj: any = {}; + if (message.state !== undefined) { + obj.state = ExploitState.toJSON(message.state); + } + return obj; + }, + + create, I>>(base?: I): UpdateExploitResponse { + return UpdateExploitResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): UpdateExploitResponse { + const message = createBaseUpdateExploitResponse(); + message.state = (object.state !== undefined && object.state !== null) + ? ExploitState.fromPartial(object.state) + : undefined; + return message; + }, +}; + +function createBaseBroadcastRequest(): BroadcastRequest { + return { command: "" }; +} + +export const BroadcastRequest = { + encode(message: BroadcastRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.command !== "") { + writer.uint32(10).string(message.command); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BroadcastRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBroadcastRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.command = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): BroadcastRequest { + return { command: isSet(object.command) ? globalThis.String(object.command) : "" }; + }, + + toJSON(message: BroadcastRequest): unknown { + const obj: any = {}; + if (message.command !== "") { + obj.command = message.command; + } + return obj; + }, + + create, I>>(base?: I): BroadcastRequest { + return BroadcastRequest.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): BroadcastRequest { + const message = createBaseBroadcastRequest(); + message.command = object.command ?? ""; + return message; + }, +}; + +function createBaseBroadcastResponse(): BroadcastResponse { + return {}; +} + +export const BroadcastResponse = { + encode(_: BroadcastResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BroadcastResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBroadcastResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(_: any): BroadcastResponse { + return {}; + }, + + toJSON(_: BroadcastResponse): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>(base?: I): BroadcastResponse { + return BroadcastResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(_: I): BroadcastResponse { + const message = createBaseBroadcastResponse(); + return message; + }, +}; + +function createBaseBroadcastSubscribeRequest(): BroadcastSubscribeRequest { + return {}; +} + +export const BroadcastSubscribeRequest = { + encode(_: BroadcastSubscribeRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BroadcastSubscribeRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBroadcastSubscribeRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(_: any): BroadcastSubscribeRequest { + return {}; + }, + + toJSON(_: BroadcastSubscribeRequest): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>(base?: I): BroadcastSubscribeRequest { + return BroadcastSubscribeRequest.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(_: I): BroadcastSubscribeRequest { + const message = createBaseBroadcastSubscribeRequest(); + return message; + }, +}; + +function createBaseBroadcastSubscribeResponse(): BroadcastSubscribeResponse { + return { command: "" }; +} + +export const BroadcastSubscribeResponse = { + encode(message: BroadcastSubscribeResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.command !== "") { + writer.uint32(10).string(message.command); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BroadcastSubscribeResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBroadcastSubscribeResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.command = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): BroadcastSubscribeResponse { + return { command: isSet(object.command) ? globalThis.String(object.command) : "" }; + }, + + toJSON(message: BroadcastSubscribeResponse): unknown { + const obj: any = {}; + if (message.command !== "") { + obj.command = message.command; + } + return obj; + }, + + create, I>>(base?: I): BroadcastSubscribeResponse { + return BroadcastSubscribeResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): BroadcastSubscribeResponse { + const message = createBaseBroadcastSubscribeResponse(); + message.command = object.command ?? ""; + return message; + }, +}; + +function createBaseSingleRunRequest(): SingleRunRequest { + return { exploitId: "" }; +} + +export const SingleRunRequest = { + encode(message: SingleRunRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.exploitId !== "") { + writer.uint32(10).string(message.exploitId); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): SingleRunRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSingleRunRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.exploitId = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): SingleRunRequest { + return { exploitId: isSet(object.exploitId) ? globalThis.String(object.exploitId) : "" }; + }, + + toJSON(message: SingleRunRequest): unknown { + const obj: any = {}; + if (message.exploitId !== "") { + obj.exploitId = message.exploitId; + } + return obj; + }, + + create, I>>(base?: I): SingleRunRequest { + return SingleRunRequest.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): SingleRunRequest { + const message = createBaseSingleRunRequest(); + message.exploitId = object.exploitId ?? ""; + return message; + }, +}; + +function createBaseSingleRunResponse(): SingleRunResponse { + return {}; +} + +export const SingleRunResponse = { + encode(_: SingleRunResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): SingleRunResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSingleRunResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(_: any): SingleRunResponse { + return {}; + }, + + toJSON(_: SingleRunResponse): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>(base?: I): SingleRunResponse { + return SingleRunResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(_: I): SingleRunResponse { + const message = createBaseSingleRunResponse(); + return message; + }, +}; + +function createBaseSingleRunSubscribeRequest(): SingleRunSubscribeRequest { + return {}; +} + +export const SingleRunSubscribeRequest = { + encode(_: SingleRunSubscribeRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): SingleRunSubscribeRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSingleRunSubscribeRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(_: any): SingleRunSubscribeRequest { + return {}; + }, + + toJSON(_: SingleRunSubscribeRequest): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>(base?: I): SingleRunSubscribeRequest { + return SingleRunSubscribeRequest.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(_: I): SingleRunSubscribeRequest { + const message = createBaseSingleRunSubscribeRequest(); + return message; + }, +}; + +function createBaseSingleRunSubscribeResponse(): SingleRunSubscribeResponse { + return { exploitId: "" }; +} + +export const SingleRunSubscribeResponse = { + encode(message: SingleRunSubscribeResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.exploitId !== "") { + writer.uint32(10).string(message.exploitId); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): SingleRunSubscribeResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSingleRunSubscribeResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.exploitId = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): SingleRunSubscribeResponse { + return { exploitId: isSet(object.exploitId) ? globalThis.String(object.exploitId) : "" }; + }, + + toJSON(message: SingleRunSubscribeResponse): unknown { + const obj: any = {}; + if (message.exploitId !== "") { + obj.exploitId = message.exploitId; + } + return obj; + }, + + create, I>>(base?: I): SingleRunSubscribeResponse { + return SingleRunSubscribeResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): SingleRunSubscribeResponse { + const message = createBaseSingleRunSubscribeResponse(); + message.exploitId = object.exploitId ?? ""; + return message; + }, +}; + +export type ServiceDefinition = typeof ServiceDefinition; +export const ServiceDefinition = { + name: "Service", + fullName: "exploits.Service", + methods: { + ping: { + name: "Ping", + requestType: PingRequest, + requestStream: false, + responseType: PingResponse, + responseStream: false, + options: {}, + }, + exploit: { + name: "Exploit", + requestType: ExploitRequest, + requestStream: false, + responseType: ExploitResponse, + responseStream: false, + options: {}, + }, + updateExploit: { + name: "UpdateExploit", + requestType: UpdateExploitRequest, + requestStream: false, + responseType: UpdateExploitResponse, + responseStream: false, + options: {}, + }, + broadcastCommand: { + name: "BroadcastCommand", + requestType: BroadcastRequest, + requestStream: false, + responseType: BroadcastResponse, + responseStream: false, + options: {}, + }, + broadcastSubscribe: { + name: "BroadcastSubscribe", + requestType: BroadcastSubscribeRequest, + requestStream: false, + responseType: BroadcastSubscribeResponse, + responseStream: true, + options: {}, + }, + singleRun: { + name: "SingleRun", + requestType: SingleRunRequest, + requestStream: false, + responseType: SingleRunResponse, + responseStream: false, + options: {}, + }, + singleRunSubscribe: { + name: "SingleRunSubscribe", + requestType: SingleRunSubscribeRequest, + requestStream: false, + responseType: SingleRunSubscribeResponse, + responseStream: true, + options: {}, + }, + }, +} as const; + +export interface ServiceImplementation { + ping(request: PingRequest, context: CallContext & CallContextExt): Promise>; + exploit(request: ExploitRequest, context: CallContext & CallContextExt): Promise>; + updateExploit( + request: UpdateExploitRequest, + context: CallContext & CallContextExt, + ): Promise>; + broadcastCommand( + request: BroadcastRequest, + context: CallContext & CallContextExt, + ): Promise>; + broadcastSubscribe( + request: BroadcastSubscribeRequest, + context: CallContext & CallContextExt, + ): ServerStreamingMethodResult>; + singleRun(request: SingleRunRequest, context: CallContext & CallContextExt): Promise>; + singleRunSubscribe( + request: SingleRunSubscribeRequest, + context: CallContext & CallContextExt, + ): ServerStreamingMethodResult>; +} + +export interface ServiceClient { + ping(request: DeepPartial, options?: CallOptions & CallOptionsExt): Promise; + exploit(request: DeepPartial, options?: CallOptions & CallOptionsExt): Promise; + updateExploit( + request: DeepPartial, + options?: CallOptions & CallOptionsExt, + ): Promise; + broadcastCommand( + request: DeepPartial, + options?: CallOptions & CallOptionsExt, + ): Promise; + broadcastSubscribe( + request: DeepPartial, + options?: CallOptions & CallOptionsExt, + ): AsyncIterable; + singleRun(request: DeepPartial, options?: CallOptions & CallOptionsExt): Promise; + singleRunSubscribe( + request: DeepPartial, + options?: CallOptions & CallOptionsExt, + ): AsyncIterable; +} + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends Long ? string | number | Long : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends { $case: string } ? { [K in keyof Omit]?: DeepPartial } & { $case: T["$case"] } + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isObject(value: any): boolean { + return typeof value === "object" && value !== null; +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} + +export type ServerStreamingMethodResult = { [Symbol.asyncIterator](): AsyncIterator }; diff --git a/front/src/proto/fileserver/api.ts b/front/src/proto/fileserver/api.ts new file mode 100644 index 0000000..0220bec --- /dev/null +++ b/front/src/proto/fileserver/api.ts @@ -0,0 +1,218 @@ +// @ts-nocheck +/* eslint-disable */ +import Long from "long"; +import type { CallContext, CallOptions } from "nice-grpc-common"; +import _m0 from "protobufjs/minimal"; + +export const protobufPackage = "fileserver"; + +export interface FileInfo { + uuid: string; +} + +export interface FileStream { + chunk: Uint8Array; +} + +function createBaseFileInfo(): FileInfo { + return { uuid: "" }; +} + +export const FileInfo = { + encode(message: FileInfo, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.uuid !== "") { + writer.uint32(10).string(message.uuid); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): FileInfo { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFileInfo(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.uuid = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): FileInfo { + return { uuid: isSet(object.uuid) ? globalThis.String(object.uuid) : "" }; + }, + + toJSON(message: FileInfo): unknown { + const obj: any = {}; + if (message.uuid !== "") { + obj.uuid = message.uuid; + } + return obj; + }, + + create, I>>(base?: I): FileInfo { + return FileInfo.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): FileInfo { + const message = createBaseFileInfo(); + message.uuid = object.uuid ?? ""; + return message; + }, +}; + +function createBaseFileStream(): FileStream { + return { chunk: new Uint8Array(0) }; +} + +export const FileStream = { + encode(message: FileStream, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.chunk.length !== 0) { + writer.uint32(10).bytes(message.chunk); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): FileStream { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFileStream(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.chunk = reader.bytes(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): FileStream { + return { chunk: isSet(object.chunk) ? bytesFromBase64(object.chunk) : new Uint8Array(0) }; + }, + + toJSON(message: FileStream): unknown { + const obj: any = {}; + if (message.chunk.length !== 0) { + obj.chunk = base64FromBytes(message.chunk); + } + return obj; + }, + + create, I>>(base?: I): FileStream { + return FileStream.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): FileStream { + const message = createBaseFileStream(); + message.chunk = object.chunk ?? new Uint8Array(0); + return message; + }, +}; + +export type ServiceDefinition = typeof ServiceDefinition; +export const ServiceDefinition = { + name: "Service", + fullName: "fileserver.Service", + methods: { + uploadFile: { + name: "UploadFile", + requestType: FileStream, + requestStream: true, + responseType: FileInfo, + responseStream: false, + options: {}, + }, + downloadFile: { + name: "DownloadFile", + requestType: FileInfo, + requestStream: false, + responseType: FileStream, + responseStream: true, + options: {}, + }, + }, +} as const; + +export interface ServiceImplementation { + uploadFile(request: AsyncIterable, context: CallContext & CallContextExt): Promise>; + downloadFile( + request: FileInfo, + context: CallContext & CallContextExt, + ): ServerStreamingMethodResult>; +} + +export interface ServiceClient { + uploadFile( + request: AsyncIterable>, + options?: CallOptions & CallOptionsExt, + ): Promise; + downloadFile(request: DeepPartial, options?: CallOptions & CallOptionsExt): AsyncIterable; +} + +function bytesFromBase64(b64: string): Uint8Array { + if (globalThis.Buffer) { + return Uint8Array.from(globalThis.Buffer.from(b64, "base64")); + } else { + const bin = globalThis.atob(b64); + const arr = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; ++i) { + arr[i] = bin.charCodeAt(i); + } + return arr; + } +} + +function base64FromBytes(arr: Uint8Array): string { + if (globalThis.Buffer) { + return globalThis.Buffer.from(arr).toString("base64"); + } else { + const bin: string[] = []; + arr.forEach((byte) => { + bin.push(globalThis.String.fromCharCode(byte)); + }); + return globalThis.btoa(bin.join("")); + } +} + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends Long ? string | number | Long : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends { $case: string } ? { [K in keyof Omit]?: DeepPartial } & { $case: T["$case"] } + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} + +export type ServerStreamingMethodResult = { [Symbol.asyncIterator](): AsyncIterator }; diff --git a/front/src/proto/google/protobuf/duration.ts b/front/src/proto/google/protobuf/duration.ts new file mode 100644 index 0000000..56fe8f3 --- /dev/null +++ b/front/src/proto/google/protobuf/duration.ts @@ -0,0 +1,182 @@ +// @ts-nocheck +/* eslint-disable */ +import Long from "long"; +import _m0 from "protobufjs/minimal"; + +export const protobufPackage = "google.protobuf"; + +/** + * A Duration represents a signed, fixed-length span of time represented + * as a count of seconds and fractions of seconds at nanosecond + * resolution. It is independent of any calendar and concepts like "day" + * or "month". It is related to Timestamp in that the difference between + * two Timestamp values is a Duration and it can be added or subtracted + * from a Timestamp. Range is approximately +-10,000 years. + * + * # Examples + * + * Example 1: Compute Duration from two Timestamps in pseudo code. + * + * Timestamp start = ...; + * Timestamp end = ...; + * Duration duration = ...; + * + * duration.seconds = end.seconds - start.seconds; + * duration.nanos = end.nanos - start.nanos; + * + * if (duration.seconds < 0 && duration.nanos > 0) { + * duration.seconds += 1; + * duration.nanos -= 1000000000; + * } else if (duration.seconds > 0 && duration.nanos < 0) { + * duration.seconds -= 1; + * duration.nanos += 1000000000; + * } + * + * Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. + * + * Timestamp start = ...; + * Duration duration = ...; + * Timestamp end = ...; + * + * end.seconds = start.seconds + duration.seconds; + * end.nanos = start.nanos + duration.nanos; + * + * if (end.nanos < 0) { + * end.seconds -= 1; + * end.nanos += 1000000000; + * } else if (end.nanos >= 1000000000) { + * end.seconds += 1; + * end.nanos -= 1000000000; + * } + * + * Example 3: Compute Duration from datetime.timedelta in Python. + * + * td = datetime.timedelta(days=3, minutes=10) + * duration = Duration() + * duration.FromTimedelta(td) + * + * # JSON Mapping + * + * In JSON format, the Duration type is encoded as a string rather than an + * object, where the string ends in the suffix "s" (indicating seconds) and + * is preceded by the number of seconds, with nanoseconds expressed as + * fractional seconds. For example, 3 seconds with 0 nanoseconds should be + * encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should + * be expressed in JSON format as "3.000000001s", and 3 seconds and 1 + * microsecond should be expressed in JSON format as "3.000001s". + */ +export interface Duration { + /** + * Signed seconds of the span of time. Must be from -315,576,000,000 + * to +315,576,000,000 inclusive. Note: these bounds are computed from: + * 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years + */ + seconds: Long; + /** + * Signed fractions of a second at nanosecond resolution of the span + * of time. Durations less than one second are represented with a 0 + * `seconds` field and a positive or negative `nanos` field. For durations + * of one second or more, a non-zero value for the `nanos` field must be + * of the same sign as the `seconds` field. Must be from -999,999,999 + * to +999,999,999 inclusive. + */ + nanos: number; +} + +function createBaseDuration(): Duration { + return { seconds: Long.ZERO, nanos: 0 }; +} + +export const Duration = { + encode(message: Duration, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (!message.seconds.isZero()) { + writer.uint32(8).int64(message.seconds); + } + if (message.nanos !== 0) { + writer.uint32(16).int32(message.nanos); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Duration { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseDuration(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.seconds = reader.int64() as Long; + continue; + case 2: + if (tag !== 16) { + break; + } + + message.nanos = reader.int32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Duration { + return { + seconds: isSet(object.seconds) ? Long.fromValue(object.seconds) : Long.ZERO, + nanos: isSet(object.nanos) ? globalThis.Number(object.nanos) : 0, + }; + }, + + toJSON(message: Duration): unknown { + const obj: any = {}; + if (!message.seconds.isZero()) { + obj.seconds = (message.seconds || Long.ZERO).toString(); + } + if (message.nanos !== 0) { + obj.nanos = Math.round(message.nanos); + } + return obj; + }, + + create, I>>(base?: I): Duration { + return Duration.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Duration { + const message = createBaseDuration(); + message.seconds = (object.seconds !== undefined && object.seconds !== null) + ? Long.fromValue(object.seconds) + : Long.ZERO; + message.nanos = object.nanos ?? 0; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends Long ? string | number | Long : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends { $case: string } ? { [K in keyof Omit]?: DeepPartial } & { $case: T["$case"] } + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/front/src/proto/google/protobuf/empty.ts b/front/src/proto/google/protobuf/empty.ts new file mode 100644 index 0000000..cd73836 --- /dev/null +++ b/front/src/proto/google/protobuf/empty.ts @@ -0,0 +1,79 @@ +// @ts-nocheck +/* eslint-disable */ +import Long from "long"; +import _m0 from "protobufjs/minimal"; + +export const protobufPackage = "google.protobuf"; + +/** + * A generic empty message that you can re-use to avoid defining duplicated + * empty messages in your APIs. A typical example is to use it as the request + * or the response type of an API method. For instance: + * + * service Foo { + * rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); + * } + */ +export interface Empty { +} + +function createBaseEmpty(): Empty { + return {}; +} + +export const Empty = { + encode(_: Empty, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Empty { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEmpty(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(_: any): Empty { + return {}; + }, + + toJSON(_: Empty): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>(base?: I): Empty { + return Empty.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(_: I): Empty { + const message = createBaseEmpty(); + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends Long ? string | number | Long : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends { $case: string } ? { [K in keyof Omit]?: DeepPartial } & { $case: T["$case"] } + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} diff --git a/front/src/proto/google/protobuf/timestamp.ts b/front/src/proto/google/protobuf/timestamp.ts new file mode 100644 index 0000000..0947940 --- /dev/null +++ b/front/src/proto/google/protobuf/timestamp.ts @@ -0,0 +1,211 @@ +// @ts-nocheck +/* eslint-disable */ +import Long from "long"; +import _m0 from "protobufjs/minimal"; + +export const protobufPackage = "google.protobuf"; + +/** + * A Timestamp represents a point in time independent of any time zone or local + * calendar, encoded as a count of seconds and fractions of seconds at + * nanosecond resolution. The count is relative to an epoch at UTC midnight on + * January 1, 1970, in the proleptic Gregorian calendar which extends the + * Gregorian calendar backwards to year one. + * + * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap + * second table is needed for interpretation, using a [24-hour linear + * smear](https://developers.google.com/time/smear). + * + * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By + * restricting to that range, we ensure that we can convert to and from [RFC + * 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. + * + * # Examples + * + * Example 1: Compute Timestamp from POSIX `time()`. + * + * Timestamp timestamp; + * timestamp.set_seconds(time(NULL)); + * timestamp.set_nanos(0); + * + * Example 2: Compute Timestamp from POSIX `gettimeofday()`. + * + * struct timeval tv; + * gettimeofday(&tv, NULL); + * + * Timestamp timestamp; + * timestamp.set_seconds(tv.tv_sec); + * timestamp.set_nanos(tv.tv_usec * 1000); + * + * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. + * + * FILETIME ft; + * GetSystemTimeAsFileTime(&ft); + * UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; + * + * // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z + * // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. + * Timestamp timestamp; + * timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); + * timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); + * + * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. + * + * long millis = System.currentTimeMillis(); + * + * Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) + * .setNanos((int) ((millis % 1000) * 1000000)).build(); + * + * Example 5: Compute Timestamp from Java `Instant.now()`. + * + * Instant now = Instant.now(); + * + * Timestamp timestamp = + * Timestamp.newBuilder().setSeconds(now.getEpochSecond()) + * .setNanos(now.getNano()).build(); + * + * Example 6: Compute Timestamp from current time in Python. + * + * timestamp = Timestamp() + * timestamp.GetCurrentTime() + * + * # JSON Mapping + * + * In JSON format, the Timestamp type is encoded as a string in the + * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the + * format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" + * where {year} is always expressed using four digits while {month}, {day}, + * {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional + * seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), + * are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone + * is required. A proto3 JSON serializer should always use UTC (as indicated by + * "Z") when printing the Timestamp type and a proto3 JSON parser should be + * able to accept both UTC and other timezones (as indicated by an offset). + * + * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past + * 01:30 UTC on January 15, 2017. + * + * In JavaScript, one can convert a Date object to this format using the + * standard + * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) + * method. In Python, a standard `datetime.datetime` object can be converted + * to this format using + * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with + * the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use + * the Joda Time's [`ISODateTimeFormat.dateTime()`]( + * http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D + * ) to obtain a formatter capable of generating timestamps in this format. + */ +export interface Timestamp { + /** + * Represents seconds of UTC time since Unix epoch + * 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + * 9999-12-31T23:59:59Z inclusive. + */ + seconds: Long; + /** + * Non-negative fractions of a second at nanosecond resolution. Negative + * second values with fractions must still have non-negative nanos values + * that count forward in time. Must be from 0 to 999,999,999 + * inclusive. + */ + nanos: number; +} + +function createBaseTimestamp(): Timestamp { + return { seconds: Long.ZERO, nanos: 0 }; +} + +export const Timestamp = { + encode(message: Timestamp, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (!message.seconds.isZero()) { + writer.uint32(8).int64(message.seconds); + } + if (message.nanos !== 0) { + writer.uint32(16).int32(message.nanos); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Timestamp { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTimestamp(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.seconds = reader.int64() as Long; + continue; + case 2: + if (tag !== 16) { + break; + } + + message.nanos = reader.int32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Timestamp { + return { + seconds: isSet(object.seconds) ? Long.fromValue(object.seconds) : Long.ZERO, + nanos: isSet(object.nanos) ? globalThis.Number(object.nanos) : 0, + }; + }, + + toJSON(message: Timestamp): unknown { + const obj: any = {}; + if (!message.seconds.isZero()) { + obj.seconds = (message.seconds || Long.ZERO).toString(); + } + if (message.nanos !== 0) { + obj.nanos = Math.round(message.nanos); + } + return obj; + }, + + create, I>>(base?: I): Timestamp { + return Timestamp.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Timestamp { + const message = createBaseTimestamp(); + message.seconds = (object.seconds !== undefined && object.seconds !== null) + ? Long.fromValue(object.seconds) + : Long.ZERO; + message.nanos = object.nanos ?? 0; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends Long ? string | number | Long : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends { $case: string } ? { [K in keyof Omit]?: DeepPartial } & { $case: T["$case"] } + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/front/src/proto/logs/api.ts b/front/src/proto/logs/api.ts new file mode 100644 index 0000000..b3c12ef --- /dev/null +++ b/front/src/proto/logs/api.ts @@ -0,0 +1,499 @@ +// @ts-nocheck +/* eslint-disable */ +import Long from "long"; +import type { CallContext, CallOptions } from "nice-grpc-common"; +import _m0 from "protobufjs/minimal"; +import { Empty } from "../google/protobuf/empty"; +import { Timestamp } from "../google/protobuf/timestamp"; + +export const protobufPackage = "logs"; + +export interface LogLine { + exploit: string; + version: Long; + message: string; + level: string; + team: string; + timestamp: Timestamp | undefined; +} + +export interface AddLogLinesRequest { + lines: LogLine[]; +} + +export interface SearchLogLinesRequest { + exploit: string; + version: Long; + limit: Long; + lastToken: string; +} + +export interface SearchLogLinesResponse { + lines: LogLine[]; + lastToken: string; +} + +function createBaseLogLine(): LogLine { + return { exploit: "", version: Long.ZERO, message: "", level: "", team: "", timestamp: undefined }; +} + +export const LogLine = { + encode(message: LogLine, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.exploit !== "") { + writer.uint32(10).string(message.exploit); + } + if (!message.version.isZero()) { + writer.uint32(16).int64(message.version); + } + if (message.message !== "") { + writer.uint32(26).string(message.message); + } + if (message.level !== "") { + writer.uint32(34).string(message.level); + } + if (message.team !== "") { + writer.uint32(42).string(message.team); + } + if (message.timestamp !== undefined) { + Timestamp.encode(message.timestamp, writer.uint32(50).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): LogLine { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseLogLine(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.exploit = reader.string(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.version = reader.int64() as Long; + continue; + case 3: + if (tag !== 26) { + break; + } + + message.message = reader.string(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.level = reader.string(); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.team = reader.string(); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.timestamp = Timestamp.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): LogLine { + return { + exploit: isSet(object.exploit) ? globalThis.String(object.exploit) : "", + version: isSet(object.version) ? Long.fromValue(object.version) : Long.ZERO, + message: isSet(object.message) ? globalThis.String(object.message) : "", + level: isSet(object.level) ? globalThis.String(object.level) : "", + team: isSet(object.team) ? globalThis.String(object.team) : "", + timestamp: isSet(object.timestamp) ? fromJsonTimestamp(object.timestamp) : undefined, + }; + }, + + toJSON(message: LogLine): unknown { + const obj: any = {}; + if (message.exploit !== "") { + obj.exploit = message.exploit; + } + if (!message.version.isZero()) { + obj.version = (message.version || Long.ZERO).toString(); + } + if (message.message !== "") { + obj.message = message.message; + } + if (message.level !== "") { + obj.level = message.level; + } + if (message.team !== "") { + obj.team = message.team; + } + if (message.timestamp !== undefined) { + obj.timestamp = fromTimestamp(message.timestamp).toISOString(); + } + return obj; + }, + + create, I>>(base?: I): LogLine { + return LogLine.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): LogLine { + const message = createBaseLogLine(); + message.exploit = object.exploit ?? ""; + message.version = (object.version !== undefined && object.version !== null) + ? Long.fromValue(object.version) + : Long.ZERO; + message.message = object.message ?? ""; + message.level = object.level ?? ""; + message.team = object.team ?? ""; + message.timestamp = (object.timestamp !== undefined && object.timestamp !== null) + ? Timestamp.fromPartial(object.timestamp) + : undefined; + return message; + }, +}; + +function createBaseAddLogLinesRequest(): AddLogLinesRequest { + return { lines: [] }; +} + +export const AddLogLinesRequest = { + encode(message: AddLogLinesRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.lines) { + LogLine.encode(v!, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AddLogLinesRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAddLogLinesRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.lines.push(LogLine.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AddLogLinesRequest { + return { lines: globalThis.Array.isArray(object?.lines) ? object.lines.map((e: any) => LogLine.fromJSON(e)) : [] }; + }, + + toJSON(message: AddLogLinesRequest): unknown { + const obj: any = {}; + if (message.lines?.length) { + obj.lines = message.lines.map((e) => LogLine.toJSON(e)); + } + return obj; + }, + + create, I>>(base?: I): AddLogLinesRequest { + return AddLogLinesRequest.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): AddLogLinesRequest { + const message = createBaseAddLogLinesRequest(); + message.lines = object.lines?.map((e) => LogLine.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseSearchLogLinesRequest(): SearchLogLinesRequest { + return { exploit: "", version: Long.ZERO, limit: Long.ZERO, lastToken: "" }; +} + +export const SearchLogLinesRequest = { + encode(message: SearchLogLinesRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.exploit !== "") { + writer.uint32(10).string(message.exploit); + } + if (!message.version.isZero()) { + writer.uint32(16).int64(message.version); + } + if (!message.limit.isZero()) { + writer.uint32(24).int64(message.limit); + } + if (message.lastToken !== "") { + writer.uint32(34).string(message.lastToken); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): SearchLogLinesRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSearchLogLinesRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.exploit = reader.string(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.version = reader.int64() as Long; + continue; + case 3: + if (tag !== 24) { + break; + } + + message.limit = reader.int64() as Long; + continue; + case 4: + if (tag !== 34) { + break; + } + + message.lastToken = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): SearchLogLinesRequest { + return { + exploit: isSet(object.exploit) ? globalThis.String(object.exploit) : "", + version: isSet(object.version) ? Long.fromValue(object.version) : Long.ZERO, + limit: isSet(object.limit) ? Long.fromValue(object.limit) : Long.ZERO, + lastToken: isSet(object.lastToken) ? globalThis.String(object.lastToken) : "", + }; + }, + + toJSON(message: SearchLogLinesRequest): unknown { + const obj: any = {}; + if (message.exploit !== "") { + obj.exploit = message.exploit; + } + if (!message.version.isZero()) { + obj.version = (message.version || Long.ZERO).toString(); + } + if (!message.limit.isZero()) { + obj.limit = (message.limit || Long.ZERO).toString(); + } + if (message.lastToken !== "") { + obj.lastToken = message.lastToken; + } + return obj; + }, + + create, I>>(base?: I): SearchLogLinesRequest { + return SearchLogLinesRequest.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): SearchLogLinesRequest { + const message = createBaseSearchLogLinesRequest(); + message.exploit = object.exploit ?? ""; + message.version = (object.version !== undefined && object.version !== null) + ? Long.fromValue(object.version) + : Long.ZERO; + message.limit = (object.limit !== undefined && object.limit !== null) ? Long.fromValue(object.limit) : Long.ZERO; + message.lastToken = object.lastToken ?? ""; + return message; + }, +}; + +function createBaseSearchLogLinesResponse(): SearchLogLinesResponse { + return { lines: [], lastToken: "" }; +} + +export const SearchLogLinesResponse = { + encode(message: SearchLogLinesResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.lines) { + LogLine.encode(v!, writer.uint32(10).fork()).ldelim(); + } + if (message.lastToken !== "") { + writer.uint32(18).string(message.lastToken); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): SearchLogLinesResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSearchLogLinesResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.lines.push(LogLine.decode(reader, reader.uint32())); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.lastToken = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): SearchLogLinesResponse { + return { + lines: globalThis.Array.isArray(object?.lines) ? object.lines.map((e: any) => LogLine.fromJSON(e)) : [], + lastToken: isSet(object.lastToken) ? globalThis.String(object.lastToken) : "", + }; + }, + + toJSON(message: SearchLogLinesResponse): unknown { + const obj: any = {}; + if (message.lines?.length) { + obj.lines = message.lines.map((e) => LogLine.toJSON(e)); + } + if (message.lastToken !== "") { + obj.lastToken = message.lastToken; + } + return obj; + }, + + create, I>>(base?: I): SearchLogLinesResponse { + return SearchLogLinesResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): SearchLogLinesResponse { + const message = createBaseSearchLogLinesResponse(); + message.lines = object.lines?.map((e) => LogLine.fromPartial(e)) || []; + message.lastToken = object.lastToken ?? ""; + return message; + }, +}; + +export type ServiceDefinition = typeof ServiceDefinition; +export const ServiceDefinition = { + name: "Service", + fullName: "logs.Service", + methods: { + addLogLines: { + name: "AddLogLines", + requestType: AddLogLinesRequest, + requestStream: false, + responseType: Empty, + responseStream: false, + options: {}, + }, + searchLogLines: { + name: "SearchLogLines", + requestType: SearchLogLinesRequest, + requestStream: false, + responseType: SearchLogLinesResponse, + responseStream: true, + options: {}, + }, + }, +} as const; + +export interface ServiceImplementation { + addLogLines(request: AddLogLinesRequest, context: CallContext & CallContextExt): Promise>; + searchLogLines( + request: SearchLogLinesRequest, + context: CallContext & CallContextExt, + ): ServerStreamingMethodResult>; +} + +export interface ServiceClient { + addLogLines(request: DeepPartial, options?: CallOptions & CallOptionsExt): Promise; + searchLogLines( + request: DeepPartial, + options?: CallOptions & CallOptionsExt, + ): AsyncIterable; +} + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends Long ? string | number | Long : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends { $case: string } ? { [K in keyof Omit]?: DeepPartial } & { $case: T["$case"] } + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function toTimestamp(date: Date): Timestamp { + const seconds = numberToLong(date.getTime() / 1_000); + const nanos = (date.getTime() % 1_000) * 1_000_000; + return { seconds, nanos }; +} + +function fromTimestamp(t: Timestamp): Date { + let millis = (t.seconds.toNumber() || 0) * 1_000; + millis += (t.nanos || 0) / 1_000_000; + return new globalThis.Date(millis); +} + +function fromJsonTimestamp(o: any): Timestamp { + if (o instanceof globalThis.Date) { + return toTimestamp(o); + } else if (typeof o === "string") { + return toTimestamp(new globalThis.Date(o)); + } else { + return Timestamp.fromJSON(o); + } +} + +function numberToLong(number: number) { + return Long.fromNumber(number); +} + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} + +export type ServerStreamingMethodResult = { [Symbol.asyncIterator](): AsyncIterator }; diff --git a/front/src/routes/auth.tsx b/front/src/routes/auth.tsx new file mode 100644 index 0000000..48bd701 --- /dev/null +++ b/front/src/routes/auth.tsx @@ -0,0 +1,86 @@ +import { rawExploitServiceClient } from "@/services/exploits"; +import { usePersistentStorageValue } from "@/utils/storage"; +import { Box, TextField } from "@mui/material"; +import { ClientError, Metadata } from "nice-grpc-web"; +import { useEffect, useState } from "react"; +import ReactDOM from "react-dom"; +import { Helmet } from "react-helmet-async"; +import { AuthContext } from "./authContext"; + +export default function AuthProvider({ + children, +}: { + children: React.ReactNode; +}) { + const [error, setError] = useState(null); + const [token, setToken] = usePersistentStorageValue("token"); + const [checkedToken, setCheckedToken] = useState(false); + + useEffect(() => { + const checkToken = async (token: string) => { + try { + await rawExploitServiceClient.ping( + { + payload: { $case: "serverInfoRequest", serverInfoRequest: {} }, + }, + { metadata: new Metadata({ authorization: token }) } + ); + ReactDOM.unstable_batchedUpdates(() => { + setToken(token); + setCheckedToken(true); + setError(null); + }); + } catch (e) { + let message = "Unknown error"; + if (e instanceof ClientError) { + message = e.message; + } else if (e instanceof Error) { + message = e.message; + } + + ReactDOM.unstable_batchedUpdates(() => { + setCheckedToken(false); + setError(message); + }); + } + }; + + if (token) { + void checkToken(token); + } + }, [token, setToken]); + + const handleTokenChange = (event: React.ChangeEvent) => { + setToken(event.target.value); + }; + + if (!token || !checkedToken) { + return ( + <> + + Neo + + + + + + ); + } + + return ( + + {children} + + ); +} diff --git a/front/src/routes/authContext.ts b/front/src/routes/authContext.ts new file mode 100644 index 0000000..1c3ac8a --- /dev/null +++ b/front/src/routes/authContext.ts @@ -0,0 +1,14 @@ +import { Metadata } from "nice-grpc-common"; +import { createContext, useContext } from "react"; + +type AuthContextType = { metadata: Metadata }; + +export const AuthContext = createContext(null); + +export function useAuthContext(): AuthContextType { + const ctx = useContext(AuthContext); + if (!ctx) { + throw new Error("useAuthContext must be used within AuthContext.Provider"); + } + return ctx; +} diff --git a/front/src/routes/exploits.tsx b/front/src/routes/exploits.tsx new file mode 100644 index 0000000..4838157 --- /dev/null +++ b/front/src/routes/exploits.tsx @@ -0,0 +1,50 @@ +import ExploitsView from "@/components/ExploitsView"; +import { ExploitState } from "@/proto/exploits/api"; +import { useExploitServiceClient } from "@/services/exploits"; +import { Box } from "@mui/material"; +import { useCallback, useEffect, useState } from "react"; +import { Helmet } from "react-helmet-async"; + +interface S { + exploits: ExploitState[]; +} + +export default function ExploitsContainer() { + const [state, setState] = useState(); + + const exploitServiceClient = useExploitServiceClient(); + + const fetchExploits = useCallback(async () => { + const response = await exploitServiceClient.ping({ + payload: { $case: "serverInfoRequest", serverInfoRequest: {} }, + }); + setState({ + exploits: + response.state?.exploits.sort((e1: ExploitState, e2: ExploitState) => { + return e1.exploitId.localeCompare(e2.exploitId); + }) || [], + }); + }, [exploitServiceClient]); + + useEffect(() => { + void fetchExploits(); + }, [exploitServiceClient, fetchExploits]); + + return ( + <> + + Neo Exploits + + + {state && ( + { + void fetchExploits(); + }} + /> + )} + + + ); +} diff --git a/front/src/routes/logs.tsx b/front/src/routes/logs.tsx new file mode 100644 index 0000000..727c672 --- /dev/null +++ b/front/src/routes/logs.tsx @@ -0,0 +1,40 @@ +import LogsRootView from "@/components/LogsRootView"; +import { ExploitState } from "@/proto/exploits/api"; +import { useExploitServiceClient } from "@/services/exploits"; +import { useEffect, useState } from "react"; +import { Helmet } from "react-helmet-async"; + +interface S { + exploits: ExploitState[]; +} + +export default function LogsContainer() { + const exploitServiceClient = useExploitServiceClient(); + const [state, setState] = useState(); + + useEffect(() => { + const fetchExploits = async () => { + const response = await exploitServiceClient.ping({ + payload: { $case: "serverInfoRequest", serverInfoRequest: {} }, + }); + setState({ + exploits: + response.state?.exploits.sort( + (e1: ExploitState, e2: ExploitState) => { + return e1.exploitId.localeCompare(e2.exploitId); + } + ) || [], + }); + }; + void fetchExploits(); + }, [exploitServiceClient]); + + return ( + <> + + Neo Logs + + {state && } + + ); +} diff --git a/front/src/routes/root.tsx b/front/src/routes/root.tsx new file mode 100644 index 0000000..3f9a1f9 --- /dev/null +++ b/front/src/routes/root.tsx @@ -0,0 +1,36 @@ +import { AppBar, Box, Button, Toolbar } from "@mui/material"; +import { useRef } from "react"; +import { Link, Outlet } from "react-router-dom"; +import { RootContext } from "./rootContext"; + +export default function Root() { + const topbarRef = useRef(null); + + return ( + <> + + + + + + + + + + + + + + ); +} diff --git a/front/src/routes/rootContext.ts b/front/src/routes/rootContext.ts new file mode 100644 index 0000000..1f260f6 --- /dev/null +++ b/front/src/routes/rootContext.ts @@ -0,0 +1,13 @@ +import { createContext, useContext } from "react"; + +type RootContextType = { topbarRef: React.RefObject }; + +export const RootContext = createContext(null); + +export function useRootContext(): RootContextType { + const ctx = useContext(RootContext); + if (!ctx) { + throw new Error("useRootContext must be used within RootContext.Provider"); + } + return ctx; +} diff --git a/front/src/services/exploits.ts b/front/src/services/exploits.ts new file mode 100644 index 0000000..31809f7 --- /dev/null +++ b/front/src/services/exploits.ts @@ -0,0 +1,38 @@ +import { grpcAddress } from "@/config.ts"; +import { ServiceClient, ServiceDefinition } from "@/proto/exploits/api.ts"; +import { useAuthContext } from "@/routes/authContext"; +import { + WebsocketTransport, + createChannel, + createClient, + createClientFactory, +} from "nice-grpc-web"; +import { useMemo } from "react"; + +const channel = createChannel(grpcAddress, WebsocketTransport()); + +export type ExploitServiceClient = ServiceClient; + +export const rawExploitServiceClient: ExploitServiceClient = createClient( + ServiceDefinition, + channel +); + +export function useExploitServiceClient(): ExploitServiceClient { + const authContext = useAuthContext(); + return useMemo( + () => + createClientFactory() + .use((call, options) => + call.next(call.request, { + ...options, + metadata: { + ...options.metadata, + ...authContext.metadata, + }, + }) + ) + .create(ServiceDefinition, channel), + [authContext] + ); +} diff --git a/front/src/services/logs.ts b/front/src/services/logs.ts new file mode 100644 index 0000000..7c72a15 --- /dev/null +++ b/front/src/services/logs.ts @@ -0,0 +1,32 @@ +import { grpcAddress } from "@/config.ts"; +import { ServiceClient, ServiceDefinition } from "@/proto/logs/api.ts"; +import { useAuthContext } from "@/routes/authContext"; +import { + WebsocketTransport, + createChannel, + createClientFactory, +} from "nice-grpc-web"; +import { useMemo } from "react"; + +const channel = createChannel(grpcAddress, WebsocketTransport()); + +export type LogsServiceClient = ServiceClient; + +export function useLogsServiceClient(): LogsServiceClient { + const authContext = useAuthContext(); + return useMemo( + () => + createClientFactory() + .use((call, options) => + call.next(call.request, { + ...options, + metadata: { + ...options.metadata, + ...authContext.metadata, + }, + }) + ) + .create(ServiceDefinition, channel), + [authContext] + ); +} diff --git a/front/src/theme.ts b/front/src/theme.ts new file mode 100644 index 0000000..dd390e0 --- /dev/null +++ b/front/src/theme.ts @@ -0,0 +1,28 @@ +import { createTheme } from "@mui/material/styles"; + +declare module "@mui/material/styles" { + interface Theme { + typography: { + fontFamily: string; + }; + palette: { + primary: { + main: string; + contrastText: string; + }; + }; + } +} + +const theme = createTheme({ + typography: { + fontFamily: "Monospace", + }, + palette: { + primary: { + main: "#333333", + }, + }, +}); + +export default theme; diff --git a/front/src/utils/duration.ts b/front/src/utils/duration.ts new file mode 100644 index 0000000..303b86c --- /dev/null +++ b/front/src/utils/duration.ts @@ -0,0 +1,60 @@ +import { Duration } from "@/proto/google/protobuf/duration"; +import Long from "long"; + +export function formatDuration(d: Duration): string { + return formatSeconds(d.seconds.toInt()); +} + +export function formatSeconds(s: number): string { + const hours = Math.floor(s / 3600); + const minutes = Math.floor((s % 3600) / 60); + const seconds = Math.floor(s % 60); + + let res = ""; + if (hours > 0) { + res += `${hours}h`; + } + if (minutes > 0) { + res += `${minutes}m`; + } + if (seconds > 0) { + res += `${seconds}s`; + } + if (res.length === 0) { + res = "0s"; + } + return res; +} + +export function parseDuration(s: string): Duration { + const numRegex = /^\d+$/; + if (numRegex.test(s)) { + return Duration.create({ + seconds: Long.fromNumber(parseInt(s)), + nanos: 0, + }); + } + + const regex = + /^(?(\d+)h)?\s*(?(\d+)m)?\s*(?(\d+)s)?$/; + const match = regex.exec(s); + if (match === null) { + throw new Error(`invalid duration: ${s}`); + } + + interface Parsed { + hours: string; + minutes: string; + seconds: string; + } + const parsed: Partial | undefined = match.groups; + + const hours = parsed?.hours === undefined ? 0 : parseInt(parsed.hours); + const minutes = parsed?.minutes === undefined ? 0 : parseInt(parsed.minutes); + const seconds = parsed?.seconds === undefined ? 0 : parseInt(parsed.seconds); + + return Duration.create({ + seconds: Long.fromNumber(hours * 3600 + minutes * 60 + seconds), + nanos: 0, + }); +} diff --git a/front/src/utils/storage.ts b/front/src/utils/storage.ts new file mode 100644 index 0000000..b846c36 --- /dev/null +++ b/front/src/utils/storage.ts @@ -0,0 +1,19 @@ +import { useEffect, useState } from "react"; + +export function usePersistentStorageValue(key: string, initialValue?: T) { + const [value, setValue] = useState(() => { + const valueFromStorage = window.localStorage.getItem(key); + if (valueFromStorage) { + return JSON.parse(valueFromStorage) as unknown as T; + } + return initialValue; + }); + + useEffect(() => { + if (value) { + window.localStorage.setItem(key, JSON.stringify(value)); + } + }, [key, value]); + + return [value, setValue] as const; +} diff --git a/front/src/utils/window.ts b/front/src/utils/window.ts new file mode 100644 index 0000000..dcb90b3 --- /dev/null +++ b/front/src/utils/window.ts @@ -0,0 +1,34 @@ +import { useSyncExternalStore } from "react"; + +export function useWindowDimensions() { + return useSyncExternalStore(subscribe, getSnapshot); +} + +function subscribe(callback: { + (this: Window, ev: UIEvent): void; + (this: Window, ev: UIEvent): void; +}) { + window.addEventListener("resize", callback); + return () => { + window.removeEventListener("resize", callback); + }; +} + +interface Snapshot { + width: number; + height: number; +} + +let lastSnapshot: Snapshot | null = null; + +function getSnapshot() { + const newSnapshot = { width: window.innerWidth, height: window.innerHeight }; + if ( + lastSnapshot == null || + newSnapshot.width != lastSnapshot.width || + newSnapshot.height != lastSnapshot.height + ) { + lastSnapshot = newSnapshot; + } + return lastSnapshot; +} diff --git a/front/src/vite-env.d.ts b/front/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/front/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/front/tsconfig.json b/front/tsconfig.json new file mode 100644 index 0000000..8775be2 --- /dev/null +++ b/front/tsconfig.json @@ -0,0 +1,40 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": [ + "ES2020", + "DOM", + "DOM.Iterable" + ], + "module": "ESNext", + "skipLibCheck": true, + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + /* Linting */ + "strict": true, + "strictNullChecks": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "baseUrl": ".", + "paths": { + "@/*": [ + "src/*", + ] + }, + }, + "include": [ + "src" + ], + "references": [ + { + "path": "./tsconfig.node.json" + } + ] +} \ No newline at end of file diff --git a/front/tsconfig.node.json b/front/tsconfig.node.json new file mode 100644 index 0000000..7f5d1a8 --- /dev/null +++ b/front/tsconfig.node.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "strictNullChecks": true, + "allowSyntheticDefaultImports": true + }, + "include": [ + "vite.config.ts" + ] +} \ No newline at end of file diff --git a/front/vite.config.ts b/front/vite.config.ts new file mode 100644 index 0000000..e8e2bf8 --- /dev/null +++ b/front/vite.config.ts @@ -0,0 +1,10 @@ +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], + resolve: { + alias: [{ find: "@", replacement: "/src" }], + }, +}); diff --git a/go.mod b/go.mod index 4d538b6..ecb013a 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/c4t-but-s4d/neo/v2 -go 1.21 +go 1.23 require ( github.com/denisbrodbeck/machineid v1.0.1 @@ -23,26 +23,33 @@ require ( ) require ( + github.com/improbable-eng/grpc-web v0.15.1-0.20230209220825-1d9bbb09a099 github.com/prometheus/client_golang v1.17.0 github.com/prometheus/common v0.44.0 github.com/samber/lo v1.38.1 + golang.org/x/net v0.17.0 golang.org/x/sys v0.13.0 + nhooyr.io/websocket v1.8.7 ) require ( github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.1.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/klauspost/compress v1.17.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect + github.com/rs/cors v1.7.0 // indirect github.com/sagikazarmark/locafero v0.3.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect @@ -51,7 +58,6 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect - golang.org/x/net v0.17.0 // indirect golang.org/x/text v0.13.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 5d00ded..959cdf2 100644 --- a/go.sum +++ b/go.sum @@ -38,9 +38,20 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f 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/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/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/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= 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/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -57,6 +68,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ= github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= 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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -69,11 +82,37 @@ github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0X github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +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-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= 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/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +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-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 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= @@ -100,6 +139,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD 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/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 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/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -116,6 +156,7 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -136,18 +177,41 @@ github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ 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/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 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/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/improbable-eng/grpc-web v0.15.1-0.20230209220825-1d9bbb09a099 h1:k07oXM8RqIaaSEF09Frr/iRMlwx2qvx6vRo2XuPIeW8= +github.com/improbable-eng/grpc-web v0.15.1-0.20230209220825-1d9bbb09a099/go.mod h1:Vkb7Iy2LTlRGIAubpODgfeKPzu8nsh1gO+vvZAiZrcs= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +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.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +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.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -155,37 +219,76 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/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 h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +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/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/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.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.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/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 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.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.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +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.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ= github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U= @@ -193,6 +296,10 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +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.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= @@ -210,8 +317,11 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -221,6 +331,10 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 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= @@ -233,10 +347,14 @@ 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.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 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= @@ -254,6 +372,7 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 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/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= @@ -281,6 +400,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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-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/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -288,6 +408,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn 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= @@ -298,6 +419,7 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL 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-20200421231249-e086a090c8fd/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= @@ -310,7 +432,9 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -322,6 +446,7 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 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= @@ -333,18 +458,24 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ 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/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20190312061237-fead79001313/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-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/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= @@ -352,10 +483,13 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w 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-20200420163511-1957bb5e6d1f/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-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -363,15 +497,21 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20220114195835-da31bd327af9/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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/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= @@ -385,6 +525,7 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 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= @@ -483,6 +624,7 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG 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-20200423170343-7949de9c1215/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= @@ -497,6 +639,7 @@ google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c h1:jHkCUWkseRf+W+edG5hMzr/Uh1xkDREY4caybAq4dpY= google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= @@ -513,6 +656,7 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji 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/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= @@ -530,10 +674,13 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj 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.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 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/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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-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= @@ -541,10 +688,16 @@ gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/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/yaml.v2 v2.2.2/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-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= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -554,6 +707,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh 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= +nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= +nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= 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= diff --git a/internal/client/client.go b/internal/client/client.go index 0a8e4fe..ee7b217 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -10,9 +10,9 @@ import ( "google.golang.org/grpc" "github.com/c4t-but-s4d/neo/v2/pkg/filestream" - epb "github.com/c4t-but-s4d/neo/v2/proto/go/exploits" - fspb "github.com/c4t-but-s4d/neo/v2/proto/go/fileserver" - logspb "github.com/c4t-but-s4d/neo/v2/proto/go/logs" + epb "github.com/c4t-but-s4d/neo/v2/pkg/proto/exploits" + fspb "github.com/c4t-but-s4d/neo/v2/pkg/proto/fileserver" + logspb "github.com/c4t-but-s4d/neo/v2/pkg/proto/logs" ) func New(cc grpc.ClientConnInterface, id string) *Client { diff --git a/internal/client/config.go b/internal/client/config.go index 94dc63c..15ae678 100644 --- a/internal/client/config.go +++ b/internal/client/config.go @@ -14,10 +14,17 @@ func MustUnmarshalConfig() *Config { return cfg } +type Metrics struct { + URL string `mapstructure:"url"` + User string `mapstructure:"user"` + Password string `mapstructure:"password"` +} + type Config struct { Host string `mapstructure:"host"` - MetricsHost string `mapstructure:"metrics_host"` ExploitDir string `mapstructure:"exploit_dir"` GrpcAuthKey string `mapstructure:"grpc_auth_key"` UseTLS bool `mapstructure:"use_tls"` + + Metrics *Metrics `mapstructure:"metrics"` } diff --git a/internal/config/config.go b/internal/config/config.go index 7f666e3..4e01476 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -7,7 +7,7 @@ import ( "google.golang.org/protobuf/types/known/durationpb" - epb "github.com/c4t-but-s4d/neo/v2/proto/go/exploits" + epb "github.com/c4t-but-s4d/neo/v2/pkg/proto/exploits" ) type ExploitsConfig struct { diff --git a/internal/exploit/cache.go b/internal/exploit/cache.go index b368256..a6f9251 100644 --- a/internal/exploit/cache.go +++ b/internal/exploit/cache.go @@ -4,7 +4,7 @@ import ( "sync" "time" - epb "github.com/c4t-but-s4d/neo/v2/proto/go/exploits" + epb "github.com/c4t-but-s4d/neo/v2/pkg/proto/exploits" ) func NewCache() *Cache { diff --git a/internal/exploit/cache_test.go b/internal/exploit/cache_test.go index 1cd1812..a3e5d77 100644 --- a/internal/exploit/cache_test.go +++ b/internal/exploit/cache_test.go @@ -7,7 +7,7 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" "google.golang.org/protobuf/testing/protocmp" - epb "github.com/c4t-but-s4d/neo/v2/proto/go/exploits" + epb "github.com/c4t-but-s4d/neo/v2/pkg/proto/exploits" ) func TestCache_Diff(t *testing.T) { diff --git a/internal/exploit/runner.go b/internal/exploit/runner.go index 0493931..a89eac9 100644 --- a/internal/exploit/runner.go +++ b/internal/exploit/runner.go @@ -21,7 +21,7 @@ import ( "github.com/c4t-but-s4d/neo/v2/internal/models" "github.com/c4t-but-s4d/neo/v2/internal/queue" "github.com/c4t-but-s4d/neo/v2/pkg/joblogger" - epb "github.com/c4t-but-s4d/neo/v2/proto/go/exploits" + epb "github.com/c4t-but-s4d/neo/v2/pkg/proto/exploits" ) var ( @@ -49,7 +49,8 @@ func NewRunner( restarts: make(chan struct{}, 1), logSender: logSender, metricsPusher: push. - New(clientConfig.MetricsHost, "neo_runner"). + New(clientConfig.Metrics.URL, "neo_runner"). + BasicAuth(clientConfig.Metrics.User, clientConfig.Metrics.Password). Grouping("client_id", clientID). Format(expfmt.FmtOpenMetrics_1_0_0). Gatherer(prometheus.DefaultGatherer), diff --git a/internal/exploit/storage.go b/internal/exploit/storage.go index b2397cd..d99bd8f 100644 --- a/internal/exploit/storage.go +++ b/internal/exploit/storage.go @@ -12,7 +12,7 @@ import ( "github.com/c4t-but-s4d/neo/v2/internal/client" "github.com/c4t-but-s4d/neo/v2/pkg/archive" - epb "github.com/c4t-but-s4d/neo/v2/proto/go/exploits" + epb "github.com/c4t-but-s4d/neo/v2/pkg/proto/exploits" ) func NewStorage(cache *Cache, exploitDir string, client *client.Client) *Storage { diff --git a/internal/exploit/storage_test.go b/internal/exploit/storage_test.go index 5fb8c86..74cefc8 100644 --- a/internal/exploit/storage_test.go +++ b/internal/exploit/storage_test.go @@ -14,7 +14,7 @@ import ( "github.com/stretchr/testify/require" "github.com/c4t-but-s4d/neo/v2/pkg/archive" - epb "github.com/c4t-but-s4d/neo/v2/proto/go/exploits" + epb "github.com/c4t-but-s4d/neo/v2/pkg/proto/exploits" ) func mockStorage() (*Storage, func() error) { diff --git a/internal/logstor/interface.go b/internal/logstor/interface.go new file mode 100644 index 0000000..267a071 --- /dev/null +++ b/internal/logstor/interface.go @@ -0,0 +1,43 @@ +package logstor + +import ( + "context" +) + +const defaultLimit = 10000 + +type SearchConfig struct { + limit int + lastToken string +} + +type SearchOption func(cfg *SearchConfig) + +func SearchWithLimit(limit int) SearchOption { + return func(cfg *SearchConfig) { + cfg.limit = limit + } +} + +func SearchWithLastToken(lastToken string) SearchOption { + return func(cfg *SearchConfig) { + cfg.lastToken = lastToken + } +} + +func GetSearchConfig(opts ...SearchOption) *SearchConfig { + cfg := &SearchConfig{ + limit: defaultLimit, + } + + for _, opt := range opts { + opt(cfg) + } + + return cfg +} + +type Storage interface { + Add(ctx context.Context, lines ...*Line) error + Search(ctx context.Context, exploit string, version int64, opts ...SearchOption) ([]*Line, error) +} diff --git a/internal/logstor/line.go b/internal/logstor/line.go new file mode 100644 index 0000000..c1cfd82 --- /dev/null +++ b/internal/logstor/line.go @@ -0,0 +1,116 @@ +package logstor + +import ( + "fmt" + "reflect" + "strconv" + "time" + + "github.com/mitchellh/mapstructure" + "google.golang.org/protobuf/types/known/timestamppb" + + logspb "github.com/c4t-but-s4d/neo/v2/pkg/proto/logs" +) + +func LineDecodeHook(from, to reflect.Type, data any) (any, error) { + switch { + case from.Kind() == reflect.String && to == reflect.TypeOf(time.Time{}): + str, ok := data.(string) + if !ok { + return data, nil + } + + res, err := time.Parse(time.RFC3339Nano, str) + if err != nil { + return nil, fmt.Errorf("parsing time: %w", err) + } + return res, nil + + case from.Kind() == reflect.String && to.Kind() == reflect.Int64: + str, ok := data.(string) + if !ok { + return data, nil + } + + res, err := strconv.ParseInt(str, 10, 64) + if err != nil { + return nil, fmt.Errorf("parsing int64: %w", err) + } + return res, nil + + default: + return data, nil + } +} + +func NewLineFromRedis(vals map[string]any) (*Line, error) { + var line Line + decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + DecodeHook: LineDecodeHook, + Result: &line, + }) + if err != nil { + return nil, fmt.Errorf("creating decoder: %w", err) + } + + if err := decoder.Decode(vals); err != nil { + return nil, fmt.Errorf("decoding structure: %w", err) + } + return &line, nil +} + +func NewLineFromProto(p *logspb.LogLine) *Line { + return &Line{ + Timestamp: p.Timestamp.AsTime(), + Exploit: p.Exploit, + Version: p.Version, + Message: p.Message, + Level: p.Level, + Team: p.Team, + } +} + +type Line struct { + Timestamp time.Time `mapstructure:"timestamp"` + Exploit string `mapstructure:"exploit"` + Version int64 `mapstructure:"version"` + Message string `mapstructure:"message"` + Level string `mapstructure:"level"` + Team string `mapstructure:"team"` +} + +func (l *Line) String() string { + return fmt.Sprintf("Line(%s.v%d)", l.Exploit, l.Version) +} + +func (l *Line) EstimateSize() int { + const ( + estNum = 5 + estDenom = 4 + structSize = 8*4 + 32 + 8 + ) + sizeEst := structSize + len(l.Exploit) + len(l.Message) + len(l.Level) + len(l.Team) + return sizeEst * estNum / estDenom +} + +func (l *Line) ToRedis() map[string]any { + return map[string]any{ + "timestamp": l.Timestamp.Format(time.RFC3339Nano), + "exploit": l.Exploit, + "version": strconv.FormatInt(l.Version, 10), + "message": l.Message, + "level": l.Level, + "team": l.Team, + } +} + +func (l *Line) ToProto() *logspb.LogLine { + return &logspb.LogLine{ + Timestamp: timestamppb.New(l.Timestamp), + Exploit: l.Exploit, + Version: l.Version, + Message: l.Message, + Level: l.Level, + Team: l.Team, + } +} diff --git a/internal/server/logs/storage.go b/internal/logstor/redis.go similarity index 51% rename from internal/server/logs/storage.go rename to internal/logstor/redis.go index 2050e98..0df43c0 100644 --- a/internal/server/logs/storage.go +++ b/internal/logstor/redis.go @@ -1,19 +1,26 @@ -package server +package logstor import ( "context" "fmt" - "strconv" "github.com/go-redis/redis/v8" ) const ( - linesPerSploitLimit = 100000 + maxRedisStreamLength = 100000 ) -func NewLogStorage(ctx context.Context, redisURL string) (*LogStorage, error) { - opts, err := redis.ParseURL(redisURL) +var ( + _ Storage = (*RedisStorage)(nil) +) + +type RedisStorage struct { + rdb *redis.Client +} + +func NewRedisStorage(ctx context.Context, url string) (*RedisStorage, error) { + opts, err := redis.ParseURL(url) if err != nil { return nil, fmt.Errorf("invalid redis url: %w", err) } @@ -21,26 +28,18 @@ func NewLogStorage(ctx context.Context, redisURL string) (*LogStorage, error) { if err := c.Ping(ctx).Err(); err != nil { return nil, fmt.Errorf("connecting to redis: %w", err) } - return &LogStorage{rdb: c}, nil + return &RedisStorage{rdb: c}, nil } -type LogStorage struct { - rdb *redis.Client -} - -func (s *LogStorage) Add(ctx context.Context, lines []LogLine) error { +func (s *RedisStorage) Add(ctx context.Context, lines ...*Line) error { if _, err := s.rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error { for _, line := range lines { key := getRedisStream(line.Exploit, line.Version) - vals, err := line.DumpValues() - if err != nil { - return fmt.Errorf("serializing %v: %w", line, err) - } args := redis.XAddArgs{ Stream: key, - MaxLen: linesPerSploitLimit, + MaxLen: maxRedisStreamLength, Approx: true, - Values: vals, + Values: line.ToRedis(), } if err := pipe.XAdd(ctx, &args).Err(); err != nil { return fmt.Errorf("adding %v: %w", line, err) @@ -53,15 +52,21 @@ func (s *LogStorage) Add(ctx context.Context, lines []LogLine) error { return nil } -func (s *LogStorage) Get(ctx context.Context, opts GetOptions) ([]*LogLine, error) { - key := getRedisStream(opts.Exploit, strconv.FormatInt(opts.Version, 10)) - res, err := s.rdb.XRange(ctx, key, "-", "+").Result() +func (s *RedisStorage) Search(ctx context.Context, exploit string, version int64, opts ...SearchOption) ([]*Line, error) { + cfg := GetSearchConfig(opts...) + key := getRedisStream(exploit, version) + + start := "-" + if cfg.lastToken != "" { + start = fmt.Sprintf("(%s", cfg.lastToken) + } + res, err := s.rdb.XRangeN(ctx, key, start, "+", int64(cfg.limit)).Result() if err != nil { return nil, fmt.Errorf("querying stream %s: %w", key, err) } - lines := make([]*LogLine, 0, len(res)) + lines := make([]*Line, 0, len(res)) for _, msg := range res { - line, err := NewLogLineFromValues(msg.Values) + line, err := NewLineFromRedis(msg.Values) if err != nil { return nil, fmt.Errorf("decoding line from %+v: %w", msg.Values, err) } @@ -70,11 +75,6 @@ func (s *LogStorage) Get(ctx context.Context, opts GetOptions) ([]*LogLine, erro return lines, nil } -type GetOptions struct { - Exploit string - Version int64 -} - -func getRedisStream(exploit string, version string) string { - return fmt.Sprintf("logs:%s:%s", exploit, version) +func getRedisStream(exploit string, version int64) string { + return fmt.Sprintf("logs:%s:%d", exploit, version) } diff --git a/internal/server/config/config.go b/internal/server/config/config.go index 43faffd..b2da314 100644 --- a/internal/server/config/config.go +++ b/internal/server/config/config.go @@ -8,17 +8,20 @@ import ( ) type Config struct { - Debug bool `mapstructure:"debug"` - Addr string `mapstructure:"addr"` - DBPath string `mapstructure:"db_path"` - RedisURL string `mapstructure:"redis_url"` - BaseDir string `mapstructure:"base_dir"` - PingEvery time.Duration `mapstructure:"ping_every"` - SubmitEvery time.Duration `mapstructure:"submit_every"` - FarmConfig FarmConfig `mapstructure:"farm"` - GrpcAuthKey string `mapstructure:"grpc_auth_key"` - Environ map[string]string `mapstructure:"env"` - MetricsNamespace string `mapstructure:"metrics_namespace"` + Debug bool `mapstructure:"debug"` + Address string `mapstructure:"address"` + StaticDir string `mapstructure:"static_dir"` + DBPath string `mapstructure:"db_path"` + RedisURL string `mapstructure:"redis_url"` + BaseDir string `mapstructure:"base_dir"` + PingEvery time.Duration `mapstructure:"ping_every"` + SubmitEvery time.Duration `mapstructure:"submit_every"` + FarmConfig FarmConfig `mapstructure:"farm"` + GrpcAuthKey string `mapstructure:"grpc_auth_key"` + Environ map[string]string `mapstructure:"env"` + + MetricsAddress string `mapstructure:"metrics_address"` + MetricsNamespace string `mapstructure:"metrics_namespace"` } type FarmConfig struct { diff --git a/internal/server/exploits/server.go b/internal/server/exploits/server.go index c084691..8a6ab24 100644 --- a/internal/server/exploits/server.go +++ b/internal/server/exploits/server.go @@ -14,8 +14,8 @@ import ( serverConfig "github.com/c4t-but-s4d/neo/v2/internal/server/config" "github.com/c4t-but-s4d/neo/v2/pkg/gstream" "github.com/c4t-but-s4d/neo/v2/pkg/hostbucket" + epb "github.com/c4t-but-s4d/neo/v2/pkg/proto/exploits" "github.com/c4t-but-s4d/neo/v2/pkg/pubsub" - epb "github.com/c4t-but-s4d/neo/v2/proto/go/exploits" ) func New(cfg *serverConfig.Config, storage *CachedStorage) *Server { @@ -61,7 +61,7 @@ func (s *Server) Ping(ctx context.Context, r *epb.PingRequest) (*epb.PingRespons case *epb.PingRequest_LeaveRequest: s.visits.MarkInvalid(r.ClientId) default: - return nil, s.WrapErrorf(ctx, codes.InvalidArgument, "Unknown payload type: %T", p) + return nil, s.WrapErrorf(ctx, codes.InvalidArgument, "unknown payload type: %T", p) } return &epb.PingResponse{ @@ -78,7 +78,7 @@ func (s *Server) Exploit(ctx context.Context, r *epb.ExploitRequest) (*epb.Explo state, ok := s.storage.GetState(r.ExploitId) if !ok { - return nil, s.WrapErrorf(ctx, codes.NotFound, "Failed to find an exploit state = %v", state.ExploitId) + return nil, s.WrapErrorf(ctx, codes.NotFound, "finding an exploit state = %v", state.ExploitId) } return &epb.ExploitResponse{ State: state, @@ -88,9 +88,19 @@ func (s *Server) Exploit(ctx context.Context, r *epb.ExploitRequest) (*epb.Explo func (s *Server) UpdateExploit(ctx context.Context, r *epb.UpdateExploitRequest) (*epb.UpdateExploitResponse, error) { s.LogRequest(ctx, r) + if r.State.Config == nil { + return nil, s.WrapErrorf(ctx, codes.InvalidArgument, "config required") + } + if r.State.Config.Timeout.AsDuration() == 0 { + return nil, s.WrapErrorf(ctx, codes.InvalidArgument, "timeout required") + } + if r.State.Config.RunEvery.AsDuration() == 0 { + return nil, s.WrapErrorf(ctx, codes.InvalidArgument, "run_every required") + } + newState, err := s.storage.UpdateExploitVersion(r.State) if err != nil { - return nil, s.WrapErrorf(ctx, codes.Internal, "Failed to update exploit version: %v", err) + return nil, s.WrapErrorf(ctx, codes.Internal, "updating exploit version: %v", err) } return &epb.UpdateExploitResponse{State: newState}, nil } diff --git a/internal/server/exploits/server_test.go b/internal/server/exploits/server_test.go index 26deb2b..1dec111 100644 --- a/internal/server/exploits/server_test.go +++ b/internal/server/exploits/server_test.go @@ -10,11 +10,12 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/require" "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/durationpb" "github.com/c4t-but-s4d/neo/v2/internal/server/config" "github.com/c4t-but-s4d/neo/v2/pkg/hostbucket" - epb "github.com/c4t-but-s4d/neo/v2/proto/go/exploits" - fspb "github.com/c4t-but-s4d/neo/v2/proto/go/fileserver" + epb "github.com/c4t-but-s4d/neo/v2/pkg/proto/exploits" + fspb "github.com/c4t-but-s4d/neo/v2/pkg/proto/fileserver" ) func testServer(t *testing.T) (*Server, func()) { @@ -40,6 +41,8 @@ func TestServer_UpdateExploit(t *testing.T) { cfg := &epb.ExploitConfiguration{ Entrypoint: "bin", IsArchive: false, + Timeout: durationpb.New(time.Minute), + RunEvery: durationpb.New(time.Minute), } r := &epb.UpdateExploitRequest{ State: &epb.ExploitState{ @@ -50,6 +53,9 @@ func TestServer_UpdateExploit(t *testing.T) { } resp, err := es.UpdateExploit(context.Background(), r) require.NoError(t, err) + + cfg.Timeout = durationpb.New(2 * time.Minute) + cfg.RunEvery = durationpb.New(2 * time.Minute) want := &epb.ExploitState{ ExploitId: "1", Version: 1, @@ -67,6 +73,8 @@ func TestServer_Exploit(t *testing.T) { cfg := &epb.ExploitConfiguration{ Entrypoint: "bin", IsArchive: false, + Timeout: durationpb.New(time.Minute), + RunEvery: durationpb.New(time.Minute), } state := &epb.ExploitState{ ExploitId: "1", @@ -99,6 +107,8 @@ func TestServer_Ping(t *testing.T) { cfg := &epb.ExploitConfiguration{ Entrypoint: "bin", IsArchive: false, + Timeout: durationpb.New(time.Minute), + RunEvery: durationpb.New(time.Minute), } state := &epb.ExploitState{ ExploitId: "1", diff --git a/internal/server/exploits/storage.go b/internal/server/exploits/storage.go index c47b1a1..6a61a11 100644 --- a/internal/server/exploits/storage.go +++ b/internal/server/exploits/storage.go @@ -10,7 +10,7 @@ import ( bolt "go.etcd.io/bbolt" "google.golang.org/protobuf/proto" - epb "github.com/c4t-but-s4d/neo/v2/proto/go/exploits" + epb "github.com/c4t-but-s4d/neo/v2/pkg/proto/exploits" ) const ( diff --git a/internal/server/exploits/storage_test.go b/internal/server/exploits/storage_test.go index 4fc987b..d69a9a5 100644 --- a/internal/server/exploits/storage_test.go +++ b/internal/server/exploits/storage_test.go @@ -12,8 +12,8 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/testing/protocmp" - epb "github.com/c4t-but-s4d/neo/v2/proto/go/exploits" - fspb "github.com/c4t-but-s4d/neo/v2/proto/go/fileserver" + epb "github.com/c4t-but-s4d/neo/v2/pkg/proto/exploits" + fspb "github.com/c4t-but-s4d/neo/v2/pkg/proto/fileserver" ) func testDB() (*bolt.DB, func()) { diff --git a/internal/server/fs/server.go b/internal/server/fs/server.go index 79325a0..2286dfc 100644 --- a/internal/server/fs/server.go +++ b/internal/server/fs/server.go @@ -12,7 +12,7 @@ import ( serverConfig "github.com/c4t-but-s4d/neo/v2/internal/server/config" "github.com/c4t-but-s4d/neo/v2/internal/server/utils" "github.com/c4t-but-s4d/neo/v2/pkg/filestream" - fspb "github.com/c4t-but-s4d/neo/v2/proto/go/fileserver" + fspb "github.com/c4t-but-s4d/neo/v2/pkg/proto/fileserver" ) func New(cfg *serverConfig.Config) (*Server, error) { diff --git a/internal/server/logs/logline.go b/internal/server/logs/logline.go deleted file mode 100644 index f2659c4..0000000 --- a/internal/server/logs/logline.go +++ /dev/null @@ -1,72 +0,0 @@ -package server - -import ( - "fmt" - "strconv" - - "github.com/mitchellh/mapstructure" - - logspb "github.com/c4t-but-s4d/neo/v2/proto/go/logs" -) - -func NewLogLineFromValues(vals map[string]interface{}) (*LogLine, error) { - var line LogLine - if err := mapstructure.Decode(vals, &line); err != nil { - return nil, fmt.Errorf("decoding structure: %w", err) - } - return &line, nil -} - -func NewLogLineFromProto(p *logspb.LogLine) *LogLine { - return &LogLine{ - Exploit: p.Exploit, - Version: strconv.FormatInt(p.Version, 10), - Message: p.Message, - Level: p.Level, - Team: p.Team, - } -} - -type LogLine struct { - Exploit string `mapstructure:"exploit"` - Version string `mapstructure:"version"` - Message string `mapstructure:"message"` - Level string `mapstructure:"level"` - Team string `mapstructure:"team"` -} - -func (l *LogLine) String() string { - return fmt.Sprintf("Line(%s.v%s)", l.Exploit, l.Version) -} - -func (l *LogLine) DumpValues() (map[string]interface{}, error) { - res := make(map[string]interface{}) - if err := mapstructure.Decode(l, &res); err != nil { - return nil, fmt.Errorf("encoding structure: %w", err) - } - return res, nil -} - -func (l *LogLine) EstimateSize() int { - const ( - estNum = 5 - estDenom = 4 - structSize = 8 * 5 - ) - sizeEst := structSize + len(l.Exploit) + len(l.Version) + len(l.Message) + len(l.Level) + len(l.Team) - return sizeEst * estNum / estDenom -} - -func (l *LogLine) ToProto() (*logspb.LogLine, error) { - version, err := strconv.ParseInt(l.Version, 10, 64) - if err != nil { - return nil, fmt.Errorf("converting version (%v): %w", l.Version, err) - } - return &logspb.LogLine{ - Exploit: l.Exploit, - Version: version, - Message: l.Message, - Level: l.Level, - Team: l.Team, - }, nil -} diff --git a/internal/server/logs/server.go b/internal/server/logs/server.go index 3812923..c828230 100644 --- a/internal/server/logs/server.go +++ b/internal/server/logs/server.go @@ -2,15 +2,16 @@ package server import ( "context" - "fmt" + "github.com/samber/lo" "google.golang.org/grpc/codes" "google.golang.org/protobuf/types/known/emptypb" + "github.com/c4t-but-s4d/neo/v2/internal/logstor" "github.com/c4t-but-s4d/neo/v2/internal/server/common" "github.com/c4t-but-s4d/neo/v2/internal/server/utils" "github.com/c4t-but-s4d/neo/v2/pkg/gstream" - logspb "github.com/c4t-but-s4d/neo/v2/proto/go/logs" + logspb "github.com/c4t-but-s4d/neo/v2/pkg/proto/logs" ) const ( @@ -18,7 +19,7 @@ const ( maxMsgSize = 4 * 1024 * 1024 ) -func New(storage *LogStorage) *Server { +func New(storage logstor.Storage) *Server { return &Server{ LoggingServer: common.NewLoggingServer("logs"), @@ -30,17 +31,16 @@ type Server struct { logspb.UnimplementedServiceServer common.LoggingServer - storage *LogStorage + storage logstor.Storage } -func (s *Server) AddLogLines(ctx context.Context, lines *logspb.AddLogLinesRequest) (*emptypb.Empty, error) { - s.GetMethodLogger(ctx).Infof("New request with %d lines", len(lines.Lines)) +func (s *Server) AddLogLines(ctx context.Context, request *logspb.AddLogLinesRequest) (*emptypb.Empty, error) { + s.GetMethodLogger(ctx).Infof("New request with %d lines", len(request.Lines)) - decoded := make([]LogLine, 0, len(lines.Lines)) - for _, line := range lines.Lines { - decoded = append(decoded, *NewLogLineFromProto(line)) - } - if err := s.storage.Add(ctx, decoded); err != nil { + decoded := lo.Map(request.Lines, func(line *logspb.LogLine, _ int) *logstor.Line { + return logstor.NewLineFromProto(line) + }) + if err := s.storage.Add(ctx, decoded...); err != nil { return nil, utils.WrapErrorf(codes.Internal, "adding log lines: %v", err) } return &emptypb.Empty{}, nil @@ -49,30 +49,28 @@ func (s *Server) AddLogLines(ctx context.Context, lines *logspb.AddLogLinesReque func (s *Server) SearchLogLines(req *logspb.SearchLogLinesRequest, stream logspb.Service_SearchLogLinesServer) error { s.LogRequest(stream.Context(), req) - opts := GetOptions{ - Exploit: req.Exploit, - Version: req.Version, + var opts []logstor.SearchOption + if req.Limit != 0 { + opts = append(opts, logstor.SearchWithLimit(int(req.Limit))) + } + if req.LastToken != "" { + opts = append(opts, logstor.SearchWithLastToken(req.LastToken)) } - lines, err := s.storage.Get(stream.Context(), opts) + + lines, err := s.storage.Search(stream.Context(), req.Exploit, req.Version, opts...) if err != nil { return utils.WrapErrorf(codes.Internal, "searching log lines: %v", err) } - cache := gstream.NewDynamicSizeCache[*LogLine, logspb.SearchLogLinesResponse]( + cache := gstream.NewDynamicSizeCache[*logstor.Line, logspb.SearchLogLinesResponse]( stream, maxMsgSize, - func(lines []*LogLine) (*logspb.SearchLogLinesResponse, error) { - resp := &logspb.SearchLogLinesResponse{ - Lines: make([]*logspb.LogLine, 0, len(lines)), - } - for _, line := range lines { - protoLine, err := line.ToProto() - if err != nil { - return nil, fmt.Errorf("converting line to proto: %w", err) - } - resp.Lines = append(resp.Lines, protoLine) - } - return resp, nil + func(lines []*logstor.Line) (*logspb.SearchLogLinesResponse, error) { + return &logspb.SearchLogLinesResponse{ + Lines: lo.Map(lines, func(line *logstor.Line, _ int) *logspb.LogLine { + return line.ToProto() + }), + }, nil }, ) diff --git a/monitoring/cfg/prometheus/config.yml b/monitoring/cfg/prometheus/config.yml index 50af731..7c5e269 100644 --- a/monitoring/cfg/prometheus/config.yml +++ b/monitoring/cfg/prometheus/config.yml @@ -3,18 +3,18 @@ scrape_configs: scrape_interval: 10s scrape_timeout: 3s static_configs: - - targets: ['server:3000'] + - targets: [ 'server:3000' ] - job_name: 's4d_farm_api' scrape_interval: 10s scrape_timeout: 3s metrics_path: '/api/metrics' static_configs: - - targets: ['host.docker.internal:5137'] + - targets: [ 'host.docker.internal:5137' ] - job_name: 's4d_farm_celery' scrape_interval: 10s scrape_timeout: 3s metrics_path: '/celery/metrics' static_configs: - - targets: ['host.docker.internal:5137'] + - targets: [ 'host.docker.internal:5137' ] diff --git a/monitoring/cfg/victoria-proxy/Caddyfile b/monitoring/cfg/victoria-proxy/Caddyfile new file mode 100644 index 0000000..d856017 --- /dev/null +++ b/monitoring/cfg/victoria-proxy/Caddyfile @@ -0,0 +1,9 @@ +http://:8428 { + handle { + basicauth { + {$HTTP_BASIC_AUTH_USER} {$HTTP_BASIC_AUTH_PASSWORD_HASH} + } + + reverse_proxy victoria:8428 + } +} diff --git a/pkg/filestream/filestream.go b/pkg/filestream/filestream.go index 3bed68a..c6a2105 100644 --- a/pkg/filestream/filestream.go +++ b/pkg/filestream/filestream.go @@ -5,7 +5,7 @@ import ( "fmt" "io" - fspb "github.com/c4t-but-s4d/neo/v2/proto/go/fileserver" + fspb "github.com/c4t-but-s4d/neo/v2/pkg/proto/fileserver" ) const ( diff --git a/pkg/filestream/filestream_test.go b/pkg/filestream/filestream_test.go index cda51bc..cff0d16 100644 --- a/pkg/filestream/filestream_test.go +++ b/pkg/filestream/filestream_test.go @@ -11,7 +11,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/require" - fspb "github.com/c4t-but-s4d/neo/v2/proto/go/fileserver" + fspb "github.com/c4t-but-s4d/neo/v2/pkg/proto/fileserver" ) type failedReadWriter struct { diff --git a/pkg/hostbucket/hostbucket.go b/pkg/hostbucket/hostbucket.go index 30e5ccf..33a46a9 100644 --- a/pkg/hostbucket/hostbucket.go +++ b/pkg/hostbucket/hostbucket.go @@ -6,8 +6,8 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + epb "github.com/c4t-but-s4d/neo/v2/pkg/proto/exploits" "github.com/c4t-but-s4d/neo/v2/pkg/rendezvous" - epb "github.com/c4t-but-s4d/neo/v2/proto/go/exploits" ) func New(teams map[string]string) *HostBucket { diff --git a/pkg/joblogger/logger.go b/pkg/joblogger/logger.go index dd5e3e1..e8213f9 100644 --- a/pkg/joblogger/logger.go +++ b/pkg/joblogger/logger.go @@ -6,9 +6,10 @@ import ( "strings" "github.com/sirupsen/logrus" + "google.golang.org/protobuf/types/known/timestamppb" "github.com/c4t-but-s4d/neo/v2/internal/logger" - logspb "github.com/c4t-but-s4d/neo/v2/proto/go/logs" + logspb "github.com/c4t-but-s4d/neo/v2/pkg/proto/logs" ) // 1 MB. @@ -56,11 +57,12 @@ func (l *JobLogger) Errorf(format string, args ...interface{}) { func (l *JobLogger) newLine(msg, level string) *logspb.LogLine { return &logspb.LogLine{ - Exploit: l.exploit, - Version: l.version, - Message: sanitizeMessage(msg), - Level: level, - Team: l.team, + Exploit: l.exploit, + Version: l.version, + Message: sanitizeMessage(msg), + Level: level, + Team: l.team, + Timestamp: timestamppb.Now(), } } @@ -72,7 +74,7 @@ func (l *JobLogger) getLogger() *logrus.Entry { }) } -func (l *JobLogger) logProxy(level logrus.Level, format string, args ...interface{}) { +func (l *JobLogger) logProxy(level logrus.Level, format string, args ...any) { if logrus.IsLevelEnabled(level) { l. getLogger(). diff --git a/pkg/joblogger/sender.go b/pkg/joblogger/sender.go index faccce5..ed4cb99 100644 --- a/pkg/joblogger/sender.go +++ b/pkg/joblogger/sender.go @@ -9,7 +9,7 @@ import ( "github.com/sirupsen/logrus" "github.com/c4t-but-s4d/neo/v2/internal/client" - logspb "github.com/c4t-but-s4d/neo/v2/proto/go/logs" + logspb "github.com/c4t-but-s4d/neo/v2/pkg/proto/logs" ) const ( @@ -17,6 +17,7 @@ const ( ) type Sender interface { + Start(ctx context.Context) Add(lines ...*logspb.LogLine) } @@ -26,6 +27,8 @@ func NewDummySender() *DummySender { type DummySender struct{} +func (s *DummySender) Start(context.Context) {} + func (s *DummySender) Add(...*logspb.LogLine) { } diff --git a/pkg/mu/options.go b/pkg/mu/options.go new file mode 100644 index 0000000..725121b --- /dev/null +++ b/pkg/mu/options.go @@ -0,0 +1,26 @@ +package mu + +import ( + "net/http" + + "golang.org/x/net/http2" +) + +type Config struct { + http2Server *http2.Server + httpHandler http.Handler +} + +type Option func(cfg *Config) + +func WithHTTP2Server(server *http2.Server) Option { + return func(cfg *Config) { + cfg.http2Server = server + } +} + +func WithHTTPHandler(handler http.Handler) Option { + return func(cfg *Config) { + cfg.httpHandler = handler + } +} diff --git a/pkg/mu/server.go b/pkg/mu/server.go new file mode 100644 index 0000000..3f7260b --- /dev/null +++ b/pkg/mu/server.go @@ -0,0 +1,57 @@ +package mu + +import ( + "net/http" + + "github.com/improbable-eng/grpc-web/go/grpcweb" + "golang.org/x/net/http2" + "golang.org/x/net/http2/h2c" + "google.golang.org/grpc" + "nhooyr.io/websocket" +) + +func NewHandler(grpcServer *grpc.Server, opts ...Option) http.Handler { + cfg := &Config{ + http2Server: &http2.Server{}, + httpHandler: http.DefaultServeMux, + } + for _, opt := range opts { + opt(cfg) + } + + return h2c.NewHandler( + &Server{ + grpcServer: grpcServer, + webServer: grpcweb.WrapServer( + grpcServer, + grpcweb.WithWebsockets(true), + grpcweb.WithOriginFunc(func(string) bool { + return true + }), + grpcweb.WithWebsocketOriginFunc(func(*http.Request) bool { + return true + }), + grpcweb.WithWebsocketCompressionMode(websocket.CompressionDisabled), + ), + httpHandler: cfg.httpHandler, + }, + cfg.http2Server, + ) +} + +type Server struct { + grpcServer *grpc.Server + webServer *grpcweb.WrappedGrpcServer + httpHandler http.Handler +} + +func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + switch { + case r.ProtoMajor == 2 && r.Header.Get("Content-Type") == "application/grpc": + s.grpcServer.ServeHTTP(w, r) + case s.webServer.IsAcceptableGrpcCorsRequest(r) || s.webServer.IsGrpcWebRequest(r) || s.webServer.IsGrpcWebSocketRequest(r): + s.webServer.ServeHTTP(w, r) + default: + s.httpHandler.ServeHTTP(w, r) + } +} diff --git a/pkg/neohttp/static.go b/pkg/neohttp/static.go new file mode 100644 index 0000000..5c858c0 --- /dev/null +++ b/pkg/neohttp/static.go @@ -0,0 +1,21 @@ +package neohttp + +import ( + "net/http" + "os" + "path/filepath" +) + +func StaticHandler(dir string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + filePath := filepath.Join(dir, r.URL.Path) + + if r.URL.Path == "" || r.URL.Path == "/" { + filePath = filepath.Join(dir, "index.html") + } else if _, err := os.Stat(filePath); err != nil { + filePath = filepath.Join(dir, "index.html") + } + + http.ServeFile(w, r, filePath) + } +} diff --git a/proto/go/exploits/api.pb.go b/pkg/proto/exploits/api.pb.go similarity index 98% rename from proto/go/exploits/api.pb.go rename to pkg/proto/exploits/api.pb.go index 76e67b5..42bd408 100644 --- a/proto/go/exploits/api.pb.go +++ b/pkg/proto/exploits/api.pb.go @@ -7,7 +7,7 @@ package exploits import ( - fileserver "github.com/c4t-but-s4d/neo/v2/proto/go/fileserver" + fileserver "github.com/c4t-but-s4d/neo/v2/pkg/proto/fileserver" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" durationpb "google.golang.org/protobuf/types/known/durationpb" @@ -1351,17 +1351,17 @@ var file_exploits_api_proto_rawDesc = []byte{ 0x65, 0x52, 0x75, 0x6e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x73, 0x2e, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x52, 0x75, 0x6e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x42, 0x89, + 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x42, 0x8a, 0x01, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x2e, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x73, 0x42, - 0x08, 0x41, 0x70, 0x69, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2f, 0x67, 0x69, 0x74, + 0x08, 0x41, 0x70, 0x69, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x34, 0x74, 0x2d, 0x62, 0x75, 0x74, 0x2d, - 0x73, 0x34, 0x64, 0x2f, 0x6e, 0x65, 0x6f, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2f, 0x67, 0x6f, 0x2f, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x73, 0xa2, 0x02, 0x03, 0x45, - 0x58, 0x58, 0xaa, 0x02, 0x08, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x73, 0xca, 0x02, 0x08, - 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x73, 0xe2, 0x02, 0x14, 0x45, 0x78, 0x70, 0x6c, 0x6f, - 0x69, 0x74, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, - 0x02, 0x08, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x73, 0x34, 0x64, 0x2f, 0x6e, 0x65, 0x6f, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x73, 0xa2, 0x02, 0x03, + 0x45, 0x58, 0x58, 0xaa, 0x02, 0x08, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x73, 0xca, 0x02, + 0x08, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x73, 0xe2, 0x02, 0x14, 0x45, 0x78, 0x70, 0x6c, + 0x6f, 0x69, 0x74, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0xea, 0x02, 0x08, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( diff --git a/proto/go/exploits/api_grpc.pb.go b/pkg/proto/exploits/api_grpc.pb.go similarity index 100% rename from proto/go/exploits/api_grpc.pb.go rename to pkg/proto/exploits/api_grpc.pb.go diff --git a/proto/go/fileserver/api.pb.go b/pkg/proto/fileserver/api.pb.go similarity index 90% rename from proto/go/fileserver/api.pb.go rename to pkg/proto/fileserver/api.pb.go index 5be2053..97b5bb0 100644 --- a/proto/go/fileserver/api.pb.go +++ b/pkg/proto/fileserver/api.pb.go @@ -132,17 +132,17 @@ var file_fileserver_api_proto_rawDesc = []byte{ 0x6c, 0x65, 0x12, 0x14, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x16, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x22, 0x00, 0x30, 0x01, 0x42, 0x95, 0x01, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x66, 0x69, 0x6c, + 0x22, 0x00, 0x30, 0x01, 0x42, 0x96, 0x01, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x42, 0x08, 0x41, 0x70, 0x69, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x6f, 0x50, 0x01, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x34, 0x74, 0x2d, 0x62, 0x75, 0x74, 0x2d, 0x73, 0x34, 0x64, 0x2f, 0x6e, 0x65, 0x6f, 0x2f, - 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x66, 0x69, 0x6c, 0x65, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0xa2, 0x02, 0x03, 0x46, 0x58, 0x58, 0xaa, 0x02, 0x0a, 0x46, - 0x69, 0x6c, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0xca, 0x02, 0x0a, 0x46, 0x69, 0x6c, 0x65, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0xe2, 0x02, 0x16, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, - 0x02, 0x0a, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x76, 0x32, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x66, 0x69, 0x6c, + 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0xa2, 0x02, 0x03, 0x46, 0x58, 0x58, 0xaa, 0x02, 0x0a, + 0x46, 0x69, 0x6c, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0xca, 0x02, 0x0a, 0x46, 0x69, 0x6c, + 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0xe2, 0x02, 0x16, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0xea, 0x02, 0x0a, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proto/go/fileserver/api_grpc.pb.go b/pkg/proto/fileserver/api_grpc.pb.go similarity index 100% rename from proto/go/fileserver/api_grpc.pb.go rename to pkg/proto/fileserver/api_grpc.pb.go diff --git a/proto/go/logs/api.pb.go b/pkg/proto/logs/api.pb.go similarity index 61% rename from proto/go/logs/api.pb.go rename to pkg/proto/logs/api.pb.go index b9757c8..bb98b68 100644 --- a/proto/go/logs/api.pb.go +++ b/pkg/proto/logs/api.pb.go @@ -10,6 +10,7 @@ import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" emptypb "google.golang.org/protobuf/types/known/emptypb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" ) @@ -26,11 +27,12 @@ type LogLine struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Exploit string `protobuf:"bytes,1,opt,name=exploit,proto3" json:"exploit,omitempty"` - Version int64 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` - Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` - Level string `protobuf:"bytes,4,opt,name=level,proto3" json:"level,omitempty"` - Team string `protobuf:"bytes,5,opt,name=team,proto3" json:"team,omitempty"` + Exploit string `protobuf:"bytes,1,opt,name=exploit,proto3" json:"exploit,omitempty"` + Version int64 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` + Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` + Level string `protobuf:"bytes,4,opt,name=level,proto3" json:"level,omitempty"` + Team string `protobuf:"bytes,5,opt,name=team,proto3" json:"team,omitempty"` + Timestamp *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=timestamp,proto3" json:"timestamp,omitempty"` } func (x *LogLine) Reset() { @@ -100,6 +102,13 @@ func (x *LogLine) GetTeam() string { return "" } +func (x *LogLine) GetTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.Timestamp + } + return nil +} + type AddLogLinesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -152,8 +161,10 @@ type SearchLogLinesRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Exploit string `protobuf:"bytes,1,opt,name=exploit,proto3" json:"exploit,omitempty"` - Version int64 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` + Exploit string `protobuf:"bytes,1,opt,name=exploit,proto3" json:"exploit,omitempty"` + Version int64 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` + Limit int64 `protobuf:"varint,3,opt,name=limit,proto3" json:"limit,omitempty"` + LastToken string `protobuf:"bytes,4,opt,name=last_token,json=lastToken,proto3" json:"last_token,omitempty"` } func (x *SearchLogLinesRequest) Reset() { @@ -202,12 +213,27 @@ func (x *SearchLogLinesRequest) GetVersion() int64 { return 0 } +func (x *SearchLogLinesRequest) GetLimit() int64 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *SearchLogLinesRequest) GetLastToken() string { + if x != nil { + return x.LastToken + } + return "" +} + type SearchLogLinesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Lines []*LogLine `protobuf:"bytes,1,rep,name=lines,proto3" json:"lines,omitempty"` + Lines []*LogLine `protobuf:"bytes,1,rep,name=lines,proto3" json:"lines,omitempty"` + LastToken string `protobuf:"bytes,2,opt,name=last_token,json=lastToken,proto3" json:"last_token,omitempty"` } func (x *SearchLogLinesResponse) Reset() { @@ -249,33 +275,51 @@ func (x *SearchLogLinesResponse) GetLines() []*LogLine { return nil } +func (x *SearchLogLinesResponse) GetLastToken() string { + if x != nil { + return x.LastToken + } + return "" +} + var File_logs_api_proto protoreflect.FileDescriptor var file_logs_api_proto_rawDesc = []byte{ 0x0a, 0x0e, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0x81, 0x01, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x12, - 0x18, 0x0a, 0x07, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x65, - 0x76, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x61, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x74, 0x65, 0x61, 0x6d, 0x22, 0x39, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x4c, 0x6f, - 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, - 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6c, - 0x6f, 0x67, 0x73, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x52, 0x05, 0x6c, 0x69, 0x6e, - 0x65, 0x73, 0x22, 0x4b, 0x0a, 0x15, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4c, 0x6f, 0x67, 0x4c, - 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x65, - 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x78, - 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, - 0x3d, 0x0a, 0x16, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x6c, 0x69, 0x6e, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6c, 0x6f, 0x67, 0x73, 0x2e, - 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x32, 0x9d, + 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbb, 0x01, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, + 0x65, 0x76, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x61, 0x6d, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x61, 0x6d, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x22, 0x39, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x4c, + 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x80, 0x01, + 0x0a, 0x15, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x70, 0x6c, 0x6f, + 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, + 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x22, 0x5c, 0x0a, 0x16, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x6c, 0x69, + 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6c, 0x6f, 0x67, 0x73, + 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x12, + 0x1d, 0x0a, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x32, 0x9d, 0x01, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x41, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x73, 0x12, 0x18, 0x2e, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, @@ -285,15 +329,15 @@ var file_logs_api_proto_rawDesc = []byte{ 0x1b, 0x2e, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x42, 0x71, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x42, 0x72, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x73, 0x42, 0x08, 0x41, 0x70, 0x69, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x34, 0x74, 0x2d, 0x62, 0x75, 0x74, 0x2d, 0x73, 0x34, 0x64, 0x2f, 0x6e, - 0x65, 0x6f, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x6c, - 0x6f, 0x67, 0x73, 0xa2, 0x02, 0x03, 0x4c, 0x58, 0x58, 0xaa, 0x02, 0x04, 0x4c, 0x6f, 0x67, 0x73, - 0xca, 0x02, 0x04, 0x4c, 0x6f, 0x67, 0x73, 0xe2, 0x02, 0x10, 0x4c, 0x6f, 0x67, 0x73, 0x5c, 0x47, - 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x04, 0x4c, 0x6f, 0x67, - 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x6f, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x6c, 0x6f, 0x67, 0x73, 0xa2, 0x02, 0x03, 0x4c, 0x58, 0x58, 0xaa, 0x02, 0x04, 0x4c, 0x6f, 0x67, + 0x73, 0xca, 0x02, 0x04, 0x4c, 0x6f, 0x67, 0x73, 0xe2, 0x02, 0x10, 0x4c, 0x6f, 0x67, 0x73, 0x5c, + 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x04, 0x4c, 0x6f, + 0x67, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -314,20 +358,22 @@ var file_logs_api_proto_goTypes = []interface{}{ (*AddLogLinesRequest)(nil), // 1: logs.AddLogLinesRequest (*SearchLogLinesRequest)(nil), // 2: logs.SearchLogLinesRequest (*SearchLogLinesResponse)(nil), // 3: logs.SearchLogLinesResponse - (*emptypb.Empty)(nil), // 4: google.protobuf.Empty + (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp + (*emptypb.Empty)(nil), // 5: google.protobuf.Empty } var file_logs_api_proto_depIdxs = []int32{ - 0, // 0: logs.AddLogLinesRequest.lines:type_name -> logs.LogLine - 0, // 1: logs.SearchLogLinesResponse.lines:type_name -> logs.LogLine - 1, // 2: logs.Service.AddLogLines:input_type -> logs.AddLogLinesRequest - 2, // 3: logs.Service.SearchLogLines:input_type -> logs.SearchLogLinesRequest - 4, // 4: logs.Service.AddLogLines:output_type -> google.protobuf.Empty - 3, // 5: logs.Service.SearchLogLines:output_type -> logs.SearchLogLinesResponse - 4, // [4:6] is the sub-list for method output_type - 2, // [2:4] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 4, // 0: logs.LogLine.timestamp:type_name -> google.protobuf.Timestamp + 0, // 1: logs.AddLogLinesRequest.lines:type_name -> logs.LogLine + 0, // 2: logs.SearchLogLinesResponse.lines:type_name -> logs.LogLine + 1, // 3: logs.Service.AddLogLines:input_type -> logs.AddLogLinesRequest + 2, // 4: logs.Service.SearchLogLines:input_type -> logs.SearchLogLinesRequest + 5, // 5: logs.Service.AddLogLines:output_type -> google.protobuf.Empty + 3, // 6: logs.Service.SearchLogLines:output_type -> logs.SearchLogLinesResponse + 5, // [5:7] is the sub-list for method output_type + 3, // [3:5] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_logs_api_proto_init() } diff --git a/proto/go/logs/api_grpc.pb.go b/pkg/proto/logs/api_grpc.pb.go similarity index 100% rename from proto/go/logs/api_grpc.pb.go rename to pkg/proto/logs/api_grpc.pb.go diff --git a/pkg/pubsub/pubsub_test.go b/pkg/pubsub/pubsub_test.go index f2726bf..c4c5a99 100644 --- a/pkg/pubsub/pubsub_test.go +++ b/pkg/pubsub/pubsub_test.go @@ -113,7 +113,7 @@ func TestPubSub_slowpoke(t *testing.T) { wgSlow.Wait() }() - slowSub := p.Subscribe(func(msg string) error { + slowSub := p.Subscribe(func(string) error { defer wgSlow.Done() select { @@ -153,7 +153,7 @@ func TestPubSub_slowpoke(t *testing.T) { func TestPubSub_unsubscribe(t *testing.T) { p := NewPubSub[string]() - sub1 := p.Subscribe(func(msg string) error { + sub1 := p.Subscribe(func(string) error { t.Error("first subscriber must not be called") return nil }) diff --git a/pkg/testutils/testutils.go b/pkg/testutils/testutils.go index c5b25f4..d764db1 100644 --- a/pkg/testutils/testutils.go +++ b/pkg/testutils/testutils.go @@ -46,9 +46,8 @@ func RandomString(length int) string { return RandomStringWithCharset(length, charset) } -func RandomInt(min, max int) int { - n := seededRand.Intn(max - min) - return min + n +func RandomInt(mn, mx int) int { + return mn + seededRand.Intn(mx-mn) } func RandomIP() string { diff --git a/proto/buf.gen.yaml b/proto/buf.gen.yaml index 9d3ad12..d17b9fa 100644 --- a/proto/buf.gen.yaml +++ b/proto/buf.gen.yaml @@ -2,14 +2,29 @@ version: v1 managed: enabled: true go_package_prefix: - default: github.com/c4t-but-s4d/neo/v2/proto/go + default: github.com/c4t-but-s4d/neo/v2/pkg/proto plugins: # Go - name: go - out: go + out: ../pkg/proto opt: - paths=source_relative - name: go-grpc - out: go + out: ../pkg/proto opt: - paths=source_relative + + # Typescript + - plugin: buf.build/community/stephenh-ts-proto + out: ../front/src/proto + opt: + - enumsAsLiterals=true + - env=browser + - esModuleInterop=true + - forceLong=long + - oneof=unions + - outputServices=nice-grpc,outputServices=generic-definitions + - unrecognizedEnum=false + - removeEnumPrefix=true + - useDate=false + - useMapType=true diff --git a/proto/buf.yaml b/proto/buf.yaml index 310c5fd..2119a15 100644 --- a/proto/buf.yaml +++ b/proto/buf.yaml @@ -4,7 +4,7 @@ breaking: - WIRE_JSON lint: use: - - DEFAULT + - STANDARD except: - PACKAGE_VERSION_SUFFIX - RPC_REQUEST_STANDARD_NAME diff --git a/proto/logs/api.proto b/proto/logs/api.proto index 0dafb6a..8c96692 100644 --- a/proto/logs/api.proto +++ b/proto/logs/api.proto @@ -4,29 +4,34 @@ package logs; option go_package = "logs"; import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; message LogLine { - string exploit = 1; - int64 version = 2; - string message = 3; - string level = 4; - string team = 5; + string exploit = 1; + int64 version = 2; + string message = 3; + string level = 4; + string team = 5; + google.protobuf.Timestamp timestamp = 6; } message AddLogLinesRequest { - repeated LogLine lines = 1; + repeated LogLine lines = 1; } message SearchLogLinesRequest { - string exploit = 1; - int64 version = 2; + string exploit = 1; + int64 version = 2; + int64 limit = 3; + string last_token = 4; } message SearchLogLinesResponse { - repeated LogLine lines = 1; + repeated LogLine lines = 1; + string last_token = 2; } service Service { - rpc AddLogLines(AddLogLinesRequest) returns (google.protobuf.Empty) {} - rpc SearchLogLines(SearchLogLinesRequest) returns (stream SearchLogLinesResponse) {} + rpc AddLogLines(AddLogLinesRequest) returns (google.protobuf.Empty) {} + rpc SearchLogLines(SearchLogLinesRequest) returns (stream SearchLogLinesResponse) {} }