├── preview.png
├── .env.example
├── templates
├── generate.go
├── page_templ.txt
├── page.templ
├── page_templ.go
├── chat_templ.txt
├── chat.templ
└── chat_templ.go
├── .github
├── dependabot.yml
└── workflows
│ └── release_build.yml
├── fly.toml
├── .gitignore
├── Dockerfile
├── limiter.go
├── user
└── user.go
├── README.md
├── go.mod
├── chat
└── chat.go
├── main.go
├── LICENSE
└── go.sum
/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mgjules/chat-demo/HEAD/preview.png
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | JWT_SECRET="d9b95e29-a92a-5e03-8ac0-984e1c39be94"
2 | HTTP_PORT="8080"
--------------------------------------------------------------------------------
/templates/generate.go:
--------------------------------------------------------------------------------
1 | package templates
2 |
3 | //go:generate templ generate
4 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "gomod" # See documentation for possible values
4 | directory: "/" # Location of package manifests
5 | schedule:
6 | interval: "weekly"
7 |
--------------------------------------------------------------------------------
/fly.toml:
--------------------------------------------------------------------------------
1 | # fly.toml app configuration file generated for chat-demo-z3h9ea on 2025-03-02T16:31:20Z
2 | #
3 | # See https://fly.io/docs/reference/configuration/ for information about how to use this file.
4 | #
5 |
6 | app = 'chat-demo-z3h9ea'
7 | primary_region = 'sin'
8 |
9 | [build]
10 |
11 | [http_service]
12 | internal_port = 8080
13 | force_https = true
14 | auto_stop_machines = 'stop'
15 | auto_start_machines = true
16 | min_machines_running = 0
17 | processes = ['app']
18 |
19 | [[vm]]
20 | memory = '256mb'
21 | cpu_kind = 'shared'
22 | cpus = 1
23 | memory_mb = 256
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # If you prefer the allow list template instead of the deny list, see community template:
2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
3 | #
4 | # Binaries for programs and plugins
5 | *.exe
6 | *.exe~
7 | *.dll
8 | *.so
9 | *.dylib
10 |
11 | # Test binary, built with `go test -c`
12 | *.test
13 |
14 | # Output of the go coverage tool, specifically when used with LiteIDE
15 | *.out
16 |
17 | # Dependency directories (remove the comment below to include it)
18 | # vendor/
19 |
20 | # Go workspace file
21 | go.work
22 |
23 | # Environment variables
24 | .env
25 |
26 | # Temporary directory
27 | tmp/*
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM --platform=$BUILDPLATFORM golang:1.23-alpine AS builder
2 |
3 | ARG TARGETPLATFORM
4 | ARG BUILDPLATFORM
5 | ARG TARGETOS
6 | ARG TARGETARCH
7 |
8 | # Add git, curl and upx support
9 | RUN apk add --no-cache git curl upx ca-certificates
10 |
11 | WORKDIR /src
12 |
13 | # Pull modules
14 | COPY go.* ./
15 | RUN go mod download
16 |
17 | # Copy code into image
18 | COPY . ./
19 |
20 | # Build application for deployment
21 | RUN --mount=type=cache,target=/root/.cache/go-build \
22 | --mount=type=cache,target=/go/pkg \
23 | CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -trimpath -ldflags '-s -w' -o /tmp/chatter .
24 |
25 | # Compress binary
26 | RUN upx --best --lzma /tmp/chatter
27 |
28 | # Create minimal image
29 | FROM --platform=$TARGETPLATFORM gcr.io/distroless/base
30 |
31 | # Add the binary
32 | COPY --from=builder /tmp/chatter /chatter
33 |
34 | EXPOSE 80/tcp
35 |
36 | ENTRYPOINT ["/chatter"]
37 |
--------------------------------------------------------------------------------
/limiter.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "sync"
5 | "time"
6 |
7 | mlimiters "github.com/mennanov/limiters"
8 | "github.com/mgjules/chat-demo/user"
9 | )
10 |
11 | type limiters struct {
12 | mu sync.RWMutex
13 | limiters map[string]*mlimiters.TokenBucket
14 | }
15 |
16 | func newLimiters() *limiters {
17 | return &limiters{
18 | limiters: make(map[string]*mlimiters.TokenBucket),
19 | }
20 | }
21 |
22 | func (l *limiters) add(u *user.User, d time.Duration, b int64) *mlimiters.TokenBucket {
23 | l.mu.RLock()
24 | limiter, found := l.limiters[u.ID.String()]
25 | l.mu.RUnlock()
26 | if found {
27 | return limiter
28 | }
29 |
30 | limiter = mlimiters.NewTokenBucket(b, d, mlimiters.NewLockNoop(), mlimiters.NewTokenBucketInMemory(), mlimiters.NewSystemClock(), mlimiters.NewStdLogger())
31 | l.mu.Lock()
32 | l.limiters[u.ID.String()] = limiter
33 | l.mu.Unlock()
34 |
35 | return limiter
36 | }
37 |
38 | func (l *limiters) remove(u *user.User) {
39 | l.mu.Lock()
40 | delete(l.limiters, u.ID.String())
41 | l.mu.Unlock()
42 | }
43 |
--------------------------------------------------------------------------------
/templates/page_templ.txt:
--------------------------------------------------------------------------------
1 |
Chat Demo
2 |
--------------------------------------------------------------------------------
/user/user.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "context"
5 | "fmt"
6 |
7 | "github.com/go-faker/faker/v4"
8 | "github.com/go-faker/faker/v4/pkg/options"
9 | "github.com/rs/xid"
10 | )
11 |
12 | type userContextKey string
13 |
14 | const userCtxKey userContextKey = "user"
15 |
16 | // User holds information about a user.
17 | type User struct {
18 | ID xid.ID
19 | Name string
20 | }
21 |
22 | // New creates a new User.
23 | func New() *User {
24 | id := xid.New()
25 | // Prevents faker from tracking duplicates since it does that in a non-threadsafe manner.
26 | // Instead we seed the Name with sections of the ID.
27 | nonunique := options.WithGenerateUniqueValues(false)
28 | return &User{
29 | ID: id,
30 | Name: fmt.Sprintf("%s %s (%s%s)",
31 | faker.FirstName(nonunique), faker.LastName(nonunique), id.String()[4:8], id.String()[15:],
32 | ),
33 | }
34 | }
35 |
36 | // AddToContext adds a user to the context.
37 | func AddToContext(ctx context.Context, user *User) context.Context {
38 | return context.WithValue(ctx, userCtxKey, user)
39 | }
40 |
41 | // FromContext retrieves a user from the context.
42 | func FromContext(ctx context.Context) *User {
43 | u, ok := ctx.Value(userCtxKey).(*User)
44 | if !ok {
45 | return nil
46 | }
47 |
48 | return u
49 | }
50 |
--------------------------------------------------------------------------------
/.github/workflows/release_build.yml:
--------------------------------------------------------------------------------
1 | name: release_build
2 |
3 | on:
4 | push:
5 | tags:
6 | - "v*.*.*"
7 |
8 | jobs:
9 | docker:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v3
14 | - name: Docker meta
15 | id: meta
16 | uses: docker/metadata-action@v4
17 | with:
18 | # list of Docker images to use as base name for tags
19 | images: |
20 | julesmike/chatter
21 | # generate Docker tags based on the following events/attributes
22 | tags: |
23 | type=semver,pattern=v{{version}}
24 | type=semver,pattern=v{{major}}.{{minor}}
25 | type=semver,pattern=v{{major}}
26 | - name: Set up QEMU
27 | uses: docker/setup-qemu-action@v2
28 | - name: Set up Docker Buildx
29 | uses: docker/setup-buildx-action@v2
30 | - name: Login to Docker Hub
31 | uses: docker/login-action@v2
32 | with:
33 | username: ${{ secrets.DOCKERHUB_USERNAME }}
34 | password: ${{ secrets.DOCKERHUB_TOKEN }}
35 | - name: Build and push
36 | uses: docker/build-push-action@v3
37 | with:
38 | context: .
39 | platforms: linux/amd64,linux/arm64
40 | push: true
41 | tags: ${{ steps.meta.outputs.tags }}
42 | labels: ${{ steps.meta.outputs.labels }}
43 | cache-from: type=gha
44 | cache-to: type=gha,mode=max
45 |
--------------------------------------------------------------------------------
/templates/page.templ:
--------------------------------------------------------------------------------
1 | package templates
2 |
3 | import (
4 | "github.com/mgjules/chat-demo/chat"
5 | "github.com/mgjules/chat-demo/user"
6 | )
7 |
8 | templ Page(user *user.User, room *chat.Room, cErr *chat.Error) {
9 |
10 |
11 |
12 |
13 |
14 | Chat Demo
15 |
16 |
21 |
45 |
46 |
47 | @Chat(user, room, cErr)
48 |
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # chat-demo
2 |
3 | Enn demonstrasion chat websocket ki servi HTMX, Templ, AlpineJS ek Go.
4 |
5 | 
6 |
7 | ## Fonksionalite
8 |
9 | - Kominikasion an tem reel par websocket
10 | - Limitasion pou anpes abuse
11 | - Otantifikasion itilizater avek JWT
12 | - Design responsive
13 | - Filtraz bann mo vilain
14 |
15 | ## Teknologi Itilize
16 |
17 | - **Backend**: Go
18 | - **Frontend**:
19 | - [HTMX](https://htmx.org/) - Extensionn HTML pou konteni dinamik
20 | - [Templ](https://github.com/a-h/templ) - Template HTML avek sekirite tip pou Go
21 | - [AlpineJS](https://alpinejs.dev/) - Framework JavaScript lezer
22 |
23 | ## Seki ou bizin avan
24 |
25 | - Go 1.23+
26 | - Docker (opsionel)
27 |
28 | ## Instalasion
29 |
30 | ### Developman Lokal
31 |
32 | 1. Clone repository-la:
33 | ```bash
34 | git clone https://github.com/mgjules/chat-demo.git
35 | cd chat-demo
36 | ```
37 |
38 | 2. Kree enn fichie `.env`:
39 | ```bash
40 | HTTP_PORT=8080
41 | JWT_SECRET=to_kle_sekre
42 | ```
43 |
44 | 3. Roul aplikasion-la:
45 | ```bash
46 | go run .
47 | ```
48 |
49 | 4. Ale lor http://localhost:8080
50 |
51 | ### Docker
52 |
53 | 1. Konstrwir ek roul container Docker:
54 | ```bash
55 | docker build -t chat-demo .
56 | docker run -p 8080:8080 -e JWT_SECRET=to_kle_sekre chat-demo
57 | ```
58 |
59 | 2. Ale lor http://localhost:8080
60 |
61 | ## Koman Servi
62 |
63 | 1. Ale lor http://localhost:8080
64 | 2. Ou pou otomatikman koneekte avek enn itilizater au azar
65 | 3. Kumans koze avek lezot itilizater an tem reel
66 |
67 | ## Lisans
68 |
69 | Sa proze-la ena lisans Apache License 2.0 - get fichie [LICENSE](LICENSE) pou plis detay.
--------------------------------------------------------------------------------
/templates/page_templ.go:
--------------------------------------------------------------------------------
1 | // Code generated by templ - DO NOT EDIT.
2 |
3 | // templ: version: v0.3.833
4 | package templates
5 |
6 | //lint:file-ignore SA4006 This context is only used if a nested component is present.
7 |
8 | import "github.com/a-h/templ"
9 | import templruntime "github.com/a-h/templ/runtime"
10 |
11 | import (
12 | "github.com/mgjules/chat-demo/chat"
13 | "github.com/mgjules/chat-demo/user"
14 | )
15 |
16 | func Page(user *user.User, room *chat.Room, cErr *chat.Error) templ.Component {
17 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
18 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
19 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
20 | return templ_7745c5c3_CtxErr
21 | }
22 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
23 | if !templ_7745c5c3_IsBuffer {
24 | defer func() {
25 | templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
26 | if templ_7745c5c3_Err == nil {
27 | templ_7745c5c3_Err = templ_7745c5c3_BufErr
28 | }
29 | }()
30 | }
31 | ctx = templ.InitializeContext(ctx)
32 | templ_7745c5c3_Var1 := templ.GetChildren(ctx)
33 | if templ_7745c5c3_Var1 == nil {
34 | templ_7745c5c3_Var1 = templ.NopComponent
35 | }
36 | ctx = templ.ClearChildren(ctx)
37 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "Chat Demo")
38 | if templ_7745c5c3_Err != nil {
39 | return templ_7745c5c3_Err
40 | }
41 | templ_7745c5c3_Err = Chat(user, room, cErr).Render(ctx, templ_7745c5c3_Buffer)
42 | if templ_7745c5c3_Err != nil {
43 | return templ_7745c5c3_Err
44 | }
45 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "")
46 | if templ_7745c5c3_Err != nil {
47 | return templ_7745c5c3_Err
48 | }
49 | return nil
50 | })
51 | }
52 |
53 | var _ = templruntime.GeneratedTemplate
54 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/mgjules/chat-demo
2 |
3 | go 1.23
4 |
5 | toolchain go1.23.6
6 |
7 | require (
8 | github.com/TwiN/go-away v1.6.13
9 | github.com/a-h/templ v0.3.833
10 | github.com/enescakir/emoji v1.0.0
11 | github.com/go-chi/chi/v5 v5.0.10
12 | github.com/go-chi/jwtauth/v5 v5.3.0
13 | github.com/go-faker/faker/v4 v4.2.0
14 | github.com/joho/godotenv v1.5.1
15 | github.com/lestrrat-go/jwx/v2 v2.0.18
16 | github.com/mennanov/limiters v1.4.1
17 | github.com/rs/xid v1.5.0
18 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9
19 | golang.org/x/net v0.33.0
20 | golang.org/x/sync v0.10.0
21 | )
22 |
23 | require (
24 | github.com/alessandro-c/gomemcached-lock v1.0.0 // indirect
25 | github.com/armon/go-metrics v0.4.1 // indirect
26 | github.com/aws/aws-sdk-go-v2 v1.21.0 // indirect
27 | github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.10.39 // indirect
28 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 // indirect
29 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 // indirect
30 | github.com/aws/aws-sdk-go-v2/service/dynamodb v1.21.5 // indirect
31 | github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.15.5 // indirect
32 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 // indirect
33 | github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.35 // indirect
34 | github.com/aws/smithy-go v1.14.2 // indirect
35 | github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
36 | github.com/cenkalti/backoff/v3 v3.2.2 // indirect
37 | github.com/cespare/xxhash/v2 v2.2.0 // indirect
38 | github.com/coreos/go-semver v0.3.1 // indirect
39 | github.com/coreos/go-systemd/v22 v22.5.0 // indirect
40 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
41 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
42 | github.com/fatih/color v1.16.0 // indirect
43 | github.com/go-redsync/redsync/v4 v4.9.4 // indirect
44 | github.com/goccy/go-json v0.10.2 // indirect
45 | github.com/gogo/protobuf v1.3.2 // indirect
46 | github.com/golang/protobuf v1.5.3 // indirect
47 | github.com/hashicorp/consul/api v1.24.0 // indirect
48 | github.com/hashicorp/errwrap v1.1.0 // indirect
49 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
50 | github.com/hashicorp/go-hclog v1.5.0 // indirect
51 | github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
52 | github.com/hashicorp/go-multierror v1.1.1 // indirect
53 | github.com/hashicorp/go-rootcerts v1.0.2 // indirect
54 | github.com/hashicorp/golang-lru v1.0.2 // indirect
55 | github.com/hashicorp/serf v0.10.1 // indirect
56 | github.com/jmespath/go-jmespath v0.4.0 // indirect
57 | github.com/lestrrat-go/blackmagic v1.0.2 // indirect
58 | github.com/lestrrat-go/httpcc v1.0.1 // indirect
59 | github.com/lestrrat-go/httprc v1.0.4 // indirect
60 | github.com/lestrrat-go/iter v1.0.2 // indirect
61 | github.com/lestrrat-go/option v1.0.1 // indirect
62 | github.com/mattn/go-colorable v0.1.13 // indirect
63 | github.com/mattn/go-isatty v0.0.20 // indirect
64 | github.com/mitchellh/go-homedir v1.1.0 // indirect
65 | github.com/mitchellh/mapstructure v1.5.0 // indirect
66 | github.com/pkg/errors v0.9.1 // indirect
67 | github.com/redis/go-redis/v9 v9.1.0 // indirect
68 | github.com/samuel/go-zookeeper v0.0.0-20201211165307-7117e9ea2414 // indirect
69 | github.com/segmentio/asm v1.2.0 // indirect
70 | github.com/thanhpk/randstr v1.0.4 // indirect
71 | go.etcd.io/etcd/api/v3 v3.5.9 // indirect
72 | go.etcd.io/etcd/client/pkg/v3 v3.5.9 // indirect
73 | go.etcd.io/etcd/client/v3 v3.5.9 // indirect
74 | go.uber.org/multierr v1.11.0 // indirect
75 | go.uber.org/zap v1.26.0 // indirect
76 | golang.org/x/crypto v0.31.0 // indirect
77 | golang.org/x/sys v0.28.0 // indirect
78 | golang.org/x/text v0.21.0 // indirect
79 | google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb // indirect
80 | google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb // indirect
81 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230913181813-007df8e322eb // indirect
82 | google.golang.org/grpc v1.58.3 // indirect
83 | google.golang.org/protobuf v1.33.0 // indirect
84 | )
85 |
--------------------------------------------------------------------------------
/templates/chat_templ.txt:
--------------------------------------------------------------------------------
1 |
4 |
11 |
12 |
13 |
16 |
17 |
18 |
26 |
28 |
38 | Copyright (c)
39 | . All rights reserved.
--------------------------------------------------------------------------------
/chat/chat.go:
--------------------------------------------------------------------------------
1 | package chat
2 |
3 | import (
4 | "container/ring"
5 | "fmt"
6 | "strings"
7 | "sync"
8 | "time"
9 |
10 | goaway "github.com/TwiN/go-away"
11 | "github.com/enescakir/emoji"
12 | "github.com/mgjules/chat-demo/user"
13 | "github.com/rs/xid"
14 | "golang.org/x/exp/slog"
15 | "golang.org/x/net/websocket"
16 | "golang.org/x/sync/semaphore"
17 | )
18 |
19 | const (
20 | maxMessageSize uint16 = 256
21 | maxSendWorker uint16 = 1000
22 | maxClients uint16 = 1000
23 | )
24 |
25 | // List of chat errors.
26 | var (
27 | ErrLoading = NewError(ErrorSeverityWarning, true, "loading...")
28 | ErrUnknown = NewError(ErrorSeverityError, false, "unknown error")
29 | ErrRateLimited = NewError(ErrorSeverityWarning, false, "please slow down")
30 | ErrMessageEmpty = NewError(ErrorSeverityError, false, "message content cannot be empty")
31 | ErrExistingSession = NewError(ErrorSeverityError, true, "you already have a running session for this room")
32 | ErrRoomFull = NewError(ErrorSeverityError, true, "the room is full")
33 | )
34 |
35 | // ErrorSeverity is the severity of an error.
36 | type ErrorSeverity uint8
37 |
38 | // List of chat errors.
39 | const (
40 | ErrorSeverityWarning ErrorSeverity = iota
41 | ErrorSeverityError
42 | )
43 |
44 | // Error represents a chat error.
45 | type Error struct {
46 | err string
47 | severity ErrorSeverity
48 | global bool
49 | }
50 |
51 | // NewError creates a new Error.
52 | func NewError(s ErrorSeverity, global bool, err string) Error {
53 | return Error{severity: s, global: global, err: err}
54 | }
55 |
56 | // IsError checks if the error of severity error.
57 | func (e Error) IsError() bool { return e.severity == ErrorSeverityError }
58 |
59 | // IsWarning checks if the error of severity warning.
60 | func (e Error) IsWarning() bool { return e.severity == ErrorSeverityWarning }
61 |
62 | // IsGlobal returns true if the error is a global error.
63 | func (e Error) IsGlobal() bool { return e.global }
64 |
65 | // Error implements the Error interface.
66 | func (e Error) Error() string {
67 | return e.err
68 | }
69 |
70 | // Message represents a single chat message.
71 | type Message struct {
72 | User *user.User
73 | Content string
74 | Time time.Time
75 | }
76 |
77 | // NewMessage creates a new Message.
78 | func NewMessage(u *user.User, content string) (*Message, error) {
79 | content = strings.TrimSpace(content)
80 | if content == "" {
81 | return nil, ErrMessageEmpty
82 | }
83 |
84 | rc := []rune(content)
85 | if len(rc) > int(maxMessageSize) {
86 | content = string(rc[:maxMessageSize]) + "..."
87 | }
88 |
89 | content = goaway.Censor(emoji.Parse(content))
90 |
91 | return &Message{
92 | User: u,
93 | Content: content,
94 | Time: time.Now().UTC(),
95 | }, nil
96 | }
97 |
98 | // Client represents the relationship between a user and websocket connections.
99 | type Client struct {
100 | user *user.User
101 | conn *websocket.Conn
102 | }
103 |
104 | // Room holds the state of a single chat room.
105 | type Room struct {
106 | muClients sync.RWMutex
107 | clients map[string]*Client
108 |
109 | muMessages sync.RWMutex
110 | messages *ring.Ring
111 | sem *semaphore.Weighted
112 | }
113 |
114 | // NewRoom creates a new Room.
115 | func NewRoom() *Room {
116 | return &Room{
117 | clients: make(map[string]*Client),
118 | messages: ring.New(100),
119 | sem: semaphore.NewWeighted(int64(maxSendWorker)),
120 | }
121 | }
122 |
123 | // AddClient adds a client along with its websocket connection.
124 | func (r *Room) AddClient(u *user.User, ws *websocket.Conn) error {
125 | if _, found := r.GetClient(u.ID); found {
126 | return ErrExistingSession
127 | }
128 |
129 | if r.IsAtCapacity() {
130 | return ErrRoomFull
131 | }
132 |
133 | r.muClients.Lock()
134 | r.clients[u.ID.String()] = &Client{
135 | user: u,
136 | conn: ws,
137 | }
138 | r.muClients.Unlock()
139 |
140 | return nil
141 | }
142 |
143 | // IsAtCapacity returns true if the room is at capacity.
144 | func (r *Room) IsAtCapacity() bool {
145 | return r.NumUsers() >= uint64(maxClients)
146 | }
147 |
148 | // GetClient gets a client.
149 | func (r *Room) GetClient(id xid.ID) (*Client, bool) {
150 | r.muClients.RLock()
151 | defer r.muClients.RUnlock()
152 | client, found := r.clients[id.String()]
153 | return client, found
154 | }
155 |
156 | // RemoveClient removes a client.
157 | func (r *Room) RemoveClient(id xid.ID) bool {
158 | if _, found := r.GetClient(id); !found {
159 | return false
160 | }
161 |
162 | r.muClients.Lock()
163 | delete(r.clients, id.String())
164 | r.muClients.Unlock()
165 |
166 | return true
167 | }
168 |
169 | // NumUsers return the current number of users as clients.
170 | func (r *Room) NumUsers() uint64 {
171 | r.muClients.RLock()
172 | defer r.muClients.RUnlock()
173 |
174 | return uint64(len(r.clients))
175 | }
176 |
177 | // AddMessage adds a new chat message.
178 | func (r *Room) AddMessage(m *Message) {
179 | r.muMessages.Lock()
180 | r.messages.Value = m
181 | r.messages = r.messages.Next()
182 | r.muMessages.Unlock()
183 | }
184 |
185 | // Messages returns the list of messages.
186 | func (r *Room) Messages() []*Message {
187 | r.muMessages.RLock()
188 | defer r.muMessages.RUnlock()
189 |
190 | messages := make([]*Message, 0)
191 | r.messages.Do(func(m any) {
192 | messages = append(messages, m.(*Message))
193 | })
194 |
195 | return messages
196 | }
197 |
198 | // Write implements the io.Writer interface.
199 | func (r *Room) Write(p []byte) (int, error) {
200 | r.IterateClients(func(u *user.User, conn *websocket.Conn) error {
201 | if _, err := conn.Write(p); err != nil {
202 | return fmt.Errorf("write: %w", err)
203 | }
204 |
205 | return nil
206 | })
207 |
208 | return len(p), nil
209 | }
210 |
211 | // IterateClients executes a function fn
212 | // (e.g. a custom send mechanism or personalized messages per client) for all the clients.
213 | func (r *Room) IterateClients(fn func(u *user.User, conn *websocket.Conn) error) {
214 | r.muClients.RLock()
215 | defer r.muClients.RUnlock()
216 |
217 | var wg sync.WaitGroup
218 | for _, c := range r.clients {
219 | if err := r.sem.Acquire(c.conn.Request().Context(), 1); err != nil {
220 | slog.WarnContext(c.conn.Request().Context(), "acquire lock", "err", err, "user.id", c.user.ID)
221 | continue
222 | }
223 |
224 | wg.Add(1)
225 | go func(c *Client) {
226 | defer func() {
227 | r.sem.Release(1)
228 | wg.Done()
229 | }()
230 |
231 | if err := fn(c.user, c.conn); err != nil {
232 | slog.WarnContext(c.conn.Request().Context(), "send message", "err", "user.id", c.user.ID)
233 | }
234 | }(c)
235 | }
236 |
237 | wg.Wait()
238 | }
239 |
--------------------------------------------------------------------------------
/templates/chat.templ:
--------------------------------------------------------------------------------
1 | package templates
2 |
3 | import (
4 | "strconv"
5 | "time"
6 |
7 | "github.com/mgjules/chat-demo/chat"
8 | "github.com/mgjules/chat-demo/user"
9 | )
10 |
11 | templ Chat(user *user.User, room *chat.Room, cErr *chat.Error) {
12 |
78 |
79 | @ChatGlobalError(cErr)
80 |
81 | @ChatHeader(room.NumUsers(), user.Name)
82 | @ChatMessages(user, room.Messages())
83 | @ChatForm(cErr)
84 | @ChatFooter()
85 |
86 |
87 | }
88 |
89 | templ ChatGlobalError(cErr *chat.Error) {
90 |
91 | if cErr != nil && cErr.IsGlobal() {
92 |
93 |
94 | { cErr.Error() }
95 |
96 | }
97 |
98 | }
99 |
100 | templ ChatHeaderNumUsers(numUsers uint64) {
101 | { strconv.Itoa(int(numUsers)) + " " + ternary(numUsers > 1, "users", "user") }
102 | }
103 |
104 | templ ChatHeader(numUsers uint64, userName string) {
105 |
106 |
107 |
111 | @ChatHeaderNumUsers(numUsers)
112 |
113 |
{ userName }
114 |
115 | }
116 |
117 | templ ChatMessageWrapped(user *user.User, message *chat.Message) {
118 |
119 | @ChatMessage(user, message)
120 |
121 | }
122 |
123 | templ ChatMessage(user *user.User, message *chat.Message) {
124 |
125 |
126 | if user.ID != message.User.ID {
127 |
{ message.User.Name }
128 | }
129 |
130 |
{ message.Content }
131 |
132 |
133 |
134 |
135 | }
136 |
137 | templ ChatMessages(user *user.User, messages []*chat.Message) {
138 |
144 | }
145 |
146 | templ ChatForm(cErr *chat.Error) {
147 |
167 | }
168 |
169 | templ ChatFooter() {
170 | Copyright (c) { time.Now().Format("2006") }. All rights reserved.
171 | }
172 |
173 | func ternary(cond bool, str1, str2 string) string {
174 | if cond {
175 | return str1
176 | }
177 |
178 | return str2
179 | }
180 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "os"
9 | "time"
10 |
11 | "github.com/go-chi/chi/v5"
12 | "github.com/go-chi/chi/v5/middleware"
13 | "github.com/go-chi/jwtauth/v5"
14 | "github.com/go-faker/faker/v4"
15 | "github.com/go-faker/faker/v4/pkg/options"
16 | "github.com/joho/godotenv"
17 | "github.com/lestrrat-go/jwx/v2/jwt"
18 | mlimiters "github.com/mennanov/limiters"
19 | "github.com/mgjules/chat-demo/chat"
20 | "github.com/mgjules/chat-demo/templates"
21 | "github.com/mgjules/chat-demo/user"
22 | "github.com/rs/xid"
23 | "golang.org/x/exp/slog"
24 | "golang.org/x/net/websocket"
25 | )
26 |
27 | func main() {
28 | logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
29 | slog.SetDefault(logger)
30 |
31 | if err := run(); err != nil {
32 | slog.Error("Failed to start server", "err", err)
33 | os.Exit(1)
34 | }
35 | }
36 |
37 | func run() error {
38 | // Load .env file is present.
39 | godotenv.Load()
40 |
41 | secret := os.Getenv("JWT_SECRET")
42 | if secret == "" {
43 | return errors.New("missing JWT_SECRET environment variable")
44 | }
45 |
46 | port := os.Getenv("HTTP_PORT")
47 | if port == "" {
48 | port = "8080"
49 | }
50 |
51 | jwt := jwtauth.New("HS256", []byte(secret), nil)
52 |
53 | r := chi.NewRouter()
54 | r.Use(middleware.Recoverer)
55 | r.Use(middleware.RealIP)
56 | r.Use(middleware.CleanPath)
57 | r.Use(middleware.StripSlashes)
58 | r.Use(middleware.Compress(5))
59 | r.Use(middleware.RequestSize(32000))
60 | r.Use(middleware.Heartbeat("/ping"))
61 | r.Use(jwtauth.Verifier(jwt))
62 |
63 | room := chat.NewRoom()
64 | // Seeding random messages in room.
65 | for i := 0; i < 1000; i++ {
66 | msg, _ := chat.NewMessage(
67 | user.New(),
68 | faker.Sentence(options.WithGenerateUniqueValues(false)),
69 | )
70 | room.AddMessage(msg)
71 | }
72 |
73 | lims := newLimiters()
74 |
75 | // Protected routes.
76 | r.Group(func(r chi.Router) {
77 | r.Use(protected)
78 |
79 | r.Get("/", index(room))
80 | r.Handle("/chatroom", websocket.Handler(chatroom(room, lims)))
81 | })
82 |
83 | r.Get("/login", login(jwt))
84 |
85 | server := &http.Server{
86 | Addr: ":" + port,
87 | Handler: r,
88 | ReadTimeout: 5 * time.Second,
89 | WriteTimeout: 10 * time.Second,
90 | }
91 | slog.Info("Running server...", "addr", "http://"+server.Addr)
92 | return server.ListenAndServe()
93 | }
94 |
95 | func login(auth *jwtauth.JWTAuth) http.HandlerFunc {
96 | return func(w http.ResponseWriter, r *http.Request) {
97 | token, _, err := jwtauth.FromContext(r.Context())
98 | if err == nil && token != nil && jwt.Validate(token) == nil {
99 | http.Redirect(w, r, "/", http.StatusFound)
100 | return
101 | }
102 |
103 | // Create a fake user and use it as claim to encode a jwt token.
104 | _, t, err := auth.Encode(map[string]any{
105 | "user": user.New(),
106 | })
107 | if err != nil {
108 | http.Error(w, err.Error(), http.StatusInternalServerError)
109 | return
110 | }
111 |
112 | http.SetCookie(w, &http.Cookie{
113 | Name: "jwt",
114 | Value: t,
115 | Expires: time.Now().Add(1 * time.Hour),
116 | Secure: false,
117 | HttpOnly: false,
118 | Path: "/",
119 | })
120 |
121 | http.Redirect(w, r, "/", http.StatusFound)
122 | }
123 | }
124 |
125 | func protected(next http.Handler) http.Handler {
126 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
127 | token, claims, err := jwtauth.FromContext(r.Context())
128 | if err != nil || token == nil || jwt.Validate(token) != nil {
129 | http.Redirect(w, r, "/login", http.StatusFound)
130 | return
131 | }
132 |
133 | // Retrieve the user from the claims and add it to the request context.
134 | // If the user ID is invalid, we attempt login again.
135 | // This could lead to an infinite loop if a user has a newer claim format.
136 | u := claims["user"].(map[string]any)
137 | id, err := xid.FromString(u["ID"].(string))
138 | if err != nil {
139 | http.Redirect(w, r, "/login", http.StatusFound)
140 | return
141 | }
142 |
143 | ctx := user.AddToContext(r.Context(), &user.User{
144 | ID: id,
145 | Name: u["Name"].(string),
146 | })
147 |
148 | next.ServeHTTP(w, r.WithContext(ctx))
149 | })
150 | }
151 |
152 | func index(room *chat.Room) http.HandlerFunc {
153 | return func(w http.ResponseWriter, r *http.Request) {
154 | ctx := r.Context()
155 | user := user.FromContext(ctx)
156 |
157 | // We lock the chat until we get a web socket connection.
158 | w.Header().Set("Content-Type", "text/html; charset=utf-8")
159 | if err := templates.Page(user, room, &chat.ErrLoading).Render(ctx, w); err != nil {
160 | slog.ErrorContext(ctx, "render index template", "err", err, "user.id", user.ID)
161 | w.Write([]byte("failed to render index template"))
162 | }
163 | }
164 | }
165 |
166 | type data struct {
167 | Message string `json:"chat_message"`
168 | Headers map[string]string `json:"HEADERS"`
169 | }
170 |
171 | func chatroom(room *chat.Room, lims *limiters) func(ws *websocket.Conn) {
172 | return func(ws *websocket.Conn) {
173 | ws.MaxPayloadBytes = 2 << 10 // 2KB
174 | defer ws.Close()
175 |
176 | // Retrieve user from context.
177 | ctx := ws.Request().Context()
178 | usr := user.FromContext(ctx)
179 | logger := slog.Default().With("user.id", usr.ID)
180 | if err := room.AddClient(usr, ws); err != nil {
181 | // Inform the current user about the error.
182 | var cErr chat.Error
183 | if errors.As(err, &cErr) {
184 | if cErr.IsGlobal() {
185 | if err := templates.ChatGlobalError(&cErr).Render(ctx, ws); err != nil {
186 | logger.ErrorContext(ctx, "render global error template", "err", err)
187 | }
188 | } else {
189 | if err := templates.ChatForm(&cErr).Render(ctx, ws); err != nil {
190 | logger.ErrorContext(ctx, "render form template", "err", err)
191 | }
192 | }
193 | }
194 |
195 | return
196 | }
197 |
198 | // Remove client from room when user disconnects.
199 | defer func() {
200 | room.RemoveClient(usr.ID)
201 | lims.remove(usr)
202 |
203 | // Update number of user online for all users.
204 | if err := templates.ChatHeaderNumUsers(room.NumUsers()).Render(ctx, room); err != nil {
205 | logger.ErrorContext(ctx, "render online template", "err", err)
206 | }
207 | }()
208 |
209 | // Update number of user online for all users.
210 | if err := templates.ChatHeaderNumUsers(room.NumUsers()).Render(ctx, room); err != nil {
211 | logger.ErrorContext(ctx, "render online template", "err", err)
212 | return
213 | }
214 |
215 | // Unlock global lock.
216 | if err := templates.ChatGlobalError(nil).Render(ctx, ws); err != nil {
217 | logger.ErrorContext(ctx, "render global error template", "err", err)
218 | return
219 | }
220 | if err := templates.ChatForm(nil).Render(ctx, ws); err != nil {
221 | logger.ErrorContext(ctx, "render global error template", "err", err)
222 | return
223 | }
224 |
225 | lim := lims.add(usr, 5*time.Second, 3)
226 |
227 | // Receiving and processing client requests.
228 | for {
229 | var d data
230 | if err := websocket.JSON.Receive(ws, &d); err != nil {
231 | if errors.Is(err, io.EOF) {
232 | break
233 | }
234 |
235 | logger.ErrorContext(ctx, "receive message", "err", err)
236 |
237 | // Inform user something went wrong.
238 | if err := templates.ChatGlobalError(&chat.ErrUnknown).Render(ctx, ws); err != nil {
239 | logger.ErrorContext(ctx, "render error template", "err", err)
240 | break
241 | }
242 |
243 | continue
244 | }
245 |
246 | // Rate limit to prevent abuse.
247 | if wait, err := lim.Limit(ctx); errors.Is(err, mlimiters.ErrLimitExhausted) {
248 | // Inform the current user to slow down and
249 | // disable the form until limiter allows.
250 | if err := templates.ChatForm(&chat.ErrRateLimited).Render(ctx, ws); err != nil {
251 | logger.ErrorContext(ctx, "render form template", "err", err)
252 | break
253 | }
254 |
255 | // Wait until user is no more rate-limited
256 | <-time.After(wait)
257 |
258 | // Re-enable the form.
259 | // Clear the error for the current user.
260 | if err := templates.ChatForm(nil).Render(ctx, ws); err != nil {
261 | logger.ErrorContext(ctx, "render form template", "err", err)
262 | break
263 | }
264 |
265 | continue
266 | }
267 |
268 | // Create and add the message to the room.
269 | msg, err := chat.NewMessage(usr, d.Message)
270 | if err != nil {
271 | // Send back an error if we could not create message.
272 | // Could be a validation error.
273 | var cErr chat.Error
274 | if errors.As(err, &cErr) {
275 | if cErr.IsGlobal() {
276 | if err := templates.ChatGlobalError(&cErr).Render(ctx, ws); err != nil {
277 | logger.ErrorContext(ctx, "render global error template", "err", err)
278 | break
279 | }
280 | } else {
281 | if err := templates.ChatForm(&cErr).Render(ctx, ws); err != nil {
282 | logger.ErrorContext(ctx, "render form template", "err", err)
283 | break
284 | }
285 | }
286 | }
287 |
288 | continue
289 | }
290 | room.AddMessage(msg)
291 |
292 | // Broadcast personalized message to all clients including the current user.
293 | room.IterateClients(func(u *user.User, conn *websocket.Conn) error {
294 | if err := templates.ChatMessageWrapped(u, msg).Render(ctx, conn); err != nil {
295 | return fmt.Errorf("render message template: %w", err)
296 | }
297 |
298 | return nil
299 | })
300 |
301 | // Reset the form and clear the error for the current user.
302 | if err := templates.ChatForm(nil).Render(ctx, ws); err != nil {
303 | logger.ErrorContext(ctx, "render form template", "err", err)
304 | break
305 | }
306 | if err := templates.ChatGlobalError(nil).Render(ctx, ws); err != nil {
307 | logger.ErrorContext(ctx, "render form template", "err", err)
308 | break
309 | }
310 | }
311 | }
312 | }
313 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/templates/chat_templ.go:
--------------------------------------------------------------------------------
1 | // Code generated by templ - DO NOT EDIT.
2 |
3 | // templ: version: v0.3.833
4 | package templates
5 |
6 | //lint:file-ignore SA4006 This context is only used if a nested component is present.
7 |
8 | import "github.com/a-h/templ"
9 | import templruntime "github.com/a-h/templ/runtime"
10 |
11 | import (
12 | "strconv"
13 | "time"
14 |
15 | "github.com/mgjules/chat-demo/chat"
16 | "github.com/mgjules/chat-demo/user"
17 | )
18 |
19 | func Chat(user *user.User, room *chat.Room, cErr *chat.Error) templ.Component {
20 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
21 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
22 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
23 | return templ_7745c5c3_CtxErr
24 | }
25 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
26 | if !templ_7745c5c3_IsBuffer {
27 | defer func() {
28 | templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
29 | if templ_7745c5c3_Err == nil {
30 | templ_7745c5c3_Err = templ_7745c5c3_BufErr
31 | }
32 | }()
33 | }
34 | ctx = templ.InitializeContext(ctx)
35 | templ_7745c5c3_Var1 := templ.GetChildren(ctx)
36 | if templ_7745c5c3_Var1 == nil {
37 | templ_7745c5c3_Var1 = templ.NopComponent
38 | }
39 | ctx = templ.ClearChildren(ctx)
40 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "")
41 | if templ_7745c5c3_Err != nil {
42 | return templ_7745c5c3_Err
43 | }
44 | templ_7745c5c3_Err = ChatGlobalError(cErr).Render(ctx, templ_7745c5c3_Buffer)
45 | if templ_7745c5c3_Err != nil {
46 | return templ_7745c5c3_Err
47 | }
48 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "
")
49 | if templ_7745c5c3_Err != nil {
50 | return templ_7745c5c3_Err
51 | }
52 | templ_7745c5c3_Err = ChatHeader(room.NumUsers(), user.Name).Render(ctx, templ_7745c5c3_Buffer)
53 | if templ_7745c5c3_Err != nil {
54 | return templ_7745c5c3_Err
55 | }
56 | templ_7745c5c3_Err = ChatMessages(user, room.Messages()).Render(ctx, templ_7745c5c3_Buffer)
57 | if templ_7745c5c3_Err != nil {
58 | return templ_7745c5c3_Err
59 | }
60 | templ_7745c5c3_Err = ChatForm(cErr).Render(ctx, templ_7745c5c3_Buffer)
61 | if templ_7745c5c3_Err != nil {
62 | return templ_7745c5c3_Err
63 | }
64 | templ_7745c5c3_Err = ChatFooter().Render(ctx, templ_7745c5c3_Buffer)
65 | if templ_7745c5c3_Err != nil {
66 | return templ_7745c5c3_Err
67 | }
68 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "
")
69 | if templ_7745c5c3_Err != nil {
70 | return templ_7745c5c3_Err
71 | }
72 | return nil
73 | })
74 | }
75 |
76 | func ChatGlobalError(cErr *chat.Error) templ.Component {
77 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
78 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
79 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
80 | return templ_7745c5c3_CtxErr
81 | }
82 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
83 | if !templ_7745c5c3_IsBuffer {
84 | defer func() {
85 | templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
86 | if templ_7745c5c3_Err == nil {
87 | templ_7745c5c3_Err = templ_7745c5c3_BufErr
88 | }
89 | }()
90 | }
91 | ctx = templ.InitializeContext(ctx)
92 | templ_7745c5c3_Var2 := templ.GetChildren(ctx)
93 | if templ_7745c5c3_Var2 == nil {
94 | templ_7745c5c3_Var2 = templ.NopComponent
95 | }
96 | ctx = templ.ClearChildren(ctx)
97 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "")
98 | if templ_7745c5c3_Err != nil {
99 | return templ_7745c5c3_Err
100 | }
101 | if cErr != nil && cErr.IsGlobal() {
102 | var templ_7745c5c3_Var3 = []any{templ.SafeClass(ternary(cErr.IsError(), "text-red", "text-orange")), "absolute z-4 flex flex-col gap-4 justify-center items-center w-screen h-screen px-2 text-center backdrop-blur-lg bg-coolgray-800/70 uppercase"}
103 | templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var3...)
104 | if templ_7745c5c3_Err != nil {
105 | return templ_7745c5c3_Err
106 | }
107 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "
")
121 | if templ_7745c5c3_Err != nil {
122 | return templ_7745c5c3_Err
123 | }
124 | var templ_7745c5c3_Var5 = []any{templ.SafeClass(ternary(cErr.IsError(), "i-carbon:error", "i-carbon:warning-alt")), "text-4xl"}
125 | templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var5...)
126 | if templ_7745c5c3_Err != nil {
127 | return templ_7745c5c3_Err
128 | }
129 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "
")
143 | if templ_7745c5c3_Err != nil {
144 | return templ_7745c5c3_Err
145 | }
146 | var templ_7745c5c3_Var7 string
147 | templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(cErr.Error())
148 | if templ_7745c5c3_Err != nil {
149 | return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/chat.templ`, Line: 94, Col: 18}
150 | }
151 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
152 | if templ_7745c5c3_Err != nil {
153 | return templ_7745c5c3_Err
154 | }
155 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "
")
156 | if templ_7745c5c3_Err != nil {
157 | return templ_7745c5c3_Err
158 | }
159 | }
160 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "
")
161 | if templ_7745c5c3_Err != nil {
162 | return templ_7745c5c3_Err
163 | }
164 | return nil
165 | })
166 | }
167 |
168 | func ChatHeaderNumUsers(numUsers uint64) templ.Component {
169 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
170 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
171 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
172 | return templ_7745c5c3_CtxErr
173 | }
174 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
175 | if !templ_7745c5c3_IsBuffer {
176 | defer func() {
177 | templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
178 | if templ_7745c5c3_Err == nil {
179 | templ_7745c5c3_Err = templ_7745c5c3_BufErr
180 | }
181 | }()
182 | }
183 | ctx = templ.InitializeContext(ctx)
184 | templ_7745c5c3_Var8 := templ.GetChildren(ctx)
185 | if templ_7745c5c3_Var8 == nil {
186 | templ_7745c5c3_Var8 = templ.NopComponent
187 | }
188 | ctx = templ.ClearChildren(ctx)
189 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "")
190 | if templ_7745c5c3_Err != nil {
191 | return templ_7745c5c3_Err
192 | }
193 | var templ_7745c5c3_Var9 string
194 | templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(int(numUsers)) + " " + ternary(numUsers > 1, "users", "user"))
195 | if templ_7745c5c3_Err != nil {
196 | return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/chat.templ`, Line: 101, Col: 147}
197 | }
198 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
199 | if templ_7745c5c3_Err != nil {
200 | return templ_7745c5c3_Err
201 | }
202 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
")
203 | if templ_7745c5c3_Err != nil {
204 | return templ_7745c5c3_Err
205 | }
206 | return nil
207 | })
208 | }
209 |
210 | func ChatHeader(numUsers uint64, userName string) templ.Component {
211 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
212 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
213 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
214 | return templ_7745c5c3_CtxErr
215 | }
216 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
217 | if !templ_7745c5c3_IsBuffer {
218 | defer func() {
219 | templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
220 | if templ_7745c5c3_Err == nil {
221 | templ_7745c5c3_Err = templ_7745c5c3_BufErr
222 | }
223 | }()
224 | }
225 | ctx = templ.InitializeContext(ctx)
226 | templ_7745c5c3_Var10 := templ.GetChildren(ctx)
227 | if templ_7745c5c3_Var10 == nil {
228 | templ_7745c5c3_Var10 = templ.NopComponent
229 | }
230 | ctx = templ.ClearChildren(ctx)
231 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "")
232 | if templ_7745c5c3_Err != nil {
233 | return templ_7745c5c3_Err
234 | }
235 | templ_7745c5c3_Err = ChatHeaderNumUsers(numUsers).Render(ctx, templ_7745c5c3_Buffer)
236 | if templ_7745c5c3_Err != nil {
237 | return templ_7745c5c3_Err
238 | }
239 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "
")
240 | if templ_7745c5c3_Err != nil {
241 | return templ_7745c5c3_Err
242 | }
243 | var templ_7745c5c3_Var11 string
244 | templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(userName)
245 | if templ_7745c5c3_Err != nil {
246 | return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/chat.templ`, Line: 113, Col: 52}
247 | }
248 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
249 | if templ_7745c5c3_Err != nil {
250 | return templ_7745c5c3_Err
251 | }
252 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "
")
253 | if templ_7745c5c3_Err != nil {
254 | return templ_7745c5c3_Err
255 | }
256 | return nil
257 | })
258 | }
259 |
260 | func ChatMessageWrapped(user *user.User, message *chat.Message) templ.Component {
261 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
262 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
263 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
264 | return templ_7745c5c3_CtxErr
265 | }
266 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
267 | if !templ_7745c5c3_IsBuffer {
268 | defer func() {
269 | templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
270 | if templ_7745c5c3_Err == nil {
271 | templ_7745c5c3_Err = templ_7745c5c3_BufErr
272 | }
273 | }()
274 | }
275 | ctx = templ.InitializeContext(ctx)
276 | templ_7745c5c3_Var12 := templ.GetChildren(ctx)
277 | if templ_7745c5c3_Var12 == nil {
278 | templ_7745c5c3_Var12 = templ.NopComponent
279 | }
280 | ctx = templ.ClearChildren(ctx)
281 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "")
282 | if templ_7745c5c3_Err != nil {
283 | return templ_7745c5c3_Err
284 | }
285 | templ_7745c5c3_Err = ChatMessage(user, message).Render(ctx, templ_7745c5c3_Buffer)
286 | if templ_7745c5c3_Err != nil {
287 | return templ_7745c5c3_Err
288 | }
289 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "
")
290 | if templ_7745c5c3_Err != nil {
291 | return templ_7745c5c3_Err
292 | }
293 | return nil
294 | })
295 | }
296 |
297 | func ChatMessage(user *user.User, message *chat.Message) templ.Component {
298 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
299 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
300 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
301 | return templ_7745c5c3_CtxErr
302 | }
303 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
304 | if !templ_7745c5c3_IsBuffer {
305 | defer func() {
306 | templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
307 | if templ_7745c5c3_Err == nil {
308 | templ_7745c5c3_Err = templ_7745c5c3_BufErr
309 | }
310 | }()
311 | }
312 | ctx = templ.InitializeContext(ctx)
313 | templ_7745c5c3_Var13 := templ.GetChildren(ctx)
314 | if templ_7745c5c3_Var13 == nil {
315 | templ_7745c5c3_Var13 = templ.NopComponent
316 | }
317 | ctx = templ.ClearChildren(ctx)
318 | var templ_7745c5c3_Var14 = []any{templ.KV("flex justify-end", user.ID == message.User.ID), "overflow-anchor-none transition-all"}
319 | templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var14...)
320 | if templ_7745c5c3_Err != nil {
321 | return templ_7745c5c3_Err
322 | }
323 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "")
337 | if templ_7745c5c3_Err != nil {
338 | return templ_7745c5c3_Err
339 | }
340 | if user.ID != message.User.ID {
341 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "
")
342 | if templ_7745c5c3_Err != nil {
343 | return templ_7745c5c3_Err
344 | }
345 | var templ_7745c5c3_Var16 string
346 | templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(message.User.Name)
347 | if templ_7745c5c3_Err != nil {
348 | return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/chat.templ`, Line: 127, Col: 50}
349 | }
350 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
351 | if templ_7745c5c3_Err != nil {
352 | return templ_7745c5c3_Err
353 | }
354 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "
")
355 | if templ_7745c5c3_Err != nil {
356 | return templ_7745c5c3_Err
357 | }
358 | }
359 | var templ_7745c5c3_Var17 = []any{templ.KV("mt-1", user.ID != message.User.ID), "flex flex-justify-between gap-2"}
360 | templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var17...)
361 | if templ_7745c5c3_Err != nil {
362 | return templ_7745c5c3_Err
363 | }
364 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "
")
378 | if templ_7745c5c3_Err != nil {
379 | return templ_7745c5c3_Err
380 | }
381 | var templ_7745c5c3_Var19 string
382 | templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(message.Content)
383 | if templ_7745c5c3_Err != nil {
384 | return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/chat.templ`, Line: 130, Col: 69}
385 | }
386 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
387 | if templ_7745c5c3_Err != nil {
388 | return templ_7745c5c3_Err
389 | }
390 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "
")
404 | if templ_7745c5c3_Err != nil {
405 | return templ_7745c5c3_Err
406 | }
407 | return nil
408 | })
409 | }
410 |
411 | func ChatMessages(user *user.User, messages []*chat.Message) templ.Component {
412 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
413 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
414 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
415 | return templ_7745c5c3_CtxErr
416 | }
417 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
418 | if !templ_7745c5c3_IsBuffer {
419 | defer func() {
420 | templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
421 | if templ_7745c5c3_Err == nil {
422 | templ_7745c5c3_Err = templ_7745c5c3_BufErr
423 | }
424 | }()
425 | }
426 | ctx = templ.InitializeContext(ctx)
427 | templ_7745c5c3_Var21 := templ.GetChildren(ctx)
428 | if templ_7745c5c3_Var21 == nil {
429 | templ_7745c5c3_Var21 = templ.NopComponent
430 | }
431 | ctx = templ.ClearChildren(ctx)
432 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "")
433 | if templ_7745c5c3_Err != nil {
434 | return templ_7745c5c3_Err
435 | }
436 | for _, msg := range messages {
437 | templ_7745c5c3_Err = ChatMessage(user, msg).Render(ctx, templ_7745c5c3_Buffer)
438 | if templ_7745c5c3_Err != nil {
439 | return templ_7745c5c3_Err
440 | }
441 | }
442 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "
")
443 | if templ_7745c5c3_Err != nil {
444 | return templ_7745c5c3_Err
445 | }
446 | return nil
447 | })
448 | }
449 |
450 | func ChatForm(cErr *chat.Error) templ.Component {
451 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
452 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
453 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
454 | return templ_7745c5c3_CtxErr
455 | }
456 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
457 | if !templ_7745c5c3_IsBuffer {
458 | defer func() {
459 | templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
460 | if templ_7745c5c3_Err == nil {
461 | templ_7745c5c3_Err = templ_7745c5c3_BufErr
462 | }
463 | }()
464 | }
465 | ctx = templ.InitializeContext(ctx)
466 | templ_7745c5c3_Var22 := templ.GetChildren(ctx)
467 | if templ_7745c5c3_Var22 == nil {
468 | templ_7745c5c3_Var22 = templ.NopComponent
469 | }
470 | ctx = templ.ClearChildren(ctx)
471 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "")
558 | if templ_7745c5c3_Err != nil {
559 | return templ_7745c5c3_Err
560 | }
561 | return nil
562 | })
563 | }
564 |
565 | func ChatFooter() templ.Component {
566 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
567 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
568 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
569 | return templ_7745c5c3_CtxErr
570 | }
571 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
572 | if !templ_7745c5c3_IsBuffer {
573 | defer func() {
574 | templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
575 | if templ_7745c5c3_Err == nil {
576 | templ_7745c5c3_Err = templ_7745c5c3_BufErr
577 | }
578 | }()
579 | }
580 | ctx = templ.InitializeContext(ctx)
581 | templ_7745c5c3_Var29 := templ.GetChildren(ctx)
582 | if templ_7745c5c3_Var29 == nil {
583 | templ_7745c5c3_Var29 = templ.NopComponent
584 | }
585 | ctx = templ.ClearChildren(ctx)
586 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "Copyright (c) ")
587 | if templ_7745c5c3_Err != nil {
588 | return templ_7745c5c3_Err
589 | }
590 | var templ_7745c5c3_Var30 string
591 | templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(time.Now().Format("2006"))
592 | if templ_7745c5c3_Err != nil {
593 | return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/chat.templ`, Line: 170, Col: 108}
594 | }
595 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
596 | if templ_7745c5c3_Err != nil {
597 | return templ_7745c5c3_Err
598 | }
599 | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, ". All rights reserved.
")
600 | if templ_7745c5c3_Err != nil {
601 | return templ_7745c5c3_Err
602 | }
603 | return nil
604 | })
605 | }
606 |
607 | func ternary(cond bool, str1, str2 string) string {
608 | if cond {
609 | return str1
610 | }
611 |
612 | return str2
613 | }
614 |
615 | var _ = templruntime.GeneratedTemplate
616 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
2 | github.com/TwiN/go-away v1.6.13 h1:aB6l/FPXmA5ds+V7I9zdhxzpsLLUvVtEuS++iU/ZmgE=
3 | github.com/TwiN/go-away v1.6.13/go.mod h1:MpvIC9Li3minq+CGgbgUDvQ9tDaeW35k5IXZrF9MVas=
4 | github.com/a-h/templ v0.3.833 h1:L/KOk/0VvVTBegtE0fp2RJQiBm7/52Zxv5fqlEHiQUU=
5 | github.com/a-h/templ v0.3.833/go.mod h1:cAu4AiZhtJfBjMY0HASlyzvkrtjnHWPeEsyGK2YYmfk=
6 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
7 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
8 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
9 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
10 | github.com/alessandro-c/gomemcached-lock v1.0.0 h1:SkaMW3WUmxHBFSoq/1jF/hVL0atJijPzaLtrvbuLbM4=
11 | github.com/alessandro-c/gomemcached-lock v1.0.0/go.mod h1:m+EMbPuavZH8fC5zy/lEVFHKMAofF+MYYPvOn9yvvKQ=
12 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
13 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
14 | github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=
15 | github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
16 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
17 | github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
18 | github.com/aws/aws-sdk-go-v2 v1.21.0 h1:gMT0IW+03wtYJhRqTVYn0wLzwdnK9sRMcxmtfGzRdJc=
19 | github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M=
20 | github.com/aws/aws-sdk-go-v2/config v1.18.17 h1:jwTkhULSrbr/SQA8tfdYqZxpG8YsRycmIXxJcbrqY5E=
21 | github.com/aws/aws-sdk-go-v2/config v1.18.17/go.mod h1:Lj3E7XcxJnxMa+AYo89YiL68s1cFJRGduChynYU67VA=
22 | github.com/aws/aws-sdk-go-v2/credentials v1.13.17 h1:IubQO/RNeIVKF5Jy77w/LfUvmmCxTnk2TP1UZZIMiF4=
23 | github.com/aws/aws-sdk-go-v2/credentials v1.13.17/go.mod h1:K9xeFo1g/YPMguMUD69YpwB4Nyi6W/5wn706xIInJFg=
24 | github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.10.39 h1:DX/r3aNL7pIVn0K5a+ESL0Fw9ti7Rj05pblEiIJtPmQ=
25 | github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.10.39/go.mod h1:oTk09orqXlwSKnKf+UQhy+4Ci7aCo9x8hn0ZvPCLrns=
26 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.0 h1:/2Cb3SK3xVOQA7Xfr5nCWCo5H3UiNINtsVvVdk8sQqA=
27 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.0/go.mod h1:neYVaeKr5eT7BzwULuG2YbLhzWZ22lpjKdCybR7AXrQ=
28 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 h1:22dGT7PneFMx4+b3pz7lMTRyN8ZKH7M2cW4GP9yUS2g=
29 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas=
30 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 h1:SijA0mgjV8E+8G45ltVHs0fvKpTj8xmZJ3VwhGKtUSI=
31 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw=
32 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.31 h1:hf+Vhp5WtTdcSdE+yEcUz8L73sAzN0R+0jQv+Z51/mI=
33 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.31/go.mod h1:5zUjguZfG5qjhG9/wqmuyHRyUftl2B5Cp6NNxNC6kRA=
34 | github.com/aws/aws-sdk-go-v2/service/dynamodb v1.21.5 h1:EeNQ3bDA6hlx3vifHf7LT/l9dh9w7D2XgCdaD11TRU4=
35 | github.com/aws/aws-sdk-go-v2/service/dynamodb v1.21.5/go.mod h1:X3ThW5RPV19hi7bnQ0RMAiBjZbzxj4rZlj+qdctbMWY=
36 | github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.15.5 h1:xoalM/e1YsT6jkLKl6KA9HUiJANwn2ypJsM9lhW2WP0=
37 | github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.15.5/go.mod h1:7QtKdGj66zM4g5hPgxHRQgFGLGal4EgwggTw5OZH56c=
38 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 h1:m0QTSI6pZYJTk5WSKx3fm5cNW/DCicVzULBgU/6IyD0=
39 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14/go.mod h1:dDilntgHy9WnHXsh7dDtUPgHKEfTJIBUTHM8OWm0f/0=
40 | github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.35 h1:UKjpIDLVF90RfV88XurdduMoTxPqtGHZMIDYZQM7RO4=
41 | github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.35/go.mod h1:B3dUg0V6eJesUTi+m27NUkj7n8hdDKYUpxj8f4+TqaQ=
42 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.24 h1:c5qGfdbCHav6viBwiyDns3OXqhqAbGjfIB4uVu2ayhk=
43 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.24/go.mod h1:HMA4FZG6fyib+NDo5bpIxX1EhYjrAOveZJY2YR0xrNE=
44 | github.com/aws/aws-sdk-go-v2/service/sso v1.12.5 h1:bdKIX6SVF3nc3xJFw6Nf0igzS6Ff/louGq8Z6VP/3Hs=
45 | github.com/aws/aws-sdk-go-v2/service/sso v1.12.5/go.mod h1:vuWiaDB30M/QTC+lI3Wj6S/zb7tpUK2MSYgy3Guh2L0=
46 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.5 h1:xLPZMyuZ4GuqRCIec/zWuIhRFPXh2UOJdLXBSi64ZWQ=
47 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.5/go.mod h1:QjxpHmCwAg0ESGtPQnLIVp7SedTOBMYy+Slr3IfMKeI=
48 | github.com/aws/aws-sdk-go-v2/service/sts v1.18.6 h1:rIFn5J3yDoeuKCE9sESXqM5POTAhOP1du3bv/qTL+tE=
49 | github.com/aws/aws-sdk-go-v2/service/sts v1.18.6/go.mod h1:48WJ9l3dwP0GSHWGc5sFGGlCkuA82Mc2xnw+T6Q8aDw=
50 | github.com/aws/smithy-go v1.14.2 h1:MJU9hqBGbvWZdApzpvoF2WAIJDbtjK2NDJSiJP7HblQ=
51 | github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
52 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
53 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
54 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
55 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
56 | github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
57 | github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous=
58 | github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
59 | github.com/bsm/ginkgo/v2 v2.5.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
60 | github.com/bsm/ginkgo/v2 v2.9.5 h1:rtVBYPs3+TC5iLUVOis1B9tjLTup7Cj5IfzosKtvTJ0=
61 | github.com/bsm/ginkgo/v2 v2.9.5/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
62 | github.com/bsm/gomega v1.20.0/go.mod h1:JifAceMQ4crZIWYUKrlGcmbN3bqHogVTADMD2ATsbwk=
63 | github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
64 | github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
65 | github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
66 | github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
67 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
68 | github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
69 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
70 | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
71 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
72 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
73 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
74 | github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
75 | github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
76 | github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
77 | github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
78 | github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
79 | github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
80 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
81 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
82 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
83 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
84 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
85 | github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
86 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
87 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
88 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
89 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
90 | github.com/enescakir/emoji v1.0.0 h1:W+HsNql8swfCQFtioDGDHCHri8nudlK1n5p2rHCJoog=
91 | github.com/enescakir/emoji v1.0.0/go.mod h1:Bt1EKuLnKDTYpLALApstIkAjdDrS/8IAgTkKp+WKFD0=
92 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
93 | github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
94 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
95 | github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
96 | github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
97 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
98 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
99 | github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
100 | github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
101 | github.com/go-chi/jwtauth/v5 v5.3.0 h1:X7RKGks1lrVeIe2omGyz47pNaNjG2YmwlRN5UKhN8qg=
102 | github.com/go-chi/jwtauth/v5 v5.3.0/go.mod h1:2PoGm/KbnzRN9ILY6HFZAI6fTnb1gEZAKogAyqkd6fY=
103 | github.com/go-faker/faker/v4 v4.2.0 h1:dGebOupKwssrODV51E0zbMrv5e2gO9VWSLNC1WDCpWg=
104 | github.com/go-faker/faker/v4 v4.2.0/go.mod h1:F/bBy8GH9NxOxMInug5Gx4WYeG6fHJZ8Ol/dhcpRub4=
105 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
106 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
107 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
108 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
109 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
110 | github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
111 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
112 | github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
113 | github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
114 | github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
115 | github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
116 | github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg=
117 | github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w=
118 | github.com/go-redsync/redsync/v4 v4.9.4 h1:vRmYusI+qF95XSpApHAdeu+RjyDvxBXbMthbc/x148c=
119 | github.com/go-redsync/redsync/v4 v4.9.4/go.mod h1:RqBDXUw0q+u9FJTeD2gMzGtHeSVV93DiqGl10B9Hn/4=
120 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
121 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
122 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
123 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
124 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
125 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
126 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
127 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
128 | github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
129 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
130 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
131 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
132 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
133 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
134 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
135 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
136 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
137 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
138 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
139 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
140 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
141 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
142 | github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
143 | github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
144 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
145 | github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
146 | github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
147 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
148 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
149 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
150 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
151 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
152 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
153 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
154 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
155 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
156 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
157 | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
158 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
159 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
160 | github.com/hashicorp/consul/api v1.24.0 h1:u2XyStA2j0jnCiVUU7Qyrt8idjRn4ORhK6DlvZ3bWhA=
161 | github.com/hashicorp/consul/api v1.24.0/go.mod h1:NZJGRFYruc/80wYowkPFCp1LbGmJC9L8izrwfyVx/Wg=
162 | github.com/hashicorp/consul/sdk v0.14.1 h1:ZiwE2bKb+zro68sWzZ1SgHF3kRMBZ94TwOCFRF4ylPs=
163 | github.com/hashicorp/consul/sdk v0.14.1/go.mod h1:vFt03juSzocLRFo59NkeQHHmQa6+g7oU0pfzdI1mUhg=
164 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
165 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
166 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
167 | github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
168 | github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
169 | github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
170 | github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
171 | github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
172 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
173 | github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
174 | github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
175 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
176 | github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
177 | github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
178 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
179 | github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
180 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
181 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
182 | github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
183 | github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
184 | github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
185 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
186 | github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
187 | github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
188 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
189 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
190 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
191 | github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
192 | github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
193 | github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=
194 | github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
195 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
196 | github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
197 | github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
198 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
199 | github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
200 | github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM=
201 | github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=
202 | github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
203 | github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
204 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
205 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
206 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
207 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
208 | github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
209 | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
210 | github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
211 | github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
212 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
213 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
214 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
215 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
216 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
217 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
218 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
219 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
220 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
221 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
222 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
223 | github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k=
224 | github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
225 | github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
226 | github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
227 | github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8=
228 | github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
229 | github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
230 | github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
231 | github.com/lestrrat-go/jwx/v2 v2.0.18 h1:HHZkYS5wWDDyAiNBwztEtDoX07WDhGEdixm8G06R50o=
232 | github.com/lestrrat-go/jwx/v2 v2.0.18/go.mod h1:fAJ+k5eTgKdDqanzCuK6DAt3W7n3cs2/FX7JhQdk83U=
233 | github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
234 | github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
235 | github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
236 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
237 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
238 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
239 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
240 | github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
241 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
242 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
243 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
244 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
245 | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
246 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
247 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
248 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
249 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
250 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
251 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
252 | github.com/mennanov/limiters v1.4.1 h1:vj5/geKFW6qJrwD473PwTnQUlzUy6QoC6c+ONvu8eqM=
253 | github.com/mennanov/limiters v1.4.1/go.mod h1:eKMAq7NiG8fYL8dABUalZzNe1WvtsId/KvpML+bEWB8=
254 | github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
255 | github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=
256 | github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
257 | github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
258 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
259 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
260 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
261 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
262 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
263 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
264 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
265 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
266 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
267 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
268 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
269 | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
270 | github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
271 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
272 | github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
273 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
274 | github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
275 | github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
276 | github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
277 | github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
278 | github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0=
279 | github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=
280 | github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw=
281 | github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
282 | github.com/onsi/ginkgo/v2 v2.8.0/go.mod h1:6JsQiECmxCa3V5st74AL/AmsV482EDdVrGaVW6z3oYU=
283 | github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
284 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
285 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
286 | github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
287 | github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
288 | github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
289 | github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
290 | github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
291 | github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
292 | github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
293 | github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
294 | github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
295 | github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
296 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
297 | github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
298 | github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
299 | github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
300 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
301 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
302 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
303 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
304 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
305 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
306 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
307 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
308 | github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
309 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
310 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
311 | github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
312 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
313 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
314 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
315 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
316 | github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
317 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
318 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
319 | github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
320 | github.com/redis/go-redis/v9 v9.0.2/go.mod h1:/xDTe9EF1LM61hek62Poq2nzQSGj0xSrEtEHbBQevps=
321 | github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY=
322 | github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c=
323 | github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
324 | github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
325 | github.com/rueian/rueidis v0.0.93 h1:cG905akj2+QyHx0x9y4mN0K8vLi6M94QiyoLulXS3l0=
326 | github.com/rueian/rueidis v0.0.93/go.mod h1:lo6LBci0D986usi5Wxjb4RVNaWENKYbHZSnufGJ9bTE=
327 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
328 | github.com/samuel/go-zookeeper v0.0.0-20201211165307-7117e9ea2414 h1:AJNDS0kP60X8wwWFvbLPwDuojxubj9pbfK7pjHw0vKg=
329 | github.com/samuel/go-zookeeper v0.0.0-20201211165307-7117e9ea2414/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
330 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
331 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
332 | github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
333 | github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
334 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
335 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
336 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
337 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
338 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
339 | github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
340 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
341 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
342 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
343 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
344 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
345 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
346 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
347 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
348 | github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
349 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
350 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
351 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
352 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
353 | github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM=
354 | github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8=
355 | github.com/thanhpk/randstr v1.0.4 h1:IN78qu/bR+My+gHCvMEXhR/i5oriVHcTB/BJJIRTsNo=
356 | github.com/thanhpk/randstr v1.0.4/go.mod h1:M/H2P1eNLZzlDwAzpkkkUvoyNNMbzRGhESZuEQk3r0U=
357 | github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
358 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
359 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
360 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
361 | github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
362 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
363 | go.etcd.io/etcd/api/v3 v3.5.9 h1:4wSsluwyTbGGmyjJktOf3wFQoTBIURXHnq9n/G/JQHs=
364 | go.etcd.io/etcd/api/v3 v3.5.9/go.mod h1:uyAal843mC8uUVSLWz6eHa/d971iDGnCRpmKd2Z+X8k=
365 | go.etcd.io/etcd/client/pkg/v3 v3.5.9 h1:oidDC4+YEuSIQbsR94rY9gur91UPL6DnxDCIYd2IGsE=
366 | go.etcd.io/etcd/client/pkg/v3 v3.5.9/go.mod h1:y+CzeSmkMpWN2Jyu1npecjB9BBnABxGM4pN8cGuJeL4=
367 | go.etcd.io/etcd/client/v3 v3.5.9 h1:r5xghnU7CwbUxD/fbUtRyJGaYNfDun8sp/gTr1hew6E=
368 | go.etcd.io/etcd/client/v3 v3.5.9/go.mod h1:i/Eo5LrZ5IKqpbtpPDuaUnDOUv471oDg8cjQaUr2MbA=
369 | go.opentelemetry.io/otel v1.12.0/go.mod h1:geaoz0L0r1BEOR81k7/n9W4TCXYCJ7bPO7K374jQHG0=
370 | go.opentelemetry.io/otel/metric v0.35.0/go.mod h1:qAcbhaTRFU6uG8QM7dDo7XvFsWcugziq/5YI065TokQ=
371 | go.opentelemetry.io/otel/sdk v1.12.0/go.mod h1:WYcvtgquYvgODEvxOry5owO2y9MyciW7JqMz6cpXShE=
372 | go.opentelemetry.io/otel/sdk/metric v0.35.0/go.mod h1:eDyp1GxSiwV98kr7w4pzrszQh/eze9MqBqPd2bCPmyE=
373 | go.opentelemetry.io/otel/trace v1.12.0/go.mod h1:pHlgBynn6s25qJ2szD+Bv+iwKJttjHSI3lUAyf0GNuQ=
374 | go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
375 | go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
376 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
377 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
378 | go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
379 | go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
380 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
381 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
382 | golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
383 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
384 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
385 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
386 | golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
387 | golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
388 | golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
389 | golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
390 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
391 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
392 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
393 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
394 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
395 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
396 | golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
397 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
398 | golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
399 | golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
400 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
401 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
402 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
403 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
404 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
405 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
406 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
407 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
408 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
409 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
410 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
411 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
412 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
413 | golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
414 | golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
415 | golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
416 | golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
417 | golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
418 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
419 | golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
420 | golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
421 | golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
422 | golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
423 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
424 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
425 | golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
426 | golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
427 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
428 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
429 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
430 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
431 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
432 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
433 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
434 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
435 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
436 | golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
437 | golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
438 | golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
439 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
440 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
441 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
442 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
443 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
444 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
445 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
446 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
447 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
448 | golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
449 | golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
450 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
451 | golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
452 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
453 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
454 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
455 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
456 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
457 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
458 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
459 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
460 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
461 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
462 | golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
463 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
464 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
465 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
466 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
467 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
468 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
469 | golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
470 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
471 | golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
472 | golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
473 | golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
474 | golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
475 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
476 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
477 | golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
478 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
479 | golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
480 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
481 | golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
482 | golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
483 | golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
484 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
485 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
486 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
487 | golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
488 | golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
489 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
490 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
491 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
492 | golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
493 | golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
494 | golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
495 | golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
496 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
497 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
498 | golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
499 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
500 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
501 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
502 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
503 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
504 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
505 | golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
506 | golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
507 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
508 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
509 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
510 | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
511 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
512 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
513 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
514 | golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
515 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
516 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
517 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
518 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
519 | golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
520 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
521 | golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
522 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
523 | golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
524 | golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
525 | golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
526 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
527 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
528 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
529 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
530 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
531 | google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb h1:XFBgcDwm7irdHTbz4Zk2h7Mh+eis4nfJEFQFYzJzuIA=
532 | google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4=
533 | google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb h1:lK0oleSc7IQsUxO3U5TjL9DWlsxpEBemh+zpB7IqhWI=
534 | google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk=
535 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230913181813-007df8e322eb h1:Isk1sSH7bovx8Rti2wZK0UZF6oraBDK74uoyLEEVFN0=
536 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230913181813-007df8e322eb/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
537 | google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ=
538 | google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
539 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
540 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
541 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
542 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
543 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
544 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
545 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
546 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
547 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
548 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
549 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
550 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
551 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
552 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
553 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
554 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
555 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
556 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
557 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
558 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
559 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
560 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
561 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
562 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
563 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
564 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
565 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
566 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
567 |
--------------------------------------------------------------------------------