Compare commits
125 Commits
ee7736b261
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 24d04c3e79 | |||
| e916eb2395 | |||
| c24ed1e7e6 | |||
| ae2c141efa | |||
| 15c513fe6d | |||
| 1c92ea36e3 | |||
| 9dacb152de | |||
| 9014f59a2c | |||
| 7294aa3e43 | |||
| dd8f6d933e | |||
| 40f752259c | |||
| e4844c3293 | |||
| d304f3b319 | |||
| eea9b85745 | |||
| e3a4988ccf | |||
| d22a435224 | |||
| c4b64dfc09 | |||
| 672c006cd5 | |||
| 7566720420 | |||
| c1d0b2f650 | |||
| 6d0caa1cde | |||
| c558044646 | |||
| acd23deed4 | |||
| d010a9e5d4 | |||
| 6bedb3a17d | |||
| bce86224bb | |||
| 47cced4e75 | |||
| bf973b3b71 | |||
| 0756806f20 | |||
| a8dbcfdb05 | |||
| 65cb542985 | |||
| fabda61822 | |||
| 47aa2ae755 | |||
| a268b7958c | |||
| 256c494ad0 | |||
| 2e18b2455b | |||
| 406eb9c93b | |||
| 2228f5054d | |||
| 397386be12 | |||
| 1975a89bbb | |||
| f368e047be | |||
| bfd3a0a1dd | |||
| 52f839ba9a | |||
| 3000b9d1a3 | |||
| 3d1c7e1bda | |||
| 5fae613da5 | |||
| 7733abde5d | |||
| 24783a3b66 | |||
| c02c8cb610 | |||
| 85ff0c501a | |||
| 070cb8e12c | |||
| 364fda8a55 | |||
| f50e376f6c | |||
| 8bf45bdefe | |||
| a78fff8897 | |||
| 7a33018e44 | |||
| 17694d398b | |||
| 8d2cc9016f | |||
| edc864d01e | |||
| aac8a3eb54 | |||
| e82f100b67 | |||
| 40ef26f75d | |||
| 519d570041 | |||
| 275fe80286 | |||
| a75144d0db | |||
| 6c5324e131 | |||
| ae48597f96 | |||
| 95d1284e68 | |||
| c642f5ee44 | |||
| 6c60e250e1 | |||
| a79995e7c0 | |||
| fce42e4557 | |||
| 614509740b | |||
| 461d4c0e70 | |||
| d9d6b81ac3 | |||
| ed73b40bf5 | |||
| 2d05be679d | |||
| 06031620b7 | |||
| cef4764d8c | |||
| fce8669059 | |||
| afb3b939e7 | |||
| a3ef3d52d7 | |||
| f958201097 | |||
| fb4e48551b | |||
| 184fc3ecba | |||
| ee16094855 | |||
| f6482225ab | |||
| e6d09ab41b | |||
| 5b78292d1b | |||
| 14e3750bd9 | |||
| f962376f42 | |||
| 41ebb572fa | |||
| 56def6ea2c | |||
| 7044907a4a | |||
| 8ae33344eb | |||
| 75f5eb1456 | |||
| b9a5bdb069 | |||
| ea94fd7b54 | |||
| 57444f58f4 | |||
| d7642c82bd | |||
| e5c46a89df | |||
| f97cb877f5 | |||
| a8a3c8cc71 | |||
| eb77ecbeb7 | |||
| d19b5ab84f | |||
| 76889d23aa | |||
| 21e7c19fca | |||
| dfa3b6b362 | |||
| 1bc687eba6 | |||
| 54739cd11e | |||
| 1867ff64f7 | |||
| e15cd25c81 | |||
| 48c9bc21e0 | |||
| 7f0fc524da | |||
| c00ee25050 | |||
| 47a5f037c8 | |||
| 583bec1619 | |||
| 4bd7f4dc8b | |||
| 262bac2a15 | |||
| fda9b7517c | |||
| 09e670b096 | |||
| 096565f4e0 | |||
| 2486b317e3 | |||
| 7e03eeee38 | |||
| 768c809cbb |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
/uploads
|
/uploads
|
||||||
/dist
|
/dist
|
||||||
|
/keys
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
# Hydrogen.Messaging
|
# Hypernet.Messaging
|
||||||
|
|
||||||
The instant message service in Hydrogen universe.
|
The instant message service in Hypernet universe.
|
||||||
128
go.mod
128
go.mod
@@ -1,99 +1,84 @@
|
|||||||
module git.solsynth.dev/hydrogen/messaging
|
module git.solsynth.dev/hypernet/messaging
|
||||||
|
|
||||||
go 1.22
|
go 1.23.2
|
||||||
|
|
||||||
toolchain go1.22.1
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.solsynth.dev/hydrogen/dealer v0.0.0-20240716024524-cfb73fde1951
|
git.solsynth.dev/hypernet/nexus v0.0.0-20250329075932-d5422ab5b04c
|
||||||
git.solsynth.dev/hydrogen/paperclip v0.0.0-20240622051057-0f56dba45745
|
git.solsynth.dev/hypernet/paperclip v0.0.0-20250310151112-1d866f317f47
|
||||||
github.com/go-playground/validator/v10 v10.17.0
|
git.solsynth.dev/hypernet/passport v0.0.0-20250315083747-32e91e26013c
|
||||||
github.com/gofiber/contrib/websocket v1.3.0
|
git.solsynth.dev/hypernet/pusher v0.0.0-20250216145944-5fb769823a88
|
||||||
github.com/gofiber/fiber/v2 v2.52.4
|
github.com/fatih/color v1.18.0
|
||||||
github.com/gofiber/template/html/v2 v2.1.1
|
github.com/go-playground/validator/v10 v10.22.1
|
||||||
|
github.com/gofiber/fiber/v2 v2.52.6
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
|
|
||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/livekit/protocol v1.14.0
|
github.com/livekit/protocol v1.14.0
|
||||||
github.com/livekit/server-sdk-go v1.1.8
|
github.com/livekit/server-sdk-go v1.1.8
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/rs/zerolog v1.31.0
|
github.com/rs/zerolog v1.33.0
|
||||||
github.com/samber/lo v1.39.0
|
github.com/samber/lo v1.47.0
|
||||||
github.com/spf13/viper v1.18.2
|
github.com/spf13/viper v1.19.0
|
||||||
golang.org/x/crypto v0.24.0
|
google.golang.org/grpc v1.70.0
|
||||||
google.golang.org/grpc v1.64.0
|
gorm.io/datatypes v1.2.4
|
||||||
gorm.io/datatypes v1.2.0
|
gorm.io/driver/postgres v1.5.9
|
||||||
gorm.io/driver/postgres v1.5.4
|
gorm.io/gorm v1.25.12
|
||||||
gorm.io/gorm v1.25.6
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
github.com/armon/go-metrics v0.4.1 // indirect
|
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||||
github.com/benbjohnson/clock v1.3.5 // indirect
|
github.com/benbjohnson/clock v1.3.5 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bep/debounce v1.2.1 // indirect
|
github.com/bep/debounce v1.2.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/eapache/channels v1.1.0 // indirect
|
github.com/eapache/channels v1.1.0 // indirect
|
||||||
github.com/eapache/queue v1.1.0 // indirect
|
github.com/eapache/queue v1.1.0 // indirect
|
||||||
github.com/fasthttp/websocket v1.5.8 // indirect
|
github.com/eko/gocache/lib/v4 v4.2.0 // indirect
|
||||||
github.com/fatih/color v1.17.0 // indirect
|
github.com/eko/gocache/store/redis/v4 v4.2.2 // indirect
|
||||||
github.com/frostbyte73/core v0.0.10 // indirect
|
github.com/frostbyte73/core v0.0.10 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
github.com/gammazero/deque v0.2.1 // indirect
|
github.com/gammazero/deque v0.2.1 // indirect
|
||||||
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
|
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
|
||||||
github.com/go-logr/logr v1.4.2 // indirect
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-playground/form v3.1.4+incompatible // indirect
|
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.7.1 // indirect
|
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||||
github.com/gofiber/template v1.8.3 // indirect
|
github.com/goccy/go-json v0.10.3 // indirect
|
||||||
github.com/gofiber/utils v1.1.0 // indirect
|
github.com/golang/mock v1.6.0 // indirect
|
||||||
github.com/gorilla/websocket v1.5.1 // indirect
|
github.com/gorilla/websocket v1.5.1 // indirect
|
||||||
github.com/hashicorp/consul/api v1.29.1 // indirect
|
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
|
||||||
github.com/hashicorp/go-hclog v1.6.3 // indirect
|
|
||||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
|
||||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
|
||||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/hashicorp/serf v0.10.1 // indirect
|
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
github.com/jackc/pgx/v5 v5.5.1 // indirect
|
github.com/jackc/pgx/v5 v5.7.1 // indirect
|
||||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/jpillora/backoff v1.0.0 // indirect
|
|
||||||
github.com/jxskiss/base62 v1.1.0 // indirect
|
github.com/jxskiss/base62 v1.1.0 // indirect
|
||||||
github.com/klauspost/compress v1.17.8 // indirect
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
github.com/leodido/go-urn v1.2.4 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/lithammer/shortuuid/v4 v4.0.0 // indirect
|
github.com/lithammer/shortuuid/v4 v4.0.0 // indirect
|
||||||
github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 // indirect
|
github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 // indirect
|
||||||
github.com/livekit/mediatransportutil v0.0.0-20231213075826-cccbf2b93d3f // indirect
|
github.com/livekit/mediatransportutil v0.0.0-20231213075826-cccbf2b93d3f // indirect
|
||||||
github.com/livekit/psrpc v0.5.3-0.20240228172457-3724cb4adbc4 // indirect
|
github.com/livekit/psrpc v0.5.3-0.20240228172457-3724cb4adbc4 // indirect
|
||||||
github.com/magefile/mage v1.15.0 // indirect
|
github.com/magefile/mage v1.15.0 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||||
github.com/mbobakov/grpc-consul-resolver v1.5.3 // indirect
|
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/nats-io/nats.go v1.31.0 // indirect
|
github.com/nats-io/nats.go v1.37.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.6 // indirect
|
github.com/nats-io/nkeys v0.4.7 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||||
github.com/philhofer/fwd v1.1.2 // indirect
|
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
|
||||||
github.com/pion/datachannel v1.5.5 // indirect
|
github.com/pion/datachannel v1.5.5 // indirect
|
||||||
github.com/pion/dtls/v2 v2.2.9 // indirect
|
github.com/pion/dtls/v2 v2.2.9 // indirect
|
||||||
github.com/pion/ice/v2 v2.3.13 // indirect
|
github.com/pion/ice/v2 v2.3.13 // indirect
|
||||||
@@ -110,43 +95,42 @@ require (
|
|||||||
github.com/pion/transport/v2 v2.2.4 // indirect
|
github.com/pion/transport/v2 v2.2.4 // indirect
|
||||||
github.com/pion/turn/v2 v2.1.3 // indirect
|
github.com/pion/turn/v2 v2.1.3 // indirect
|
||||||
github.com/pion/webrtc/v3 v3.2.28 // indirect
|
github.com/pion/webrtc/v3 v3.2.28 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/prometheus/client_golang v1.19.0 // indirect
|
github.com/prometheus/client_golang v1.19.0 // indirect
|
||||||
github.com/prometheus/client_model v0.5.0 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
github.com/prometheus/common v0.48.0 // indirect
|
github.com/prometheus/common v0.52.3 // indirect
|
||||||
github.com/prometheus/procfs v0.12.0 // indirect
|
github.com/prometheus/procfs v0.13.0 // indirect
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.1.0 // indirect
|
github.com/puzpuzpuz/xsync/v3 v3.1.0 // indirect
|
||||||
github.com/redis/go-redis/v9 v9.5.1 // indirect
|
github.com/redis/go-redis/v9 v9.7.3 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
github.com/sagikazarmark/locafero v0.6.0 // indirect
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 // indirect
|
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/spf13/afero v1.11.0 // indirect
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
github.com/spf13/cast v1.6.0 // indirect
|
github.com/spf13/cast v1.7.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/stretchr/testify v1.9.0 // indirect
|
github.com/stretchr/testify v1.9.0 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/thoas/go-funk v0.9.3 // indirect
|
github.com/thoas/go-funk v0.9.3 // indirect
|
||||||
github.com/tinylib/msgp v1.1.8 // indirect
|
github.com/tinylib/msgp v1.2.5 // indirect
|
||||||
github.com/twitchtv/twirp v8.1.3+incompatible // indirect
|
github.com/twitchtv/twirp v8.1.3+incompatible // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.52.0 // indirect
|
github.com/valyala/fasthttp v1.59.0 // indirect
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
|
||||||
github.com/zeebo/xxh3 v1.0.2 // indirect
|
github.com/zeebo/xxh3 v1.0.2 // indirect
|
||||||
go.uber.org/atomic v1.11.0 // indirect
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
|
go.uber.org/mock v0.4.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/zap v1.27.0 // indirect
|
go.uber.org/zap v1.27.0 // indirect
|
||||||
go.uber.org/zap/exp v0.2.0 // indirect
|
go.uber.org/zap/exp v0.2.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
|
golang.org/x/crypto v0.33.0 // indirect
|
||||||
golang.org/x/net v0.26.0 // indirect
|
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
|
||||||
golang.org/x/sync v0.7.0 // indirect
|
golang.org/x/net v0.35.0 // indirect
|
||||||
golang.org/x/sys v0.21.0 // indirect
|
golang.org/x/sync v0.11.0 // indirect
|
||||||
golang.org/x/text v0.16.0 // indirect
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect
|
golang.org/x/text v0.22.0 // indirect
|
||||||
google.golang.org/protobuf v1.34.2 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.4 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
gorm.io/driver/mysql v1.5.2 // indirect
|
gorm.io/driver/mysql v1.5.7 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
413
go.sum
413
go.sum
@@ -1,38 +1,29 @@
|
|||||||
git.solsynth.dev/hydrogen/dealer v0.0.0-20240716024524-cfb73fde1951 h1:RgZJK4PXhrjhUX75BclBCg2xfetMkswcMwMqfTBtryQ=
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
git.solsynth.dev/hydrogen/dealer v0.0.0-20240716024524-cfb73fde1951/go.mod h1:eZwAwP7ahL7TO8GWBlYFYDdjlna+8zHYbDfNabnuUEU=
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
git.solsynth.dev/hydrogen/paperclip v0.0.0-20240622051057-0f56dba45745 h1:40BUsQMNXjqHyytkyF9py1HjTAWlRgO6R57YXUrHNy4=
|
git.solsynth.dev/hypernet/nexus v0.0.0-20250329053929-488793a2dc56 h1:SnT9NVcXQ1WDka9kKAA+lH/r2UJouND7FDugu4ZZwLc=
|
||||||
git.solsynth.dev/hydrogen/paperclip v0.0.0-20240622051057-0f56dba45745/go.mod h1:FsQGSLTl0gvo+9Jmbot02S72suyF9tFTrzDj70Xhifo=
|
git.solsynth.dev/hypernet/nexus v0.0.0-20250329053929-488793a2dc56/go.mod h1:5tk62VQ1DcbR0EAN2jAOqYxHiegUPEC805JlfQ/G19I=
|
||||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
git.solsynth.dev/hypernet/nexus v0.0.0-20250329075932-d5422ab5b04c h1:XgdTgJxSAQuCbiG15hN5pY6chzcz8sX3Onm2itS+Ufs=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
git.solsynth.dev/hypernet/nexus v0.0.0-20250329075932-d5422ab5b04c/go.mod h1:5tk62VQ1DcbR0EAN2jAOqYxHiegUPEC805JlfQ/G19I=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
git.solsynth.dev/hypernet/paperclip v0.0.0-20250310151112-1d866f317f47 h1:fvu+bNKPTNtQocssnKbEZ66MqR0iBfAxY3HwlqnmYyE=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
git.solsynth.dev/hypernet/paperclip v0.0.0-20250310151112-1d866f317f47/go.mod h1:jvxq2qftz2v72x+24+cTFJdQKr9eHQTdk3KVR7cx36s=
|
||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
git.solsynth.dev/hypernet/passport v0.0.0-20250315083747-32e91e26013c h1:XB8EBX34WB2skmjaVFot5IlxKF2qFZ2SueG/Y9SiJ6Y=
|
||||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
git.solsynth.dev/hypernet/passport v0.0.0-20250315083747-32e91e26013c/go.mod h1:k7MZQWYBpxlk3g9bx0HTh5C3m+MG/wr0hAiRM/VyAqs=
|
||||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
git.solsynth.dev/hypernet/pusher v0.0.0-20250216145944-5fb769823a88 h1:2HEENe9KUrdaJeNBzx9lsuXQGyzWqCgnLTKQnr8xFr8=
|
||||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
git.solsynth.dev/hypernet/pusher v0.0.0-20250216145944-5fb769823a88/go.mod h1:ildzMtLagNsLK0Rkw4Hgk2TrrwqZnjwJIUx0MNZwcDY=
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||||
github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=
|
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||||
github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
|
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
|
||||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
|
||||||
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
|
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
|
||||||
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
|
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
|
||||||
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
|
||||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
|
||||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
|
||||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -44,31 +35,26 @@ github.com/eapache/channels v1.1.0 h1:F1taHcn7/F0i8DYqKXJnyhJcVpp2kgFcNePxXtnyu4
|
|||||||
github.com/eapache/channels v1.1.0/go.mod h1:jMm2qB5Ubtg9zLd+inMZd2/NUvXgzmWXsDaLyQIGfH0=
|
github.com/eapache/channels v1.1.0/go.mod h1:jMm2qB5Ubtg9zLd+inMZd2/NUvXgzmWXsDaLyQIGfH0=
|
||||||
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
|
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
|
||||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||||
github.com/fasthttp/websocket v1.5.8 h1:k5DpirKkftIF/w1R8ZzjSgARJrs54Je9YJK37DL/Ah8=
|
github.com/eko/gocache/lib/v4 v4.2.0 h1:MNykyi5Xw+5Wu3+PUrvtOCaKSZM1nUSVftbzmeC7Yuw=
|
||||||
github.com/fasthttp/websocket v1.5.8/go.mod h1:d08g8WaT6nnyvg9uMm8K9zMYyDjfKyj3170AtPRuVU0=
|
github.com/eko/gocache/lib/v4 v4.2.0/go.mod h1:7ViVmbU+CzDHzRpmB4SXKyyzyuJ8A3UW3/cszpcqB4M=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/eko/gocache/store/redis/v4 v4.2.2 h1:Thw31fzGuH3WzJywsdbMivOmP550D6JS7GDHhvCJPA0=
|
||||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
github.com/eko/gocache/store/redis/v4 v4.2.2/go.mod h1:LaTxLKx9TG/YUEybQvPMij++D7PBTIJ4+pzvk0ykz0w=
|
||||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||||
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
github.com/frostbyte73/core v0.0.10 h1:D4DQXdPb8ICayz0n75rs4UYTXrUSdxzUfeleuNJORsU=
|
github.com/frostbyte73/core v0.0.10 h1:D4DQXdPb8ICayz0n75rs4UYTXrUSdxzUfeleuNJORsU=
|
||||||
github.com/frostbyte73/core v0.0.10/go.mod h1:XsOGqrqe/VEV7+8vJ+3a8qnCIXNbKsoEiu/czs7nrcU=
|
github.com/frostbyte73/core v0.0.10/go.mod h1:XsOGqrqe/VEV7+8vJ+3a8qnCIXNbKsoEiu/czs7nrcU=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||||
github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0=
|
github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0=
|
||||||
github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU=
|
github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU=
|
||||||
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
|
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
|
||||||
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
|
||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
@@ -76,38 +62,30 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
|||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/form v3.1.4+incompatible h1:lvKiHVxE2WvzDIoyMnWcjyiBxKt2+uFJyZcPYWsLnjI=
|
|
||||||
github.com/go-playground/form v3.1.4+incompatible/go.mod h1:lhcKXfTuhRtIZCIKUeJ0b5F207aeQCPbZU09ScKjwWg=
|
|
||||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74=
|
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
|
||||||
github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
|
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||||
|
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gofiber/contrib/websocket v1.3.0 h1:XADFAGorer1VJ1bqC4UkCjqS37kwRTV0415+050NrMk=
|
github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI=
|
||||||
github.com/gofiber/contrib/websocket v1.3.0/go.mod h1:xguaOzn2ZZ759LavtosEP+rcxIgBEE/rdumPINhR+Xo=
|
github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
|
||||||
github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM=
|
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||||
github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc=
|
|
||||||
github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
|
|
||||||
github.com/gofiber/template/html/v2 v2.1.1 h1:QEy3O3EBkvwDthy5bXVGUseOyO6ldJoiDxlF4+MJiV8=
|
|
||||||
github.com/gofiber/template/html/v2 v2.1.1/go.mod h1:2G0GHHOUx70C1LDncoBpe4T6maQbNa4x1CVNFW0wju0=
|
|
||||||
github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
|
|
||||||
github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
|
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
|
||||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||||
|
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
@@ -116,9 +94,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
|||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
@@ -133,84 +110,29 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||||
github.com/hashicorp/consul/api v1.29.1 h1:UEwOjYJrd3lG1x5w7HxDRMGiAUPrb3f103EoeKuuEcc=
|
|
||||||
github.com/hashicorp/consul/api v1.29.1/go.mod h1:lumfRkY/coLuqMICkI7Fh3ylMG31mQSRZyef2c5YvJI=
|
|
||||||
github.com/hashicorp/consul/proto-public v0.6.1 h1:+uzH3olCrksXYWAYHKqK782CtK9scfqH+Unlw3UHhCg=
|
|
||||||
github.com/hashicorp/consul/proto-public v0.6.1/go.mod h1:cXXbOg74KBNGajC+o8RlA502Esf0R9prcoJgiOX/2Tg=
|
|
||||||
github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg=
|
|
||||||
github.com/hashicorp/consul/sdk v0.16.1/go.mod h1:fSXvwxB2hmh1FMZCNl6PwX0Q/1wdWtHJcZ7Ea5tns0s=
|
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
|
||||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
|
||||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
|
||||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
|
||||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
|
||||||
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
|
|
||||||
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
|
||||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
|
||||||
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
|
|
||||||
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
|
||||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
|
||||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
|
||||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
|
||||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
|
||||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
|
||||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
|
||||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
|
||||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
|
||||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
|
||||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
|
||||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
|
||||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
|
||||||
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
|
|
||||||
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
|
||||||
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
|
|
||||||
github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM=
|
|
||||||
github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=
|
|
||||||
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
|
|
||||||
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
|
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
github.com/jackc/pgx/v5 v5.5.1 h1:5I9etrGkLrN+2XPCsi6XLlV5DITbSL/xBZdmAxFcXPI=
|
github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs=
|
||||||
github.com/jackc/pgx/v5 v5.5.1/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
|
github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA=
|
||||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
|
|
||||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
|
|
||||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
|
||||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
|
||||||
github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw=
|
github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw=
|
||||||
github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc=
|
github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc=
|
||||||
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
@@ -218,8 +140,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c=
|
github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c=
|
||||||
github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y=
|
github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y=
|
||||||
github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 h1:jm09419p0lqTkDaKb5iXdynYrzB84ErPPO4LbRASk58=
|
github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 h1:jm09419p0lqTkDaKb5iXdynYrzB84ErPPO4LbRASk58=
|
||||||
@@ -236,52 +158,30 @@ github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
|
|||||||
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
|
||||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
|
||||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
|
||||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
|
||||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
|
||||||
github.com/mbobakov/grpc-consul-resolver v1.5.3 h1:xL7nJm8qCvxgHMqlnF4naXruBUoHqfUWORl3UmwKByU=
|
|
||||||
github.com/mbobakov/grpc-consul-resolver v1.5.3/go.mod h1:0wN8+McBocuk5mO9xlAfrmBSothm7sps43bFGubg0m4=
|
|
||||||
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
|
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
|
||||||
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
|
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
|
||||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
|
||||||
github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=
|
|
||||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
|
||||||
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
|
|
||||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE=
|
||||||
github.com/nats-io/nats.go v1.31.0 h1:/WFBHEc/dOKBF6qf1TZhrdEfTmOZ5JzdJ+Y3m6Y/p7E=
|
github.com/nats-io/nats.go v1.37.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
||||||
github.com/nats-io/nats.go v1.31.0/go.mod h1:di3Bm5MLsoB4Bx61CBTsxuarI36WbhAwOm8QrW39+i8=
|
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
||||||
github.com/nats-io/nkeys v0.4.6 h1:IzVe95ru2CT6ta874rt9saQRkWfe2nFj1NtvYSLqMzY=
|
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
||||||
github.com/nats-io/nkeys v0.4.6/go.mod h1:4DxZNzenSVd1cYQoAa8948QY3QDjrHfcfVADymtkpts=
|
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
@@ -293,13 +193,10 @@ github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042
|
|||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY=
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
|
||||||
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
|
|
||||||
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
|
|
||||||
github.com/pion/datachannel v1.5.5 h1:10ef4kwdjije+M9d7Xm9im2Y3O6A6ccQb0zcqZcJew8=
|
github.com/pion/datachannel v1.5.5 h1:10ef4kwdjije+M9d7Xm9im2Y3O6A6ccQb0zcqZcJew8=
|
||||||
github.com/pion/datachannel v1.5.5/go.mod h1:iMz+lECmfdCMqFRhXhcA/219B0SQlbpoR2V118yimL0=
|
github.com/pion/datachannel v1.5.5/go.mod h1:iMz+lECmfdCMqFRhXhcA/219B0SQlbpoR2V118yimL0=
|
||||||
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
|
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
|
||||||
@@ -344,38 +241,23 @@ github.com/pion/turn/v2 v2.1.3 h1:pYxTVWG2gpC97opdRc5IGsQ1lJ9O/IlNhkzj7MMrGAA=
|
|||||||
github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
|
github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
|
||||||
github.com/pion/webrtc/v3 v3.2.28 h1:ienStxZ6HcjtH2UlmnFpMM0loENiYjaX437uIUpQSKo=
|
github.com/pion/webrtc/v3 v3.2.28 h1:ienStxZ6HcjtH2UlmnFpMM0loENiYjaX437uIUpQSKo=
|
||||||
github.com/pion/webrtc/v3 v3.2.28/go.mod h1:PNRCEuQlibrmuBhOTnol9j6KkIbUG11aHLEfNpUYey0=
|
github.com/pion/webrtc/v3 v3.2.28/go.mod h1:PNRCEuQlibrmuBhOTnol9j6KkIbUG11aHLEfNpUYey0=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
|
||||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
|
||||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
|
||||||
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
|
||||||
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
||||||
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/common v0.52.3 h1:5f8uj6ZwHSscOGNdIQg6OiZv/ybiK2CO2q2drVZAQSA=
|
||||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
github.com/prometheus/common v0.52.3/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U=
|
||||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o=
|
||||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g=
|
||||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
|
||||||
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
|
||||||
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
|
||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
|
||||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
|
||||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
|
||||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.1.0 h1:EewKT7/LNac5SLiEblJeUu8z5eERHrmRLnMQL2d7qX4=
|
github.com/puzpuzpuz/xsync/v3 v3.1.0 h1:EewKT7/LNac5SLiEblJeUu8z5eERHrmRLnMQL2d7qX4=
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.1.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
github.com/puzpuzpuz/xsync/v3 v3.1.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
||||||
github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8=
|
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
|
||||||
github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
@@ -384,48 +266,35 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG
|
|||||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
|
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||||
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
|
||||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
|
||||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||||
github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA=
|
github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
|
||||||
github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
|
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
|
||||||
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 h1:KanIMPX0QdEdB4R3CiimCAbxFrhB3j7h0/OvpYGVQa8=
|
|
||||||
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
|
|
||||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
|
||||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
|
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||||
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
|
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
@@ -434,36 +303,46 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
|
|||||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw=
|
github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw=
|
||||||
github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
|
github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
|
||||||
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
|
github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po=
|
||||||
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
|
github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
|
||||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
|
||||||
github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU=
|
github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU=
|
||||||
github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A=
|
github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0=
|
github.com/valyala/fasthttp v1.59.0 h1:Qu0qYHfXvPk1mSLNqcFtEk6DpxgA26hy6bmydotDpRI=
|
||||||
github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ=
|
github.com/valyala/fasthttp v1.59.0/go.mod h1:GTxNb9Bc6r2a9D0TWNSPwDz78UxnTGBViY3xZNEqyYU=
|
||||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
||||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||||
|
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
||||||
|
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
||||||
|
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
|
||||||
|
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
|
||||||
|
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
|
||||||
|
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
||||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
|
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||||
|
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
go.uber.org/zap/exp v0.2.0 h1:FtGenNNeCATRB3CmB/yEUnjEFeJWpB/pMcy7e2bKPYs=
|
go.uber.org/zap/exp v0.2.0 h1:FtGenNNeCATRB3CmB/yEUnjEFeJWpB/pMcy7e2bKPYs=
|
||||||
go.uber.org/zap/exp v0.2.0/go.mod h1:t0gqAIdh1MfKv9EwN/dLwfZnJxe9ITAZN78HEWPFWDQ=
|
go.uber.org/zap/exp v0.2.0/go.mod h1:t0gqAIdh1MfKv9EwN/dLwfZnJxe9ITAZN78HEWPFWDQ=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
@@ -472,82 +351,59 @@ golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIi
|
|||||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
|
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
|
||||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
|
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@@ -558,12 +414,11 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
@@ -572,34 +427,31 @@ golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
|||||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287 h1:J1H9f+LEdWAfHcez/4cvaVBox7cOYT+IU6rgqj5x++8=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
|
||||||
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
|
||||||
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
|
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
@@ -608,38 +460,33 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
|
|||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
|
||||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
|
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
|
||||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gorm.io/datatypes v1.2.0 h1:5YT+eokWdIxhJgWHdrb2zYUimyk0+TaFth+7a0ybzco=
|
gorm.io/datatypes v1.2.4 h1:uZmGAcK/QZ0uyfCuVg0VQY1ZmV9h1fuG0tMwKByO1z4=
|
||||||
gorm.io/datatypes v1.2.0/go.mod h1:o1dh0ZvjIjhH/bngTpypG6lVRJ5chTBxE09FH/71k04=
|
gorm.io/datatypes v1.2.4/go.mod h1:f4BsLcFAX67szSv8svwLRjklArSHAvHLeE3pXAS5DZI=
|
||||||
gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs=
|
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
|
||||||
gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8=
|
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
||||||
gorm.io/driver/postgres v1.5.4 h1:Iyrp9Meh3GmbSuyIAGyjkN+n9K+GHX9b9MqsTL4EJCo=
|
gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8=
|
||||||
gorm.io/driver/postgres v1.5.4/go.mod h1:Bgo89+h0CRcdA33Y6frlaHHVuTdOf87pmyzwW9C/BH0=
|
gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
|
||||||
gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU=
|
gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU=
|
||||||
gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
|
gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
|
||||||
gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
|
gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
|
||||||
gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig=
|
gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig=
|
||||||
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||||
gorm.io/gorm v1.25.6 h1:V92+vVda1wEISSOMtodHVRcUIOPYa2tgQtyF+DfFx+A=
|
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||||
gorm.io/gorm v1.25.6/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/models"
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/models"
|
||||||
|
authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
var DatabaseAutoActionRange = []any{
|
var AutoMaintainRange = []any{
|
||||||
&models.Account{},
|
&authm.Realm{},
|
||||||
&models.Realm{},
|
|
||||||
&models.Channel{},
|
&models.Channel{},
|
||||||
&models.ChannelMember{},
|
&models.ChannelMember{},
|
||||||
&models.Call{},
|
&models.Call{},
|
||||||
@@ -15,7 +15,7 @@ var DatabaseAutoActionRange = []any{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RunMigration(source *gorm.DB) error {
|
func RunMigration(source *gorm.DB) error {
|
||||||
if err := source.AutoMigrate(DatabaseAutoActionRange...); err != nil {
|
if err := source.AutoMigrate(AutoMaintainRange...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +1,31 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/gap"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cruda"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"gorm.io/driver/postgres"
|
"gorm.io/driver/postgres"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/logger"
|
"gorm.io/gorm/logger"
|
||||||
"gorm.io/gorm/schema"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var C *gorm.DB
|
var C *gorm.DB
|
||||||
|
|
||||||
func NewSource() error {
|
func NewGorm() error {
|
||||||
var err error
|
dsn, err := cruda.NewCrudaConn(gap.Nx).AllocDatabase("messaging")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to alloc database from nexus: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
dialector := postgres.Open(viper.GetString("database.dsn"))
|
C, err = gorm.Open(postgres.Open(dsn), &gorm.Config{Logger: logger.New(&log.Logger, logger.Config{
|
||||||
C, err = gorm.Open(dialector, &gorm.Config{NamingStrategy: schema.NamingStrategy{
|
|
||||||
TablePrefix: viper.GetString("database.prefix"),
|
|
||||||
}, Logger: logger.New(&log.Logger, logger.Config{
|
|
||||||
Colorful: true,
|
Colorful: true,
|
||||||
IgnoreRecordNotFoundError: true,
|
IgnoreRecordNotFoundError: true,
|
||||||
LogLevel: lo.Ternary(viper.GetBool("debug.database"), logger.Info, logger.Silent),
|
LogLevel: lo.Ternary(viper.GetBool("debug.database"), logger.Info, logger.Silent),
|
||||||
})})
|
}), DisableForeignKeyConstraintWhenMigrating: true})
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
package pkg
|
|
||||||
|
|
||||||
import "embed"
|
|
||||||
|
|
||||||
//go:embed views/*
|
|
||||||
var FS embed.FS
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package gap
|
|
||||||
|
|
||||||
import "net"
|
|
||||||
|
|
||||||
func GetOutboundIP() (net.IP, error) {
|
|
||||||
conn, err := net.Dial("udp", "1.1.1.1:80")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else {
|
|
||||||
defer conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
|
||||||
return localAddr.IP, nil
|
|
||||||
}
|
|
||||||
@@ -2,41 +2,61 @@ package gap
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.solsynth.dev/hydrogen/dealer/pkg/hyper"
|
|
||||||
"git.solsynth.dev/hydrogen/dealer/pkg/proto"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cachekit"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||||
|
"git.solsynth.dev/hypernet/pusher/pkg/pushkit/pushcon"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
var H *hyper.HyperConn
|
var (
|
||||||
|
Nx *nex.Conn
|
||||||
|
Px *pushcon.Conn
|
||||||
|
Ca *cachekit.Conn
|
||||||
|
)
|
||||||
|
|
||||||
func RegisterService() error {
|
func InitializeToNexus() error {
|
||||||
grpcBind := strings.SplitN(viper.GetString("grpc_bind"), ":", 2)
|
grpcBind := strings.SplitN(viper.GetString("grpc_bind"), ":", 2)
|
||||||
httpBind := strings.SplitN(viper.GetString("bind"), ":", 2)
|
httpBind := strings.SplitN(viper.GetString("bind"), ":", 2)
|
||||||
|
|
||||||
outboundIp, _ := GetOutboundIP()
|
outboundIp, _ := nex.GetOutboundIP()
|
||||||
|
|
||||||
grpcOutbound := fmt.Sprintf("%s:%s", outboundIp, grpcBind[1])
|
grpcOutbound := fmt.Sprintf("%s:%s", outboundIp, grpcBind[1])
|
||||||
httpOutbound := fmt.Sprintf("%s:%s", outboundIp, httpBind[1])
|
httpOutbound := fmt.Sprintf("%s:%s", outboundIp, httpBind[1])
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
H, err = hyper.NewHyperConn(viper.GetString("dealer.addr"), &proto.ServiceInfo{
|
Nx, err = nex.NewNexusConn(viper.GetString("nexus_addr"), &proto.ServiceInfo{
|
||||||
Id: viper.GetString("id"),
|
Id: viper.GetString("id"),
|
||||||
Type: hyper.ServiceTypeMessagingProvider,
|
Type: "im",
|
||||||
Label: "Messaging",
|
Label: "Messaging",
|
||||||
GrpcAddr: grpcOutbound,
|
GrpcAddr: grpcOutbound,
|
||||||
HttpAddr: &httpOutbound,
|
HttpAddr: lo.ToPtr("http://" + httpOutbound + "/api"),
|
||||||
})
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
go func() {
|
go func() {
|
||||||
err := H.KeepRegisterService()
|
err := Nx.RunRegistering()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("An error occurred while registering service...")
|
log.Error().Err(err).Msg("An error occurred while registering service...")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Px, err = pushcon.NewConn(Nx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error during initialize pushcon: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ca, err = cachekit.NewConn(Nx, 3*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error during initialize cachekit: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ package grpc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
health "google.golang.org/grpc/health/grpc_health_v1"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
health "google.golang.org/grpc/health/grpc_health_v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (v *Server) Check(ctx context.Context, request *health.HealthCheckRequest) (*health.HealthCheckResponse, error) {
|
func (v *Server) Check(ctx context.Context, request *health.HealthCheckRequest) (*health.HealthCheckResponse, error) {
|
||||||
|
|||||||
@@ -1,33 +1,41 @@
|
|||||||
package grpc
|
package grpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.solsynth.dev/hydrogen/paperclip/pkg/proto"
|
"net"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
health "google.golang.org/grpc/health/grpc_health_v1"
|
health "google.golang.org/grpc/health/grpc_health_v1"
|
||||||
"google.golang.org/grpc/reflection"
|
"google.golang.org/grpc/reflection"
|
||||||
"net"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
proto.UnimplementedAttachmentsServer
|
proto.UnimplementedStreamServiceServer
|
||||||
|
proto.UnimplementedDirectoryServiceServer
|
||||||
|
|
||||||
|
srv *grpc.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
var S *grpc.Server
|
func NewGrpc() *Server {
|
||||||
|
server := &Server{
|
||||||
|
srv: grpc.NewServer(),
|
||||||
|
}
|
||||||
|
|
||||||
func NewGRPC() {
|
health.RegisterHealthServer(server.srv, server)
|
||||||
S = grpc.NewServer()
|
proto.RegisterStreamServiceServer(server.srv, server)
|
||||||
|
proto.RegisterDirectoryServiceServer(server.srv, server)
|
||||||
|
|
||||||
health.RegisterHealthServer(S, &Server{})
|
reflection.Register(server.srv)
|
||||||
|
|
||||||
reflection.Register(S)
|
return server
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListenGRPC() error {
|
func (v *Server) Listen() error {
|
||||||
listener, err := net.Listen("tcp", viper.GetString("grpc_bind"))
|
listener, err := net.Listen("tcp", viper.GetString("grpc_bind"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return S.Serve(listener)
|
return v.srv.Serve(listener)
|
||||||
}
|
}
|
||||||
|
|||||||
69
pkg/internal/grpc/services.go
Normal file
69
pkg/internal/grpc/services.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/models"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/services"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v *Server) BroadcastEvent(ctx context.Context, in *proto.EventInfo) (*proto.EventResponse, error) {
|
||||||
|
log.Debug().Str("event", in.GetEvent()).
|
||||||
|
Msg("Got a broadcasting event...")
|
||||||
|
|
||||||
|
switch in.GetEvent() {
|
||||||
|
// Clear the subscribed channel
|
||||||
|
case "ws.client.unregister":
|
||||||
|
// Update user last seen at
|
||||||
|
data := nex.DecodeMap(in.GetData())
|
||||||
|
id := data["id"].(string)
|
||||||
|
services.UnsubscribeAllWithClient(id)
|
||||||
|
log.Info().Str("client", id).Msg("Client unregistered, cleaning up subscribed channels...")
|
||||||
|
// Account recycle
|
||||||
|
case "deletion":
|
||||||
|
data := nex.DecodeMap(in.GetData())
|
||||||
|
resType, ok := data["type"].(string)
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch resType {
|
||||||
|
case "account":
|
||||||
|
var data struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
}
|
||||||
|
if err := jsoniter.Unmarshal(in.GetData(), &data); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
tx := database.C.Begin()
|
||||||
|
for _, model := range database.AutoMaintainRange {
|
||||||
|
switch model.(type) {
|
||||||
|
default:
|
||||||
|
tx.Delete(model, "account_id = ?", data.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tx.Commit()
|
||||||
|
case "realm":
|
||||||
|
var data struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
}
|
||||||
|
if err := jsoniter.Unmarshal(in.GetData(), &data); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
var channels []models.Channel
|
||||||
|
if err := database.C.Where("realm_id = ?", data.ID).Find(&channels).Error; err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for _, channel := range channels {
|
||||||
|
_ = services.DeleteChannel(channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &proto.EventResponse{}, nil
|
||||||
|
}
|
||||||
111
pkg/internal/grpc/stream.go
Normal file
111
pkg/internal/grpc/stream.go
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
package grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/gap"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/web/exts"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v *Server) PushStream(_ context.Context, request *proto.PushStreamRequest) (*proto.PushStreamResponse, error) {
|
||||||
|
sc := proto.NewStreamServiceClient(gap.Nx.GetNexusGrpcConn())
|
||||||
|
|
||||||
|
var in nex.WebSocketPackage
|
||||||
|
if err := jsoniter.Unmarshal(request.GetBody(), &in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch in.Action {
|
||||||
|
case "status.typing":
|
||||||
|
var data struct {
|
||||||
|
ChannelID uint `json:"channel_id" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := jsoniter.Unmarshal(in.RawPayload(), &data)
|
||||||
|
if err == nil {
|
||||||
|
err = exts.ValidateStruct(data)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
_, _ = sc.PushStream(context.Background(), &proto.PushStreamRequest{
|
||||||
|
ClientId: request.ClientId,
|
||||||
|
Body: nex.WebSocketPackage{
|
||||||
|
Action: "error",
|
||||||
|
Message: fmt.Sprintf("unable parse payload: %v", err),
|
||||||
|
}.Marshal(),
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
err = services.SetTypingStatus(data.ChannelID, uint(request.GetUserId()))
|
||||||
|
if err != nil {
|
||||||
|
_, _ = sc.PushStream(context.Background(), &proto.PushStreamRequest{
|
||||||
|
ClientId: request.ClientId,
|
||||||
|
Body: nex.WebSocketPackage{
|
||||||
|
Action: "error",
|
||||||
|
Message: fmt.Sprintf("unable boardcast status: %v", err),
|
||||||
|
}.Marshal(),
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case "events.subscribe", "events.unsubscribe", "events.unsubscribeAll":
|
||||||
|
var data struct {
|
||||||
|
ChannelID uint `json:"channel_id" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := jsoniter.Unmarshal(in.RawPayload(), &data)
|
||||||
|
if err == nil {
|
||||||
|
err = exts.ValidateStruct(data)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
_, _ = sc.PushStream(context.Background(), &proto.PushStreamRequest{
|
||||||
|
ClientId: request.ClientId,
|
||||||
|
Body: nex.WebSocketPackage{
|
||||||
|
Action: "error",
|
||||||
|
Message: fmt.Sprintf("unable parse payload: %v", err),
|
||||||
|
}.Marshal(),
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
action := strings.Split(in.Action, ".")[1]
|
||||||
|
switch action {
|
||||||
|
case "subscribe":
|
||||||
|
services.SubscribeChannel(uint(request.GetUserId()), data.ChannelID, request.GetClientId())
|
||||||
|
case "unsubscribe":
|
||||||
|
services.UnsubscribeChannel(uint(request.GetUserId()), data.ChannelID)
|
||||||
|
case "unsubscribeAll":
|
||||||
|
services.UnsubscribeAll(uint(request.GetUserId()))
|
||||||
|
}
|
||||||
|
case "events.read":
|
||||||
|
var data struct {
|
||||||
|
ChannelMemberID uint `json:"channel_member_id" validate:"required"`
|
||||||
|
EventID uint `json:"event_id" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := jsoniter.Unmarshal(in.RawPayload(), &data)
|
||||||
|
if err == nil {
|
||||||
|
err = exts.ValidateStruct(data)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
_, _ = sc.PushStream(context.Background(), &proto.PushStreamRequest{
|
||||||
|
ClientId: request.ClientId,
|
||||||
|
Body: nex.WebSocketPackage{
|
||||||
|
Action: "error",
|
||||||
|
Message: fmt.Sprintf("unable parse payload: %v", err),
|
||||||
|
}.Marshal(),
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// WARN We trust the user here, so we don't need to check if the channel member is valid for performance
|
||||||
|
services.SetReadingAnchor(data.ChannelMemberID, data.EventID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &proto.PushStreamResponse{}, nil
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
// Account profiles basically fetched from Hydrogen.Identity
|
|
||||||
// But cached at here for better usage
|
|
||||||
// At the same time, this model can make relations between local models
|
|
||||||
type Account struct {
|
|
||||||
BaseModel
|
|
||||||
|
|
||||||
Name string `json:"name"`
|
|
||||||
Nick string `json:"nick"`
|
|
||||||
Avatar string `json:"avatar"`
|
|
||||||
Banner string `json:"banner"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
EmailAddress string `json:"email_address"`
|
|
||||||
PowerLevel int `json:"power_level"`
|
|
||||||
Channels []Channel `json:"channels"`
|
|
||||||
ExternalID uint `json:"external_id"`
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"gorm.io/datatypes"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
type JSONMap = datatypes.JSONType[map[string]any]
|
|
||||||
|
|
||||||
type BaseModel struct {
|
|
||||||
ID uint `json:"id" gorm:"primaryKey"`
|
|
||||||
CreatedAt time.Time `json:"created_at"`
|
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
|
||||||
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"`
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cruda"
|
||||||
|
"github.com/livekit/protocol/livekit"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type Call struct {
|
type Call struct {
|
||||||
BaseModel
|
cruda.BaseModel
|
||||||
|
|
||||||
EndedAt *time.Time `json:"ended_at"`
|
EndedAt *time.Time `json:"ended_at"`
|
||||||
|
|
||||||
@@ -12,4 +16,6 @@ type Call struct {
|
|||||||
ChannelID uint `json:"channel_id"`
|
ChannelID uint `json:"channel_id"`
|
||||||
Founder ChannelMember `json:"founder"`
|
Founder ChannelMember `json:"founder"`
|
||||||
Channel Channel `json:"channel"`
|
Channel Channel `json:"channel"`
|
||||||
|
|
||||||
|
Participants []*livekit.ParticipantInfo `json:"participants" gorm:"-"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cruda"
|
||||||
|
authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
)
|
||||||
|
|
||||||
type ChannelType = uint8
|
type ChannelType = uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -8,7 +15,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Channel struct {
|
type Channel struct {
|
||||||
BaseModel
|
cruda.BaseModel
|
||||||
|
|
||||||
Alias string `json:"alias"`
|
Alias string `json:"alias"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@@ -17,12 +24,22 @@ type Channel struct {
|
|||||||
Messages []Event `json:"messages"`
|
Messages []Event `json:"messages"`
|
||||||
Calls []Call `json:"calls"`
|
Calls []Call `json:"calls"`
|
||||||
Type ChannelType `json:"type"`
|
Type ChannelType `json:"type"`
|
||||||
Account Account `json:"account"`
|
|
||||||
AccountID uint `json:"account_id"`
|
AccountID uint `json:"account_id"`
|
||||||
IsEncrypted bool `json:"is_encrypted"`
|
IsPublic bool `json:"is_public"`
|
||||||
|
IsCommunity bool `json:"is_community"`
|
||||||
|
|
||||||
Realm Realm `json:"realm"`
|
Realm *authm.Realm `json:"realm" gorm:"-"`
|
||||||
RealmID *uint `json:"realm_id"`
|
RealmID *uint `json:"realm_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Channel) DisplayText() string {
|
||||||
|
if v.Type == ChannelTypeDirect {
|
||||||
|
return "DM"
|
||||||
|
}
|
||||||
|
if v.Realm != nil {
|
||||||
|
return fmt.Sprintf("%s, %s", v.Name, v.Realm.Name)
|
||||||
|
}
|
||||||
|
return v.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotifyLevel = int8
|
type NotifyLevel = int8
|
||||||
@@ -34,15 +51,18 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ChannelMember struct {
|
type ChannelMember struct {
|
||||||
BaseModel
|
cruda.BaseModel
|
||||||
|
|
||||||
ChannelID uint `json:"channel_id"`
|
Name string `json:"name"`
|
||||||
AccountID uint `json:"account_id"`
|
Nick string `json:"nick"`
|
||||||
Nick *string `json:"nick"`
|
Avatar *string `json:"avatar"`
|
||||||
Channel Channel `json:"channel"`
|
|
||||||
Account Account `json:"account"`
|
ChannelID uint `json:"channel_id"`
|
||||||
Notify NotifyLevel `json:"notify"`
|
AccountID uint `json:"account_id"`
|
||||||
PowerLevel int `json:"power_level"`
|
Channel Channel `json:"channel"`
|
||||||
|
Notify NotifyLevel `json:"notify"`
|
||||||
|
PowerLevel int `json:"power_level"`
|
||||||
|
ReadingAnchor *int `json:"reading_anchor"`
|
||||||
|
|
||||||
Calls []Call `json:"calls" gorm:"foreignKey:FounderID"`
|
Calls []Call `json:"calls" gorm:"foreignKey:FounderID"`
|
||||||
Events []Event `json:"events" gorm:"foreignKey:SenderID"`
|
Events []Event `json:"events" gorm:"foreignKey:SenderID"`
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import "gorm.io/datatypes"
|
import (
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cruda"
|
||||||
|
"gorm.io/datatypes"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
EventMessageNew = "messages.new"
|
EventMessageNew = "messages.new"
|
||||||
@@ -10,24 +13,27 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Event struct {
|
type Event struct {
|
||||||
BaseModel
|
cruda.BaseModel
|
||||||
|
|
||||||
Uuid string `json:"uuid"`
|
Uuid string `json:"uuid"`
|
||||||
Body datatypes.JSONMap `json:"body"`
|
Body datatypes.JSONMap `json:"body"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Channel Channel `json:"channel"`
|
Channel Channel `json:"channel"`
|
||||||
Sender ChannelMember `json:"sender"`
|
Sender ChannelMember `json:"sender"`
|
||||||
ChannelID uint `json:"channel_id"`
|
QuoteEventID *uint `json:"quote_event_id,omitempty"`
|
||||||
SenderID uint `json:"sender_id"`
|
RelatedEventID *uint `json:"related_event_id,omitempty"`
|
||||||
|
ChannelID uint `json:"channel_id"`
|
||||||
|
SenderID uint `json:"sender_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event Payloads
|
// Event Payloads
|
||||||
|
|
||||||
type EventMessageBody struct {
|
type EventMessageBody struct {
|
||||||
Text string `json:"text,omitempty"`
|
Text string `json:"text,omitempty"`
|
||||||
Algorithm string `json:"algorithm,omitempty"`
|
KeyPair string `json:"keypair_id,omitempty"`
|
||||||
Attachments []uint `json:"attachments,omitempty"`
|
Algorithm string `json:"algorithm,omitempty"`
|
||||||
QuoteEvent uint `json:"quote_event,omitempty"`
|
Attachments []string `json:"attachments,omitempty"`
|
||||||
RelatedEvent uint `json:"related_event,omitempty"`
|
QuoteEventID *uint `json:"quote_event,omitempty"`
|
||||||
RelatedUsers []uint `json:"related_users,omitempty"`
|
RelatedEventID *uint `json:"related_event,omitempty"`
|
||||||
|
RelatedUsers []uint `json:"related_users,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
// Realm profiles basically fetched from Hydrogen.Passport
|
|
||||||
// But cache at here for better usage and database relations
|
|
||||||
type Realm struct {
|
|
||||||
BaseModel
|
|
||||||
|
|
||||||
Alias string `json:"alias"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Channels []Channel `json:"channels"`
|
|
||||||
IsPublic bool `json:"is_public"`
|
|
||||||
IsCommunity bool `json:"is_community"`
|
|
||||||
ExternalID uint `json:"external_id"`
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import jsoniter "github.com/json-iterator/go"
|
|
||||||
|
|
||||||
type UnifiedCommand struct {
|
|
||||||
Action string `json:"w"`
|
|
||||||
Message string `json:"m"`
|
|
||||||
Payload any `json:"p"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func UnifiedCommandFromError(err error) UnifiedCommand {
|
|
||||||
return UnifiedCommand{
|
|
||||||
Action: "error",
|
|
||||||
Message: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v UnifiedCommand) Marshal() []byte {
|
|
||||||
data, _ := jsoniter.Marshal(v)
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
@@ -1,250 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/gap"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/server/exts"
|
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/database"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/models"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/services"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func listChannelMembers(c *fiber.Ctx) error {
|
|
||||||
alias := c.Params("channel")
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var channel models.Channel
|
|
||||||
if val, ok := c.Locals("realm").(models.Realm); ok {
|
|
||||||
channel, err = services.GetChannelWithAlias(alias, val.ID)
|
|
||||||
} else {
|
|
||||||
channel, err = services.GetChannelWithAlias(alias)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if members, err := services.ListChannelMember(channel.ID); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
||||||
} else {
|
|
||||||
return c.JSON(members)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMyChannelMembership(c *fiber.Ctx) error {
|
|
||||||
alias := c.Params("channel")
|
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
user := c.Locals("user").(models.Account)
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var channel models.Channel
|
|
||||||
if val, ok := c.Locals("realm").(models.Realm); ok {
|
|
||||||
channel, err = services.GetChannelWithAlias(alias, val.ID)
|
|
||||||
} else {
|
|
||||||
channel, err = services.GetChannelWithAlias(alias)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if member, err := services.GetChannelMember(user, channel.ID); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
} else {
|
|
||||||
return c.JSON(member)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func addChannelMember(c *fiber.Ctx) error {
|
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
user := c.Locals("user").(models.Account)
|
|
||||||
alias := c.Params("channel")
|
|
||||||
|
|
||||||
var data struct {
|
|
||||||
Target string `json:"target" validate:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := exts.BindAndValidate(c, &data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var channel models.Channel
|
|
||||||
if err := database.C.Where(&models.Channel{
|
|
||||||
Alias: alias,
|
|
||||||
}).First(&channel).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
} else if channel.Type == models.ChannelTypeDirect {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "direct message member changes was not allowed")
|
|
||||||
}
|
|
||||||
|
|
||||||
if member, err := services.GetChannelMember(user, channel.ID); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusForbidden, err.Error())
|
|
||||||
} else if member.PowerLevel < 50 {
|
|
||||||
return fiber.NewError(fiber.StatusForbidden, "you must be a moderator of a channel to add member into it")
|
|
||||||
}
|
|
||||||
|
|
||||||
var account models.Account
|
|
||||||
if err := database.C.Where(&models.Account{
|
|
||||||
Name: data.Target,
|
|
||||||
}).First(&account).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := services.AddChannelMemberWithCheck(account, channel); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
} else {
|
|
||||||
return c.SendStatus(fiber.StatusOK)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeChannelMember(c *fiber.Ctx) error {
|
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
user := c.Locals("user").(models.Account)
|
|
||||||
alias := c.Params("channel")
|
|
||||||
|
|
||||||
var data struct {
|
|
||||||
Target string `json:"target" validate:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := exts.BindAndValidate(c, &data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var channel models.Channel
|
|
||||||
if err := database.C.Where(&models.Channel{
|
|
||||||
Alias: alias,
|
|
||||||
AccountID: user.ID,
|
|
||||||
}).First(&channel).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
} else if channel.Type == models.ChannelTypeDirect {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "direct message member changes was not allowed")
|
|
||||||
}
|
|
||||||
|
|
||||||
if member, err := services.GetChannelMember(user, channel.ID); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusForbidden, err.Error())
|
|
||||||
} else if member.PowerLevel < 50 {
|
|
||||||
return fiber.NewError(fiber.StatusForbidden, "you must be a moderator of a channel to remove member into it")
|
|
||||||
}
|
|
||||||
|
|
||||||
var account models.Account
|
|
||||||
if err := database.C.Where(&models.Account{
|
|
||||||
Name: data.Target,
|
|
||||||
}).First(&account).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := services.RemoveChannelMember(account, channel); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
} else {
|
|
||||||
return c.SendStatus(fiber.StatusOK)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func editMyChannelMembership(c *fiber.Ctx) error {
|
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
user := c.Locals("user").(models.Account)
|
|
||||||
alias := c.Params("channel")
|
|
||||||
|
|
||||||
var data struct {
|
|
||||||
Nick string `json:"nick"`
|
|
||||||
NotifyLevel int8 `json:"notify_level"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := exts.BindAndValidate(c, &data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var channel models.Channel
|
|
||||||
if val, ok := c.Locals("realm").(models.Realm); ok {
|
|
||||||
channel, err = services.GetChannelWithAlias(alias, val.ID)
|
|
||||||
} else {
|
|
||||||
channel, err = services.GetChannelWithAlias(alias)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
var membership models.ChannelMember
|
|
||||||
if err := database.C.Where(&models.ChannelMember{
|
|
||||||
ChannelID: channel.ID,
|
|
||||||
AccountID: user.ID,
|
|
||||||
}).First(&membership).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
membership.Notify = data.NotifyLevel
|
|
||||||
if len(data.Nick) > 0 {
|
|
||||||
membership.Nick = &data.Nick
|
|
||||||
} else {
|
|
||||||
membership.Nick = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if membership, err := services.EditChannelMember(membership); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
} else {
|
|
||||||
return c.JSON(membership)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func joinChannel(c *fiber.Ctx) error {
|
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
user := c.Locals("user").(models.Account)
|
|
||||||
alias := c.Params("channel")
|
|
||||||
|
|
||||||
var channel models.Channel
|
|
||||||
if err := database.C.Where(&models.Channel{
|
|
||||||
Alias: alias,
|
|
||||||
}).Preload("Realm").First(&channel).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
} else if _, _, err := services.GetAvailableChannel(channel.ID, user); err == nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "you already joined the channel")
|
|
||||||
} else if channel.RealmID == nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "you was impossible to join a channel without related realm")
|
|
||||||
}
|
|
||||||
|
|
||||||
if realm, err := services.GetRealmWithExtID(channel.Realm.ExternalID); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("invalid channel, related realm was not found: %v", err))
|
|
||||||
} else if _, err := services.GetRealmMember(realm.ExternalID, user.ExternalID); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("you are not a part of the realm: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := services.AddChannelMember(user, channel); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
} else {
|
|
||||||
return c.SendStatus(fiber.StatusOK)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func leaveChannel(c *fiber.Ctx) error {
|
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
user := c.Locals("user").(models.Account)
|
|
||||||
alias := c.Params("channel")
|
|
||||||
|
|
||||||
var channel models.Channel
|
|
||||||
if err := database.C.Where(&models.Channel{
|
|
||||||
Alias: alias,
|
|
||||||
}).First(&channel).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
} else if user.ID == channel.AccountID {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "you cannot leave your own channel")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := services.RemoveChannelMember(user, channel); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
} else {
|
|
||||||
return c.SendStatus(fiber.StatusOK)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,253 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/gap"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/server/exts"
|
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/database"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/models"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/services"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getChannel(c *fiber.Ctx) error {
|
|
||||||
alias := c.Params("channel")
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var channel models.Channel
|
|
||||||
if val, ok := c.Locals("realm").(models.Realm); ok {
|
|
||||||
channel, err = services.GetChannelWithAlias(alias, val.ID)
|
|
||||||
} else {
|
|
||||||
channel, err = services.GetChannelWithAlias(alias)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(channel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getChannelIdentity(c *fiber.Ctx) error {
|
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
user := c.Locals("user").(models.Account)
|
|
||||||
alias := c.Params("channel")
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var member models.ChannelMember
|
|
||||||
if val, ok := c.Locals("realm").(models.Realm); ok {
|
|
||||||
_, member, err = services.GetAvailableChannelWithAlias(alias, user, val.ID)
|
|
||||||
} else {
|
|
||||||
_, member, err = services.GetAvailableChannelWithAlias(alias, user)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return c.SendStatus(fiber.StatusForbidden)
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(member)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listChannel(c *fiber.Ctx) error {
|
|
||||||
var err error
|
|
||||||
var channels []models.Channel
|
|
||||||
if val, ok := c.Locals("realm").(models.Realm); ok {
|
|
||||||
channels, err = services.ListChannel(val.ID)
|
|
||||||
} else {
|
|
||||||
channels, err = services.ListChannel()
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(channels)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listOwnedChannel(c *fiber.Ctx) error {
|
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
user := c.Locals("user").(models.Account)
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var channels []models.Channel
|
|
||||||
if val, ok := c.Locals("realm").(models.Realm); ok {
|
|
||||||
channels, err = services.ListChannelWithUser(user, val.ID)
|
|
||||||
} else {
|
|
||||||
channels, err = services.ListChannelWithUser(user)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(channels)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listAvailableChannel(c *fiber.Ctx) error {
|
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
user := c.Locals("user").(models.Account)
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var channels []models.Channel
|
|
||||||
if val, ok := c.Locals("realm").(models.Realm); ok {
|
|
||||||
channels, err = services.ListAvailableChannel(user, val.ID)
|
|
||||||
} else {
|
|
||||||
channels, err = services.ListAvailableChannel(user)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(channels)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createChannel(c *fiber.Ctx) error {
|
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
user := c.Locals("user").(models.Account)
|
|
||||||
|
|
||||||
var data struct {
|
|
||||||
Alias string `json:"alias" validate:"required,lowercase,min=4,max=32"`
|
|
||||||
Name string `json:"name" validate:"required"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
IsEncrypted bool `json:"is_encrypted"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := exts.BindAndValidate(c, &data); err != nil {
|
|
||||||
return err
|
|
||||||
} else if err = services.GetChannelAliasAvailability(data.Alias); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
var realm *models.Realm
|
|
||||||
if val, ok := c.Locals("realm").(models.Realm); ok {
|
|
||||||
if info, err := services.GetRealmMember(val.ExternalID, user.ExternalID); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusForbidden, "you must be a part of that realm then can create channel related to it")
|
|
||||||
} else if info.GetPowerLevel() < 50 {
|
|
||||||
return fiber.NewError(fiber.StatusForbidden, "you must be a moderator of that realm then can create channel related to it")
|
|
||||||
} else {
|
|
||||||
realm = &val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
channel := models.Channel{
|
|
||||||
Alias: data.Alias,
|
|
||||||
Name: data.Name,
|
|
||||||
Description: data.Description,
|
|
||||||
IsEncrypted: data.IsEncrypted,
|
|
||||||
AccountID: user.ID,
|
|
||||||
Type: models.ChannelTypeCommon,
|
|
||||||
Members: []models.ChannelMember{
|
|
||||||
{AccountID: user.ID, PowerLevel: 100},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if realm != nil {
|
|
||||||
channel.RealmID = &realm.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
channel, err := services.NewChannel(channel)
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(channel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func editChannel(c *fiber.Ctx) error {
|
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
user := c.Locals("user").(models.Account)
|
|
||||||
id, _ := c.ParamsInt("channelId", 0)
|
|
||||||
|
|
||||||
var data struct {
|
|
||||||
Alias string `json:"alias" validate:"required,min=4,max=32"`
|
|
||||||
Name string `json:"name" validate:"required"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
IsEncrypted bool `json:"is_encrypted"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := exts.BindAndValidate(c, &data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tx := database.C.Where(&models.Channel{BaseModel: models.BaseModel{ID: uint(id)}})
|
|
||||||
|
|
||||||
if val, ok := c.Locals("realm").(models.Realm); ok {
|
|
||||||
if info, err := services.GetRealmMember(val.ExternalID, user.ExternalID); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusForbidden, "you must be a part of that realm then can edit channel related to it")
|
|
||||||
} else if info.GetPowerLevel() < 50 {
|
|
||||||
return fiber.NewError(fiber.StatusForbidden, "you must be a moderator of that realm then can edit channel related to it")
|
|
||||||
} else {
|
|
||||||
tx = tx.Where("realm_id = ?", val.ID)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tx = tx.Where("realm_id IS NULL")
|
|
||||||
}
|
|
||||||
|
|
||||||
var channel models.Channel
|
|
||||||
if err := tx.First(&channel).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if channel.RealmID != nil {
|
|
||||||
if member, err := services.GetChannelMember(user, channel.ID); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusForbidden, "you must be a part of this channel to edit it")
|
|
||||||
} else if member.PowerLevel < 100 {
|
|
||||||
return fiber.NewError(fiber.StatusForbidden, "you must be channel admin to edit it")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
channel, err := services.EditChannel(channel, data.Alias, data.Name, data.Description, data.IsEncrypted)
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(channel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteChannel(c *fiber.Ctx) error {
|
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
user := c.Locals("user").(models.Account)
|
|
||||||
id, _ := c.ParamsInt("channelId", 0)
|
|
||||||
|
|
||||||
tx := database.C.Where(&models.Channel{BaseModel: models.BaseModel{ID: uint(id)}})
|
|
||||||
|
|
||||||
if val, ok := c.Locals("realm").(models.Realm); ok {
|
|
||||||
if info, err := services.GetRealmMember(val.ExternalID, user.ExternalID); err != nil {
|
|
||||||
return fmt.Errorf("you must be a part of that realm then can delete channel related to it")
|
|
||||||
} else if info.GetPowerLevel() < 50 {
|
|
||||||
return fmt.Errorf("you must be a moderator of that realm then can delete channel related to it")
|
|
||||||
} else {
|
|
||||||
tx = tx.Where("realm_id = ?", val.ID)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tx = tx.Where("(account_id = ? OR type = ?) AND realm_id IS NULL", user.ID, models.ChannelTypeDirect)
|
|
||||||
}
|
|
||||||
|
|
||||||
var channel models.Channel
|
|
||||||
if err := tx.First(&channel).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if channel.Type == models.ChannelTypeDirect {
|
|
||||||
if member, err := services.GetChannelMember(user, channel.ID); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusForbidden, "you must related to this direct message if you want delete it")
|
|
||||||
} else if member.PowerLevel < 100 {
|
|
||||||
return fiber.NewError(fiber.StatusForbidden, "you must be a moderator of this direct message if you want delete it")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := services.DeleteChannel(channel); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.SendStatus(fiber.StatusOK)
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/database"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/gap"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/models"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getUserinfo(c *fiber.Ctx) error {
|
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
user := c.Locals("user").(models.Account)
|
|
||||||
|
|
||||||
var data models.Account
|
|
||||||
if err := database.C.
|
|
||||||
Where(&models.Account{BaseModel: models.BaseModel{ID: user.ID}}).
|
|
||||||
First(&data).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getOthersInfo(c *fiber.Ctx) error {
|
|
||||||
accountId := c.Params("accountId")
|
|
||||||
|
|
||||||
var data models.Account
|
|
||||||
if err := database.C.
|
|
||||||
Where(&models.Account{Name: accountId}).
|
|
||||||
First(&data).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(data)
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package exts
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.solsynth.dev/hydrogen/dealer/pkg/proto"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/services"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func LinkAccountMiddleware(c *fiber.Ctx) error {
|
|
||||||
if val, ok := c.Locals("p_user").(*proto.UserInfo); ok {
|
|
||||||
if account, err := services.LinkAccount(val); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
||||||
} else {
|
|
||||||
c.Locals("user", account)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Next()
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/gap"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/server/api"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/server/exts"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2/middleware/favicon"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
|
||||||
"github.com/gofiber/fiber/v2/middleware/idempotency"
|
|
||||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
|
||||||
"github.com/gofiber/template/html/v2"
|
|
||||||
jsoniter "github.com/json-iterator/go"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
var A *fiber.App
|
|
||||||
|
|
||||||
func NewServer() {
|
|
||||||
templates := html.NewFileSystem(http.FS(pkg.FS), ".gohtml")
|
|
||||||
|
|
||||||
A = fiber.New(fiber.Config{
|
|
||||||
DisableStartupMessage: true,
|
|
||||||
EnableIPValidation: true,
|
|
||||||
ServerHeader: "Hydrogen.Messaging",
|
|
||||||
AppName: "Hydrogen.Messaging",
|
|
||||||
ProxyHeader: fiber.HeaderXForwardedFor,
|
|
||||||
JSONEncoder: jsoniter.ConfigCompatibleWithStandardLibrary.Marshal,
|
|
||||||
JSONDecoder: jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal,
|
|
||||||
BodyLimit: 50 * 1024 * 1024,
|
|
||||||
EnablePrintRoutes: viper.GetBool("debug.print_routes"),
|
|
||||||
Views: templates,
|
|
||||||
ViewsLayout: "views/index",
|
|
||||||
})
|
|
||||||
|
|
||||||
A.Use(idempotency.New())
|
|
||||||
A.Use(cors.New(cors.Config{
|
|
||||||
AllowCredentials: true,
|
|
||||||
AllowMethods: strings.Join([]string{
|
|
||||||
fiber.MethodGet,
|
|
||||||
fiber.MethodPost,
|
|
||||||
fiber.MethodHead,
|
|
||||||
fiber.MethodOptions,
|
|
||||||
fiber.MethodPut,
|
|
||||||
fiber.MethodDelete,
|
|
||||||
fiber.MethodPatch,
|
|
||||||
}, ","),
|
|
||||||
AllowOriginsFunc: func(origin string) bool {
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
|
|
||||||
A.Use(logger.New(logger.Config{
|
|
||||||
Format: "${status} | ${latency} | ${method} ${path}\n",
|
|
||||||
Output: log.Logger,
|
|
||||||
}))
|
|
||||||
|
|
||||||
A.Use(gap.H.AuthMiddleware)
|
|
||||||
A.Use(exts.LinkAccountMiddleware)
|
|
||||||
|
|
||||||
A.Use(favicon.New(favicon.Config{
|
|
||||||
FileSystem: http.FS(pkg.FS),
|
|
||||||
File: "views/favicon.png",
|
|
||||||
URL: "/favicon.png",
|
|
||||||
}))
|
|
||||||
|
|
||||||
api.MapAPIs(A)
|
|
||||||
|
|
||||||
A.Get("/", func(c *fiber.Ctx) error {
|
|
||||||
return c.Render("views/open", fiber.Map{
|
|
||||||
"frontend": viper.GetString("frontend"),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func Listen() {
|
|
||||||
if err := A.Listen(viper.GetString("bind")); err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("An error occurred when starting server...")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"git.solsynth.dev/hydrogen/dealer/pkg/hyper"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/database"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/gap"
|
|
||||||
jsoniter "github.com/json-iterator/go"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/dealer/pkg/proto"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CheckUserPerm(userId, otherId uint, key string, val any) error {
|
|
||||||
var user models.Account
|
|
||||||
if err := database.C.Where("id = ?", userId).First(&user).Error; err != nil {
|
|
||||||
return fmt.Errorf("account not found: %v", err)
|
|
||||||
}
|
|
||||||
var other models.Account
|
|
||||||
if err := database.C.Where("id = ?", otherId).First(&other).Error; err != nil {
|
|
||||||
return fmt.Errorf("other not found: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
encodedData, _ := jsoniter.Marshal(val)
|
|
||||||
|
|
||||||
pc, err := gap.H.GetServiceGrpcConn(hyper.ServiceTypeAuthProvider)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
out, err := proto.NewAuthClient(pc).EnsureUserPermGranted(ctx, &proto.CheckUserPermRequest{
|
|
||||||
UserId: uint64(user.ExternalID),
|
|
||||||
OtherId: uint64(other.ExternalID),
|
|
||||||
Key: key,
|
|
||||||
Value: encodedData,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else if !out.IsValid {
|
|
||||||
return fmt.Errorf("missing permission: %v", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NotifyAccountMessager(user models.Account, title, body string, subtitle *string, realtime bool, forcePush bool) error {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
pc, err := gap.H.GetServiceGrpcConn(hyper.ServiceTypeAuthProvider)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = proto.NewNotifierClient(pc).NotifyUser(ctx, &proto.NotifyUserRequest{
|
|
||||||
UserId: uint64(user.ID),
|
|
||||||
Notify: &proto.NotifyRequest{
|
|
||||||
Topic: "messaging.message",
|
|
||||||
Title: title,
|
|
||||||
Subtitle: subtitle,
|
|
||||||
Body: body,
|
|
||||||
IsRealtime: realtime,
|
|
||||||
IsForcePush: forcePush,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"git.solsynth.dev/hydrogen/dealer/pkg/hyper"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/gap"
|
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/paperclip/pkg/proto"
|
|
||||||
"github.com/samber/lo"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CheckAttachmentByIDExists(id uint, usage string) bool {
|
|
||||||
pc, err := gap.H.GetServiceGrpcConn(hyper.ServiceTypeAuthProvider)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
_, err = proto.NewAttachmentsClient(pc).CheckAttachmentExists(context.Background(), &proto.AttachmentLookupRequest{
|
|
||||||
Id: lo.ToPtr(uint64(id)),
|
|
||||||
Usage: &usage,
|
|
||||||
})
|
|
||||||
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"git.solsynth.dev/hydrogen/dealer/pkg/proto"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/database"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/models"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
func LinkAccount(userinfo *proto.UserInfo) (models.Account, error) {
|
|
||||||
var account models.Account
|
|
||||||
if userinfo == nil {
|
|
||||||
return account, fmt.Errorf("remote userinfo was not found")
|
|
||||||
}
|
|
||||||
if err := database.C.Where(&models.Account{
|
|
||||||
ExternalID: uint(userinfo.Id),
|
|
||||||
}).First(&account).Error; err != nil {
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
account = models.Account{
|
|
||||||
Name: userinfo.Name,
|
|
||||||
Nick: userinfo.Nick,
|
|
||||||
Avatar: userinfo.Avatar,
|
|
||||||
Banner: userinfo.Banner,
|
|
||||||
Description: userinfo.GetDescription(),
|
|
||||||
EmailAddress: userinfo.Email,
|
|
||||||
PowerLevel: 0,
|
|
||||||
ExternalID: uint(userinfo.Id),
|
|
||||||
}
|
|
||||||
return account, database.C.Save(&account).Error
|
|
||||||
}
|
|
||||||
return account, err
|
|
||||||
}
|
|
||||||
|
|
||||||
prev := account
|
|
||||||
account.Name = userinfo.Name
|
|
||||||
account.Nick = userinfo.Nick
|
|
||||||
account.Avatar = userinfo.Avatar
|
|
||||||
account.Banner = userinfo.Banner
|
|
||||||
account.Description = userinfo.GetDescription()
|
|
||||||
account.EmailAddress = userinfo.Email
|
|
||||||
|
|
||||||
var err error
|
|
||||||
if !reflect.DeepEqual(account, prev) {
|
|
||||||
err = database.C.Save(&account).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
return account, err
|
|
||||||
}
|
|
||||||
@@ -4,8 +4,17 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/database"
|
"time"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/models"
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/gap"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cruda"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit"
|
||||||
|
authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"git.solsynth.dev/hypernet/pusher/pkg/pushkit"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/models"
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
"github.com/livekit/protocol/auth"
|
"github.com/livekit/protocol/auth"
|
||||||
"github.com/livekit/protocol/livekit"
|
"github.com/livekit/protocol/livekit"
|
||||||
@@ -13,7 +22,6 @@ import (
|
|||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ListCall(channel models.Channel, take, offset int) ([]models.Call, error) {
|
func ListCall(channel models.Channel, take, offset int) ([]models.Call, error) {
|
||||||
@@ -23,7 +31,6 @@ func ListCall(channel models.Channel, take, offset int) ([]models.Call, error) {
|
|||||||
Limit(take).
|
Limit(take).
|
||||||
Offset(offset).
|
Offset(offset).
|
||||||
Preload("Founder").
|
Preload("Founder").
|
||||||
Preload("Founder.Account").
|
|
||||||
Preload("Channel").
|
Preload("Channel").
|
||||||
Order("created_at DESC").
|
Order("created_at DESC").
|
||||||
Find(&calls).Error; err != nil {
|
Find(&calls).Error; err != nil {
|
||||||
@@ -37,11 +44,10 @@ func GetCall(channel models.Channel, id uint) (models.Call, error) {
|
|||||||
var call models.Call
|
var call models.Call
|
||||||
if err := database.C.
|
if err := database.C.
|
||||||
Where(models.Call{
|
Where(models.Call{
|
||||||
BaseModel: models.BaseModel{ID: id},
|
BaseModel: cruda.BaseModel{ID: id},
|
||||||
ChannelID: channel.ID,
|
ChannelID: channel.ID,
|
||||||
}).
|
}).
|
||||||
Preload("Founder").
|
Preload("Founder").
|
||||||
Preload("Founder.Account").
|
|
||||||
Preload("Channel").
|
Preload("Channel").
|
||||||
Order("created_at DESC").
|
Order("created_at DESC").
|
||||||
First(&call).Error; err != nil {
|
First(&call).Error; err != nil {
|
||||||
@@ -66,10 +72,21 @@ func GetOngoingCall(channel models.Channel) (models.Call, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetCallParticipants(call models.Call) ([]*livekit.ParticipantInfo, error) {
|
||||||
|
res, err := Lk.ListParticipants(context.Background(), &livekit.ListParticipantsRequest{
|
||||||
|
Room: call.ExternalID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res.Participants, nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewCall(channel models.Channel, founder models.ChannelMember) (models.Call, error) {
|
func NewCall(channel models.Channel, founder models.ChannelMember) (models.Call, error) {
|
||||||
|
id := fmt.Sprintf("%s+%d", channel.Alias, channel.ID)
|
||||||
call := models.Call{
|
call := models.Call{
|
||||||
ExternalID: channel.Alias,
|
ExternalID: id,
|
||||||
FounderID: founder.AccountID,
|
FounderID: founder.ID,
|
||||||
ChannelID: channel.ID,
|
ChannelID: channel.ID,
|
||||||
Founder: founder,
|
Founder: founder,
|
||||||
Channel: channel,
|
Channel: channel,
|
||||||
@@ -80,7 +97,7 @@ func NewCall(channel models.Channel, founder models.ChannelMember) (models.Call,
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, err := Lk.CreateRoom(context.Background(), &livekit.CreateRoomRequest{
|
_, err := Lk.CreateRoom(context.Background(), &livekit.CreateRoomRequest{
|
||||||
Name: call.ExternalID,
|
Name: id,
|
||||||
EmptyTimeout: viper.GetUint32("calling.empty_timeout_duration"),
|
EmptyTimeout: viper.GetUint32("calling.empty_timeout_duration"),
|
||||||
MaxParticipants: viper.GetUint32("calling.max_participants"),
|
MaxParticipants: viper.GetUint32("calling.max_participants"),
|
||||||
})
|
})
|
||||||
@@ -93,26 +110,49 @@ func NewCall(channel models.Channel, founder models.ChannelMember) (models.Call,
|
|||||||
return call, err
|
return call, err
|
||||||
} else if err = database.C.Where(models.ChannelMember{
|
} else if err = database.C.Where(models.ChannelMember{
|
||||||
ChannelID: call.ChannelID,
|
ChannelID: call.ChannelID,
|
||||||
}).Preload("Account").Find(&members).Error; err == nil {
|
}).Find(&members).Error; err == nil {
|
||||||
channel := call.Channel
|
|
||||||
call, _ = GetCall(call.Channel, call.ID)
|
call, _ = GetCall(call.Channel, call.ID)
|
||||||
|
var pendingUsers []uint64
|
||||||
for _, member := range members {
|
for _, member := range members {
|
||||||
if member.ID != call.Founder.ID {
|
if member.ID != call.Founder.ID {
|
||||||
err = NotifyAccountMessager(member.Account,
|
pendingUsers = append(pendingUsers, uint64(member.AccountID))
|
||||||
fmt.Sprintf("Call in #%s", channel.Alias),
|
|
||||||
fmt.Sprintf("%s started a new call", call.Founder.Account.Name),
|
|
||||||
nil,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn().Err(err).Msg("An error occurred when trying notify user.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
PushCommand(member.AccountID, models.UnifiedCommand{
|
}
|
||||||
Action: "calls.new",
|
|
||||||
Payload: call,
|
channel, _ = GetChannel(channel.ID)
|
||||||
})
|
if channel.RealmID != nil {
|
||||||
|
realm, err := authkit.GetRealm(gap.Nx, *channel.RealmID)
|
||||||
|
if err == nil {
|
||||||
|
channel.Realm = &realm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The call notification is not happen very often
|
||||||
|
// So we don't need to optimize the performance for passive users
|
||||||
|
PushCommandBatch(pendingUsers, nex.WebSocketPackage{
|
||||||
|
Action: "calls.new",
|
||||||
|
Payload: call,
|
||||||
|
})
|
||||||
|
|
||||||
|
err = authkit.NotifyUserBatch(
|
||||||
|
gap.Nx,
|
||||||
|
pendingUsers,
|
||||||
|
pushkit.Notification{
|
||||||
|
Topic: "messaging.callStart",
|
||||||
|
Title: fmt.Sprintf("Call in (%s)", channel.DisplayText()),
|
||||||
|
Body: fmt.Sprintf("%s is calling", call.Founder.Name),
|
||||||
|
Metadata: map[string]any{
|
||||||
|
"avatar": call.Founder.Avatar,
|
||||||
|
"user_id": call.Founder.AccountID,
|
||||||
|
"user_name": call.Founder.Name,
|
||||||
|
"user_nick": call.Founder.Nick,
|
||||||
|
"channel_id": call.ChannelID,
|
||||||
|
},
|
||||||
|
Priority: 5,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msg("An error occurred when trying notify user.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,24 +173,34 @@ func EndCall(call models.Call) (models.Call, error) {
|
|||||||
return call, err
|
return call, err
|
||||||
} else if err = database.C.Where(models.ChannelMember{
|
} else if err = database.C.Where(models.ChannelMember{
|
||||||
ChannelID: call.ChannelID,
|
ChannelID: call.ChannelID,
|
||||||
}).Preload("Account").Find(&members).Error; err == nil {
|
}).Find(&members).Error; err == nil {
|
||||||
call, _ = GetCall(call.Channel, call.ID)
|
call, _ = GetCall(call.Channel, call.ID)
|
||||||
|
var pendingUsers []uint64
|
||||||
for _, member := range members {
|
for _, member := range members {
|
||||||
PushCommand(member.AccountID, models.UnifiedCommand{
|
if member.ID != call.Founder.ID {
|
||||||
Action: "calls.end",
|
pendingUsers = append(pendingUsers, uint64(member.AccountID))
|
||||||
Payload: call,
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PushCommandBatch(pendingUsers, nex.WebSocketPackage{
|
||||||
|
Action: "calls.end",
|
||||||
|
Payload: call,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return call, nil
|
return call, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func EncodeCallToken(user models.Account, call models.Call) (string, error) {
|
func KickParticipantInCall(call models.Call, username string) error {
|
||||||
isAdmin := false
|
_, err := Lk.RemoveParticipant(context.Background(), &livekit.RoomParticipantIdentity{
|
||||||
if user.ID == call.FounderID || user.ID == call.Channel.AccountID {
|
Room: call.ExternalID,
|
||||||
isAdmin = true
|
Identity: username,
|
||||||
}
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func EncodeCallToken(user authm.Account, call models.Call) (string, error) {
|
||||||
|
isAdmin := user.ID == call.FounderID || user.ID == call.Channel.AccountID
|
||||||
|
|
||||||
grant := &auth.VideoGrant{
|
grant := &auth.VideoGrant{
|
||||||
Room: call.ExternalID,
|
Room: call.ExternalID,
|
||||||
|
|||||||
@@ -1,18 +1,36 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/database"
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/gap"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/models"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cachekit"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit"
|
||||||
|
authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ListChannelMember(channelId uint) ([]models.ChannelMember, error) {
|
func CountChannelMember(channelId uint) (int64, error) {
|
||||||
|
var count int64
|
||||||
|
if err := database.C.Where(&models.ChannelMember{
|
||||||
|
ChannelID: channelId,
|
||||||
|
}).Model(&models.ChannelMember{}).Count(&count).Error; err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else {
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListChannelMember(channelId uint, take int, offset int) ([]models.ChannelMember, error) {
|
||||||
var members []models.ChannelMember
|
var members []models.ChannelMember
|
||||||
|
|
||||||
if err := database.C.
|
if err := database.C.
|
||||||
|
Limit(take).Offset(offset).
|
||||||
Where(&models.ChannelMember{ChannelID: channelId}).
|
Where(&models.ChannelMember{ChannelID: channelId}).
|
||||||
Preload("Account").
|
|
||||||
Find(&members).Error; err != nil {
|
Find(&members).Error; err != nil {
|
||||||
return members, err
|
return members, err
|
||||||
}
|
}
|
||||||
@@ -20,58 +38,81 @@ func ListChannelMember(channelId uint) ([]models.ChannelMember, error) {
|
|||||||
return members, nil
|
return members, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetChannelMember(user models.Account, channelId uint) (models.ChannelMember, error) {
|
func GetChannelMember(user authm.Account, channelId uint) (models.ChannelMember, error) {
|
||||||
var member models.ChannelMember
|
var member models.ChannelMember
|
||||||
|
|
||||||
if err := database.C.
|
if err := database.C.
|
||||||
Where(&models.ChannelMember{AccountID: user.ID, ChannelID: channelId}).
|
Where(&models.ChannelMember{AccountID: user.ID, ChannelID: channelId}).
|
||||||
Find(&member).Error; err != nil {
|
First(&member).Error; err != nil {
|
||||||
return member, err
|
return member, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return member, nil
|
return member, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddChannelMemberWithCheck(user models.Account, target models.Channel) error {
|
func AddChannelMemberWithCheck(user, op authm.Account, target models.Channel) error {
|
||||||
if err := CheckUserPerm(user.ID, target.AccountID, "ChannelAdd", true); err != nil {
|
if user.ID != op.ID {
|
||||||
return fmt.Errorf("unable to add user into your channel")
|
if err := authkit.EnsureUserPermGranted(gap.Nx, user.ID, op.ID, "ChannelAdd", true); err != nil {
|
||||||
|
return fmt.Errorf("unable to add user into your channel due to access denied: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
member := models.ChannelMember{
|
return AddChannelMember(user, target)
|
||||||
ChannelID: target.ID,
|
|
||||||
AccountID: user.ID,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := database.C.Save(&member).Error
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddChannelMember(user models.Account, target models.Channel) error {
|
func AddChannelMember(user authm.Account, target models.Channel) error {
|
||||||
member := models.ChannelMember{
|
var member models.ChannelMember
|
||||||
|
if err := database.C.Where(&models.ChannelMember{
|
||||||
|
AccountID: user.ID,
|
||||||
|
ChannelID: target.ID,
|
||||||
|
}).First(&member).Error; err == nil || !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
member = models.ChannelMember{
|
||||||
ChannelID: target.ID,
|
ChannelID: target.ID,
|
||||||
AccountID: user.ID,
|
AccountID: user.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := database.C.Save(&member).Error
|
err := database.C.Save(&member).Error
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
cachekit.DeleteByTags(
|
||||||
|
gap.Ca,
|
||||||
|
fmt.Sprintf("channel#%d", target.ID),
|
||||||
|
fmt.Sprintf("user#%d", user.ID),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func EditChannelMember(membership models.ChannelMember) (models.ChannelMember, error) {
|
func EditChannelMember(membership models.ChannelMember) (models.ChannelMember, error) {
|
||||||
if err := database.C.Save(&membership).Error; err != nil {
|
if err := database.C.Save(&membership).Error; err != nil {
|
||||||
return membership, err
|
return membership, err
|
||||||
|
} else {
|
||||||
|
cachekit.DeleteByTags(
|
||||||
|
gap.Ca,
|
||||||
|
fmt.Sprintf("channel#%d", membership.ChannelID),
|
||||||
|
fmt.Sprintf("user#%d", membership.AccountID),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return membership, nil
|
return membership, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RemoveChannelMember(user models.Account, target models.Channel) error {
|
func RemoveChannelMember(member models.ChannelMember, target models.Channel) error {
|
||||||
var member models.ChannelMember
|
if err := database.C.Delete(&member).Error; err == nil {
|
||||||
|
database.C.Where("sender_id = ?").Delete(&models.Event{})
|
||||||
|
|
||||||
if err := database.C.Where(&models.ChannelMember{
|
cachekit.DeleteByTags(
|
||||||
ChannelID: target.ID,
|
gap.Ca,
|
||||||
AccountID: user.ID,
|
fmt.Sprintf("channel#%d", target.ID),
|
||||||
}).First(&member).Error; err != nil {
|
fmt.Sprintf("user#%d", member.AccountID),
|
||||||
|
)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return database.C.Delete(&member).Error
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,96 @@ package services
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/database"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cachekit"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/models"
|
authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/gap"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/models"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type channelIdentityCacheEntry struct {
|
||||||
|
Channel models.Channel `json:"channel"`
|
||||||
|
ChannelMember models.ChannelMember `json:"channel_member"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func KgChannelIdentityCache(channel string, user uint, realm ...uint) string {
|
||||||
|
if len(realm) > 0 {
|
||||||
|
return fmt.Sprintf("channel-identity-%s#%d@%d", channel, user, realm)
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("channel-identity-%s#%d", channel, user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CacheChannelIdentity(channel models.Channel, member models.ChannelMember, user uint, realm ...uint) {
|
||||||
|
key := KgChannelIdentityCache(channel.Alias, user, realm...)
|
||||||
|
|
||||||
|
cachekit.Set(
|
||||||
|
gap.Ca,
|
||||||
|
key,
|
||||||
|
channelIdentityCacheEntry{channel, member},
|
||||||
|
60*time.Minute,
|
||||||
|
fmt.Sprintf("channel#%d", channel.ID),
|
||||||
|
fmt.Sprintf("user#%d", user),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetChannelIdentityWithID(id uint, user uint) (models.Channel, models.ChannelMember, error) {
|
||||||
|
var member models.ChannelMember
|
||||||
|
|
||||||
|
if err := database.C.Where(models.ChannelMember{
|
||||||
|
AccountID: user,
|
||||||
|
ChannelID: id,
|
||||||
|
}).Preload("Channel").First(&member).Error; err != nil {
|
||||||
|
return member.Channel, member, fmt.Errorf("channel principal not found: %v", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return member.Channel, member, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetChannelIdentity(alias string, user uint, realm ...authm.Realm) (models.Channel, models.ChannelMember, error) {
|
||||||
|
var err error
|
||||||
|
var channel models.Channel
|
||||||
|
var member models.ChannelMember
|
||||||
|
|
||||||
|
hitCache := false
|
||||||
|
if len(realm) > 0 {
|
||||||
|
if val, err := cachekit.Get[channelIdentityCacheEntry](
|
||||||
|
gap.Ca,
|
||||||
|
KgChannelIdentityCache(alias, user, realm[0].ID),
|
||||||
|
); err == nil {
|
||||||
|
channel = val.Channel
|
||||||
|
member = val.ChannelMember
|
||||||
|
hitCache = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if val, err := cachekit.Get[channelIdentityCacheEntry](
|
||||||
|
gap.Ca,
|
||||||
|
KgChannelIdentityCache(alias, user),
|
||||||
|
); err == nil {
|
||||||
|
channel = val.Channel
|
||||||
|
member = val.ChannelMember
|
||||||
|
hitCache = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hitCache {
|
||||||
|
if len(realm) > 0 {
|
||||||
|
channel, member, err = GetAvailableChannelWithAlias(alias, user, realm[0].ID)
|
||||||
|
CacheChannelIdentity(channel, member, user, realm[0].ID)
|
||||||
|
} else {
|
||||||
|
channel, member, err = GetAvailableChannelWithAlias(alias, user)
|
||||||
|
CacheChannelIdentity(channel, member, user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return channel, member, err
|
||||||
|
}
|
||||||
|
|
||||||
func GetChannelAliasAvailability(alias string) error {
|
func GetChannelAliasAvailability(alias string) error {
|
||||||
if !regexp.MustCompile("^[a-z0-9-]+$").MatchString(alias) {
|
if !regexp.MustCompile("^[a-z0-9-]+$").MatchString(alias) {
|
||||||
return fmt.Errorf("channel alias should only contains lowercase letters, numbers, and hyphens")
|
return fmt.Errorf("channel alias should only contains lowercase letters, numbers, and hyphens")
|
||||||
@@ -20,9 +102,7 @@ func GetChannelAliasAvailability(alias string) error {
|
|||||||
|
|
||||||
func GetChannel(id uint) (models.Channel, error) {
|
func GetChannel(id uint) (models.Channel, error) {
|
||||||
var channel models.Channel
|
var channel models.Channel
|
||||||
tx := database.C.Where(models.Channel{
|
tx := database.C.Where("id = ?", id)
|
||||||
BaseModel: models.BaseModel{ID: id},
|
|
||||||
}).Preload("Account").Preload("Realm")
|
|
||||||
tx = PreloadDirectChannelMembers(tx)
|
tx = PreloadDirectChannelMembers(tx)
|
||||||
if err := tx.First(&channel).Error; err != nil {
|
if err := tx.First(&channel).Error; err != nil {
|
||||||
return channel, err
|
return channel, err
|
||||||
@@ -33,7 +113,7 @@ func GetChannel(id uint) (models.Channel, error) {
|
|||||||
|
|
||||||
func GetChannelWithAlias(alias string, realmId ...uint) (models.Channel, error) {
|
func GetChannelWithAlias(alias string, realmId ...uint) (models.Channel, error) {
|
||||||
var channel models.Channel
|
var channel models.Channel
|
||||||
tx := database.C.Where(models.Channel{Alias: alias}).Preload("Account").Preload("Realm")
|
tx := database.C.Where(models.Channel{Alias: alias})
|
||||||
if len(realmId) > 0 {
|
if len(realmId) > 0 {
|
||||||
tx = tx.Where("realm_id = ?", realmId)
|
tx = tx.Where("realm_id = ?", realmId)
|
||||||
} else {
|
} else {
|
||||||
@@ -47,7 +127,7 @@ func GetChannelWithAlias(alias string, realmId ...uint) (models.Channel, error)
|
|||||||
return channel, nil
|
return channel, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAvailableChannelWithAlias(alias string, user models.Account, realmId ...uint) (models.Channel, models.ChannelMember, error) {
|
func GetAvailableChannelWithAlias(alias string, user uint, realmId ...uint) (models.Channel, models.ChannelMember, error) {
|
||||||
var err error
|
var err error
|
||||||
var member models.ChannelMember
|
var member models.ChannelMember
|
||||||
var channel models.Channel
|
var channel models.Channel
|
||||||
@@ -56,7 +136,7 @@ func GetAvailableChannelWithAlias(alias string, user models.Account, realmId ...
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := database.C.Where(models.ChannelMember{
|
if err := database.C.Where(models.ChannelMember{
|
||||||
AccountID: user.ID,
|
AccountID: user,
|
||||||
ChannelID: channel.ID,
|
ChannelID: channel.ID,
|
||||||
}).First(&member).Error; err != nil {
|
}).First(&member).Error; err != nil {
|
||||||
return channel, member, fmt.Errorf("channel principal not found: %v", err.Error())
|
return channel, member, fmt.Errorf("channel principal not found: %v", err.Error())
|
||||||
@@ -65,7 +145,7 @@ func GetAvailableChannelWithAlias(alias string, user models.Account, realmId ...
|
|||||||
return channel, member, nil
|
return channel, member, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAvailableChannel(id uint, user models.Account) (models.Channel, models.ChannelMember, error) {
|
func GetAvailableChannel(id uint, user authm.Account) (models.Channel, models.ChannelMember, error) {
|
||||||
var err error
|
var err error
|
||||||
var member models.ChannelMember
|
var member models.ChannelMember
|
||||||
var channel models.Channel
|
var channel models.Channel
|
||||||
@@ -92,12 +172,24 @@ func PreloadDirectChannelMembers(tx *gorm.DB) *gorm.DB {
|
|||||||
),
|
),
|
||||||
models.ChannelTypeDirect,
|
models.ChannelTypeDirect,
|
||||||
)
|
)
|
||||||
}).Preload("Members.Account")
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListChannel(realmId ...uint) ([]models.Channel, error) {
|
func ListChannel(user *authm.Account, realmId ...uint) ([]models.Channel, error) {
|
||||||
|
var identities []models.ChannelMember
|
||||||
|
var idRange []uint
|
||||||
|
if user != nil {
|
||||||
|
if err := database.C.Where("account_id = ?", user.ID).Find(&identities).Error; err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to get identities: %v", err)
|
||||||
|
}
|
||||||
|
for _, identity := range identities {
|
||||||
|
idRange = append(idRange, identity.ChannelID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var channels []models.Channel
|
var channels []models.Channel
|
||||||
tx := database.C.Preload("Account").Preload("Realm")
|
tx := database.C
|
||||||
|
tx = tx.Where("id IN ? OR is_public = true", idRange)
|
||||||
if len(realmId) > 0 {
|
if len(realmId) > 0 {
|
||||||
tx = tx.Where("realm_id = ?", realmId)
|
tx = tx.Where("realm_id = ?", realmId)
|
||||||
}
|
}
|
||||||
@@ -111,9 +203,10 @@ func ListChannel(realmId ...uint) ([]models.Channel, error) {
|
|||||||
return channels, nil
|
return channels, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListChannelWithUser(user models.Account, realmId ...uint) ([]models.Channel, error) {
|
func ListChannelPublic(realmId ...uint) ([]models.Channel, error) {
|
||||||
var channels []models.Channel
|
var channels []models.Channel
|
||||||
tx := database.C.Where(&models.Channel{AccountID: user.ID}).Preload("Realm")
|
tx := database.C
|
||||||
|
tx = tx.Where("is_public = true")
|
||||||
if len(realmId) > 0 {
|
if len(realmId) > 0 {
|
||||||
tx = tx.Where("realm_id = ?", realmId)
|
tx = tx.Where("realm_id = ?", realmId)
|
||||||
}
|
}
|
||||||
@@ -127,7 +220,25 @@ func ListChannelWithUser(user models.Account, realmId ...uint) ([]models.Channel
|
|||||||
return channels, nil
|
return channels, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListAvailableChannel(user models.Account, realmId ...uint) ([]models.Channel, error) {
|
func ListChannelWithUser(user authm.Account, realmId ...uint) ([]models.Channel, error) {
|
||||||
|
var channels []models.Channel
|
||||||
|
tx := database.C.Where(&models.Channel{AccountID: user.ID})
|
||||||
|
if len(realmId) > 0 {
|
||||||
|
if realmId[0] != 0 {
|
||||||
|
tx = tx.Where("realm_id = ?", realmId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tx = PreloadDirectChannelMembers(tx)
|
||||||
|
|
||||||
|
if err := tx.Find(&channels).Error; err != nil {
|
||||||
|
return channels, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return channels, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListAvailableChannel(tx *gorm.DB, user authm.Account, realmId ...uint) ([]models.Channel, error) {
|
||||||
var channels []models.Channel
|
var channels []models.Channel
|
||||||
var members []models.ChannelMember
|
var members []models.ChannelMember
|
||||||
if err := database.C.Where(&models.ChannelMember{
|
if err := database.C.Where(&models.ChannelMember{
|
||||||
@@ -140,9 +251,13 @@ func ListAvailableChannel(user models.Account, realmId ...uint) ([]models.Channe
|
|||||||
return item.ChannelID
|
return item.ChannelID
|
||||||
})
|
})
|
||||||
|
|
||||||
tx := database.C.Preload("Realm").Where("id IN ?", idx)
|
tx = tx.Where("id IN ?", idx)
|
||||||
if len(realmId) > 0 {
|
if len(realmId) > 0 {
|
||||||
tx = tx.Where("realm_id = ?", realmId)
|
if realmId[0] != 0 {
|
||||||
|
tx = tx.Where("realm_id = ?", realmId)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tx = tx.Where("realm_id IS NULL")
|
||||||
}
|
}
|
||||||
|
|
||||||
tx = PreloadDirectChannelMembers(tx)
|
tx = PreloadDirectChannelMembers(tx)
|
||||||
@@ -159,19 +274,27 @@ func NewChannel(channel models.Channel) (models.Channel, error) {
|
|||||||
return channel, err
|
return channel, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func EditChannel(channel models.Channel, alias, name, description string, isEncrypted bool) (models.Channel, error) {
|
func EditChannel(channel models.Channel) (models.Channel, error) {
|
||||||
channel.Alias = alias
|
|
||||||
channel.Name = name
|
|
||||||
channel.Description = description
|
|
||||||
if !channel.IsEncrypted {
|
|
||||||
channel.IsEncrypted = isEncrypted
|
|
||||||
}
|
|
||||||
|
|
||||||
err := database.C.Save(&channel).Error
|
err := database.C.Save(&channel).Error
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
cachekit.DeleteByTags(gap.Ca, fmt.Sprintf("channel#%d", channel.ID))
|
||||||
|
}
|
||||||
|
|
||||||
return channel, err
|
return channel, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteChannel(channel models.Channel) error {
|
func DeleteChannel(channel models.Channel) error {
|
||||||
return database.C.Delete(&channel).Error
|
if err := database.C.Delete(&channel).Error; err == nil {
|
||||||
|
UnsubscribeAllWithChannels(channel.ID)
|
||||||
|
|
||||||
|
database.C.Where("channel_id = ?", channel.ID).Delete(&models.Event{})
|
||||||
|
database.C.Where("channel_id = ?", channel.ID).Delete(&models.ChannelMember{})
|
||||||
|
|
||||||
|
cachekit.DeleteByTags(gap.Ca, fmt.Sprintf("channel#%d", channel.ID))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package services
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/database"
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/database"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ func DoAutoDatabaseCleanup() {
|
|||||||
|
|
||||||
// Deal soft-deletion
|
// Deal soft-deletion
|
||||||
var count int64
|
var count int64
|
||||||
for _, model := range database.DatabaseAutoActionRange {
|
for _, model := range database.AutoMaintainRange {
|
||||||
tx := database.C.Unscoped().Delete(model, "deleted_at >= ?", deadline)
|
tx := database.C.Unscoped().Delete(model, "deleted_at >= ?", deadline)
|
||||||
if tx.Error != nil {
|
if tx.Error != nil {
|
||||||
log.Error().Err(tx.Error).Msg("An error occurred when running database cleanup...")
|
log.Error().Err(tx.Error).Msg("An error occurred when running database cleanup...")
|
||||||
|
|||||||
@@ -2,14 +2,15 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/database"
|
authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/models"
|
|
||||||
"github.com/spf13/viper"
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetDirectChannelByUser(user models.Account, other models.Account) (models.Channel, error) {
|
func GetDirectChannelByUser(user authm.Account, other authm.Account) (models.Channel, error) {
|
||||||
memberTable := fmt.Sprintf("%schannel_members", viper.GetString("database.prefix"))
|
memberTable := "channel_members"
|
||||||
channelTable := fmt.Sprintf("%schannels", viper.GetString("database.prefix"))
|
channelTable := "channels"
|
||||||
|
|
||||||
var channel models.Channel
|
var channel models.Channel
|
||||||
if err := database.C.Preload("Members").
|
if err := database.C.Preload("Members").
|
||||||
|
|||||||
@@ -4,11 +4,20 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/database"
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/gap"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/models"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cruda"
|
||||||
|
"git.solsynth.dev/hypernet/paperclip/pkg/filekit"
|
||||||
|
"git.solsynth.dev/hypernet/paperclip/pkg/proto"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit"
|
||||||
|
"git.solsynth.dev/hypernet/pusher/pkg/pushkit"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/models"
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CountEvent(channel models.Channel) int64 {
|
func CountEvent(channel models.Channel) int64 {
|
||||||
@@ -34,7 +43,6 @@ func ListEvent(channel models.Channel, take int, offset int) ([]models.Event, er
|
|||||||
}).Limit(take).Offset(offset).
|
}).Limit(take).Offset(offset).
|
||||||
Order("created_at DESC").
|
Order("created_at DESC").
|
||||||
Preload("Sender").
|
Preload("Sender").
|
||||||
Preload("Sender.Account").
|
|
||||||
Find(&events).Error; err != nil {
|
Find(&events).Error; err != nil {
|
||||||
return events, err
|
return events, err
|
||||||
} else {
|
} else {
|
||||||
@@ -42,15 +50,11 @@ func ListEvent(channel models.Channel, take int, offset int) ([]models.Event, er
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetEvent(channel models.Channel, id uint) (models.Event, error) {
|
func GetEvent(channelId uint, id uint) (models.Event, error) {
|
||||||
var event models.Event
|
var event models.Event
|
||||||
if err := database.C.
|
if err := database.C.
|
||||||
Where(models.Event{
|
Where("id = ? AND channel_id = ?", id, channelId).
|
||||||
BaseModel: models.BaseModel{ID: id},
|
|
||||||
ChannelID: channel.ID,
|
|
||||||
}).
|
|
||||||
Preload("Sender").
|
Preload("Sender").
|
||||||
Preload("Sender.Account").
|
|
||||||
First(&event).Error; err != nil {
|
First(&event).Error; err != nil {
|
||||||
return event, err
|
return event, err
|
||||||
} else {
|
} else {
|
||||||
@@ -61,7 +65,7 @@ func GetEvent(channel models.Channel, id uint) (models.Event, error) {
|
|||||||
func GetEventWithSender(channel models.Channel, member models.ChannelMember, id uint) (models.Event, error) {
|
func GetEventWithSender(channel models.Channel, member models.ChannelMember, id uint) (models.Event, error) {
|
||||||
var event models.Event
|
var event models.Event
|
||||||
if err := database.C.Where(models.Event{
|
if err := database.C.Where(models.Event{
|
||||||
BaseModel: models.BaseModel{ID: id},
|
BaseModel: cruda.BaseModel{ID: id},
|
||||||
ChannelID: channel.ID,
|
ChannelID: channel.ID,
|
||||||
SenderID: member.ID,
|
SenderID: member.ID,
|
||||||
}).First(&event).Error; err != nil {
|
}).First(&event).Error; err != nil {
|
||||||
@@ -77,23 +81,47 @@ func NewEvent(event models.Event) (models.Event, error) {
|
|||||||
return event, err
|
return event, err
|
||||||
} else if err = database.C.Where(models.ChannelMember{
|
} else if err = database.C.Where(models.ChannelMember{
|
||||||
ChannelID: event.ChannelID,
|
ChannelID: event.ChannelID,
|
||||||
}).Preload("Account").Find(&members).Error; err != nil {
|
}).Find(&members).Error; err != nil {
|
||||||
// Couldn't get channel members, skip notifying
|
// Couldn't get channel members, skip notifying
|
||||||
|
log.Warn().Err(err).Msg("Failed to fetch members, the notifying of new event was terminated...")
|
||||||
return event, nil
|
return event, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
channel := event.Channel
|
if val, ok := event.Body["attachments"].([]string); ok && len(val) > 0 {
|
||||||
event, _ = GetEvent(event.Channel, event.ID)
|
filekit.CountAttachmentUsage(gap.Nx, &proto.UpdateUsageRequest{
|
||||||
for _, member := range members {
|
Rid: val,
|
||||||
PushCommand(member.AccountID, models.UnifiedCommand{
|
Delta: 1,
|
||||||
Action: "events.new",
|
|
||||||
Payload: event,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event, err := GetEvent(event.ChannelID, event.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to fetch event, the notifying of new event was terminated...")
|
||||||
|
return event, err
|
||||||
|
}
|
||||||
|
idxList := lo.Map(lo.Filter(members, func(item models.ChannelMember, index int) bool {
|
||||||
|
if !viper.GetBool("performance.passive_user_optimize") {
|
||||||
|
// Leave this for backward compatibility
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return CheckSubscribed(item.AccountID, event.ChannelID)
|
||||||
|
}), func(item models.ChannelMember, index int) uint64 {
|
||||||
|
return uint64(item.AccountID)
|
||||||
|
})
|
||||||
|
_ = PushCommandBatch(idxList, nex.WebSocketPackage{
|
||||||
|
Action: "events.new",
|
||||||
|
Payload: event,
|
||||||
|
})
|
||||||
|
|
||||||
if strings.HasPrefix(event.Type, "messages") {
|
if strings.HasPrefix(event.Type, "messages") {
|
||||||
event.Channel = channel
|
event.Channel, _ = GetChannel(event.ChannelID)
|
||||||
NotifyMessageEvent(members, event)
|
if event.Channel.RealmID != nil {
|
||||||
|
realm, err := authkit.GetRealm(gap.Nx, *event.Channel.RealmID)
|
||||||
|
if err == nil {
|
||||||
|
event.Channel.Realm = &realm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
go NotifyMessageEvent(members, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
return event, nil
|
return event, nil
|
||||||
@@ -104,43 +132,148 @@ func NotifyMessageEvent(members []models.ChannelMember, event models.Event) {
|
|||||||
raw, _ := jsoniter.Marshal(event.Body)
|
raw, _ := jsoniter.Marshal(event.Body)
|
||||||
_ = jsoniter.Unmarshal(raw, &body)
|
_ = jsoniter.Unmarshal(raw, &body)
|
||||||
|
|
||||||
|
var pendingUsers []uint64
|
||||||
|
var mentionedUsers []uint64
|
||||||
|
|
||||||
for _, member := range members {
|
for _, member := range members {
|
||||||
|
if CheckSubscribed(member.AccountID, event.ChannelID) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if member.ID != event.SenderID {
|
if member.ID != event.SenderID {
|
||||||
switch member.Notify {
|
switch member.Notify {
|
||||||
case models.NotifyLevelNone:
|
case models.NotifyLevelNone:
|
||||||
continue
|
continue
|
||||||
case models.NotifyLevelMentioned:
|
case models.NotifyLevelMentioned:
|
||||||
if len(body.RelatedUsers) == 0 || !lo.Contains(body.RelatedUsers, member.AccountID) {
|
if len(body.RelatedUsers) != 0 && lo.Contains(body.RelatedUsers, member.AccountID) {
|
||||||
continue
|
mentionedUsers = append(mentionedUsers, uint64(member.AccountID))
|
||||||
}
|
}
|
||||||
|
continue
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
var displayText string
|
if lo.Contains(body.RelatedUsers, member.AccountID) {
|
||||||
if body.Algorithm == "plain" {
|
mentionedUsers = append(mentionedUsers, uint64(member.AccountID))
|
||||||
displayText = body.Text
|
} else {
|
||||||
|
pendingUsers = append(pendingUsers, uint64(member.AccountID))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(displayText) == 0 {
|
var displayText string
|
||||||
displayText = fmt.Sprintf("%d attachment(s)", len(body.Attachments))
|
var displaySubtitle string
|
||||||
|
switch event.Type {
|
||||||
|
case models.EventMessageNew:
|
||||||
|
if body.Algorithm == "plain" {
|
||||||
|
displayText = body.Text
|
||||||
|
}
|
||||||
|
case models.EventMessageEdit:
|
||||||
|
displaySubtitle = "Edited a message"
|
||||||
|
if body.Algorithm == "plain" {
|
||||||
|
displayText = body.Text
|
||||||
|
}
|
||||||
|
case models.EventMessageDelete:
|
||||||
|
displayText = "Deleted a message"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(displayText) == 0 {
|
||||||
|
if len(displayText) == 1 {
|
||||||
|
displayText = fmt.Sprintf("%d file", len(body.Attachments))
|
||||||
|
} else {
|
||||||
|
displayText = fmt.Sprintf("%d files", len(body.Attachments))
|
||||||
|
}
|
||||||
|
} else if len(body.Attachments) > 0 {
|
||||||
|
if len(displayText) == 1 {
|
||||||
|
displayText += fmt.Sprintf(" (%d file)", len(body.Attachments))
|
||||||
|
} else {
|
||||||
|
displayText += fmt.Sprintf(" (%d files)", len(body.Attachments))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := authkit.GetUser(gap.Nx, event.Sender.AccountID)
|
||||||
|
if err == nil {
|
||||||
|
event.Sender.Avatar = user.Avatar
|
||||||
|
if len(event.Sender.Nick) == 0 {
|
||||||
|
event.Sender.Nick = user.Nick
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
displayTitle := fmt.Sprintf("%s (%s)", event.Sender.Nick, event.Channel.DisplayText())
|
||||||
|
|
||||||
|
metadata := map[string]any{
|
||||||
|
"avatar": event.Sender.Avatar,
|
||||||
|
"user_id": event.Sender.AccountID,
|
||||||
|
"user_name": event.Sender.Name,
|
||||||
|
"user_nick": event.Sender.Nick,
|
||||||
|
"channel_id": event.ChannelID,
|
||||||
|
"event_id": event.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pendingUsers) > 0 {
|
||||||
|
log.Debug().
|
||||||
|
Uint("event_id", event.ID).
|
||||||
|
Str("title", displayTitle).
|
||||||
|
Int("count", len(pendingUsers)).
|
||||||
|
Msg("Notifying new event...")
|
||||||
|
|
||||||
|
for _, pendingUser := range pendingUsers {
|
||||||
|
replyToken, err := CreateReplyToken(event.ID, uint(pendingUser))
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msg("An error occurred when trying create reply token.")
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
metadata["reply_token"] = replyToken
|
||||||
var channelDisplay string
|
err = authkit.NotifyUser(
|
||||||
if event.Channel.Type == models.ChannelTypeDirect {
|
gap.Nx,
|
||||||
channelDisplay = "DM"
|
pendingUser,
|
||||||
}
|
pushkit.Notification{
|
||||||
|
Topic: "messaging.message",
|
||||||
if len(channelDisplay) == 0 {
|
Title: displayTitle,
|
||||||
channelDisplay = fmt.Sprintf("#%s", event.Channel.Alias)
|
Subtitle: displaySubtitle,
|
||||||
}
|
Body: displayText,
|
||||||
|
Metadata: metadata,
|
||||||
err := NotifyAccountMessager(member.Account,
|
Priority: 10,
|
||||||
fmt.Sprintf("%s in %s", event.Sender.Account.Nick, channelDisplay),
|
},
|
||||||
fmt.Sprintf("%s", displayText),
|
true,
|
||||||
nil,
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msg("An error occurred when trying notify user.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(mentionedUsers) > 0 {
|
||||||
|
if len(displaySubtitle) > 0 {
|
||||||
|
displaySubtitle += ", and mentioned you"
|
||||||
|
} else {
|
||||||
|
displaySubtitle = "Mentioned you"
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug().
|
||||||
|
Uint("event_id", event.ID).
|
||||||
|
Str("title", displayTitle).
|
||||||
|
Int("count", len(mentionedUsers)).
|
||||||
|
Msg("Notifying new event...")
|
||||||
|
|
||||||
|
for _, mentionedUser := range mentionedUsers {
|
||||||
|
replyToken, err := CreateReplyToken(event.ID, uint(mentionedUser))
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msg("An error occurred when trying create reply token.")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
metadata["reply_token"] = replyToken
|
||||||
|
err = authkit.NotifyUser(
|
||||||
|
gap.Nx,
|
||||||
|
mentionedUser,
|
||||||
|
pushkit.Notification{
|
||||||
|
Topic: "messaging.message",
|
||||||
|
Title: displayTitle,
|
||||||
|
Subtitle: displaySubtitle,
|
||||||
|
Body: displayText,
|
||||||
|
Metadata: metadata,
|
||||||
|
Priority: 10,
|
||||||
|
},
|
||||||
true,
|
true,
|
||||||
false,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn().Err(err).Msg("An error occurred when trying notify user.")
|
log.Warn().Err(err).Msg("An error occurred when trying notify user.")
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
"net/smtp"
|
|
||||||
"net/textproto"
|
|
||||||
|
|
||||||
"github.com/jordan-wright/email"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
func SendMail(target string, subject string, content string) error {
|
|
||||||
mail := &email.Email{
|
|
||||||
To: []string{target},
|
|
||||||
From: viper.GetString("mailer.name"),
|
|
||||||
Subject: subject,
|
|
||||||
Text: []byte(content),
|
|
||||||
Headers: textproto.MIMEHeader{},
|
|
||||||
}
|
|
||||||
return mail.SendWithTLS(
|
|
||||||
fmt.Sprintf("%s:%d", viper.GetString("mailer.smtp_host"), viper.GetInt("mailer.smtp_port")),
|
|
||||||
smtp.PlainAuth(
|
|
||||||
"",
|
|
||||||
viper.GetString("mailer.username"),
|
|
||||||
viper.GetString("mailer.password"),
|
|
||||||
viper.GetString("mailer.smtp_host"),
|
|
||||||
),
|
|
||||||
&tls.Config{ServerName: viper.GetString("mailer.smtp_host")},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SendMailHTML(target string, subject string, content string) error {
|
|
||||||
mail := &email.Email{
|
|
||||||
To: []string{target},
|
|
||||||
From: viper.GetString("mailer.name"),
|
|
||||||
Subject: subject,
|
|
||||||
HTML: []byte(content),
|
|
||||||
Headers: textproto.MIMEHeader{},
|
|
||||||
}
|
|
||||||
return mail.SendWithTLS(
|
|
||||||
fmt.Sprintf("%s:%d", viper.GetString("mailer.smtp_host"), viper.GetInt("mailer.smtp_port")),
|
|
||||||
smtp.PlainAuth(
|
|
||||||
"",
|
|
||||||
viper.GetString("mailer.username"),
|
|
||||||
viper.GetString("mailer.password"),
|
|
||||||
viper.GetString("mailer.smtp_host"),
|
|
||||||
),
|
|
||||||
&tls.Config{ServerName: viper.GetString("mailer.smtp_host")},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/models"
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/models"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
)
|
)
|
||||||
@@ -19,15 +19,17 @@ func EditMessage(event models.Event, body models.EventMessageBody) (models.Event
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return event, err
|
return event, err
|
||||||
}
|
}
|
||||||
body.RelatedEvent = event.ID
|
body.RelatedEventID = &event.ID
|
||||||
_, err = NewEvent(models.Event{
|
_, err = NewEvent(models.Event{
|
||||||
Uuid: uuid.NewString(),
|
Uuid: uuid.NewString(),
|
||||||
Body: EncodeMessageBody(body),
|
Body: EncodeMessageBody(body),
|
||||||
Type: models.EventMessageEdit,
|
Type: models.EventMessageEdit,
|
||||||
Channel: event.Channel,
|
Channel: event.Channel,
|
||||||
Sender: event.Sender,
|
Sender: event.Sender,
|
||||||
ChannelID: event.ChannelID,
|
QuoteEventID: body.QuoteEventID,
|
||||||
SenderID: event.SenderID,
|
RelatedEventID: &event.ID,
|
||||||
|
ChannelID: event.ChannelID,
|
||||||
|
SenderID: event.SenderID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return event, err
|
return event, err
|
||||||
@@ -37,20 +39,22 @@ func EditMessage(event models.Event, body models.EventMessageBody) (models.Event
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DeleteMessage(event models.Event) (models.Event, error) {
|
func DeleteMessage(event models.Event) (models.Event, error) {
|
||||||
_, err := DeleteEvent(event)
|
clonedEvent := event
|
||||||
|
_, err := DeleteEvent(clonedEvent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return event, err
|
return event, err
|
||||||
}
|
}
|
||||||
_, err = NewEvent(models.Event{
|
_, err = NewEvent(models.Event{
|
||||||
Uuid: uuid.NewString(),
|
Uuid: uuid.NewString(),
|
||||||
Body: EncodeMessageBody(models.EventMessageBody{
|
Body: EncodeMessageBody(models.EventMessageBody{
|
||||||
RelatedEvent: event.ID,
|
RelatedEventID: &event.ID,
|
||||||
}),
|
}),
|
||||||
Type: models.EventMessageDelete,
|
Type: models.EventMessageDelete,
|
||||||
Channel: event.Channel,
|
Channel: event.Channel,
|
||||||
Sender: event.Sender,
|
Sender: event.Sender,
|
||||||
ChannelID: event.ChannelID,
|
RelatedEventID: &event.ID,
|
||||||
SenderID: event.SenderID,
|
ChannelID: event.ChannelID,
|
||||||
|
SenderID: event.SenderID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return event, err
|
return event, err
|
||||||
|
|||||||
35
pkg/internal/services/reading_anchor.go
Normal file
35
pkg/internal/services/reading_anchor.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/models"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var readingAnchorQueue = make(map[uint]uint)
|
||||||
|
|
||||||
|
func SetReadingAnchor(memberId uint, eventId uint) {
|
||||||
|
if val, ok := readingAnchorQueue[memberId]; ok {
|
||||||
|
readingAnchorQueue[memberId] = max(eventId, val)
|
||||||
|
} else {
|
||||||
|
readingAnchorQueue[memberId] = eventId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FlushReadingAnchor() {
|
||||||
|
if len(readingAnchorQueue) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for k, v := range readingAnchorQueue {
|
||||||
|
if err := database.C.Model(&models.ChannelMember{}).
|
||||||
|
Where("id = ?", k).
|
||||||
|
Updates(map[string]any{
|
||||||
|
"reading_anchor": gorm.Expr("GREATEST(COALESCE(reading_anchor, 0), ?)", v),
|
||||||
|
}).Error; err != nil {
|
||||||
|
log.Error().Err(err).Msg("An error occurred when flushing reading anchor...")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clear(readingAnchorQueue)
|
||||||
|
}
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"git.solsynth.dev/hydrogen/dealer/pkg/hyper"
|
|
||||||
"git.solsynth.dev/hydrogen/dealer/pkg/proto"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/database"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/gap"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/models"
|
|
||||||
"github.com/samber/lo"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetRealmWithExtID(id uint) (models.Realm, error) {
|
|
||||||
var realm models.Realm
|
|
||||||
pc, err := gap.H.GetServiceGrpcConn(hyper.ServiceTypeAuthProvider)
|
|
||||||
if err != nil {
|
|
||||||
return realm, err
|
|
||||||
}
|
|
||||||
response, err := proto.NewRealmClient(pc).GetRealm(context.Background(), &proto.LookupRealmRequest{
|
|
||||||
Id: lo.ToPtr(uint64(id)),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return realm, err
|
|
||||||
}
|
|
||||||
return LinkRealm(response)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetRealmWithAlias(alias string) (models.Realm, error) {
|
|
||||||
var realm models.Realm
|
|
||||||
pc, err := gap.H.GetServiceGrpcConn(hyper.ServiceTypeAuthProvider)
|
|
||||||
if err != nil {
|
|
||||||
return realm, err
|
|
||||||
}
|
|
||||||
response, err := proto.NewRealmClient(pc).GetRealm(context.Background(), &proto.LookupRealmRequest{
|
|
||||||
Alias: &alias,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return realm, err
|
|
||||||
}
|
|
||||||
return LinkRealm(response)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetRealmMember(realmId uint, userId uint) (*proto.RealmMemberInfo, error) {
|
|
||||||
var realm models.Realm
|
|
||||||
if err := database.C.Where("id = ?", realmId).First(&realm).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
pc, err := gap.H.GetServiceGrpcConn(hyper.ServiceTypeAuthProvider)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
response, err := proto.NewRealmClient(pc).GetRealmMember(context.Background(), &proto.RealmMemberLookupRequest{
|
|
||||||
RealmId: uint64(realm.ExternalID),
|
|
||||||
UserId: lo.ToPtr(uint64(userId)),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else {
|
|
||||||
return response, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ListRealmMember(realmId uint) ([]*proto.RealmMemberInfo, error) {
|
|
||||||
pc, err := gap.H.GetServiceGrpcConn(hyper.ServiceTypeAuthProvider)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
response, err := proto.NewRealmClient(pc).ListRealmMember(context.Background(), &proto.RealmMemberLookupRequest{
|
|
||||||
RealmId: uint64(realmId),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else {
|
|
||||||
return response.Data, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func LinkRealm(info *proto.RealmInfo) (models.Realm, error) {
|
|
||||||
var realm models.Realm
|
|
||||||
if info == nil {
|
|
||||||
return realm, fmt.Errorf("remote realm info was not found")
|
|
||||||
}
|
|
||||||
if err := database.C.Where(&models.Realm{
|
|
||||||
ExternalID: uint(info.Id),
|
|
||||||
}).First(&realm).Error; err != nil {
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
realm = models.Realm{
|
|
||||||
Alias: info.Alias,
|
|
||||||
Name: info.Name,
|
|
||||||
Description: info.Description,
|
|
||||||
IsPublic: info.IsPublic,
|
|
||||||
IsCommunity: info.IsCommunity,
|
|
||||||
ExternalID: uint(info.Id),
|
|
||||||
}
|
|
||||||
return realm, database.C.Save(&realm).Error
|
|
||||||
}
|
|
||||||
return realm, err
|
|
||||||
}
|
|
||||||
|
|
||||||
prev := realm
|
|
||||||
realm.Alias = info.Alias
|
|
||||||
realm.Name = info.Name
|
|
||||||
realm.Description = info.Description
|
|
||||||
realm.IsPublic = info.IsPublic
|
|
||||||
realm.IsCommunity = info.IsCommunity
|
|
||||||
|
|
||||||
var err error
|
|
||||||
if !reflect.DeepEqual(prev, realm) {
|
|
||||||
err = database.C.Save(&realm).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
return realm, err
|
|
||||||
}
|
|
||||||
58
pkg/internal/services/reply_token.go
Normal file
58
pkg/internal/services/reply_token.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ReplyClaims struct {
|
||||||
|
UserID uint `json:"user_id"`
|
||||||
|
EventID uint `json:"event_id"`
|
||||||
|
jwt.RegisteredClaims
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateReplyToken(eventId uint, userId uint) (string, error) {
|
||||||
|
claims := ReplyClaims{
|
||||||
|
UserID: userId,
|
||||||
|
EventID: eventId,
|
||||||
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
|
Issuer: "messaging",
|
||||||
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24 * 30)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS512, claims)
|
||||||
|
tks, err := token.SignedString([]byte(viper.GetString("security.reply_token_secret")))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to sign token: %v", err)
|
||||||
|
}
|
||||||
|
return tks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseReplyToken(tk string) (ReplyClaims, error) {
|
||||||
|
var claims ReplyClaims
|
||||||
|
token, err := jwt.ParseWithClaims(tk, &claims, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||||
|
return nil, fmt.Errorf("unexpected signing method: %v", token.Method)
|
||||||
|
}
|
||||||
|
return []byte(viper.GetString("security.reply_token_secret")), nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
// Check if the error is an expired token error
|
||||||
|
if errors.Is(err, jwt.ErrTokenExpired) {
|
||||||
|
// Treat expired token as valid (allow it)
|
||||||
|
return claims, nil
|
||||||
|
}
|
||||||
|
return claims, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !token.Valid {
|
||||||
|
return claims, fmt.Errorf("invalid token")
|
||||||
|
}
|
||||||
|
|
||||||
|
return claims, nil
|
||||||
|
}
|
||||||
91
pkg/internal/services/status.go
Normal file
91
pkg/internal/services/status.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/gap"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/models"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cachekit"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
type statusQueryCacheEntry struct {
|
||||||
|
Target []uint64
|
||||||
|
Data any
|
||||||
|
}
|
||||||
|
|
||||||
|
func KgTypingStatusCache(channelId uint, userId uint) string {
|
||||||
|
return fmt.Sprintf("chat-typing-status#%d@%d", userId, channelId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetTypingStatus(channelId uint, userId uint) error {
|
||||||
|
var broadcastTarget []uint64
|
||||||
|
var data any
|
||||||
|
|
||||||
|
hitCache := false
|
||||||
|
if val, err := cachekit.Get[statusQueryCacheEntry](
|
||||||
|
gap.Ca,
|
||||||
|
KgTypingStatusCache(channelId, userId),
|
||||||
|
); err == nil {
|
||||||
|
broadcastTarget = val.Target
|
||||||
|
data = val.Data
|
||||||
|
hitCache = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hitCache {
|
||||||
|
var member models.ChannelMember
|
||||||
|
if err := database.C.
|
||||||
|
Where("account_id = ? AND channel_id = ?", userId, channelId).
|
||||||
|
First(&member).Error; err != nil {
|
||||||
|
return fmt.Errorf("channel member not found: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var channel models.Channel
|
||||||
|
if err := database.C.
|
||||||
|
Preload("Members").
|
||||||
|
Where("id = ?", channelId).
|
||||||
|
First(&channel).Error; err != nil {
|
||||||
|
return fmt.Errorf("channel not found: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range channel.Members {
|
||||||
|
broadcastTarget = append(broadcastTarget, uint64(item.AccountID))
|
||||||
|
}
|
||||||
|
|
||||||
|
data = map[string]any{
|
||||||
|
"user_id": userId,
|
||||||
|
"member_id": member.ID,
|
||||||
|
"channel_id": channelId,
|
||||||
|
"member": member,
|
||||||
|
"channel": channel,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache queries
|
||||||
|
cachekit.Set(
|
||||||
|
gap.Ca,
|
||||||
|
KgTypingStatusCache(channelId, userId),
|
||||||
|
statusQueryCacheEntry{broadcastTarget, data},
|
||||||
|
60*time.Minute,
|
||||||
|
fmt.Sprintf("channel#%d", channelId),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastTarget = lo.Filter(broadcastTarget, func(item uint64, index int) bool {
|
||||||
|
if !viper.GetBool("performance.passive_user_optimize") {
|
||||||
|
// Leave this for backward compatibility
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return CheckSubscribed(uint(item), channelId)
|
||||||
|
})
|
||||||
|
|
||||||
|
PushCommandBatch(broadcastTarget, nex.WebSocketPackage{
|
||||||
|
Action: "status.typing",
|
||||||
|
Payload: data,
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
63
pkg/internal/services/subscribe.go
Normal file
63
pkg/internal/services/subscribe.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
// ChannelID -> UserID -> Client ID
|
||||||
|
var subscribeInfo = make(map[uint]map[uint]string)
|
||||||
|
var subscribeLock sync.Mutex
|
||||||
|
|
||||||
|
// If user subscribed to a channel
|
||||||
|
// Push the new message to them via websocket
|
||||||
|
// And skip the notification
|
||||||
|
|
||||||
|
func CheckSubscribed(UserID uint, ChannelID uint) bool {
|
||||||
|
if _, ok := subscribeInfo[ChannelID]; ok {
|
||||||
|
if _, ok := subscribeInfo[ChannelID][UserID]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func SubscribeChannel(userId uint, channelId uint, clientId string) {
|
||||||
|
subscribeLock.Lock()
|
||||||
|
defer subscribeLock.Unlock()
|
||||||
|
if _, ok := subscribeInfo[channelId]; !ok {
|
||||||
|
subscribeInfo[channelId] = make(map[uint]string)
|
||||||
|
}
|
||||||
|
subscribeInfo[channelId][userId] = clientId
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnsubscribeChannel(userId uint, channelId uint) {
|
||||||
|
subscribeLock.Lock()
|
||||||
|
defer subscribeLock.Unlock()
|
||||||
|
if _, ok := subscribeInfo[channelId]; ok {
|
||||||
|
delete(subscribeInfo[channelId], userId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnsubscribeAll(userId uint) {
|
||||||
|
subscribeLock.Lock()
|
||||||
|
defer subscribeLock.Unlock()
|
||||||
|
for _, v := range subscribeInfo {
|
||||||
|
delete(v, userId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnsubscribeAllWithChannels(channelId uint) {
|
||||||
|
subscribeLock.Lock()
|
||||||
|
defer subscribeLock.Unlock()
|
||||||
|
delete(subscribeInfo, channelId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnsubscribeAllWithClient(clientId string) {
|
||||||
|
subscribeLock.Lock()
|
||||||
|
defer subscribeLock.Unlock()
|
||||||
|
for _, v := range subscribeInfo {
|
||||||
|
for k, item := range v {
|
||||||
|
if item == clientId {
|
||||||
|
delete(v, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,19 +2,47 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"git.solsynth.dev/hydrogen/dealer/pkg/proto"
|
"strconv"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/gap"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/models"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
|
"github.com/samber/lo"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/gap"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func PushCommand(userId uint, task models.UnifiedCommand) {
|
func PushCommand(userId uint, task nex.WebSocketPackage) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
pc := gap.H.GetDealerGrpcConn()
|
pc := gap.Nx.GetNexusGrpcConn()
|
||||||
_, _ = proto.NewStreamControllerClient(pc).PushStream(ctx, &proto.PushStreamRequest{
|
_, err := proto.NewStreamServiceClient(pc).PushStream(ctx, &proto.PushStreamRequest{
|
||||||
UserId: uint64(userId),
|
UserId: lo.ToPtr(uint64(userId)),
|
||||||
Body: task.Marshal(),
|
Body: task.Marshal(),
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msg("Failed to push websocket command to nexus...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PushCommandBatch(userId []uint64, task nex.WebSocketPackage) []uint64 {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
pc := gap.Nx.GetNexusGrpcConn()
|
||||||
|
resp, err := proto.NewStreamServiceClient(pc).PushStreamBatch(ctx, &proto.PushStreamBatchRequest{
|
||||||
|
UserId: userId,
|
||||||
|
Body: task.Marshal(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msg("Failed to push websocket command to nexus in batches...")
|
||||||
|
}
|
||||||
|
|
||||||
|
return lo.Map(resp.GetSuccessList(), func(item string, _ int) uint64 {
|
||||||
|
val, _ := strconv.ParseUint(item, 10, 64)
|
||||||
|
return val
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 72 KiB |
@@ -1,28 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport"
|
|
||||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
||||||
|
|
||||||
<link rel="icon" type="image/png" href="favicon.png">
|
|
||||||
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap" rel="stylesheet">
|
|
||||||
|
|
||||||
<title>Hydrogen.Interactive</title>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
html, body {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
font-family: Roboto Mono, monospace;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
{{embed}}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
<div class="container">
|
|
||||||
<div>
|
|
||||||
<img src="/favicon.png" width="128" height="128" alt="Icon"/>
|
|
||||||
|
|
||||||
<p class="caption text-blinking">Launching Solian... 🚀</p>
|
|
||||||
<p class="description">
|
|
||||||
Hold on a second... <br/>
|
|
||||||
We are redirecting you to our application...
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function redirect() {
|
|
||||||
window.location.href = {{ .frontend }}
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => redirect(), 1850)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.container {
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.caption {
|
|
||||||
margin-top: 4px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-blinking {
|
|
||||||
animation: text-blinking ease-in-out infinite 1.5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description {
|
|
||||||
margin-top: 4px;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes text-blinking {
|
|
||||||
0% {
|
|
||||||
opacity: 100%;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
opacity: 10%;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,15 +1,21 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/database"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/gap"
|
authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/models"
|
"sync"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/services"
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/web/exts"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/models"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/services"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var callLocks sync.Map
|
||||||
|
|
||||||
func listCall(c *fiber.Ctx) error {
|
func listCall(c *fiber.Ctx) error {
|
||||||
take := c.QueryInt("take", 0)
|
take := c.QueryInt("take", 0)
|
||||||
offset := c.QueryInt("offset", 0)
|
offset := c.QueryInt("offset", 0)
|
||||||
@@ -41,16 +47,19 @@ func getOngoingCall(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
if call, err := services.GetOngoingCall(channel); err != nil {
|
if call, err := services.GetOngoingCall(channel); err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
} else if res, err := services.GetCallParticipants(call); err != nil {
|
||||||
|
return c.JSON(call)
|
||||||
} else {
|
} else {
|
||||||
|
call.Participants = res
|
||||||
return c.JSON(call)
|
return c.JSON(call)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func startCall(c *fiber.Ctx) error {
|
func startCall(c *fiber.Ctx) error {
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
if err := sec.EnsureGrantedPerm(c, "CreateCalls", true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user := c.Locals("user").(models.Account)
|
user := c.Locals("user").(authm.Account)
|
||||||
alias := c.Params("channel")
|
alias := c.Params("channel")
|
||||||
|
|
||||||
var channel models.Channel
|
var channel models.Channel
|
||||||
@@ -66,10 +75,19 @@ func startCall(c *fiber.Ctx) error {
|
|||||||
AccountID: user.ID,
|
AccountID: user.ID,
|
||||||
}).Find(&membership).Error; err != nil {
|
}).Find(&membership).Error; err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
} else if membership.PowerLevel < 0 {
|
||||||
|
return fiber.NewError(fiber.StatusForbidden, "you have not enough permission to create a call")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := callLocks.Load(channel.ID); ok {
|
||||||
|
return fiber.NewError(fiber.StatusLocked, "there is already a call in creation progress for this channel")
|
||||||
|
} else {
|
||||||
|
callLocks.Store(channel.ID, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
call, err := services.NewCall(channel, membership)
|
call, err := services.NewCall(channel, membership)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
callLocks.Delete(channel.ID)
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
_, _ = services.NewEvent(models.Event{
|
_, _ = services.NewEvent(models.Event{
|
||||||
@@ -82,15 +100,16 @@ func startCall(c *fiber.Ctx) error {
|
|||||||
SenderID: membership.ID,
|
SenderID: membership.ID,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
callLocks.Delete(channel.ID)
|
||||||
return c.JSON(call)
|
return c.JSON(call)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func endCall(c *fiber.Ctx) error {
|
func endCall(c *fiber.Ctx) error {
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user := c.Locals("user").(models.Account)
|
user := c.Locals("user").(authm.Account)
|
||||||
alias := c.Params("channel")
|
alias := c.Params("channel")
|
||||||
|
|
||||||
var channel models.Channel
|
var channel models.Channel
|
||||||
@@ -111,8 +130,8 @@ func endCall(c *fiber.Ctx) error {
|
|||||||
call, err := services.GetOngoingCall(channel)
|
call, err := services.GetOngoingCall(channel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
} else if call.FounderID != user.ID && membership.PowerLevel < 100 {
|
} else if call.FounderID != membership.ID && membership.PowerLevel < 50 {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "only call founder or channel admin can end this call")
|
return fiber.NewError(fiber.StatusBadRequest, "only call founder or channel moderator can end this call")
|
||||||
}
|
}
|
||||||
|
|
||||||
if call, err := services.EndCall(call); err != nil {
|
if call, err := services.EndCall(call); err != nil {
|
||||||
@@ -132,11 +151,53 @@ func endCall(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func exchangeCallToken(c *fiber.Ctx) error {
|
func kickParticipantInCall(c *fiber.Ctx) error {
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user := c.Locals("user").(models.Account)
|
user := c.Locals("user").(authm.Account)
|
||||||
|
alias := c.Params("channel")
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
Username string `json:"username" validate:"required"`
|
||||||
|
}
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var channel models.Channel
|
||||||
|
if err := database.C.Where(&models.Channel{
|
||||||
|
Alias: alias,
|
||||||
|
}).First(&channel).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var membership models.ChannelMember
|
||||||
|
if err := database.C.Where(&models.ChannelMember{
|
||||||
|
ChannelID: channel.ID,
|
||||||
|
AccountID: user.ID,
|
||||||
|
}).Find(&membership).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
call, err := services.GetOngoingCall(channel)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
} else if call.FounderID != user.ID && membership.PowerLevel < 50 {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "only call founder or channel admin can kick participant in this call")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = services.KickParticipantInCall(call, data.Username); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func exchangeCallToken(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(authm.Account)
|
||||||
alias := c.Params("channel")
|
alias := c.Params("channel")
|
||||||
|
|
||||||
var channel models.Channel
|
var channel models.Channel
|
||||||
267
pkg/internal/web/api/channel_members_api.go
Normal file
267
pkg/internal/web/api/channel_members_api.go
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/gap"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/web/exts"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cruda"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit"
|
||||||
|
authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/models"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/services"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func listChannelMembers(c *fiber.Ctx) error {
|
||||||
|
alias := c.Params("channel")
|
||||||
|
take := c.QueryInt("take", 0)
|
||||||
|
offset := c.QueryInt("offset", 0)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var channel models.Channel
|
||||||
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
|
channel, err = services.GetChannelWithAlias(alias, val.ID)
|
||||||
|
} else {
|
||||||
|
channel, err = services.GetChannelWithAlias(alias)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := services.CountChannelMember(channel.ID)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if members, err := services.ListChannelMember(channel.ID, take, offset); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
} else {
|
||||||
|
return c.JSON(fiber.Map{
|
||||||
|
"count": count,
|
||||||
|
"data": members,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addChannelMember(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(authm.Account)
|
||||||
|
alias := c.Params("channel")
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
Related string `json:"related" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var channel models.Channel
|
||||||
|
if err := database.C.Where(&models.Channel{
|
||||||
|
Alias: alias,
|
||||||
|
}).First(&channel).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
} else if channel.Type == models.ChannelTypeDirect {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "direct message member changes was not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !channel.IsPublic {
|
||||||
|
if member, err := services.GetChannelMember(user, channel.ID); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusForbidden, err.Error())
|
||||||
|
} else if member.PowerLevel < 50 {
|
||||||
|
return fiber.NewError(fiber.StatusForbidden, "you must be a moderator of a channel to add member into it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var account authm.Account
|
||||||
|
var numericId int
|
||||||
|
if numericId, err = strconv.Atoi(data.Related); err == nil {
|
||||||
|
account, err = authkit.GetUser(gap.Nx, uint(numericId))
|
||||||
|
} else {
|
||||||
|
account, err = authkit.GetUserByName(gap.Nx, data.Related)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := services.AddChannelMemberWithCheck(account, user, channel); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
} else {
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeChannelMember(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(authm.Account)
|
||||||
|
alias := c.Params("channel")
|
||||||
|
memberId := c.Params("memberId")
|
||||||
|
|
||||||
|
numericId, err := strconv.Atoi(memberId)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "invalid member id")
|
||||||
|
}
|
||||||
|
|
||||||
|
var channel models.Channel
|
||||||
|
if err := database.C.Where(&models.Channel{
|
||||||
|
Alias: alias,
|
||||||
|
}).First(&channel).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
} else if channel.Type == models.ChannelTypeDirect {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "direct message member changes was not allowed")
|
||||||
|
} else if channel.AccountID == user.ID {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "you cannot remove yourself from your own channel")
|
||||||
|
}
|
||||||
|
|
||||||
|
var member models.ChannelMember
|
||||||
|
if me, err := services.GetChannelMember(user, channel.ID); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusForbidden, err.Error())
|
||||||
|
} else if me.PowerLevel < 50 {
|
||||||
|
return fiber.NewError(fiber.StatusForbidden, "you must be a moderator of a channel to remove member from it")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := database.C.Where(&models.ChannelMember{
|
||||||
|
BaseModel: cruda.BaseModel{ID: uint(numericId)},
|
||||||
|
ChannelID: channel.ID,
|
||||||
|
}).First(&member).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := services.RemoveChannelMember(member, channel); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
} else {
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteChannelIdentity(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(authm.Account)
|
||||||
|
alias := c.Params("channel")
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var channel models.Channel
|
||||||
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
|
channel, err = services.GetChannelWithAlias(alias, val.ID)
|
||||||
|
} else {
|
||||||
|
channel, err = services.GetChannelWithAlias(alias)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var membership models.ChannelMember
|
||||||
|
if err := database.C.Where(&models.ChannelMember{
|
||||||
|
ChannelID: channel.ID,
|
||||||
|
AccountID: user.ID,
|
||||||
|
}).First(&membership).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = services.RemoveChannelMember(membership, channel); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
} else {
|
||||||
|
return c.JSON(membership)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func editChannelIdentity(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(authm.Account)
|
||||||
|
alias := c.Params("channel")
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
Nick string `json:"nick"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var channel models.Channel
|
||||||
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
|
channel, err = services.GetChannelWithAlias(alias, val.ID)
|
||||||
|
} else {
|
||||||
|
channel, err = services.GetChannelWithAlias(alias)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var membership models.ChannelMember
|
||||||
|
if err := database.C.Where(&models.ChannelMember{
|
||||||
|
ChannelID: channel.ID,
|
||||||
|
AccountID: user.ID,
|
||||||
|
}).First(&membership).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
membership.Name = user.Name
|
||||||
|
if len(data.Nick) > 0 {
|
||||||
|
membership.Nick = data.Nick
|
||||||
|
} else {
|
||||||
|
membership.Nick = user.Nick
|
||||||
|
}
|
||||||
|
|
||||||
|
if membership, err := services.EditChannelMember(membership); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
} else {
|
||||||
|
return c.JSON(membership)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func editChannelNotifyLevel(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(authm.Account)
|
||||||
|
alias := c.Params("channel")
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
NotifyLevel int8 `json:"notify_level"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var channel models.Channel
|
||||||
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
|
channel, err = services.GetChannelWithAlias(alias, val.ID)
|
||||||
|
} else {
|
||||||
|
channel, err = services.GetChannelWithAlias(alias)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var membership models.ChannelMember
|
||||||
|
if err := database.C.Where(&models.ChannelMember{
|
||||||
|
ChannelID: channel.ID,
|
||||||
|
AccountID: user.ID,
|
||||||
|
}).First(&membership).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
membership.Notify = data.NotifyLevel
|
||||||
|
|
||||||
|
if membership, err := services.EditChannelMember(membership); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
} else {
|
||||||
|
return c.JSON(membership)
|
||||||
|
}
|
||||||
|
}
|
||||||
348
pkg/internal/web/api/channels_api.go
Normal file
348
pkg/internal/web/api/channels_api.go
Normal file
@@ -0,0 +1,348 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/gap"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit"
|
||||||
|
authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/web/exts"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/models"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/services"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getChannel(c *fiber.Ctx) error {
|
||||||
|
alias := c.Params("channel")
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var channel models.Channel
|
||||||
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
|
channel, err = services.GetChannelWithAlias(alias, val.ID)
|
||||||
|
} else {
|
||||||
|
channel, err = services.GetChannelWithAlias(alias)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getChannelIdentity(c *fiber.Ctx) error {
|
||||||
|
alias := c.Params("channel")
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(authm.Account)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var channel models.Channel
|
||||||
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
|
channel, err = services.GetChannelWithAlias(alias, val.ID)
|
||||||
|
} else {
|
||||||
|
channel, err = services.GetChannelWithAlias(alias)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if member, err := services.GetChannelMember(user, channel.ID); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
} else {
|
||||||
|
return c.JSON(member)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func listChannel(c *fiber.Ctx) error {
|
||||||
|
var user *authm.Account
|
||||||
|
if err := sec.EnsureAuthenticated(c); err == nil {
|
||||||
|
user = lo.ToPtr(c.Locals("user").(authm.Account))
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var channels []models.Channel
|
||||||
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
|
channels, err = services.ListChannel(user, val.ID)
|
||||||
|
} else {
|
||||||
|
channels, err = services.ListChannel(user)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(channels)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listPublicChannel(c *fiber.Ctx) error {
|
||||||
|
var err error
|
||||||
|
var channels []models.Channel
|
||||||
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
|
channels, err = services.ListChannelPublic(val.ID)
|
||||||
|
} else {
|
||||||
|
channels, err = services.ListChannelPublic()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(channels)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listOwnedChannel(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(authm.Account)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var channels []models.Channel
|
||||||
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
|
channels, err = services.ListChannelWithUser(user, val.ID)
|
||||||
|
} else {
|
||||||
|
channels, err = services.ListChannelWithUser(user)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(channels)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listOwnedChannelGlobalWide(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(authm.Account)
|
||||||
|
|
||||||
|
channels, err := services.ListChannelWithUser(user, 0)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(channels)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listAvailableChannel(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(authm.Account)
|
||||||
|
|
||||||
|
tx := database.C
|
||||||
|
isDirect := c.QueryBool("direct", false)
|
||||||
|
if isDirect {
|
||||||
|
tx = tx.Where("type = ?", models.ChannelTypeDirect)
|
||||||
|
} else {
|
||||||
|
tx = tx.Where("type = ?", models.ChannelTypeCommon)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var channels []models.Channel
|
||||||
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
|
channels, err = services.ListAvailableChannel(tx, user, val.ID)
|
||||||
|
} else {
|
||||||
|
channels, err = services.ListAvailableChannel(tx, user)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(channels)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listAvailableChannelGlobalWide(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(authm.Account)
|
||||||
|
|
||||||
|
channels, err := services.ListAvailableChannel(database.C, user, 0)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(channels)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createChannel(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureGrantedPerm(c, "CreateChannels", true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(authm.Account)
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
Alias string `json:"alias" validate:"required,lowercase,min=4,max=32"`
|
||||||
|
Name string `json:"name" validate:"required"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
IsPublic bool `json:"is_public"`
|
||||||
|
IsCommunity bool `json:"is_community"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
} else if err = services.GetChannelAliasAvailability(data.Alias); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var realm *authm.Realm
|
||||||
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
|
if info, err := authkit.GetRealmMember(gap.Nx, val.ID, user.ID); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusForbidden, "you must be a part of that realm then can create channel related to it")
|
||||||
|
} else if info.PowerLevel < 50 {
|
||||||
|
return fiber.NewError(fiber.StatusForbidden, "you must be a moderator of that realm then can create channel related to it")
|
||||||
|
} else {
|
||||||
|
realm = &val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
channel := models.Channel{
|
||||||
|
Alias: data.Alias,
|
||||||
|
Name: data.Name,
|
||||||
|
Description: data.Description,
|
||||||
|
AccountID: user.ID,
|
||||||
|
Type: models.ChannelTypeCommon,
|
||||||
|
IsPublic: data.IsPublic,
|
||||||
|
IsCommunity: data.IsCommunity,
|
||||||
|
Members: []models.ChannelMember{
|
||||||
|
{AccountID: user.ID, PowerLevel: 100},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if realm != nil {
|
||||||
|
channel.RealmID = &realm.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
channel, err := services.NewChannel(channel)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func editChannel(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(authm.Account)
|
||||||
|
id, _ := c.ParamsInt("channelId", 0)
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
Alias string `json:"alias" validate:"required,min=4,max=32"`
|
||||||
|
Name string `json:"name" validate:"required"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
IsPublic bool `json:"is_public"`
|
||||||
|
IsCommunity bool `json:"is_community"`
|
||||||
|
NewBelongsRealm *string `json:"new_belongs_realm"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := database.C.Where("id = ?", id)
|
||||||
|
|
||||||
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
|
if info, err := authkit.GetRealmMember(gap.Nx, val.ID, user.ID); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusForbidden, "you must be a part of that realm then can edit channel related to it")
|
||||||
|
} else if info.PowerLevel < 50 {
|
||||||
|
return fiber.NewError(fiber.StatusForbidden, "you must be a moderator of that realm then can edit channel related to it")
|
||||||
|
} else {
|
||||||
|
tx = tx.Where("realm_id = ?", val.ID)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tx = tx.Where("realm_id IS NULL")
|
||||||
|
}
|
||||||
|
|
||||||
|
var channel models.Channel
|
||||||
|
if err := tx.First(&channel).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if channel.RealmID != nil {
|
||||||
|
if member, err := services.GetChannelMember(user, channel.ID); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusForbidden, "you must be a part of this channel to edit it")
|
||||||
|
} else if member.PowerLevel < 100 {
|
||||||
|
return fiber.NewError(fiber.StatusForbidden, "you must be channel admin to edit it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.NewBelongsRealm != nil {
|
||||||
|
if *data.NewBelongsRealm == "global" {
|
||||||
|
channel.RealmID = nil
|
||||||
|
} else {
|
||||||
|
realm, err := authkit.GetRealmByAlias(gap.Nx, *data.NewBelongsRealm)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("requested channel with realm, but realm was not found: %v", err))
|
||||||
|
} else {
|
||||||
|
if info, err := authkit.GetRealmMember(gap.Nx, realm.ID, user.ID); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusForbidden, "you must be a part of that realm then can transfer channel related to it")
|
||||||
|
} else if info.PowerLevel < 50 {
|
||||||
|
return fiber.NewError(fiber.StatusForbidden, "you must be a moderator of that realm then can transfer channel related to it")
|
||||||
|
} else {
|
||||||
|
channel.RealmID = &realm.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.Alias = data.Alias
|
||||||
|
channel.Name = data.Name
|
||||||
|
channel.Description = data.Description
|
||||||
|
channel.IsPublic = data.IsPublic
|
||||||
|
channel.IsCommunity = data.IsCommunity
|
||||||
|
|
||||||
|
channel, err := services.EditChannel(channel)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteChannel(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(authm.Account)
|
||||||
|
id, _ := c.ParamsInt("channelId", 0)
|
||||||
|
|
||||||
|
tx := database.C.Where("id = ?", id)
|
||||||
|
|
||||||
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
|
if info, err := authkit.GetRealmMember(gap.Nx, val.ID, user.ID); err != nil {
|
||||||
|
return fmt.Errorf("you must be a part of that realm then can delete channel related to it")
|
||||||
|
} else if info.PowerLevel < 50 {
|
||||||
|
return fmt.Errorf("you must be a moderator of that realm then can delete channel related to it")
|
||||||
|
} else {
|
||||||
|
tx = tx.Where("realm_id = ?", val.ID)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tx = tx.Where("(account_id = ? OR type = ?) AND realm_id IS NULL", user.ID, models.ChannelTypeDirect)
|
||||||
|
}
|
||||||
|
|
||||||
|
var channel models.Channel
|
||||||
|
if err := tx.First(&channel).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if channel.Type == models.ChannelTypeDirect {
|
||||||
|
if member, err := services.GetChannelMember(user, channel.ID); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusForbidden, "you must related to this direct message if you want delete it")
|
||||||
|
} else if member.PowerLevel < 100 {
|
||||||
|
return fiber.NewError(fiber.StatusForbidden, "you must be a moderator of this direct message if you want delete it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := services.DeleteChannel(channel); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
||||||
@@ -2,27 +2,29 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/gap"
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/gap"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/server/exts"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit"
|
||||||
|
authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/database"
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/web/exts"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/models"
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/services"
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/models"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/services"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createDirectChannel(c *fiber.Ctx) error {
|
func createDirectChannel(c *fiber.Ctx) error {
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user := c.Locals("user").(models.Account)
|
user := c.Locals("user").(authm.Account)
|
||||||
|
|
||||||
var data struct {
|
var data struct {
|
||||||
Alias string `json:"alias" validate:"required,lowercase,min=4,max=32"`
|
Alias string `json:"alias" validate:"required,lowercase,min=4,max=32"`
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
RelatedUser uint `json:"related_user"`
|
RelatedUser uint `json:"related_user"`
|
||||||
IsEncrypted bool `json:"is_encrypted"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := exts.BindAndValidate(c, &data); err != nil {
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
@@ -31,21 +33,19 @@ func createDirectChannel(c *fiber.Ctx) error {
|
|||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
var realm *models.Realm
|
var realm *authm.Realm
|
||||||
if val, ok := c.Locals("realm").(models.Realm); ok {
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
if info, err := services.GetRealmMember(val.ExternalID, user.ExternalID); err != nil {
|
if info, err := authkit.GetRealmMember(gap.Nx, val.ID, user.ID); err != nil {
|
||||||
return fiber.NewError(fiber.StatusForbidden, "you must be a part of that realm then can create channel related to it")
|
return fiber.NewError(fiber.StatusForbidden, "you must be a part of that realm then can create channel related to it")
|
||||||
} else if info.GetPowerLevel() < 50 {
|
} else if info.PowerLevel < 50 {
|
||||||
return fiber.NewError(fiber.StatusForbidden, "you must be a moderator of that realm then can create channel related to it")
|
return fiber.NewError(fiber.StatusForbidden, "you must be a moderator of that realm then can create channel related to it")
|
||||||
} else {
|
} else {
|
||||||
realm = &val
|
realm = &val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var relatedUser models.Account
|
relatedUser, err := authkit.GetUser(gap.Nx, data.RelatedUser)
|
||||||
if err := database.C.
|
if err != nil {
|
||||||
Where("external_id = ?", data.RelatedUser).
|
|
||||||
First(&relatedUser).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("unable to find related user: %v", err))
|
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("unable to find related user: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,11 +53,16 @@ func createDirectChannel(c *fiber.Ctx) error {
|
|||||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("you already have a direct with that user #%d", ch.ID))
|
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("you already have a direct with that user #%d", ch.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := authkit.EnsureUserPermGranted(gap.Nx, user.ID, relatedUser.ID, "ChannelAdd", true); err != nil {
|
||||||
|
return fmt.Errorf("unable to add user into your channel due to access denied: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
channel := models.Channel{
|
channel := models.Channel{
|
||||||
Alias: data.Alias,
|
Alias: data.Alias,
|
||||||
Name: data.Name,
|
Name: data.Name,
|
||||||
Description: data.Description,
|
Description: data.Description,
|
||||||
IsEncrypted: data.IsEncrypted,
|
IsPublic: false,
|
||||||
|
IsCommunity: false,
|
||||||
AccountID: user.ID,
|
AccountID: user.ID,
|
||||||
Type: models.ChannelTypeDirect,
|
Type: models.ChannelTypeDirect,
|
||||||
Members: []models.ChannelMember{
|
Members: []models.ChannelMember{
|
||||||
@@ -70,7 +75,7 @@ func createDirectChannel(c *fiber.Ctx) error {
|
|||||||
channel.RealmID = &realm.ID
|
channel.RealmID = &realm.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
channel, err := services.NewChannel(channel)
|
channel, err = services.NewChannel(channel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
@@ -2,25 +2,28 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||||
|
authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/gap"
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/web/exts"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/models"
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/models"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/server/exts"
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/services"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/services"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getEvent(c *fiber.Ctx) error {
|
func getEvent(c *fiber.Ctx) error {
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user := c.Locals("user").(models.Account)
|
user := c.Locals("user").(authm.Account)
|
||||||
alias := c.Params("channel")
|
alias := c.Params("channel")
|
||||||
id, _ := c.ParamsInt("eventId")
|
id, _ := c.ParamsInt("eventId")
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var channel models.Channel
|
var channel models.Channel
|
||||||
if val, ok := c.Locals("realm").(models.Realm); ok {
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
channel, err = services.GetChannelWithAlias(alias, val.ID)
|
channel, err = services.GetChannelWithAlias(alias, val.ID)
|
||||||
} else {
|
} else {
|
||||||
channel, err = services.GetChannelWithAlias(alias)
|
channel, err = services.GetChannelWithAlias(alias)
|
||||||
@@ -31,7 +34,7 @@ func getEvent(c *fiber.Ctx) error {
|
|||||||
return fiber.NewError(fiber.StatusForbidden, fmt.Sprintf("you need join the channel before you read the messages: %v", err))
|
return fiber.NewError(fiber.StatusForbidden, fmt.Sprintf("you need join the channel before you read the messages: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
event, err := services.GetEvent(channel, uint(id))
|
event, err := services.GetEvent(channel.ID, uint(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
}
|
||||||
@@ -40,17 +43,17 @@ func getEvent(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func listEvent(c *fiber.Ctx) error {
|
func listEvent(c *fiber.Ctx) error {
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user := c.Locals("user").(models.Account)
|
user := c.Locals("user").(authm.Account)
|
||||||
take := c.QueryInt("take", 0)
|
take := c.QueryInt("take", 0)
|
||||||
offset := c.QueryInt("offset", 0)
|
offset := c.QueryInt("offset", 0)
|
||||||
alias := c.Params("channel")
|
alias := c.Params("channel")
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var channel models.Channel
|
var channel models.Channel
|
||||||
if val, ok := c.Locals("realm").(models.Realm); ok {
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
channel, err = services.GetChannelWithAlias(alias, val.ID)
|
channel, err = services.GetChannelWithAlias(alias, val.ID)
|
||||||
} else {
|
} else {
|
||||||
channel, err = services.GetChannelWithAlias(alias)
|
channel, err = services.GetChannelWithAlias(alias)
|
||||||
@@ -73,11 +76,50 @@ func listEvent(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRawEvent(c *fiber.Ctx) error {
|
func checkHasNewEvent(c *fiber.Ctx) error {
|
||||||
if err := gap.H.EnsureGrantedPerm(c, "CreateMessagingRawEvent", true); err != nil {
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user := c.Locals("user").(models.Account)
|
user := c.Locals("user").(authm.Account)
|
||||||
|
pivot := c.QueryInt("pivot", 0)
|
||||||
|
alias := c.Params("channel")
|
||||||
|
|
||||||
|
if pivot < 1 {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "pivot must be greater than zero")
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var channel models.Channel
|
||||||
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
|
channel, err = services.GetChannelWithAlias(alias, val.ID)
|
||||||
|
} else {
|
||||||
|
channel, err = services.GetChannelWithAlias(alias)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
} else if _, _, err := services.GetAvailableChannel(channel.ID, user); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusForbidden, fmt.Sprintf("you need join the channel before you read the messages: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
var count int64
|
||||||
|
if err = database.C.
|
||||||
|
Where("channel_id = ?", channel.ID).
|
||||||
|
Where("id > ?", pivot).
|
||||||
|
Model(&models.Event{}).Count(&count).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
} else {
|
||||||
|
return c.JSON(fiber.Map{
|
||||||
|
"up_to_date": lo.Ternary(count > 0, false, true),
|
||||||
|
"count": count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRawEvent(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureGrantedPerm(c, "CreateMessagingRawEvent", true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(authm.Account)
|
||||||
alias := c.Params("channel")
|
alias := c.Params("channel")
|
||||||
|
|
||||||
var data struct {
|
var data struct {
|
||||||
@@ -95,20 +137,17 @@ func newRawEvent(c *fiber.Ctx) error {
|
|||||||
var err error
|
var err error
|
||||||
var channel models.Channel
|
var channel models.Channel
|
||||||
var member models.ChannelMember
|
var member models.ChannelMember
|
||||||
if val, ok := c.Locals("realm").(models.Realm); ok {
|
|
||||||
channel, member, err = services.GetAvailableChannelWithAlias(alias, user, val.ID)
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
if err != nil {
|
channel, member, err = services.GetChannelIdentity(alias, user.ID, val)
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
} else if member.PowerLevel < 0 {
|
|
||||||
return fiber.NewError(fiber.StatusForbidden, "you have not enough permission to send message")
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
channel, member, err = services.GetAvailableChannelWithAlias(alias, user)
|
channel, member, err = services.GetChannelIdentity(alias, user.ID)
|
||||||
if err != nil {
|
}
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
} else if member.PowerLevel < 0 {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusForbidden, "you have not enough permission to send message")
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
} else if member.PowerLevel < 0 {
|
||||||
|
return fiber.NewError(fiber.StatusForbidden, "you have not enough permission to send message")
|
||||||
}
|
}
|
||||||
|
|
||||||
event := models.Event{
|
event := models.Event{
|
||||||
@@ -1,19 +1,22 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/gap"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/models"
|
authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/server/exts"
|
"strings"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/services"
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/web/exts"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/models"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/services"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newMessageEvent(c *fiber.Ctx) error {
|
func newMessageEvent(c *fiber.Ctx) error {
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user := c.Locals("user").(models.Account)
|
user := c.Locals("user").(authm.Account)
|
||||||
alias := c.Params("channel")
|
alias := c.Params("channel")
|
||||||
|
|
||||||
var data struct {
|
var data struct {
|
||||||
@@ -28,27 +31,25 @@ func newMessageEvent(c *fiber.Ctx) error {
|
|||||||
return fiber.NewError(fiber.StatusBadRequest, "message uuid was not valid")
|
return fiber.NewError(fiber.StatusBadRequest, "message uuid was not valid")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data.Body.Text = strings.TrimSpace(data.Body.Text)
|
||||||
if len(data.Body.Text) == 0 && len(data.Body.Attachments) == 0 {
|
if len(data.Body.Text) == 0 && len(data.Body.Attachments) == 0 {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "you cannot send an empty message")
|
return fiber.NewError(fiber.StatusBadRequest, "empty message was not allowed")
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var channel models.Channel
|
var channel models.Channel
|
||||||
var member models.ChannelMember
|
var member models.ChannelMember
|
||||||
if val, ok := c.Locals("realm").(models.Realm); ok {
|
|
||||||
channel, member, err = services.GetAvailableChannelWithAlias(alias, user, val.ID)
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
if err != nil {
|
channel, member, err = services.GetChannelIdentity(alias, user.ID, val)
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
} else if member.PowerLevel < 0 {
|
|
||||||
return fiber.NewError(fiber.StatusForbidden, "you have not enough permission to send message")
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
channel, member, err = services.GetAvailableChannelWithAlias(alias, user)
|
channel, member, err = services.GetChannelIdentity(alias, user.ID)
|
||||||
if err != nil {
|
}
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
} else if member.PowerLevel < 0 {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusForbidden, "you have not enough permission to send message")
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
} else if member.PowerLevel < 0 {
|
||||||
|
return fiber.NewError(fiber.StatusForbidden, "unable to send message, access denied")
|
||||||
}
|
}
|
||||||
|
|
||||||
var parsed map[string]any
|
var parsed map[string]any
|
||||||
@@ -56,13 +57,15 @@ func newMessageEvent(c *fiber.Ctx) error {
|
|||||||
_ = jsoniter.Unmarshal(raw, &parsed)
|
_ = jsoniter.Unmarshal(raw, &parsed)
|
||||||
|
|
||||||
event := models.Event{
|
event := models.Event{
|
||||||
Uuid: data.Uuid,
|
Uuid: data.Uuid,
|
||||||
Body: parsed,
|
Body: parsed,
|
||||||
Type: data.Type,
|
Type: data.Type,
|
||||||
Sender: member,
|
Sender: member,
|
||||||
Channel: channel,
|
Channel: channel,
|
||||||
ChannelID: channel.ID,
|
QuoteEventID: data.Body.QuoteEventID,
|
||||||
SenderID: member.ID,
|
RelatedEventID: data.Body.RelatedEventID,
|
||||||
|
ChannelID: channel.ID,
|
||||||
|
SenderID: member.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
if event, err = services.NewEvent(event); err != nil {
|
if event, err = services.NewEvent(event); err != nil {
|
||||||
@@ -73,10 +76,10 @@ func newMessageEvent(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func editMessageEvent(c *fiber.Ctx) error {
|
func editMessageEvent(c *fiber.Ctx) error {
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user := c.Locals("user").(models.Account)
|
user := c.Locals("user").(authm.Account)
|
||||||
alias := c.Params("channel")
|
alias := c.Params("channel")
|
||||||
messageId, _ := c.ParamsInt("messageId", 0)
|
messageId, _ := c.ParamsInt("messageId", 0)
|
||||||
|
|
||||||
@@ -97,16 +100,15 @@ func editMessageEvent(c *fiber.Ctx) error {
|
|||||||
var err error
|
var err error
|
||||||
var channel models.Channel
|
var channel models.Channel
|
||||||
var member models.ChannelMember
|
var member models.ChannelMember
|
||||||
if val, ok := c.Locals("realm").(models.Realm); ok {
|
|
||||||
channel, member, err = services.GetAvailableChannelWithAlias(alias, user, val.ID)
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
if err != nil {
|
channel, member, err = services.GetChannelIdentity(alias, user.ID, val)
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
channel, member, err = services.GetAvailableChannelWithAlias(alias, user)
|
channel, member, err = services.GetChannelIdentity(alias, user.ID)
|
||||||
if err != nil {
|
}
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
var event models.Event
|
var event models.Event
|
||||||
@@ -123,26 +125,25 @@ func editMessageEvent(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func deleteMessageEvent(c *fiber.Ctx) error {
|
func deleteMessageEvent(c *fiber.Ctx) error {
|
||||||
if err := gap.H.EnsureAuthenticated(c); err != nil {
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user := c.Locals("user").(models.Account)
|
user := c.Locals("user").(authm.Account)
|
||||||
alias := c.Params("channel")
|
alias := c.Params("channel")
|
||||||
messageId, _ := c.ParamsInt("messageId", 0)
|
messageId, _ := c.ParamsInt("messageId", 0)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var channel models.Channel
|
var channel models.Channel
|
||||||
var member models.ChannelMember
|
var member models.ChannelMember
|
||||||
if val, ok := c.Locals("realm").(models.Realm); ok {
|
|
||||||
channel, member, err = services.GetAvailableChannelWithAlias(alias, user, val.ID)
|
if val, ok := c.Locals("realm").(authm.Realm); ok {
|
||||||
if err != nil {
|
channel, member, err = services.GetChannelIdentity(alias, user.ID, val)
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
channel, member, err = services.GetAvailableChannelWithAlias(alias, user)
|
channel, member, err = services.GetChannelIdentity(alias, user.ID)
|
||||||
if err != nil {
|
}
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
var event models.Event
|
var event models.Event
|
||||||
@@ -4,34 +4,41 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MapAPIs(app *fiber.App) {
|
func MapAPIs(app *fiber.App, baseURL string) {
|
||||||
api := app.Group("/api").Name("API")
|
api := app.Group(baseURL).Name("API")
|
||||||
{
|
{
|
||||||
api.Get("/users/me", getUserinfo)
|
quick := api.Group("/quick")
|
||||||
api.Get("/users/:accountId", getOthersInfo)
|
{
|
||||||
|
quick.Post("/:channelId/reply/:eventId", quickReply)
|
||||||
|
}
|
||||||
|
|
||||||
|
api.Get("/channels/me", listOwnedChannelGlobalWide)
|
||||||
|
api.Get("/channels/me/available", listAvailableChannelGlobalWide)
|
||||||
|
|
||||||
channels := api.Group("/channels/:realm").Use(realmMiddleware).Name("Channels API")
|
channels := api.Group("/channels/:realm").Use(realmMiddleware).Name("Channels API")
|
||||||
{
|
{
|
||||||
channels.Get("/", listChannel)
|
channels.Get("/", listChannel)
|
||||||
|
channels.Get("/public", listPublicChannel)
|
||||||
channels.Get("/me", listOwnedChannel)
|
channels.Get("/me", listOwnedChannel)
|
||||||
channels.Get("/me/available", listAvailableChannel)
|
channels.Get("/me/available", listAvailableChannel)
|
||||||
channels.Get("/:channel", getChannel)
|
channels.Get("/:channel", getChannel)
|
||||||
channels.Get("/:channel/me", getChannelIdentity)
|
channels.Get("/:channel/me", getChannelIdentity)
|
||||||
|
channels.Get("/:channel/members/me", getChannelIdentity)
|
||||||
|
channels.Put("/:channel/me", editChannelIdentity)
|
||||||
|
channels.Put("/:channel/me/notify", editChannelNotifyLevel)
|
||||||
|
channels.Put("/:channel/members/me/notify", editChannelNotifyLevel)
|
||||||
|
channels.Delete("/:channel/me", deleteChannelIdentity)
|
||||||
|
|
||||||
channels.Post("/", createChannel)
|
channels.Post("/", createChannel)
|
||||||
channels.Post("/dm", createDirectChannel)
|
channels.Post("/dm", createDirectChannel)
|
||||||
channels.Put("/:channelId", editChannel)
|
channels.Put("/:channelId", editChannel)
|
||||||
channels.Delete("/:channelId", deleteChannel)
|
channels.Delete("/:channelId", deleteChannel)
|
||||||
|
|
||||||
channels.Get("/:channel/members", listChannelMembers)
|
channels.Get("/:channel/members", listChannelMembers)
|
||||||
channels.Get("/:channel/members/me", getMyChannelMembership)
|
|
||||||
channels.Put("/:channel/members/me", editMyChannelMembership)
|
|
||||||
channels.Post("/:channel/members", addChannelMember)
|
channels.Post("/:channel/members", addChannelMember)
|
||||||
channels.Post("/:channel/members/me", joinChannel)
|
channels.Delete("/:channel/members/:memberId", removeChannelMember)
|
||||||
channels.Delete("/:channel/members", removeChannelMember)
|
|
||||||
channels.Delete("/:channel/members/me", leaveChannel)
|
|
||||||
|
|
||||||
channels.Get("/:channel/events", listEvent)
|
channels.Get("/:channel/events", listEvent)
|
||||||
|
channels.Get("/:channel/events/update", checkHasNewEvent)
|
||||||
channels.Get("/:channel/events/:eventId", getEvent)
|
channels.Get("/:channel/events/:eventId", getEvent)
|
||||||
channels.Post("/:channel/events", newRawEvent)
|
channels.Post("/:channel/events", newRawEvent)
|
||||||
|
|
||||||
@@ -43,7 +50,10 @@ func MapAPIs(app *fiber.App) {
|
|||||||
channels.Get("/:channel/calls/ongoing", getOngoingCall)
|
channels.Get("/:channel/calls/ongoing", getOngoingCall)
|
||||||
channels.Post("/:channel/calls", startCall)
|
channels.Post("/:channel/calls", startCall)
|
||||||
channels.Delete("/:channel/calls/ongoing", endCall)
|
channels.Delete("/:channel/calls/ongoing", endCall)
|
||||||
|
channels.Delete("/:channel/calls/ongoing/participant", kickParticipantInCall)
|
||||||
channels.Post("/:channel/calls/ongoing/token", exchangeCallToken)
|
channels.Post("/:channel/calls/ongoing/token", exchangeCallToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
api.Get("/whats-new", getWhatsNew)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
79
pkg/internal/web/api/quick_actions_api.go
Normal file
79
pkg/internal/web/api/quick_actions_api.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/web/exts"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/models"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/services"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
)
|
||||||
|
|
||||||
|
// quickReply is a simplified API for replying to a message
|
||||||
|
// It used in the iOS notification action and others
|
||||||
|
// It did not support all the features of the message event
|
||||||
|
// But it just works
|
||||||
|
func quickReply(c *fiber.Ctx) error {
|
||||||
|
replyTk := c.Query("replyToken")
|
||||||
|
if len(replyTk) == 0 {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "reply token is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
claims, err := services.ParseReplyToken(replyTk)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("reply token is invaild: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
channelId, _ := c.ParamsInt("channelId", 0)
|
||||||
|
eventId, _ := c.ParamsInt("eventId", 0)
|
||||||
|
|
||||||
|
if claims.EventID != uint(eventId) {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "reply token is invaild, event id mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
Type string `json:"type" validate:"required"`
|
||||||
|
Body models.EventMessageBody `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
data.Body.QuoteEventID = lo.ToPtr(uint(eventId))
|
||||||
|
}
|
||||||
|
|
||||||
|
data.Body.Text = strings.TrimSpace(data.Body.Text)
|
||||||
|
if len(data.Body.Text) == 0 && len(data.Body.Attachments) == 0 {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "empty message was not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
channel, member, err := services.GetChannelIdentityWithID(uint(channelId), claims.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("channel / member not found: %v", err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsed map[string]any
|
||||||
|
raw, _ := jsoniter.Marshal(data.Body)
|
||||||
|
_ = jsoniter.Unmarshal(raw, &parsed)
|
||||||
|
|
||||||
|
event, err := services.NewEvent(models.Event{
|
||||||
|
Uuid: uuid.NewString(),
|
||||||
|
Body: parsed,
|
||||||
|
Type: data.Type,
|
||||||
|
Sender: member,
|
||||||
|
Channel: channel,
|
||||||
|
QuoteEventID: data.Body.QuoteEventID,
|
||||||
|
RelatedEventID: data.Body.RelatedEventID,
|
||||||
|
ChannelID: channel.ID,
|
||||||
|
SenderID: member.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(event)
|
||||||
|
}
|
||||||
@@ -2,14 +2,16 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/services"
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/gap"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func realmMiddleware(c *fiber.Ctx) error {
|
func realmMiddleware(c *fiber.Ctx) error {
|
||||||
realmAlias := c.Params("realm")
|
realmAlias := c.Params("realm")
|
||||||
if len(realmAlias) > 0 && realmAlias != "global" {
|
if len(realmAlias) > 0 && realmAlias != "global" {
|
||||||
realm, err := services.GetRealmWithAlias(realmAlias)
|
realm, err := authkit.GetRealmByAlias(gap.Nx, realmAlias)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("requested channel with realm, but realm was not found: %v", err))
|
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("requested channel with realm, but realm was not found: %v", err))
|
||||||
} else {
|
} else {
|
||||||
31
pkg/internal/web/api/whats_new_api.go
Normal file
31
pkg/internal/web/api/whats_new_api.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||||
|
authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/database"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getWhatsNew(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(authm.Account)
|
||||||
|
|
||||||
|
var result []struct {
|
||||||
|
ChannelID uint `json:"channel_id"`
|
||||||
|
UnreadMessageCount int `json:"count"`
|
||||||
|
}
|
||||||
|
if err := database.C.Table("channel_members cm").
|
||||||
|
Select("cm.channel_id, COUNT(m.id) AS unread_message_count").
|
||||||
|
Joins("JOIN events m ON m.channel_id = cm.channel_id").
|
||||||
|
Where("m.id > cm.reading_anchor AND cm.account_id = ?", user.ID).
|
||||||
|
Group("cm.channel_id").
|
||||||
|
Scan(&result).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(result)
|
||||||
|
}
|
||||||
@@ -16,3 +16,7 @@ func BindAndValidate(c *fiber.Ctx, out any) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ValidateStruct(in any) error {
|
||||||
|
return validation.Struct(in)
|
||||||
|
}
|
||||||
75
pkg/internal/web/server.go
Normal file
75
pkg/internal/web/server.go
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/web/api"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||||
|
"github.com/gofiber/fiber/v2/middleware/idempotency"
|
||||||
|
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
var IReader *sec.InternalTokenReader
|
||||||
|
|
||||||
|
type App struct {
|
||||||
|
app *fiber.App
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer() *App {
|
||||||
|
app := fiber.New(fiber.Config{
|
||||||
|
DisableStartupMessage: true,
|
||||||
|
EnableIPValidation: true,
|
||||||
|
ServerHeader: "Hypernet.Messaging",
|
||||||
|
AppName: "Hypernet.Messaging",
|
||||||
|
ProxyHeader: fiber.HeaderXForwardedFor,
|
||||||
|
JSONEncoder: jsoniter.ConfigCompatibleWithStandardLibrary.Marshal,
|
||||||
|
JSONDecoder: jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal,
|
||||||
|
BodyLimit: 50 * 1024 * 1024,
|
||||||
|
ReadBufferSize: 5 * 1024 * 1024, // 5MB for large JWT
|
||||||
|
EnablePrintRoutes: viper.GetBool("debug.print_routes"),
|
||||||
|
})
|
||||||
|
|
||||||
|
app.Use(idempotency.New())
|
||||||
|
app.Use(cors.New(cors.Config{
|
||||||
|
AllowCredentials: true,
|
||||||
|
AllowMethods: strings.Join([]string{
|
||||||
|
fiber.MethodGet,
|
||||||
|
fiber.MethodPost,
|
||||||
|
fiber.MethodHead,
|
||||||
|
fiber.MethodOptions,
|
||||||
|
fiber.MethodPut,
|
||||||
|
fiber.MethodDelete,
|
||||||
|
fiber.MethodPatch,
|
||||||
|
}, ","),
|
||||||
|
AllowOriginsFunc: func(origin string) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
app.Use(logger.New(logger.Config{
|
||||||
|
Format: "${status} | ${latency} | ${method} ${path}\n",
|
||||||
|
Output: log.Logger,
|
||||||
|
}))
|
||||||
|
|
||||||
|
app.Use(sec.ContextMiddleware(IReader))
|
||||||
|
app.Use(authkit.ParseAccountMiddleware)
|
||||||
|
|
||||||
|
api.MapAPIs(app, "/api")
|
||||||
|
|
||||||
|
return &App{
|
||||||
|
app: app,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *App) Listen() {
|
||||||
|
if err := v.app.Listen(viper.GetString("bind")); err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("An error occurred when starting http...")
|
||||||
|
}
|
||||||
|
}
|
||||||
52
pkg/main.go
52
pkg/main.go
@@ -1,19 +1,24 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/gap"
|
"fmt"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/grpc"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/services"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||||
|
"github.com/fatih/color"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/gap"
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/grpc"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/services"
|
||||||
"github.com/robfig/cron/v3"
|
"github.com/robfig/cron/v3"
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/server"
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/web"
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal"
|
pkg "git.solsynth.dev/hypernet/messaging/pkg/internal"
|
||||||
"git.solsynth.dev/hydrogen/messaging/pkg/internal/database"
|
"git.solsynth.dev/hypernet/messaging/pkg/internal/database"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
@@ -25,6 +30,12 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// Booting screen
|
||||||
|
fmt.Println(color.YellowString(" __ __ _\n| \\/ | ___ ___ ___ __ _ __ _(_)_ __ __ _\n| |\\/| |/ _ \\/ __/ __|/ _` |/ _` | | '_ \\ / _` |\n| | | | __/\\__ \\__ \\ (_| | (_| | | | | | (_| |\n|_| |_|\\___||___/___/\\__,_|\\__, |_|_| |_|\\__, |\n |___/ |___/"))
|
||||||
|
fmt.Printf("%s v%s\n", color.New(color.FgHiYellow).Add(color.Bold).Sprintf("Hypernet.Messaging"), pkg.AppVersion)
|
||||||
|
fmt.Printf("The instant messaging service in Hypernet\n")
|
||||||
|
color.HiBlack("=====================================================\n")
|
||||||
|
|
||||||
// Configure settings
|
// Configure settings
|
||||||
viper.AddConfigPath(".")
|
viper.AddConfigPath(".")
|
||||||
viper.AddConfigPath("..")
|
viper.AddConfigPath("..")
|
||||||
@@ -36,39 +47,44 @@ func main() {
|
|||||||
log.Panic().Err(err).Msg("An error occurred when loading settings.")
|
log.Panic().Err(err).Msg("An error occurred when loading settings.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Connect to nexus
|
||||||
|
if err := gap.InitializeToNexus(); err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("An error occurred when connecting to nexus...")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load keypair
|
||||||
|
if reader, err := sec.NewInternalTokenReader(viper.GetString("security.internal_public_key")); err != nil {
|
||||||
|
log.Error().Err(err).Msg("An error occurred when reading internal public key for jwt. Authentication related features will be disabled.")
|
||||||
|
} else {
|
||||||
|
web.IReader = reader
|
||||||
|
log.Info().Msg("Internal jwt public key loaded.")
|
||||||
|
}
|
||||||
|
|
||||||
// Connect to database
|
// Connect to database
|
||||||
if err := database.NewSource(); err != nil {
|
if err := database.NewGorm(); err != nil {
|
||||||
log.Fatal().Err(err).Msg("An error occurred when connect to database.")
|
log.Fatal().Err(err).Msg("An error occurred when connect to database.")
|
||||||
} else if err := database.RunMigration(database.C); err != nil {
|
} else if err := database.RunMigration(database.C); err != nil {
|
||||||
log.Fatal().Err(err).Msg("An error occurred when running database auto migration.")
|
log.Fatal().Err(err).Msg("An error occurred when running database auto migration.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect other services
|
// Connect other services
|
||||||
if err := gap.RegisterService(); err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("An error occurred when connecting to consul...")
|
|
||||||
}
|
|
||||||
services.SetupLiveKit()
|
services.SetupLiveKit()
|
||||||
|
|
||||||
// Server
|
// Server
|
||||||
server.NewServer()
|
go web.NewServer().Listen()
|
||||||
go server.Listen()
|
|
||||||
|
|
||||||
grpc.NewGRPC()
|
go grpc.NewGrpc().Listen()
|
||||||
go grpc.ListenGRPC()
|
|
||||||
|
|
||||||
// Configure timed tasks
|
// Configure timed tasks
|
||||||
quartz := cron.New(cron.WithLogger(cron.VerbosePrintfLogger(&log.Logger)))
|
quartz := cron.New(cron.WithLogger(cron.VerbosePrintfLogger(&log.Logger)))
|
||||||
quartz.AddFunc("@every 60m", services.DoAutoDatabaseCleanup)
|
quartz.AddFunc("@every 60m", services.DoAutoDatabaseCleanup)
|
||||||
|
quartz.AddFunc("@every 30s", services.FlushReadingAnchor)
|
||||||
quartz.Start()
|
quartz.Start()
|
||||||
|
|
||||||
// Messages
|
// Messages
|
||||||
log.Info().Msgf("Messaging v%s is started...", pkg.AppVersion)
|
|
||||||
|
|
||||||
quit := make(chan os.Signal, 1)
|
quit := make(chan os.Signal, 1)
|
||||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||||
<-quit
|
<-quit
|
||||||
|
|
||||||
log.Info().Msgf("Messaging v%s is quitting...", pkg.AppVersion)
|
|
||||||
|
|
||||||
quartz.Stop()
|
quartz.Stop()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
id = "messaging01"
|
id = "messaging01"
|
||||||
|
|
||||||
frontend = "https://lian.solsynth.dev"
|
|
||||||
|
|
||||||
bind = "0.0.0.0:8447"
|
bind = "0.0.0.0:8447"
|
||||||
grpc_bind = "0.0.0.0:7447"
|
grpc_bind = "0.0.0.0:7447"
|
||||||
domain = "im.solsynth.dev"
|
|
||||||
secret = "LtTjzAGFLshwXhN4ZD4nG5KlMv1MWcsvfv03TSZYnT1VhiAnLIZFTnHUwR0XhGgi"
|
|
||||||
|
|
||||||
[dealer]
|
nexus_addr = "localhost:7001"
|
||||||
addr = "127.0.0.1:7442"
|
|
||||||
|
[performance]
|
||||||
|
passive_user_optimize = true
|
||||||
|
|
||||||
[debug]
|
[debug]
|
||||||
database = false
|
database = false
|
||||||
@@ -22,19 +20,6 @@ token_duration = 43200
|
|||||||
empty_timeout_duration = 600
|
empty_timeout_duration = 600
|
||||||
max_participants = 20
|
max_participants = 20
|
||||||
|
|
||||||
[mailer]
|
|
||||||
name = "Alphabot <alphabot@smartsheep.studio>"
|
|
||||||
smtp_host = "smtp.exmail.qq.com"
|
|
||||||
smtp_port = 465
|
|
||||||
username = "alphabot@smartsheep.studio"
|
|
||||||
password = "gz937Zxxzfcd9SeH"
|
|
||||||
|
|
||||||
[security]
|
[security]
|
||||||
cookie_domain = "localhost"
|
internal_public_key = "keys/internal_public_key.pem"
|
||||||
cookie_samesite = "Lax"
|
reply_token_secret = "QSu8iYYLDkGj10H3FtNI7OabpGl8N7Rg6rBagofQN4Uza3nIrXpVzDEfAiU1G2Yn"
|
||||||
access_token_duration = 300
|
|
||||||
refresh_token_duration = 2592000
|
|
||||||
|
|
||||||
[database]
|
|
||||||
dsn = "host=localhost user=postgres password=password dbname=hy_messaging port=5432 sslmode=disable"
|
|
||||||
prefix = "messaging_"
|
|
||||||
|
|||||||
Reference in New Issue
Block a user