From 7b45735a5767dfac86eae1b34d74e0c965ccac7b Mon Sep 17 00:00:00 2001 From: or-else Date: Thu, 30 May 2024 16:55:53 -0700 Subject: [PATCH 01/27] fix PC language --- server/tinode.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/tinode.conf b/server/tinode.conf index bcaada92..bd87ca6f 100644 --- a/server/tinode.conf +++ b/server/tinode.conf @@ -394,7 +394,7 @@ "smtp_helo_host": "example.com", // Skip verification of the server's certificate chain and host name. - // In this mode, TLS is susceptible to machine-in-the-middle attacks. + // In this mode, TLS is susceptible to man-in-the-middle attacks. "insecure_skip_verify": false, // Optional list of human languages to try to load templates for. If you don't care about i18n, From 417a454b0e980cdda0f3efd7b5265d16c79bf9d0 Mon Sep 17 00:00:00 2001 From: or-else Date: Mon, 1 Jul 2024 11:28:41 -0700 Subject: [PATCH 02/27] clarify comment --- server/tinode.conf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/tinode.conf b/server/tinode.conf index bd87ca6f..72a84c9b 100644 --- a/server/tinode.conf +++ b/server/tinode.conf @@ -464,7 +464,9 @@ } }, - // Configuration for stale account garbage collector. + // Configuration for stale account garbage collector: remove + // stale unvalidated user accounts which have been last updated at least + // 'gc_min_account_age' hours ago. "acc_gc_config": { "enabled": true, // How often to run GC (seconds). From b642cd45074f2a4236c97dab7e85f0bb68f6682b Mon Sep 17 00:00:00 2001 From: Zhang Xingjian Date: Sun, 11 Aug 2024 01:35:45 +0800 Subject: [PATCH 03/27] Fix botUID in chatbot --- chatbot/python/.gitignore | 1 + chatbot/python/chatbot.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 chatbot/python/.gitignore diff --git a/chatbot/python/.gitignore b/chatbot/python/.gitignore new file mode 100644 index 00000000..0e11643a --- /dev/null +++ b/chatbot/python/.gitignore @@ -0,0 +1 @@ +.tn-cookie \ No newline at end of file diff --git a/chatbot/python/chatbot.py b/chatbot/python/chatbot.py index 78b81ae8..81a195c0 100644 --- a/chatbot/python/chatbot.py +++ b/chatbot/python/chatbot.py @@ -301,6 +301,7 @@ def read_auth_cookie(cookie_file_name): return schema, secret def on_login(cookie_file_name, params): + global botUID client_post(subscribe('me')) """Save authentication token to file""" @@ -308,7 +309,7 @@ def on_login(cookie_file_name, params): return if 'user' in params: - botUID = params['user'].decode("ascii") + botUID = params['user'].decode("ascii")[1:-1] # Protobuf map 'params' is not a python object or dictionary. Convert it. nice = {'schema': 'token'} From c904259463bed239c754d7b9459816e577e36c80 Mon Sep 17 00:00:00 2001 From: or-else Date: Sun, 18 Aug 2024 08:52:21 +0300 Subject: [PATCH 04/27] add explanation for pgsql missing db problem --- docs/faq.md | 8 ++++++++ server/store/store.go | 2 +- server/tinode.conf | 2 +- server/topic.go | 3 ++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index e9b8bd1a..0dfb5e4a 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -98,3 +98,11 @@ The test database has a stock user `xena` which has root access. ### Q: What is the proper way to format gRPC {pub content}?
**A**: The gPRC sends `content` field of a `{pub}` message as a byte array while the client applications expect it to be valid JSON. Consequently, you have to format the field to be valid JSON before passing it to gRPC. For example, to send a plain text `Hello world` message you have to send a quoted string `"Hello world"`. In most cases the string you pass to the gRPC call would look like `"\"Hello world\""` or `'"Hello world"'`. + + +### Q: How to fix PostgreSQL initialization failing with 'missing database' error?
+**A**: PostgreSQL has a (mis)feature: a DB connection must always select a database. If the connection tries to use a database (even with intent to create it) which does not exist, the connection fails. When Tinode is started for the first time, it tries to create a database, usually `tinode` (see `tinode.conf`, `"store_config": {"adapters": {"postgres": {"DBName": "tinode"}}}`. The database `tinode` obviously does not exist, so Tinode connection falls back to 'default' database which has the same name as the name of the connecting PostgreSQL user. The default configuration specifies user as `postgres` (`"User": "postgres"`), the database `postgres` always exists, so the connection succeeds and everything works as expected. But if you change the user to anything other than `postgres`, let's say `tinodeadmin`, then trouble starts: the database with the name `tinodeadmin` does not exist and the connection fails. If you want to change the user name to anything other than `postgres`, then you must create either a database `tinode` (or whaever you named your Tinode database) or an empty database with the same name as your user `tinodeadmin`. For example: ``` +$ psql + postgres=# create database tinode; + exit +``` diff --git a/server/store/store.go b/server/store/store.go index 114764c6..530938e8 100644 --- a/server/store/store.go +++ b/server/store/store.go @@ -218,7 +218,7 @@ func (storeObj) GetUid() types.Uid { return uGen.Get() } -// GetUidString generate unique ID as string +// GetUidString generate unique ID as a string. func (storeObj) GetUidString() string { return uGen.GetStr() } diff --git a/server/tinode.conf b/server/tinode.conf index 72a84c9b..69845682 100644 --- a/server/tinode.conf +++ b/server/tinode.conf @@ -174,7 +174,7 @@ // The maximum length is 32 and it cannot be changed. "min_login_length": 4, // The minimum length of a password in unicode runes, "пароль" is length 6, not 12. - // There is no limit on maximum length. + // There is no limit on maximum length, but MySQL & PgSQL adapters have a limit of 32 bytes. "min_password_length": 6 }, diff --git a/server/topic.go b/server/topic.go index 1baeb752..11b9c99e 100644 --- a/server/topic.go +++ b/server/topic.go @@ -3764,7 +3764,8 @@ func topicCat(name string) types.TopicCat { return types.GetTopicCat(name) } -// Generate random string as a name of the group topic +// Generate the name of the group topic as a "grp" followed by random-looking +// unique string. func genTopicName() string { return "grp" + store.Store.GetUidString() } From db5911616f13fe9a394869a09d3a4cab96616719 Mon Sep 17 00:00:00 2001 From: or-else Date: Sun, 18 Aug 2024 08:53:59 +0300 Subject: [PATCH 05/27] markdown formatting --- docs/faq.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/faq.md b/docs/faq.md index 0dfb5e4a..6f0906e7 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -101,7 +101,8 @@ The test database has a stock user `xena` which has root access. ### Q: How to fix PostgreSQL initialization failing with 'missing database' error?
-**A**: PostgreSQL has a (mis)feature: a DB connection must always select a database. If the connection tries to use a database (even with intent to create it) which does not exist, the connection fails. When Tinode is started for the first time, it tries to create a database, usually `tinode` (see `tinode.conf`, `"store_config": {"adapters": {"postgres": {"DBName": "tinode"}}}`. The database `tinode` obviously does not exist, so Tinode connection falls back to 'default' database which has the same name as the name of the connecting PostgreSQL user. The default configuration specifies user as `postgres` (`"User": "postgres"`), the database `postgres` always exists, so the connection succeeds and everything works as expected. But if you change the user to anything other than `postgres`, let's say `tinodeadmin`, then trouble starts: the database with the name `tinodeadmin` does not exist and the connection fails. If you want to change the user name to anything other than `postgres`, then you must create either a database `tinode` (or whaever you named your Tinode database) or an empty database with the same name as your user `tinodeadmin`. For example: ``` +**A**: PostgreSQL has a (mis)feature: a DB connection must always select a database. If the connection tries to use a database (even with intent to create it) which does not exist, the connection fails. When Tinode is started for the first time, it tries to create a database, usually `tinode` (see `tinode.conf`, `"store_config": {"adapters": {"postgres": {"DBName": "tinode"}}}`. The database `tinode` obviously does not exist, so Tinode connection falls back to 'default' database which has the same name as the name of the connecting PostgreSQL user. The default configuration specifies user as `postgres` (`"User": "postgres"`), the database `postgres` always exists, so the connection succeeds and everything works as expected. But if you change the user to anything other than `postgres`, let's say `tinodeadmin`, then trouble starts: the database with the name `tinodeadmin` does not exist and the connection fails. If you want to change the user name to anything other than `postgres`, then you must create either a database `tinode` (or whaever you named your Tinode database) or an empty database with the same name as your user `tinodeadmin`. For example: +``` $ psql postgres=# create database tinode; exit From f69bae7bb3ac8af2f1efac06fc151f945185952a Mon Sep 17 00:00:00 2001 From: or-else Date: Sun, 18 Aug 2024 08:59:32 +0300 Subject: [PATCH 06/27] typo --- docs/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.md b/docs/faq.md index 6f0906e7..b97c5ecb 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -101,7 +101,7 @@ The test database has a stock user `xena` which has root access. ### Q: How to fix PostgreSQL initialization failing with 'missing database' error?
-**A**: PostgreSQL has a (mis)feature: a DB connection must always select a database. If the connection tries to use a database (even with intent to create it) which does not exist, the connection fails. When Tinode is started for the first time, it tries to create a database, usually `tinode` (see `tinode.conf`, `"store_config": {"adapters": {"postgres": {"DBName": "tinode"}}}`. The database `tinode` obviously does not exist, so Tinode connection falls back to 'default' database which has the same name as the name of the connecting PostgreSQL user. The default configuration specifies user as `postgres` (`"User": "postgres"`), the database `postgres` always exists, so the connection succeeds and everything works as expected. But if you change the user to anything other than `postgres`, let's say `tinodeadmin`, then trouble starts: the database with the name `tinodeadmin` does not exist and the connection fails. If you want to change the user name to anything other than `postgres`, then you must create either a database `tinode` (or whaever you named your Tinode database) or an empty database with the same name as your user `tinodeadmin`. For example: +**A**: PostgreSQL has a (mis)feature: a DB connection must always select a database. If the connection tries to use a database (even with intent to create it) which does not exist, the connection fails. When Tinode is started for the first time, it tries to create a database, usually `tinode` (see `tinode.conf`, `"store_config": {"adapters": {"postgres": {"DBName": "tinode"}}}`. The database `tinode` obviously does not exist, so Tinode connection falls back to 'default' database which has the same name as the name of the connecting PostgreSQL user. The default configuration specifies user as `postgres` (`"User": "postgres"`), the database `postgres` always exists, so the connection succeeds and everything works as expected. But if you change the user to anything other than `postgres`, let's say `tinodeadmin`, then trouble starts: the database with the name `tinodeadmin` does not exist and the connection fails. If you want to change the user name to anything other than `postgres`, then you must create either a database `tinode` (or whatever you named your Tinode database) or an empty database with the same name as your user `tinodeadmin`. For example: ``` $ psql postgres=# create database tinode; From b0b9c94046aef8b84ca7b9350faa5f4e052c74bc Mon Sep 17 00:00:00 2001 From: aheiabai <718156994@qq.com> Date: Thu, 22 Aug 2024 20:49:30 +0800 Subject: [PATCH 07/27] Update INSTALL.md in Running a Standalone Server section, there is no 'init-db', 'tinode' in $GOPATH/bin but tinode-db, server. --- INSTALL.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 1499c94f..5d4445f4 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -127,11 +127,11 @@ MongoDB should run as single node replicaset. See https://docs.mongodb.com/manua 2. Run DB initializer ``` - $GOPATH/bin/init-db -config=./tinode-db/tinode.conf + $GOPATH/bin/tinode-db -config=./tinode-db/tinode.conf ``` add `-data=./tinode-db/data.json` flag if you want sample data to be loaded: ``` - $GOPATH/bin/init-db -config=./tinode-db/tinode.conf -data=./tinode-db/data.json + $GOPATH/bin/tinode-db -config=./tinode-db/tinode.conf -data=./tinode-db/data.json ``` DB initializer needs to be run only once per installation. See [instructions](tinode-db/README.md) for more options. @@ -145,7 +145,7 @@ MongoDB should run as single node replicaset. See https://docs.mongodb.com/manua 5. Run the server ``` - $GOPATH/bin/tinode -config=./server/tinode.conf -static_data=$HOME/tinode/webapp/ + $GOPATH/bin/server -config=./server/tinode.conf -static_data=$HOME/tinode/webapp/ ``` 6. Test your installation by pointing your browser to [http://localhost:6060/](http://localhost:6060/). The static files from the `-static_data` path are served at web root `/`. You can change this by editing the line `static_mount` in the config file. From e0684fc0284c7dfa7a4d0cb2809cbe418327213d Mon Sep 17 00:00:00 2001 From: or-else Date: Sat, 24 Aug 2024 12:42:41 +0300 Subject: [PATCH 08/27] comments and error message clarified --- server/auth/auth.go | 2 +- server/auth/basic/auth_basic.go | 2 +- server/auth/rest/auth_rest.go | 4 ++-- server/user.go | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/server/auth/auth.go b/server/auth/auth.go index 4f808c05..836bef0b 100644 --- a/server/auth/auth.go +++ b/server/auth/auth.go @@ -258,7 +258,7 @@ type AuthHandler interface { AsTag(token string) string // IsUnique verifies if the provided secret can be considered unique by the auth scheme - // E.g. if login is unique. + // E.g. if login is unique. It also may check for policy compliance, i.e. not too short, etc. IsUnique(secret []byte, remoteAddr string) (bool, error) // GenSecret generates a new secret, if appropriate. diff --git a/server/auth/basic/auth_basic.go b/server/auth/basic/auth_basic.go index b7b48bc3..4dc9869c 100644 --- a/server/auth/basic/auth_basic.go +++ b/server/auth/basic/auth_basic.go @@ -263,7 +263,7 @@ func (a *authenticator) AsTag(token string) string { return a.name + ":" + token } -// IsUnique checks login uniqueness. +// IsUnique checks login uniqueness and policy compliance. func (a *authenticator) IsUnique(secret []byte, remoteAddr string) (bool, error) { uname, _, err := parseSecret(secret) if err != nil { diff --git a/server/auth/rest/auth_rest.go b/server/auth/rest/auth_rest.go index 237acda0..f1dd6703 100644 --- a/server/auth/rest/auth_rest.go +++ b/server/auth/rest/auth_rest.go @@ -245,8 +245,8 @@ func (a *authenticator) AsTag(token string) string { return "" } -// IsUnique verifies if the provided secret can be considered unique by the auth scheme -// E.g. if login is unique. +// IsUnique verifies if the provided secret can be considered unique by the auth +// scheme as well as policy compliance. E.g. if login is unique and not too short/long. func (a *authenticator) IsUnique(secret []byte, remoteAddr string) (bool, error) { resp, err := a.callEndpoint("checkunique", nil, secret, remoteAddr) if err != nil { diff --git a/server/user.go b/server/user.go index 57ab6d5e..e796c5e0 100644 --- a/server/user.go +++ b/server/user.go @@ -38,9 +38,9 @@ func replyCreateUser(s *Session, msg *ClientComMessage, rec *auth.Rec) { return } - // Check if login is unique. + // Check if login is unique and compliance with the policy (not too long or too short). if ok, err := authhdl.IsUnique(msg.Acc.Secret, s.remoteAddr); !ok { - logs.Warn.Println("create user: auth secret is not unique", err, "sid=", s.sid) + logs.Warn.Println("create user: auth secret is not compliant", err, "sid=", s.sid) s.queueOut(decodeStoreError(err, msg.Id, msg.Timestamp, map[string]any{"what": "auth"})) return From 0812b9e73af0e53ab89bc0f2bdf5d46388037208 Mon Sep 17 00:00:00 2001 From: or-else Date: Sun, 1 Sep 2024 10:50:59 +0300 Subject: [PATCH 09/27] mention that drafty button-act-note is not implemented --- docs/drafty.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/drafty.md b/docs/drafty.md index ab0b8fe8..20f6271d 100644 --- a/docs/drafty.md +++ b/docs/drafty.md @@ -192,7 +192,7 @@ _IMPORTANT Security Consideration_: the `val` and `ref` fields may contain malic { "tp":"EX", "data":{ "mime":"application/json", "val": { "seq": 3, "resp": { "confirmation": "some-value" } } } } ``` * `url`: issue an `HTTP GET` request to the URL from the `data.ref` field. The following query parameters are appended to the URL: `=`, `uid=`, `topic=`, `seq=`. - * `note`: send a `{note}` message to the current topic with `what` set to `data`. + * `note`: send a `{note}` message to the current topic with `what` set to `data` (not currently implemented, contact us if you need it). ```js { "what": "data", "data": { "mime": "application/json", "val": { "seq": 3, "resp": { "confirmation": "some-value" } } } ``` From 76ce16425fbeec388a30c5fddb0f64df48a48dd8 Mon Sep 17 00:00:00 2001 From: or-else Date: Tue, 15 Oct 2024 11:57:20 +0300 Subject: [PATCH 10/27] adding support for Twilio SMS validation --- go.mod | 3 +++ go.sum | 6 ++++++ server/tinode.conf | 6 ++++++ server/validate/tel/validate.go | 20 +++++++++++++++++--- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6fe7af34..79973540 100644 --- a/go.mod +++ b/go.mod @@ -29,6 +29,8 @@ require ( gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.2 ) +require github.com/pkg/errors v0.9.1 // indirect + require ( cloud.google.com/go v0.110.9 // indirect cloud.google.com/go/compute v1.23.1 // indirect @@ -63,6 +65,7 @@ require ( github.com/prometheus/procfs v0.12.0 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect + github.com/twilio/twilio-go v1.23.3 github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect diff --git a/go.sum b/go.sum index e99e1b35..0f68a24d 100644 --- a/go.sum +++ b/go.sum @@ -19,6 +19,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/aws/aws-sdk-go v1.46.4 h1:48tKgtm9VMPkb6y7HuYlsfhQmoIRAsTEXTsWLVlty4M= github.com/aws/aws-sdk-go v1.46.4/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= 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/bitly/go-hostpool v0.1.0 h1:XKmsF6k5el6xHG3WPJ8U0Ku/ye7njX7W81Ng7O2ioR0= @@ -55,6 +56,7 @@ github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= @@ -180,6 +182,8 @@ github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275 h1:IZycmTpoUtQK3PD60UYBwjaCUHUP7cML494ao9/O8+Q= +github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275/go.mod h1:zt6UU74K6Z6oMOYJbJzYpYucqdcQwSMPBEdSvGiaUMw= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -250,6 +254,8 @@ github.com/tinode/jsonco v1.0.0 h1:zVcpjzDvjuA1G+HLrckI5EiiRyq9jgV3x37OQl6e5FE= github.com/tinode/jsonco v1.0.0/go.mod h1:Bnavu3302Qfn2pILMNwASkelodgeew3IvDrbdzU84u8= github.com/tinode/snowflake v1.0.0 h1:YciQ9ZKn1TrnvpS8yZErt044XJaxWVtR9aMO9rOZVOE= github.com/tinode/snowflake v1.0.0/go.mod h1:5JiaCe3o7QdDeyRcAeZBGVghwRS+ygt2CF/hxmAoptQ= +github.com/twilio/twilio-go v1.23.3 h1:9DsuC9+6CfQW9dlzdeQeyhn3z2oPjZQcOhMCgh5VkgE= +github.com/twilio/twilio-go v1.23.3/go.mod h1:zRkMjudW7v7MqQ3cWNZmSoZJ7EBjPZ4OpNh2zm7Q6ko= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= diff --git a/server/tinode.conf b/server/tinode.conf index 69845682..0a7f55e8 100644 --- a/server/tinode.conf +++ b/server/tinode.conf @@ -453,6 +453,12 @@ // Allow this many confirmation attempts before blocking the credential. "max_retries": 3, + // Twilio configuration (optional). + //"twilio_conf": { + // "account_sid": "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + // "auth_token": "f2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + //}, + // Dummy response to accept. // // === IMPORTANT === diff --git a/server/validate/tel/validate.go b/server/validate/tel/validate.go index e1d2545d..8bc9b338 100644 --- a/server/validate/tel/validate.go +++ b/server/validate/tel/validate.go @@ -25,7 +25,7 @@ type validator struct { Languages []string `json:"languages"` // Path to email validation and password reset templates, either a template itself or a literal string. UniversalTemplFile string `json:"universal_templ"` - // Sender address. + // Sender address (phone number). Sender string `json:"sender"` // Debug response to accept during testing. DebugResponse string `json:"debug_response"` @@ -33,6 +33,8 @@ type validator struct { MaxRetries int `json:"max_retries"` // Length of secret numeric code to sent for validation. CodeLength int `json:"code_length"` + // Twilio-specific config. + Twilio json.RawMessage `json:"twilio_conf"` // Must use index into language array instead of language tags because language.Matcher is brain damaged: // https://github.com/golang/go/issues/24211 @@ -93,6 +95,12 @@ func (v *validator) Init(jsonconf string) error { } } + if v.Twilio != nil { + if err = twilioInit(v.Twilio); err != nil { + return err + } + } + if v.Sender == "" { v.Sender = defaultSender } @@ -254,8 +262,14 @@ func (v *validator) TempAuthScheme() (string, error) { } // Implement sending the SMS. -func (*validator) send(to, body string) error { - logs.Info.Println("Send SMS, To:", to, "\nText:", body) +func (v *validator) send(to, body string) error { + if v.Twilio != nil { + if err := twilioSend(v.Sender, to, body); err != nil { + logs.Warn.Println("Twilio SMS error", to, err) + } + } else { + logs.Info.Println("Send SMS, To:", to, "\nText:", body) + } return nil } From 0f91507e98bb04cee03c6e558dcc7f02d2c929fa Mon Sep 17 00:00:00 2001 From: or-else Date: Sun, 20 Oct 2024 20:59:01 +0300 Subject: [PATCH 11/27] forgot to check-in twilio.go --- server/validate/tel/twilio.go | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 server/validate/tel/twilio.go diff --git a/server/validate/tel/twilio.go b/server/validate/tel/twilio.go new file mode 100644 index 00000000..6ad9dc13 --- /dev/null +++ b/server/validate/tel/twilio.go @@ -0,0 +1,39 @@ +package tel + +import ( + "encoding/json" + + "github.com/twilio/twilio-go" + twapi "github.com/twilio/twilio-go/rest/api/v2010" +) + +type twilioConfig struct { + AccountSid string `json:"account_sid"` + AuthToken string `json:"auth_token"` +} + +var twilioClient *twilio.RestClient + +func twilioInit(jsonconf json.RawMessage) error { + var conf twilioConfig + + if err := json.Unmarshal(jsonconf, &conf); err != nil { + return err + } + + twilioClient = twilio.NewRestClientWithParams(twilio.ClientParams{ + Username: conf.AccountSid, + Password: conf.AuthToken, + }) + + return nil +} + +func twilioSend(from, to, body string) error { + _, err := twilioClient.Api.CreateMessage(&twapi.CreateMessageParams{ + From: &from, + To: &to, + Body: &body, + }) + return err +} From 3bd4b4c537ffb6167ea83916f79a287b02be9b7b Mon Sep 17 00:00:00 2001 From: BobConanDev Date: Fri, 22 Nov 2024 14:36:57 -0500 Subject: [PATCH 12/27] Updated README_ko.md, fix typo(s) --- README_ko.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_ko.md b/README_ko.md index 34bece7c..8429f245 100644 --- a/README_ko.md +++ b/README_ko.md @@ -19,7 +19,7 @@ Tinode는 XMPP/ Jabber 가 아닙니다. Tinode는 XMPP와 호환되지 않습 XMPP: XML에 기반한 메시지 지향 통신 프로토콜 -IM: Instant Messanger +IM: Instant Messenger ## 설치 및 실행 From ceb9c9df499397a543c31c9b660730d0bb326865 Mon Sep 17 00:00:00 2001 From: yinebebt Date: Thu, 28 Nov 2024 13:57:05 +0300 Subject: [PATCH 13/27] feat: add link-preview handler --- go.mod | 2 +- server/linkpreview.go | 113 ++++++++++++++++++++++++++++++++++++++++++ server/main.go | 2 + 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 server/linkpreview.go diff --git a/go.mod b/go.mod index 6fe7af34..1eab4366 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/tinode/snowflake v1.0.0 go.mongodb.org/mongo-driver v1.12.1 golang.org/x/crypto v0.21.0 + golang.org/x/net v0.23.0 golang.org/x/oauth2 v0.16.0 golang.org/x/text v0.14.0 google.golang.org/api v0.148.0 @@ -68,7 +69,6 @@ require ( github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect go.opencensus.io v0.24.0 // indirect - golang.org/x/net v0.23.0 // indirect golang.org/x/sync v0.4.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/time v0.3.0 // indirect diff --git a/server/linkpreview.go b/server/linkpreview.go new file mode 100644 index 00000000..8c6d9545 --- /dev/null +++ b/server/linkpreview.go @@ -0,0 +1,113 @@ +package main + +import ( + "encoding/json" + "golang.org/x/net/html" + "net/http" + "strings" + "time" +) + +type LinkPreview struct { + Title string `json:"title"` + Description string `json:"description"` + Image string `json:"image"` + URL string `json:"url"` +} + +// PreviewLink handles the HTTP request, fetches the URL, and returns the link preview +func PreviewLink(w http.ResponseWriter, r *http.Request) { + url := r.URL.Query().Get("url") + if url == "" { + http.Error(w, "Missing 'url' query parameter", http.StatusBadRequest) + return + } + + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + + client := &http.Client{ + Timeout: time.Second * 5, + } + resp, err := client.Do(req) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + http.Error(w, "Non-OK HTTP status", http.StatusInternalServerError) + return + } + + doc, err := html.Parse(resp.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + metadata := extractMetadata(doc) + + linkPreview := LinkPreview{ + Title: metadata["og:title"], + Description: metadata["og:description"], + Image: metadata["og:image"], + URL: url, + } + + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(linkPreview); err != nil { + http.Error(w, "Failed to encode response", http.StatusInternalServerError) + } +} + +func extractMetadata(n *html.Node) map[string]string { + metaTags := map[string]string{} + var traverse func(*html.Node) + traverse = func(n *html.Node) { + if n.Type == html.ElementNode && n.Data == "meta" { + attrs := make(map[string]string) + for _, attr := range n.Attr { + attrs[attr.Key] = attr.Val + } + name := attrs["name"] + property := attrs["property"] + content := attrs["content"] + + if strings.HasPrefix(property, "og:") && content != "" { + metaTags[property] = content + } else if name != "" && content != "" { + metaTags[name] = content + } + } + + for child := n.FirstChild; child != nil; child = child.NextSibling { + traverse(child) + } + } + + traverse(n) + + if _, exists := metaTags["og:title"]; !exists { + metaTags["og:title"] = extractTitle(n) + } + return metaTags +} + +func extractTitle(n *html.Node) string { + if n.Type == html.ElementNode && n.Data == "title" { + if n.FirstChild != nil { + return n.FirstChild.Data + } + } + for child := n.FirstChild; child != nil; child = child.NextSibling { + title := extractTitle(child) + if title != "" { + return title + } + } + return "" +} diff --git a/server/main.go b/server/main.go index a7b1c24e..906631a5 100644 --- a/server/main.go +++ b/server/main.go @@ -734,6 +734,8 @@ func main() { mux.HandleFunc("/", serve404) } + mux.HandleFunc(config.ApiPath+"v0/preview-link", PreviewLink) + if err = listenAndServe(config.Listen, mux, tlsConfig, signalHandler()); err != nil { logs.Err.Fatal(err) } From 3c1489a037f65198d290afebe1170d7fadce81ef Mon Sep 17 00:00:00 2001 From: yinebebt Date: Fri, 29 Nov 2024 02:14:57 +0300 Subject: [PATCH 14/27] fix: add response body limit, url validation and update config --- server/linkpreview.go | 115 +++++++++++++++++++++++++++--------------- server/main.go | 29 +++++++---- server/tinode.conf | 5 +- 3 files changed, 97 insertions(+), 52 deletions(-) diff --git a/server/linkpreview.go b/server/linkpreview.go index 8c6d9545..dced553d 100644 --- a/server/linkpreview.go +++ b/server/linkpreview.go @@ -3,60 +3,83 @@ package main import ( "encoding/json" "golang.org/x/net/html" + "io" + "net" "net/http" + "net/url" "strings" "time" ) -type LinkPreview struct { - Title string `json:"title"` - Description string `json:"description"` - Image string `json:"image"` - URL string `json:"url"` +type linkPreview struct { + Title string `json:"title,omitempty"` + Description string `json:"description,omitempty"` + ImageURL string `json:"image_url,omitempty"` } -// PreviewLink handles the HTTP request, fetches the URL, and returns the link preview -func PreviewLink(w http.ResponseWriter, r *http.Request) { - url := r.URL.Query().Get("url") - if url == "" { +var client = &http.Client{ + Transport: &http.Transport{}, + Timeout: time.Second * 2, +} + +// previewLink handles the HTTP request, fetches the URL, and returns the link preview. +func previewLink(w http.ResponseWriter, r *http.Request) { + u := r.URL.Query().Get("url") + if u == "" { http.Error(w, "Missing 'url' query parameter", http.StatusBadRequest) return } - req, err := http.NewRequest(http.MethodGet, url, nil) + parsedURL, err := url.Parse(u) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if parsedURL.Scheme != "http" && parsedURL.Scheme != "https" { + http.Error(w, "invalid schema", http.StatusBadRequest) + return } - client := &http.Client{ - Timeout: time.Second * 5, + ips, err := net.LookupIP(parsedURL.Hostname()) + if err != nil { + http.Error(w, "invalid host", http.StatusBadRequest) + return } + for _, ip := range ips { + if ip.IsLoopback() || ip.IsPrivate() { + http.Error(w, "non routable IP address", http.StatusBadRequest) + return + } + } + + req, err := http.NewRequest(http.MethodGet, u, nil) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + resp, err := client.Do(req) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + http.Error(w, err.Error(), http.StatusBadGateway) return } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - http.Error(w, "Non-OK HTTP status", http.StatusInternalServerError) + if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices { // StatusCode != 20X + http.Error(w, "Non-OK HTTP status", http.StatusBadGateway) return } - doc, err := html.Parse(resp.Body) + body := io.LimitReader(resp.Body, 2*1024) // 2KB limit + doc, err := html.Parse(body) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write([]byte("{}")) return } - metadata := extractMetadata(doc) - - linkPreview := LinkPreview{ - Title: metadata["og:title"], - Description: metadata["og:description"], - Image: metadata["og:image"], - URL: url, - } + linkPreview := extractMetadata(doc) w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(linkPreview); err != nil { @@ -64,23 +87,34 @@ func PreviewLink(w http.ResponseWriter, r *http.Request) { } } -func extractMetadata(n *html.Node) map[string]string { - metaTags := map[string]string{} +func extractMetadata(n *html.Node) linkPreview { + var preview linkPreview + var traverse func(*html.Node) traverse = func(n *html.Node) { - if n.Type == html.ElementNode && n.Data == "meta" { - attrs := make(map[string]string) + if n.Type == html.ElementNode && strings.ToLower(n.Data) == "meta" { + var name, property, content string for _, attr := range n.Attr { - attrs[attr.Key] = attr.Val + switch attr.Key { + case "name": + name = attr.Val + case "property": + property = attr.Val + case "content": + content = attr.Val + } } - name := attrs["name"] - property := attrs["property"] - content := attrs["content"] if strings.HasPrefix(property, "og:") && content != "" { - metaTags[property] = content - } else if name != "" && content != "" { - metaTags[name] = content + if property == "og:title" { + preview.Title = content + } else if property == "og:description" { + preview.Description = content + } else if property == "og:image" { + preview.ImageURL = content + } + } else if name == "description" && preview.Description == "" { + preview.Description = content } } @@ -91,10 +125,11 @@ func extractMetadata(n *html.Node) map[string]string { traverse(n) - if _, exists := metaTags["og:title"]; !exists { - metaTags["og:title"] = extractTitle(n) + if preview.Title == "" { + preview.Title = extractTitle(n) } - return metaTags + + return preview } func extractTitle(n *html.Node) string { diff --git a/server/main.go b/server/main.go index 906631a5..7c52e16c 100644 --- a/server/main.go +++ b/server/main.go @@ -240,6 +240,10 @@ type mediaConfig struct { Handlers map[string]json.RawMessage `json:"handlers"` } +type LinkPreviewConfig struct { + Enabled bool `json:"enabled"` +} + // Contentx of the configuration file type configType struct { // HTTP(S) address:port to listen on for websocket and long polling clients. Either a @@ -292,16 +296,17 @@ type configType struct { DefaultCountryCode string `json:"default_country_code"` // Configs for subsystems - Cluster json.RawMessage `json:"cluster_config"` - Plugin json.RawMessage `json:"plugins"` - Store json.RawMessage `json:"store_config"` - Push json.RawMessage `json:"push"` - TLS json.RawMessage `json:"tls"` - Auth map[string]json.RawMessage `json:"auth_config"` - Validator map[string]*validatorConfig `json:"acc_validation"` - AccountGC *accountGcConfig `json:"acc_gc_config"` - Media *mediaConfig `json:"media"` - WebRTC json.RawMessage `json:"webrtc"` + Cluster json.RawMessage `json:"cluster_config"` + Plugin json.RawMessage `json:"plugins"` + Store json.RawMessage `json:"store_config"` + Push json.RawMessage `json:"push"` + TLS json.RawMessage `json:"tls"` + Auth map[string]json.RawMessage `json:"auth_config"` + Validator map[string]*validatorConfig `json:"acc_validation"` + AccountGC *accountGcConfig `json:"acc_gc_config"` + Media *mediaConfig `json:"media"` + WebRTC json.RawMessage `json:"webrtc"` + LinkPreview *LinkPreviewConfig `json:"link_preview"` } func main() { @@ -734,7 +739,9 @@ func main() { mux.HandleFunc("/", serve404) } - mux.HandleFunc(config.ApiPath+"v0/preview-link", PreviewLink) + if config.LinkPreview != nil && config.LinkPreview.Enabled { + mux.HandleFunc(config.ApiPath+"v0/preview-link", previewLink) + } if err = listenAndServe(config.Listen, mux, tlsConfig, signalHandler()); err != nil { logs.Err.Fatal(err) diff --git a/server/tinode.conf b/server/tinode.conf index 69845682..36e84e8d 100644 --- a/server/tinode.conf +++ b/server/tinode.conf @@ -678,5 +678,8 @@ // Address of the plugin. "service_addr": "tcp://localhost:40051" } - ] + ], + "link_preview": { + "enabled":true + } } From 5156f90c44baa286a6e8e354e42994d321760664 Mon Sep 17 00:00:00 2001 From: yinebebt Date: Fri, 29 Nov 2024 15:08:40 +0300 Subject: [PATCH 15/27] fix: add authorization check and use html tokenizer instead of parser --- server/linkpreview.go | 165 +++++++++++++++++++++++------------------- server/main.go | 28 +++---- server/tinode.conf | 4 +- 3 files changed, 104 insertions(+), 93 deletions(-) diff --git a/server/linkpreview.go b/server/linkpreview.go index dced553d..cf87cfd9 100644 --- a/server/linkpreview.go +++ b/server/linkpreview.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "errors" "golang.org/x/net/html" "io" "net" @@ -18,39 +19,42 @@ type linkPreview struct { } var client = &http.Client{ - Transport: &http.Transport{}, - Timeout: time.Second * 2, + Timeout: time.Second * 2, + CheckRedirect: func(req *http.Request, via []*http.Request) error { + if err := validateURL(req.URL.String()); err != nil { + return err + } + return nil + }, } // previewLink handles the HTTP request, fetches the URL, and returns the link preview. func previewLink(w http.ResponseWriter, r *http.Request) { + // check authorization + uid, challenge, err := authHttpRequest(r) + if err != nil { + http.Error(w, "invalid auth secret", http.StatusBadRequest) + return + } + if challenge != nil { + http.Error(w, "login challenge not done", http.StatusMultipleChoices) + return + } + if uid.IsZero() { + http.Error(w, "user not authenticated", http.StatusUnauthorized) + return + } + u := r.URL.Query().Get("url") if u == "" { http.Error(w, "Missing 'url' query parameter", http.StatusBadRequest) return } - parsedURL, err := url.Parse(u) - if err != nil { + if err := validateURL(u); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } - if parsedURL.Scheme != "http" && parsedURL.Scheme != "https" { - http.Error(w, "invalid schema", http.StatusBadRequest) - return - } - - ips, err := net.LookupIP(parsedURL.Hostname()) - if err != nil { - http.Error(w, "invalid host", http.StatusBadRequest) - return - } - for _, ip := range ips { - if ip.IsLoopback() || ip.IsPrivate() { - http.Error(w, "non routable IP address", http.StatusBadRequest) - return - } - } req, err := http.NewRequest(http.MethodGet, u, nil) if err != nil { @@ -70,79 +74,92 @@ func previewLink(w http.ResponseWriter, r *http.Request) { return } - body := io.LimitReader(resp.Body, 2*1024) // 2KB limit - doc, err := html.Parse(body) - if err != nil { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - w.Write([]byte("{}")) - return - } - - linkPreview := extractMetadata(doc) + body := http.MaxBytesReader(nil, resp.Body, 2*1024) // 2KB limit w.Header().Set("Content-Type", "application/json") - if err := json.NewEncoder(w).Encode(linkPreview); err != nil { + if err := json.NewEncoder(w).Encode(extractMetadata(body)); err != nil { http.Error(w, "Failed to encode response", http.StatusInternalServerError) } } -func extractMetadata(n *html.Node) linkPreview { +func extractMetadata(body io.Reader) linkPreview { var preview linkPreview + var gotTitle, gotDesc, gotImg, inTitleTag bool - var traverse func(*html.Node) - traverse = func(n *html.Node) { - if n.Type == html.ElementNode && strings.ToLower(n.Data) == "meta" { - var name, property, content string - for _, attr := range n.Attr { - switch attr.Key { - case "name": - name = attr.Val - case "property": - property = attr.Val - case "content": - content = attr.Val + tokenizer := html.NewTokenizer(body) + for { + switch tokenizer.Next() { + case html.ErrorToken: + return preview + + case html.StartTagToken, html.SelfClosingTagToken: + token := tokenizer.Token() + data := strings.ToLower(token.Data) + if data == "meta" { + var name, property, content string + for _, attr := range token.Attr { + switch strings.ToLower(attr.Key) { + case "name": + name = attr.Val + case "property": + property = attr.Val + case "content": + content = attr.Val + } } - } - if strings.HasPrefix(property, "og:") && content != "" { - if property == "og:title" { - preview.Title = content - } else if property == "og:description" { + if strings.HasPrefix(property, "og:") && content != "" { + switch property { + case "og:title": + preview.Title = content + gotTitle = true + case "og:description": + preview.Description = content + gotDesc = true + case "og:image": + preview.ImageURL = content + gotImg = true + } + } else if name == "description" && preview.Description == "" { preview.Description = content - } else if property == "og:image" { - preview.ImageURL = content + gotDesc = true } - } else if name == "description" && preview.Description == "" { - preview.Description = content + } else if data == "title" { + inTitleTag = true + } + + case html.TextToken: + if !gotTitle && inTitleTag { + preview.Title = strings.TrimSpace(tokenizer.Token().Data) + gotTitle = true + inTitleTag = false } } - - for child := n.FirstChild; child != nil; child = child.NextSibling { - traverse(child) + if gotTitle && gotDesc && gotImg { + break } } - - traverse(n) - - if preview.Title == "" { - preview.Title = extractTitle(n) - } - return preview } -func extractTitle(n *html.Node) string { - if n.Type == html.ElementNode && n.Data == "title" { - if n.FirstChild != nil { - return n.FirstChild.Data +func validateURL(u string) error { + parsedURL, err := url.Parse(u) + if err != nil { + return err + } + if parsedURL.Scheme != "http" && parsedURL.Scheme != "https" { + return &url.Error{Op: "validate", Err: errors.New("invalid scheme")} + } + + ips, err := net.LookupIP(parsedURL.Hostname()) + if err != nil { + return &url.Error{Op: "validate", Err: errors.New("invalid host")} + } + for _, ip := range ips { + if ip.IsLoopback() || ip.IsPrivate() { + return &url.Error{Op: "validate", Err: errors.New("non routable IP address")} } } - for child := n.FirstChild; child != nil; child = child.NextSibling { - title := extractTitle(child) - if title != "" { - return title - } - } - return "" + + return nil } diff --git a/server/main.go b/server/main.go index 7c52e16c..2182b08c 100644 --- a/server/main.go +++ b/server/main.go @@ -240,10 +240,6 @@ type mediaConfig struct { Handlers map[string]json.RawMessage `json:"handlers"` } -type LinkPreviewConfig struct { - Enabled bool `json:"enabled"` -} - // Contentx of the configuration file type configType struct { // HTTP(S) address:port to listen on for websocket and long polling clients. Either a @@ -296,17 +292,17 @@ type configType struct { DefaultCountryCode string `json:"default_country_code"` // Configs for subsystems - Cluster json.RawMessage `json:"cluster_config"` - Plugin json.RawMessage `json:"plugins"` - Store json.RawMessage `json:"store_config"` - Push json.RawMessage `json:"push"` - TLS json.RawMessage `json:"tls"` - Auth map[string]json.RawMessage `json:"auth_config"` - Validator map[string]*validatorConfig `json:"acc_validation"` - AccountGC *accountGcConfig `json:"acc_gc_config"` - Media *mediaConfig `json:"media"` - WebRTC json.RawMessage `json:"webrtc"` - LinkPreview *LinkPreviewConfig `json:"link_preview"` + Cluster json.RawMessage `json:"cluster_config"` + Plugin json.RawMessage `json:"plugins"` + Store json.RawMessage `json:"store_config"` + Push json.RawMessage `json:"push"` + TLS json.RawMessage `json:"tls"` + Auth map[string]json.RawMessage `json:"auth_config"` + Validator map[string]*validatorConfig `json:"acc_validation"` + AccountGC *accountGcConfig `json:"acc_gc_config"` + Media *mediaConfig `json:"media"` + WebRTC json.RawMessage `json:"webrtc"` + LinkPreviewEnabled bool `json:"link_preview_enabled"` } func main() { @@ -739,7 +735,7 @@ func main() { mux.HandleFunc("/", serve404) } - if config.LinkPreview != nil && config.LinkPreview.Enabled { + if config.LinkPreviewEnabled { mux.HandleFunc(config.ApiPath+"v0/preview-link", previewLink) } diff --git a/server/tinode.conf b/server/tinode.conf index 36e84e8d..d799b15d 100644 --- a/server/tinode.conf +++ b/server/tinode.conf @@ -679,7 +679,5 @@ "service_addr": "tcp://localhost:40051" } ], - "link_preview": { - "enabled":true - } + "link_preview_enabled":true } From 49f7b809fd8a9f2e53f0a1f95c6a5f2f0c673f9e Mon Sep 17 00:00:00 2001 From: yinebebt Date: Fri, 29 Nov 2024 23:36:43 +0300 Subject: [PATCH 16/27] fix: check request method, add config value into hi response and forward cache-control header --- server/linkpreview.go | 82 +++++++++++++++++++++++++++---------------- server/main.go | 4 ++- server/session.go | 1 + server/tinode.conf | 2 +- 4 files changed, 57 insertions(+), 32 deletions(-) diff --git a/server/linkpreview.go b/server/linkpreview.go index cf87cfd9..e88ee804 100644 --- a/server/linkpreview.go +++ b/server/linkpreview.go @@ -4,12 +4,14 @@ import ( "encoding/json" "errors" "golang.org/x/net/html" + "golang.org/x/net/html/atom" "io" "net" "net/http" "net/url" "strings" "time" + "unicode/utf8" ) type linkPreview struct { @@ -21,7 +23,7 @@ type linkPreview struct { var client = &http.Client{ Timeout: time.Second * 2, CheckRedirect: func(req *http.Request, via []*http.Request) error { - if err := validateURL(req.URL.String()); err != nil { + if err := validateURL(req.URL); err != nil { return err } return nil @@ -30,17 +32,18 @@ var client = &http.Client{ // previewLink handles the HTTP request, fetches the URL, and returns the link preview. func previewLink(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet && r.Method != http.MethodHead { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + // check authorization uid, challenge, err := authHttpRequest(r) if err != nil { http.Error(w, "invalid auth secret", http.StatusBadRequest) return } - if challenge != nil { - http.Error(w, "login challenge not done", http.StatusMultipleChoices) - return - } - if uid.IsZero() { + if challenge != nil || uid.IsZero() { http.Error(w, "user not authenticated", http.StatusUnauthorized) return } @@ -51,7 +54,12 @@ func previewLink(w http.ResponseWriter, r *http.Request) { return } - if err := validateURL(u); err != nil { + pu, err := url.Parse(u) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if err := validateURL(pu); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } @@ -75,30 +83,35 @@ func previewLink(w http.ResponseWriter, r *http.Request) { } body := http.MaxBytesReader(nil, resp.Body, 2*1024) // 2KB limit - + if cc := resp.Header.Get("Cache-Control"); cc != "" { + w.Header().Set("Cache-Control", cc) + } + if r.Method == http.MethodHead { + w.WriteHeader(http.StatusOK) + return + } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(extractMetadata(body)); err != nil { http.Error(w, "Failed to encode response", http.StatusInternalServerError) } } -func extractMetadata(body io.Reader) linkPreview { +func extractMetadata(body io.Reader) *linkPreview { var preview linkPreview - var gotTitle, gotDesc, gotImg, inTitleTag bool + var inTitleTag bool tokenizer := html.NewTokenizer(body) for { switch tokenizer.Next() { case html.ErrorToken: - return preview + return sanitizePreview(preview) case html.StartTagToken, html.SelfClosingTagToken: token := tokenizer.Token() - data := strings.ToLower(token.Data) - if data == "meta" { + if token.DataAtom == atom.Meta { var name, property, content string for _, attr := range token.Attr { - switch strings.ToLower(attr.Key) { + switch attr.Key { case "name": name = attr.Val case "property": @@ -112,46 +125,41 @@ func extractMetadata(body io.Reader) linkPreview { switch property { case "og:title": preview.Title = content - gotTitle = true case "og:description": preview.Description = content - gotDesc = true case "og:image": preview.ImageURL = content - gotImg = true } } else if name == "description" && preview.Description == "" { preview.Description = content - gotDesc = true } - } else if data == "title" { + } else if token.DataAtom == atom.Title { inTitleTag = true } case html.TextToken: - if !gotTitle && inTitleTag { + if preview.Title == "" && inTitleTag { preview.Title = strings.TrimSpace(tokenizer.Token().Data) - gotTitle = true + } + case html.EndTagToken: + if tokenizer.Token().DataAtom == atom.Title { inTitleTag = false } } - if gotTitle && gotDesc && gotImg { + if preview.Title != "" && preview.Description != "" && preview.ImageURL != "" { break } } - return preview + + return sanitizePreview(preview) } -func validateURL(u string) error { - parsedURL, err := url.Parse(u) - if err != nil { - return err - } - if parsedURL.Scheme != "http" && parsedURL.Scheme != "https" { +func validateURL(u *url.URL) error { + if u.Scheme != "http" && u.Scheme != "https" { return &url.Error{Op: "validate", Err: errors.New("invalid scheme")} } - ips, err := net.LookupIP(parsedURL.Hostname()) + ips, err := net.LookupIP(u.Hostname()) if err != nil { return &url.Error{Op: "validate", Err: errors.New("invalid host")} } @@ -163,3 +171,17 @@ func validateURL(u string) error { return nil } + +func sanitizePreview(preview linkPreview) *linkPreview { + if utf8.RuneCountInString(preview.Title) > 80 { + preview.Title = string([]rune(preview.Title)[:80]) + } + if utf8.RuneCountInString(preview.Description) > 256 { + preview.Description = string([]rune(preview.Description)[:256]) + } + if len(preview.ImageURL) > 2000 { + preview.ImageURL = preview.ImageURL[:2000] + } + + return &preview +} diff --git a/server/main.go b/server/main.go index 2182b08c..4b4c9313 100644 --- a/server/main.go +++ b/server/main.go @@ -202,7 +202,8 @@ var globals struct { // URL of the main endpoint. // TODO: implement file-serving API for gRPC and remove this feature. - servingAt string + servingAt string + linkPreviewEnabled bool } // Credential validator config. @@ -735,6 +736,7 @@ func main() { mux.HandleFunc("/", serve404) } + globals.linkPreviewEnabled = config.LinkPreviewEnabled if config.LinkPreviewEnabled { mux.HandleFunc(config.ApiPath+"v0/preview-link", previewLink) } diff --git a/server/session.go b/server/session.go index 8cf00b7a..a88bb90e 100644 --- a/server/session.go +++ b/server/session.go @@ -760,6 +760,7 @@ func (s *Session) hello(msg *ClientComMessage) { "maxTagCount": globals.maxTagCount, "maxFileUploadSize": globals.maxFileUploadSize, "reqCred": globals.validatorClientConfig, + "linkPreviewEnabled": globals.linkPreviewEnabled, } if len(globals.iceServers) > 0 { params["iceServers"] = globals.iceServers diff --git a/server/tinode.conf b/server/tinode.conf index d799b15d..e84ac49b 100644 --- a/server/tinode.conf +++ b/server/tinode.conf @@ -679,5 +679,5 @@ "service_addr": "tcp://localhost:40051" } ], - "link_preview_enabled":true + "link_preview_enabled":false } From 3fbfc5ceefa93c612f7f98897fcffbe6cfac8534 Mon Sep 17 00:00:00 2001 From: yinebebt Date: Mon, 2 Dec 2024 13:06:53 +0300 Subject: [PATCH 17/27] fix: use TagAttr and move trim space to sanitization --- server/linkpreview.go | 66 +++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/server/linkpreview.go b/server/linkpreview.go index e88ee804..fc5959bd 100644 --- a/server/linkpreview.go +++ b/server/linkpreview.go @@ -86,11 +86,13 @@ func previewLink(w http.ResponseWriter, r *http.Request) { if cc := resp.Header.Get("Cache-Control"); cc != "" { w.Header().Set("Cache-Control", cc) } + + w.Header().Set("Content-Type", "application/json") if r.Method == http.MethodHead { w.WriteHeader(http.StatusOK) return } - w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(extractMetadata(body)); err != nil { http.Error(w, "Failed to encode response", http.StatusInternalServerError) } @@ -101,50 +103,60 @@ func extractMetadata(body io.Reader) *linkPreview { var inTitleTag bool tokenizer := html.NewTokenizer(body) +lp: for { switch tokenizer.Next() { case html.ErrorToken: - return sanitizePreview(preview) + break lp case html.StartTagToken, html.SelfClosingTagToken: - token := tokenizer.Token() - if token.DataAtom == atom.Meta { + tag, hasAttr := tokenizer.TagName() + tagName := atom.Lookup(tag) + if tagName == atom.Meta && hasAttr { var name, property, content string - for _, attr := range token.Attr { - switch attr.Key { + for { + key, val, moreAttr := tokenizer.TagAttr() + switch atom.String(key) { case "name": - name = attr.Val + name = string(val) case "property": - property = attr.Val + property = string(val) case "content": - content = attr.Val + content = string(val) + } + if !moreAttr { + break } } - if strings.HasPrefix(property, "og:") && content != "" { - switch property { - case "og:title": - preview.Title = content - case "og:description": + if content != "" { + if strings.HasPrefix(property, "og:") { + switch property { + case "og:title": + preview.Title = content + case "og:description": + preview.Description = content + case "og:image": + preview.ImageURL = content + } + } else if name == "description" && preview.Description == "" { preview.Description = content - case "og:image": - preview.ImageURL = content } - } else if name == "description" && preview.Description == "" { - preview.Description = content } - } else if token.DataAtom == atom.Title { + } else if tagName == atom.Title { inTitleTag = true } case html.TextToken: - if preview.Title == "" && inTitleTag { - preview.Title = strings.TrimSpace(tokenizer.Token().Data) - } - case html.EndTagToken: - if tokenizer.Token().DataAtom == atom.Title { + if inTitleTag { + if preview.Title == "" { + preview.Title = tokenizer.Token().Data + } inTitleTag = false } + + case html.EndTagToken: + inTitleTag = false } if preview.Title != "" && preview.Description != "" && preview.ImageURL != "" { break @@ -174,13 +186,13 @@ func validateURL(u *url.URL) error { func sanitizePreview(preview linkPreview) *linkPreview { if utf8.RuneCountInString(preview.Title) > 80 { - preview.Title = string([]rune(preview.Title)[:80]) + preview.Title = string([]rune(strings.TrimSpace(preview.Title))[:80]) } if utf8.RuneCountInString(preview.Description) > 256 { - preview.Description = string([]rune(preview.Description)[:256]) + preview.Description = string([]rune(strings.TrimSpace(preview.Description))[:256]) } if len(preview.ImageURL) > 2000 { - preview.ImageURL = preview.ImageURL[:2000] + preview.ImageURL = strings.TrimSpace(preview.ImageURL)[:2000] } return &preview From fb6ebfaa128dc68e06cf9486bf58fb8a56781947 Mon Sep 17 00:00:00 2001 From: yinebebt Date: Mon, 2 Dec 2024 14:32:44 +0300 Subject: [PATCH 18/27] fix: add content-type check and move TrimSpace to the end --- server/linkpreview.go | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/server/linkpreview.go b/server/linkpreview.go index fc5959bd..98234bcc 100644 --- a/server/linkpreview.go +++ b/server/linkpreview.go @@ -92,9 +92,10 @@ func previewLink(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) return } - - if err := json.NewEncoder(w).Encode(extractMetadata(body)); err != nil { - http.Error(w, "Failed to encode response", http.StatusInternalServerError) + if strings.HasPrefix(resp.Header.Get("Content-Type"), "text/html") { + if err := json.NewEncoder(w).Encode(extractMetadata(body)); err != nil { + http.Error(w, "Failed to encode response", http.StatusInternalServerError) + } } } @@ -186,14 +187,18 @@ func validateURL(u *url.URL) error { func sanitizePreview(preview linkPreview) *linkPreview { if utf8.RuneCountInString(preview.Title) > 80 { - preview.Title = string([]rune(strings.TrimSpace(preview.Title))[:80]) + preview.Title = string([]rune(preview.Title)[:80]) } if utf8.RuneCountInString(preview.Description) > 256 { - preview.Description = string([]rune(strings.TrimSpace(preview.Description))[:256]) + preview.Description = string([]rune(preview.Description)[:256]) } if len(preview.ImageURL) > 2000 { - preview.ImageURL = strings.TrimSpace(preview.ImageURL)[:2000] + preview.ImageURL = preview.ImageURL[:2000] } - return &preview + return &linkPreview{ + Title: strings.TrimSpace(preview.Title), + Description: strings.TrimSpace(preview.Description), + ImageURL: strings.TrimSpace(preview.ImageURL), + } } From a5c55a1a3d260ae64e5afa873510e80b4f536227 Mon Sep 17 00:00:00 2001 From: or-else Date: Tue, 3 Dec 2024 20:45:24 +0300 Subject: [PATCH 19/27] link preview comments, formatting, documentation --- docs/API.md | 138 +++++++++++++++++++++++------------------- server/linkpreview.go | 15 +++-- server/main.go | 40 ++++++------ server/tinode.conf | 8 ++- 4 files changed, 115 insertions(+), 86 deletions(-) diff --git a/docs/API.md b/docs/API.md index e54aa054..6ed096cd 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1,66 +1,67 @@ - + - [Server API](#server-api) - - [How it Works?](#how-it-works) - - [General Considerations](#general-considerations) - - [Connecting to the Server](#connecting-to-the-server) - - [gRPC](#grpc) - - [WebSocket](#websocket) - - [Long Polling](#long-polling) - - [Out of Band Large Files](#out-of-band-large-files) - - [Running Behind a Reverse Proxy](#running-behind-a-reverse-proxy) - - [Users](#users) - - [Authentication](#authentication) - - [Creating an Account](#creating-an-account) - - [Logging in](#logging-in) - - [Changing Authentication Parameters](#changing-authentication-parameters) - - [Resetting a Password, i.e. "Forgot Password"](#resetting-a-password-ie-forgot-password) - - [Suspending a User](#suspending-a-user) - - [Credential Validation](#credential-validation) - - [Access Control](#access-control) - - [Topics](#topics) - - [`me` Topic](#me-topic) - - [`fnd` and Tags: Finding Users and Topics](#fnd-and-tags-finding-users-and-topics) - - [Query Language](#query-language) - - [Incremental Updates to Queries](#incremental-updates-to-queries) - - [Query Rewrite](#query-rewrite) - - [Possible Use Cases](#possible-use-cases) - - [Peer to Peer Topics](#peer-to-peer-topics) - - [Group Topics](#group-topics) - - [`sys` Topic](#sys-topic) - - [Using Server-Issued Message IDs](#using-server-issued-message-ids) - - [User Agent and Presence Notifications](#user-agent-and-presence-notifications) - - [Trusted, Public, and Private Fields](#trusted-public-and-private-fields) - - [Trusted](#trusted) - - [Public](#public) - - [Private](#private) - - [Format of Content](#format-of-content) - - [Out-of-Band Handling of Large Files](#out-of-band-handling-of-large-files) - - [Uploading](#uploading) - - [Downloading](#downloading) - - [Push Notifications](#push-notifications) - - [Tinode Push Gateway](#tinode-push-gateway) - - [Google FCM](#google-fcm) - - [Stdout](#stdout) - - [Video Calls](#video-calls) - - [Messages](#messages) - - [Client to Server Messages](#client-to-server-messages) - - [`{hi}`](#hi) - - [`{acc}`](#acc) - - [`{login}`](#login) - - [`{sub}`](#sub) - - [`{leave}`](#leave) - - [`{pub}`](#pub) - - [`{get}`](#get) - - [`{set}`](#set) - - [`{del}`](#del) - - [`{note}`](#note) - - [Server to Client Messages](#server-to-client-messages) - - [`{data}`](#data) - - [`{ctrl}`](#ctrl) - - [`{meta}`](#meta) - - [`{pres}`](#pres) - - [`{info}`](#info) + - [How it Works?](#how-it-works) + - [General Considerations](#general-considerations) + - [Connecting to the Server](#connecting-to-the-server) + - [gRPC](#grpc) + - [WebSocket](#websocket) + - [Long Polling](#long-polling) + - [Out of Band Large Files](#out-of-band-large-files) + - [Running Behind a Reverse Proxy](#running-behind-a-reverse-proxy) + - [Users](#users) + - [Authentication](#authentication) + - [Creating an Account](#creating-an-account) + - [Logging in](#logging-in) + - [Changing Authentication Parameters](#changing-authentication-parameters) + - [Resetting a Password, i.e. "Forgot Password"](#resetting-a-password-ie-forgot-password) + - [Suspending a User](#suspending-a-user) + - [Credential Validation](#credential-validation) + - [Access Control](#access-control) + - [Topics](#topics) + - [me Topic](#me-topic) + - [fnd and Tags: Finding Users and Topics](#fnd-and-tags-finding-users-and-topics) + - [Query Language](#query-language) + - [Incremental Updates to Queries](#incremental-updates-to-queries) + - [Query Rewrite](#query-rewrite) + - [Possible Use Cases](#possible-use-cases) + - [Peer to Peer Topics](#peer-to-peer-topics) + - [Group Topics](#group-topics) + - [sys Topic](#sys-topic) + - [Using Server-Issued Message IDs](#using-server-issued-message-ids) + - [User Agent and Presence Notifications](#user-agent-and-presence-notifications) + - [Trusted, Public, and Private Fields](#trusted-public-and-private-fields) + - [Trusted](#trusted) + - [Public](#public) + - [Private](#private) + - [Format of Content](#format-of-content) + - [Out-of-Band Handling of Large Files](#out-of-band-handling-of-large-files) + - [Uploading](#uploading) + - [Downloading](#downloading) + - [Push Notifications](#push-notifications) + - [Tinode Push Gateway](#tinode-push-gateway) + - [Google FCM](#google-fcm) + - [Stdout](#stdout) + - [Video Calls](#video-calls) + - [Link Previews](#link-previews) + - [Messages](#messages) + - [Client to Server Messages](#client-to-server-messages) + - [{hi}](#hi) + - [{acc}](#acc) + - [{login}](#login) + - [{sub}](#sub) + - [{leave}](#leave) + - [{pub}](#pub) + - [{get}](#get) + - [{set}](#set) + - [{del}](#del) + - [{note}](#note) + - [Server to Client Messages](#server-to-client-messages) + - [{data}](#data) + - [{ctrl}](#ctrl) + - [{meta}](#meta) + - [{pres}](#pres) + - [{info}](#info) @@ -601,6 +602,21 @@ The `stdout` adapter is mostly useful for debugging and logging. It writes push [See separate document](call-establishment.md). +## Link Previews + +Tinode provides an optional service which helps client applications generate link (URL) previews for inclusion into messages. The enpoint of this service (if enabled) is located at `/v0/urlpreview`. The service takes a single parameter `url`: + +``` +/v0/urlpreview?url=https%3A%2F%2Ftinode.co +``` +The first several kilobytes of the document at the given URL is fetched by issuing an HTTP(S) GET request. If the returned document has content-type `text/html`, the HTML is parsed for page title, description, and image URL. The result is formatted as JSON and returned as + +```json +{"title": "Page title", "description": "This is a page description", "image_url": "https://tinode.co/img/logo64x64.png"} +``` + +The link preview service requires authentication. It's exactly the same as authentication for [Out of Band Large Files](#out-of-band-handling-of-large-files). + ## Messages A message is a logically associated set of data. Messages are passed as JSON-formatted UTF-8 text. diff --git a/server/linkpreview.go b/server/linkpreview.go index 98234bcc..19ad55fd 100644 --- a/server/linkpreview.go +++ b/server/linkpreview.go @@ -3,8 +3,6 @@ package main import ( "encoding/json" "errors" - "golang.org/x/net/html" - "golang.org/x/net/html/atom" "io" "net" "net/http" @@ -12,6 +10,9 @@ import ( "strings" "time" "unicode/utf8" + + "golang.org/x/net/html" + "golang.org/x/net/html/atom" ) type linkPreview struct { @@ -31,26 +32,27 @@ var client = &http.Client{ } // previewLink handles the HTTP request, fetches the URL, and returns the link preview. +// urlpreview?url=https%3A%2F%2Ftinode.co func previewLink(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet && r.Method != http.MethodHead { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } // check authorization uid, challenge, err := authHttpRequest(r) if err != nil { - http.Error(w, "invalid auth secret", http.StatusBadRequest) + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } if challenge != nil || uid.IsZero() { - http.Error(w, "user not authenticated", http.StatusUnauthorized) + http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) return } u := r.URL.Query().Get("url") if u == "" { - http.Error(w, "Missing 'url' query parameter", http.StatusBadRequest) + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } @@ -92,6 +94,7 @@ func previewLink(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) return } + if strings.HasPrefix(resp.Header.Get("Content-Type"), "text/html") { if err := json.NewEncoder(w).Encode(extractMetadata(body)); err != nil { http.Error(w, "Failed to encode response", http.StatusInternalServerError) diff --git a/server/main.go b/server/main.go index 4b4c9313..157aecc2 100644 --- a/server/main.go +++ b/server/main.go @@ -202,7 +202,9 @@ var globals struct { // URL of the main endpoint. // TODO: implement file-serving API for gRPC and remove this feature. - servingAt string + servingAt string + + // Indicator if link preview generator is enabled. linkPreviewEnabled bool } @@ -292,18 +294,22 @@ type configType struct { // it's impossible to infer it. DefaultCountryCode string `json:"default_country_code"` + // Enable service which generates link previews: in response to a GET request with a URL + // /v0/urlpreview?url=https%3A%2F%2Ftinode.co visit the URL, parse HTML, and return JSON like + // {"title": "Page title", description: "This is a demo page", image_url: "https://tinode.co/img/logo.png"}. + LinkPreviewEnabled bool `json:"link_preview"` + // Configs for subsystems - Cluster json.RawMessage `json:"cluster_config"` - Plugin json.RawMessage `json:"plugins"` - Store json.RawMessage `json:"store_config"` - Push json.RawMessage `json:"push"` - TLS json.RawMessage `json:"tls"` - Auth map[string]json.RawMessage `json:"auth_config"` - Validator map[string]*validatorConfig `json:"acc_validation"` - AccountGC *accountGcConfig `json:"acc_gc_config"` - Media *mediaConfig `json:"media"` - WebRTC json.RawMessage `json:"webrtc"` - LinkPreviewEnabled bool `json:"link_preview_enabled"` + Cluster json.RawMessage `json:"cluster_config"` + Plugin json.RawMessage `json:"plugins"` + Store json.RawMessage `json:"store_config"` + Push json.RawMessage `json:"push"` + TLS json.RawMessage `json:"tls"` + Auth map[string]json.RawMessage `json:"auth_config"` + Validator map[string]*validatorConfig `json:"acc_validation"` + AccountGC *accountGcConfig `json:"acc_gc_config"` + Media *mediaConfig `json:"media"` + WebRTC json.RawMessage `json:"webrtc"` } func main() { @@ -731,16 +737,16 @@ func main() { logs.Info.Println("Large media handling enabled", config.Media.UseHandler) } + if config.LinkPreviewEnabled { + globals.linkPreviewEnabled = true + mux.HandleFunc(config.ApiPath+"v0/urlpreview", previewLink) + } + if staticMountPoint != "/" { // Serve json-formatted 404 for all other URLs mux.HandleFunc("/", serve404) } - globals.linkPreviewEnabled = config.LinkPreviewEnabled - if config.LinkPreviewEnabled { - mux.HandleFunc(config.ApiPath+"v0/preview-link", previewLink) - } - if err = listenAndServe(config.Listen, mux, tlsConfig, signalHandler()); err != nil { logs.Err.Fatal(err) } diff --git a/server/tinode.conf b/server/tinode.conf index e84ac49b..2d9d6a7c 100644 --- a/server/tinode.conf +++ b/server/tinode.conf @@ -68,6 +68,11 @@ // If missing, the server will default to "US". "default_country_code": "", + // Enable service which generates link previews: in response to a GET request with a URL + // /v0/urlpreview?url=https%3A%2F%2Ftinode.co visit the URL, parse HTML, and return JSON like + // {"title": "Page title", description: "This is a page description", image_url: "https://tinode.co/img/logo.png"}. + "link_preview_enabled": false, + // Large media/blob handlers: large files/images included in messages. "media": { // The name of the media handler to use. @@ -678,6 +683,5 @@ // Address of the plugin. "service_addr": "tcp://localhost:40051" } - ], - "link_preview_enabled":false + ] } From e88301d7fc0e7f0345c5129e5d3e920a62ca2150 Mon Sep 17 00:00:00 2001 From: or-else Date: Fri, 13 Dec 2024 10:25:50 +0300 Subject: [PATCH 20/27] warn that even number of cluster nodes is not a good idea --- server/cluster.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/cluster.go b/server/cluster.go index ce9e26a1..8cc27ace 100644 --- a/server/cluster.go +++ b/server/cluster.go @@ -1002,6 +1002,11 @@ func clusterInit(configString json.RawMessage, self *string) int { logs.Err.Fatal("Cluster: invalid cluster size: 1") } + if len(globals.cluster.nodes)%2 == 1 { + // Even number of cluster nodes (self + odd number). + logs.Warn.Println("Cluster: use odd number of cluster nodes") + } + if !globals.cluster.failoverInit(config.Failover) { globals.cluster.rehash(nil) } From cc5bb68056f0127311acdfce9ef4bb4653b2261c Mon Sep 17 00:00:00 2001 From: or-else Date: Thu, 19 Dec 2024 11:02:38 +0300 Subject: [PATCH 21/27] dependencies updated --- go.mod | 123 ++++++++++++-------- go.sum | 347 +++++++++++++++++++++++++++++---------------------------- 2 files changed, 250 insertions(+), 220 deletions(-) diff --git a/go.mod b/go.mod index 64535d7a..b2eba7b8 100644 --- a/go.mod +++ b/go.mod @@ -1,84 +1,109 @@ module github.com/tinode/chat -go 1.19 +go 1.22.7 + +toolchain go1.23.4 require ( firebase.google.com/go v3.13.0+incompatible - github.com/aws/aws-sdk-go v1.46.4 - github.com/go-sql-driver/mysql v1.7.1 + github.com/aws/aws-sdk-go v1.55.5 + github.com/go-sql-driver/mysql v1.8.1 github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.6.0 - github.com/gorilla/handlers v1.5.1 - github.com/gorilla/websocket v1.5.0 + github.com/gorilla/handlers v1.5.2 + github.com/gorilla/websocket v1.5.3 github.com/jackc/pgconn v1.14.3 github.com/jackc/pgx/v4 v4.18.3 - github.com/jmoiron/sqlx v1.3.5 - github.com/nyaruka/phonenumbers v1.1.8 - github.com/prometheus/client_golang v1.19.0 - github.com/prometheus/common v0.48.0 + github.com/jmoiron/sqlx v1.4.0 + github.com/nyaruka/phonenumbers v1.4.3 + github.com/prometheus/client_golang v1.20.5 + github.com/prometheus/common v0.61.0 github.com/rivo/uniseg v0.4.7 github.com/tinode/jsonco v1.0.0 github.com/tinode/snowflake v1.0.0 - go.mongodb.org/mongo-driver v1.12.1 - golang.org/x/crypto v0.21.0 - golang.org/x/net v0.23.0 - golang.org/x/oauth2 v0.16.0 - golang.org/x/text v0.14.0 - google.golang.org/api v0.148.0 - google.golang.org/grpc v1.59.0 - google.golang.org/protobuf v1.33.0 + go.mongodb.org/mongo-driver v1.17.1 + golang.org/x/crypto v0.31.0 + golang.org/x/net v0.33.0 + golang.org/x/oauth2 v0.24.0 + golang.org/x/text v0.21.0 + google.golang.org/api v0.213.0 + google.golang.org/grpc v1.69.2 + google.golang.org/protobuf v1.36.0 gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.2 ) -require github.com/pkg/errors v0.9.1 // indirect +require ( + cel.dev/expr v0.19.1 // indirect + cloud.google.com/go/auth v0.13.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect + cloud.google.com/go/monitoring v1.22.0 // indirect + filippo.io/edwards25519 v1.1.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect + github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect + github.com/cncf/xds/go v0.0.0-20241213214725-57cfbe6fad57 // indirect + github.com/envoyproxy/go-control-plane v0.13.1 // indirect + github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.33.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect + go.opentelemetry.io/otel v1.33.0 // indirect + go.opentelemetry.io/otel/metric v1.33.0 // indirect + go.opentelemetry.io/otel/sdk v1.33.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.33.0 // indirect + go.opentelemetry.io/otel/trace v1.33.0 // indirect +) require ( - cloud.google.com/go v0.110.9 // indirect - cloud.google.com/go/compute v1.23.1 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/firestore v1.14.0 // indirect - cloud.google.com/go/iam v1.1.3 // indirect - cloud.google.com/go/longrunning v0.5.2 // indirect - cloud.google.com/go/storage v1.33.0 // indirect + cloud.google.com/go v0.117.0 // indirect + cloud.google.com/go/compute/metadata v0.6.0 // indirect + cloud.google.com/go/firestore v1.17.0 // indirect + cloud.google.com/go/iam v1.3.0 // indirect + cloud.google.com/go/longrunning v0.6.3 // indirect + cloud.google.com/go/storage v1.48.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/felixge/httpsnoop v1.0.3 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/s2a-go v0.1.7 // indirect - github.com/google/uuid v1.3.1 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect - github.com/googleapis/gax-go/v2 v2.12.0 // indirect + github.com/google/s2a-go v0.1.8 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect + github.com/googleapis/gax-go/v2 v2.14.0 // indirect github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgproto3/v2 v2.3.3 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect - github.com/jackc/pgtype v1.14.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgtype v1.14.4 // indirect github.com/jackc/puddle v1.3.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/klauspost/compress v1.17.2 // indirect + github.com/klauspost/compress v1.17.11 // indirect github.com/montanaflynn/stats v0.7.1 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect - github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/procfs v0.15.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/twilio/twilio-go v1.23.3 + github.com/twilio/twilio-go v1.23.8 github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect - github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect - go.opencensus.io v0.24.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/time v0.8.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect + google.golang.org/genproto v0.0.0-20241216192217-9240e9c98484 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241216192217-9240e9c98484 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241216192217-9240e9c98484 // indirect gopkg.in/cenkalti/backoff.v2 v2.2.1 // indirect ) diff --git a/go.sum b/go.sum index 0f68a24d..410cb20d 100644 --- a/go.sum +++ b/go.sum @@ -1,24 +1,43 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.110.9 h1:e7ITSqGFFk4rbz/JFIqZh3G4VEHguhAL4BQcFlWtU68= -cloud.google.com/go v0.110.9/go.mod h1:rpxevX/0Lqvlbc88b7Sc1SPNdyK1riNBTUU6JXhYNpM= -cloud.google.com/go/compute v1.23.1 h1:V97tBoDaZHb6leicZ1G6DLK2BAaZLJ/7+9BB/En3hR0= -cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/firestore v1.14.0 h1:8aLcKnMPoldYU3YHgu4t2exrKhLQkqaXAGqT0ljrFVw= -cloud.google.com/go/firestore v1.14.0/go.mod h1:96MVaHLsEhbvkBEdZgfN+AS/GIkco1LRpH9Xp9YZfzQ= -cloud.google.com/go/iam v1.1.3 h1:18tKG7DzydKWUnLjonWcJO6wjSCAtzh4GcRKlH/Hrzc= -cloud.google.com/go/iam v1.1.3/go.mod h1:3khUlaBXfPKKe7huYgEpDn6FtgRyMEqbkvBxrQyY5SE= -cloud.google.com/go/longrunning v0.5.2 h1:u+oFqfEwwU7F9dIELigxbe0XVnBAo9wqMuQLA50CZ5k= -cloud.google.com/go/longrunning v0.5.2/go.mod h1:nqo6DQbNV2pXhGDbDMoN2bWz68MjZUzqv2YttZiveCs= -cloud.google.com/go/storage v1.33.0 h1:PVrDOkIC8qQVa1P3SXGpQvfuJhN2LHOoyZvWs8D2X5M= -cloud.google.com/go/storage v1.33.0/go.mod h1:Hhh/dogNRGca7IWv1RC2YqEn0c0G77ctA/OxflYkiD8= +cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= +cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cloud.google.com/go v0.117.0 h1:Z5TNFfQxj7WG2FgOGX1ekC5RiXrYgms6QscOm32M/4s= +cloud.google.com/go v0.117.0/go.mod h1:ZbwhVTb1DBGt2Iwb3tNO6SEK4q+cplHZmLWH+DelYYc= +cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs= +cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q= +cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU= +cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= +cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= +cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= +cloud.google.com/go/firestore v1.17.0 h1:iEd1LBbkDZTFsLw3sTH50eyg4qe8eoG6CjocmEXO9aQ= +cloud.google.com/go/firestore v1.17.0/go.mod h1:69uPx1papBsY8ZETooc71fOhoKkD70Q1DwMrtKuOT/Y= +cloud.google.com/go/iam v1.3.0 h1:4Wo2qTaGKFtajbLpF6I4mywg900u3TLlHDb6mriLDPU= +cloud.google.com/go/iam v1.3.0/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY= +cloud.google.com/go/logging v1.12.0 h1:ex1igYcGFd4S/RZWOCU51StlIEuey5bjqwH9ZYjHibk= +cloud.google.com/go/logging v1.12.0/go.mod h1:wwYBt5HlYP1InnrtYI0wtwttpVU1rifnMT7RejksUAM= +cloud.google.com/go/longrunning v0.6.3 h1:A2q2vuyXysRcwzqDpMMLSI6mb6o39miS52UEG/Rd2ng= +cloud.google.com/go/longrunning v0.6.3/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI= +cloud.google.com/go/monitoring v1.22.0 h1:mQ0040B7dpuRq1+4YiQD43M2vW9HgoVxY98xhqGT+YI= +cloud.google.com/go/monitoring v1.22.0/go.mod h1:hS3pXvaG8KgWTSz+dAdyzPrGUYmi2Q+WFX8g2hqVEZU= +cloud.google.com/go/storage v1.48.0 h1:FhBDHACbVtdPx7S/AbcKujPWiHvfO6F8OXGgCEbB2+o= +cloud.google.com/go/storage v1.48.0/go.mod h1:aFoDYNMAjv67lp+xcuZqjUKv/ctmplzQ3wJgodA7b+M= +cloud.google.com/go/trace v1.11.2 h1:4ZmaBdL8Ng/ajrgKqY5jfvzqMXbrDcBsUGXOT9aqTtI= +cloud.google.com/go/trace v1.11.2/go.mod h1:bn7OwXd4pd5rFuAnTrzBuoZ4ax2XQeG3qNgYmfCy0Io= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4= firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 h1:o90wcURuxekmXrtxmYWTyNla0+ZEHhud6DI1ZTxd1vI= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0/go.mod h1:6fTWu4m3jocfUZLYF5KsZC1TUfRvEjs7lM4crme/irw= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.49.0 h1:jJKWl98inONJAr/IZrdFQUWcwUO95DLY1XMD1ZIut+g= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.49.0/go.mod h1:l2fIqmwB+FKSfvn3bAD/0i+AXAxhIZjTK2svT/mgUXs= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 h1:GYUJLfvd++4DMuMhCFLgLXvFwofIxh/qOwoGuS/LTew= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0/go.mod h1:wRbFgBQUVm1YXrvWKofAEmq9HNJTDphbAaJSSX01KUI= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/aws/aws-sdk-go v1.46.4 h1:48tKgtm9VMPkb6y7HuYlsfhQmoIRAsTEXTsWLVlty4M= -github.com/aws/aws-sdk-go v1.46.4/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -26,11 +45,12 @@ github.com/bitly/go-hostpool v0.1.0 h1:XKmsF6k5el6xHG3WPJ8U0Ku/ye7njX7W81Ng7O2io github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cncf/xds/go v0.0.0-20241213214725-57cfbe6fad57 h1:put7Je9ZyxbHtwr7IqGrW4LLVUupJQ2gbsDshKISSgU= +github.com/cncf/xds/go v0.0.0-20241213214725-57cfbe6fad57/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -40,72 +60,56 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE= +github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw= +github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= +github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= -github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +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/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +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-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +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/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= 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.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= +github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= +github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= -github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= -github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= -github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= -github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= -github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= +github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= +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/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= +github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= +github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPqMNIe8o= +github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk= +github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= +github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -139,18 +143,21 @@ github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgtype v1.14.4 h1:fKuNiCumbKTAIxQwXfB/nsrnkEI6bPJrrSiMKgbJ2j8= +github.com/jackc/pgtype v1.14.4/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= @@ -162,26 +169,29 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= -github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= +github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= -github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275 h1:IZycmTpoUtQK3PD60UYBwjaCUHUP7cML494ao9/O8+Q= github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275/go.mod h1:zt6UU74K6Z6oMOYJbJzYpYucqdcQwSMPBEdSvGiaUMw= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= @@ -189,14 +199,15 @@ github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nyaruka/phonenumbers v1.1.8 h1:mjFu85FeoH2Wy18aOMUvxqi1GgAqiQSJsa/cCC5yu2s= -github.com/nyaruka/phonenumbers v1.1.8/go.mod h1:DC7jZd321FqUe+qWSNcHi10tyIyGNXGcNbfkPvdp1Vs= +github.com/nyaruka/phonenumbers v1.4.3 h1:tR71UJ+DZu7TSkxoG8JI8HzHJkPD/m4KNiUX34Fvmlo= +github.com/nyaruka/phonenumbers v1.4.3/go.mod h1:gv+CtldaFz+G3vHHnasBSirAi3O2XLqZzVWz4V1pl2E= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= @@ -207,22 +218,23 @@ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYr github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang 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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= -github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ= +github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -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.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= @@ -248,30 +260,50 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tinode/jsonco v1.0.0 h1:zVcpjzDvjuA1G+HLrckI5EiiRyq9jgV3x37OQl6e5FE= github.com/tinode/jsonco v1.0.0/go.mod h1:Bnavu3302Qfn2pILMNwASkelodgeew3IvDrbdzU84u8= github.com/tinode/snowflake v1.0.0 h1:YciQ9ZKn1TrnvpS8yZErt044XJaxWVtR9aMO9rOZVOE= github.com/tinode/snowflake v1.0.0/go.mod h1:5JiaCe3o7QdDeyRcAeZBGVghwRS+ygt2CF/hxmAoptQ= -github.com/twilio/twilio-go v1.23.3 h1:9DsuC9+6CfQW9dlzdeQeyhn3z2oPjZQcOhMCgh5VkgE= -github.com/twilio/twilio-go v1.23.3/go.mod h1:zRkMjudW7v7MqQ3cWNZmSoZJ7EBjPZ4OpNh2zm7Q6ko= +github.com/twilio/twilio-go v1.23.8 h1:kuuYWsNHFVK9JEAnOqBfnsgtLy+fYdapqCV5SBr3nXU= +github.com/twilio/twilio-go v1.23.8/go.mod h1:zRkMjudW7v7MqQ3cWNZmSoZJ7EBjPZ4OpNh2zm7Q6ko= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= -github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= 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/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.mongodb.org/mongo-driver v1.12.1 h1:nLkghSU8fQNaK7oUmDhQFsnrtcoNy7Z6LVFKsEecqgE= -go.mongodb.org/mongo-driver v1.12.1/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ= +go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM= +go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/detectors/gcp v1.33.0 h1:FVPoXEoILwgbZUu4X7YSgsESsAmGRgoYcnXkzgQPhP4= +go.opentelemetry.io/contrib/detectors/gcp v1.33.0/go.mod h1:ZHrLmr4ikK2AwRj9QL+c9s2SOlgoSRyMpNVzUj2fZqI= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= +go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= +go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I= +go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= +go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= +go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= +go.opentelemetry.io/otel/sdk/metric v1.33.0 h1:Gs5VK9/WUJhNXZgn8MR6ITatvAmKeIuCtNbsP3JkNqU= +go.opentelemetry.io/otel/sdk/metric v1.33.0/go.mod h1:dL5ykHZmm1B1nVRk9dDjChwDmt81MjVp3gLkQRwKf/Q= +go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= +go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -294,45 +326,38 @@ golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 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/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 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-20190423024810-112230192c58/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.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -345,22 +370,24 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w 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-20200223170610-d5e6a3e2c0ae/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-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-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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -368,18 +395,16 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -388,49 +413,29 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 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.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -google.golang.org/api v0.148.0 h1:HBq4TZlN4/1pNcu0geJZ/Q50vIwIXT532UIMYoo0vOs= -google.golang.org/api v0.148.0/go.mod h1:8/TBgwaKjfqTdacOJrOv2+2Q6fBDU1uHKK06oGSkxzU= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/api v0.213.0 h1:KmF6KaDyFqB417T68tMPbVmmwtIXs2VB60OJKIHB0xQ= +google.golang.org/api v0.213.0/go.mod h1:V0T5ZhNUUNpYAlL306gFZPFt5F5D/IeyLoktduYYnvQ= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b h1:+YaDE2r2OG8t/z5qmsh7Y+XXwCbvadxxZ0YY6mTdrVA= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= -google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k= -google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/genproto v0.0.0-20241216192217-9240e9c98484 h1:a/U5otbGrI6mYIO598WriFB1172i6Ktr6FGcatZD3Yw= +google.golang.org/genproto v0.0.0-20241216192217-9240e9c98484/go.mod h1:Gmd/M/W9fEyf6VSu/mWLnl+9Be51B9CLdxdsKokYq7Y= +google.golang.org/genproto/googleapis/api v0.0.0-20241216192217-9240e9c98484 h1:ChAdCYNQFDk5fYvFZMywKLIijG7TC2m1C2CMEu11G3o= +google.golang.org/genproto/googleapis/api v0.0.0-20241216192217-9240e9c98484/go.mod h1:KRUmxRI4JmbpAm8gcZM4Jsffi859fo5LQjILwuqj9z8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241216192217-9240e9c98484 h1:Z7FRVJPSMaHQxD0uXU8WdgFh8PseLM8Q8NzhnpMrBhQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241216192217-9240e9c98484/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA= +google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= +google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= 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.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ= +google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/cenkalti/backoff.v2 v2.2.1 h1:eJ9UAg01/HIHG987TwxvnzK2MgxXq97YY6rYDpY9aII= gopkg.in/cenkalti/backoff.v2 v2.2.1/go.mod h1:S0QdOvT2AlerfSBkp0O+dk+bbIMaNbEmVk876gPCthU= @@ -438,6 +443,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= @@ -449,9 +455,8 @@ 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.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= From 6d5c407c50609573cd157574b1228967972ad4da Mon Sep 17 00:00:00 2001 From: or-else Date: Sat, 21 Dec 2024 10:35:44 +0300 Subject: [PATCH 22/27] clarify what not to report as security problems --- SECURITY.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 3bff6c23..adae5871 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,6 +2,11 @@ ## Reporting a Vulnerability -Please report a vulnerability to security@tinode.co +Please report a vulnerability to `security@tinode.co`. + +## What not to report + + * Firebase initialization tokens. The Firebase tokens are really public: they must be included into client applications and consequently are not private by design. + * Exposed `/pprof` or `/expvar`. We know they are exposed. It's intentional and harmless. + * Exposed Prometheus metrics `/metrics`. Like above, it's intentional and harmless. -Please do not report Firebase initialization tokens. The Firebase tokens are really public: they must be included into client applications and consequently are not private by design. From 3271beb6930ea37126b8c6f4b173d1ed90394ae5 Mon Sep 17 00:00:00 2001 From: or-else Date: Thu, 9 Jan 2025 12:27:05 +0300 Subject: [PATCH 23/27] remove public video conferencing config to avoid confusion --- server/tinode.conf | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/server/tinode.conf b/server/tinode.conf index 5684516a..a1c5d0e7 100644 --- a/server/tinode.conf +++ b/server/tinode.conf @@ -618,20 +618,7 @@ } ], // An alternative way to provide STUN/TURN configuration. - "ice_servers_file": "/path/to/ice-servers-config.json", - - // Video conferencing configuration. - "vc": { - "enabled": true, - // Media server endpoint url. - "endpoint_url": "ws://localhost:7880/", - // Media server API key. - "api_key": "devkey", - // Media server secret. - "api_secret": "secret", - // Maximum call duration in seconds (1800 seconds = 30 min). - "max_duration": 1800 - } + "ice_servers_file": "/path/to/ice-servers-config.json" }, // Cluster-mode configuration. From 777c2ab914609805e04c80c2d96438a773ee3761 Mon Sep 17 00:00:00 2001 From: or-else Date: Sun, 12 Jan 2025 19:13:39 +0300 Subject: [PATCH 24/27] explain database support --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5999fbed..aa0e6797 100644 --- a/README.md +++ b/README.md @@ -112,11 +112,11 @@ When you register a new account you are asked for an email address to send valid * Java with dependencies on [Jackson](https://github.com/FasterXML/jackson) and [Java-Websocket](https://github.com/TooTallNate/Java-WebSocket). Suitable for Android but with no Android SDK dependencies. * Swift with no external dependencies. * C/C++, C#, Go, Python, PHP, Ruby and many other languages using [gRPC](https://grpc.io/docs/languages/). - * Choice of a database backend. Other databases can be added with by writing [adapters](server/db/adapter.go). - * MySQL + * Choice of a database backend. Other databases can be added by writing [adapters](server/db/adapter.go). + * MySQL (and MariaDB, Percona as long as they remain SQL and wire protocol compatible) * PostgreSQL * MongoDB - * [RethinkDB](http://rethinkdb.com/) + * [RethinkDB](http://rethinkdb.com/). Support is deprecated because RethinkDB is no longer being developed ### Planned From d7bd87e25c1d26ef606ec9faa8283c919f38e08d Mon Sep 17 00:00:00 2001 From: or-else Date: Wed, 15 Jan 2025 17:20:44 +0300 Subject: [PATCH 25/27] updted the list of dependencies for the java client --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aa0e6797..ae4b9bb5 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ When you register a new account you are asked for an email address to send valid * JSON or [protobuf version 3](https://developers.google.com/protocol-buffers/) wire protocols. * Bindings for various programming languages: * Javascript with no external dependencies. - * Java with dependencies on [Jackson](https://github.com/FasterXML/jackson) and [Java-Websocket](https://github.com/TooTallNate/Java-WebSocket). Suitable for Android but with no Android SDK dependencies. + * Java with dependencies on [Jackson](https://github.com/FasterXML/jackson), [Java-Websocket](https://github.com/TooTallNate/Java-WebSocket), [ICU4J](https://github.com/unicode-org/icu). Suitable for Android but with no Android SDK dependencies. * Swift with no external dependencies. * C/C++, C#, Go, Python, PHP, Ruby and many other languages using [gRPC](https://grpc.io/docs/languages/). * Choice of a database backend. Other databases can be added by writing [adapters](server/db/adapter.go). From 80fb0c64bfb01d3f82269f29c22b67c344d7b109 Mon Sep 17 00:00:00 2001 From: or-else Date: Wed, 15 Jan 2025 17:24:57 +0300 Subject: [PATCH 26/27] removed link previews (saved to link-preview branch) --- server/linkpreview.go | 207 ------------------------------------------ server/main.go | 13 --- server/session.go | 1 - server/tinode.conf | 5 - 4 files changed, 226 deletions(-) delete mode 100644 server/linkpreview.go diff --git a/server/linkpreview.go b/server/linkpreview.go deleted file mode 100644 index 19ad55fd..00000000 --- a/server/linkpreview.go +++ /dev/null @@ -1,207 +0,0 @@ -package main - -import ( - "encoding/json" - "errors" - "io" - "net" - "net/http" - "net/url" - "strings" - "time" - "unicode/utf8" - - "golang.org/x/net/html" - "golang.org/x/net/html/atom" -) - -type linkPreview struct { - Title string `json:"title,omitempty"` - Description string `json:"description,omitempty"` - ImageURL string `json:"image_url,omitempty"` -} - -var client = &http.Client{ - Timeout: time.Second * 2, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - if err := validateURL(req.URL); err != nil { - return err - } - return nil - }, -} - -// previewLink handles the HTTP request, fetches the URL, and returns the link preview. -// urlpreview?url=https%3A%2F%2Ftinode.co -func previewLink(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet && r.Method != http.MethodHead { - http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) - return - } - - // check authorization - uid, challenge, err := authHttpRequest(r) - if err != nil { - http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) - return - } - if challenge != nil || uid.IsZero() { - http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) - return - } - - u := r.URL.Query().Get("url") - if u == "" { - http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) - return - } - - pu, err := url.Parse(u) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - if err := validateURL(pu); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - req, err := http.NewRequest(http.MethodGet, u, nil) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - resp, err := client.Do(req) - if err != nil { - http.Error(w, err.Error(), http.StatusBadGateway) - return - } - defer resp.Body.Close() - - if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices { // StatusCode != 20X - http.Error(w, "Non-OK HTTP status", http.StatusBadGateway) - return - } - - body := http.MaxBytesReader(nil, resp.Body, 2*1024) // 2KB limit - if cc := resp.Header.Get("Cache-Control"); cc != "" { - w.Header().Set("Cache-Control", cc) - } - - w.Header().Set("Content-Type", "application/json") - if r.Method == http.MethodHead { - w.WriteHeader(http.StatusOK) - return - } - - if strings.HasPrefix(resp.Header.Get("Content-Type"), "text/html") { - if err := json.NewEncoder(w).Encode(extractMetadata(body)); err != nil { - http.Error(w, "Failed to encode response", http.StatusInternalServerError) - } - } -} - -func extractMetadata(body io.Reader) *linkPreview { - var preview linkPreview - var inTitleTag bool - - tokenizer := html.NewTokenizer(body) -lp: - for { - switch tokenizer.Next() { - case html.ErrorToken: - break lp - - case html.StartTagToken, html.SelfClosingTagToken: - tag, hasAttr := tokenizer.TagName() - tagName := atom.Lookup(tag) - if tagName == atom.Meta && hasAttr { - var name, property, content string - for { - key, val, moreAttr := tokenizer.TagAttr() - switch atom.String(key) { - case "name": - name = string(val) - case "property": - property = string(val) - case "content": - content = string(val) - } - if !moreAttr { - break - } - } - - if content != "" { - if strings.HasPrefix(property, "og:") { - switch property { - case "og:title": - preview.Title = content - case "og:description": - preview.Description = content - case "og:image": - preview.ImageURL = content - } - } else if name == "description" && preview.Description == "" { - preview.Description = content - } - } - } else if tagName == atom.Title { - inTitleTag = true - } - - case html.TextToken: - if inTitleTag { - if preview.Title == "" { - preview.Title = tokenizer.Token().Data - } - inTitleTag = false - } - - case html.EndTagToken: - inTitleTag = false - } - if preview.Title != "" && preview.Description != "" && preview.ImageURL != "" { - break - } - } - - return sanitizePreview(preview) -} - -func validateURL(u *url.URL) error { - if u.Scheme != "http" && u.Scheme != "https" { - return &url.Error{Op: "validate", Err: errors.New("invalid scheme")} - } - - ips, err := net.LookupIP(u.Hostname()) - if err != nil { - return &url.Error{Op: "validate", Err: errors.New("invalid host")} - } - for _, ip := range ips { - if ip.IsLoopback() || ip.IsPrivate() { - return &url.Error{Op: "validate", Err: errors.New("non routable IP address")} - } - } - - return nil -} - -func sanitizePreview(preview linkPreview) *linkPreview { - if utf8.RuneCountInString(preview.Title) > 80 { - preview.Title = string([]rune(preview.Title)[:80]) - } - if utf8.RuneCountInString(preview.Description) > 256 { - preview.Description = string([]rune(preview.Description)[:256]) - } - if len(preview.ImageURL) > 2000 { - preview.ImageURL = preview.ImageURL[:2000] - } - - return &linkPreview{ - Title: strings.TrimSpace(preview.Title), - Description: strings.TrimSpace(preview.Description), - ImageURL: strings.TrimSpace(preview.ImageURL), - } -} diff --git a/server/main.go b/server/main.go index 157aecc2..a7b1c24e 100644 --- a/server/main.go +++ b/server/main.go @@ -203,9 +203,6 @@ var globals struct { // URL of the main endpoint. // TODO: implement file-serving API for gRPC and remove this feature. servingAt string - - // Indicator if link preview generator is enabled. - linkPreviewEnabled bool } // Credential validator config. @@ -294,11 +291,6 @@ type configType struct { // it's impossible to infer it. DefaultCountryCode string `json:"default_country_code"` - // Enable service which generates link previews: in response to a GET request with a URL - // /v0/urlpreview?url=https%3A%2F%2Ftinode.co visit the URL, parse HTML, and return JSON like - // {"title": "Page title", description: "This is a demo page", image_url: "https://tinode.co/img/logo.png"}. - LinkPreviewEnabled bool `json:"link_preview"` - // Configs for subsystems Cluster json.RawMessage `json:"cluster_config"` Plugin json.RawMessage `json:"plugins"` @@ -737,11 +729,6 @@ func main() { logs.Info.Println("Large media handling enabled", config.Media.UseHandler) } - if config.LinkPreviewEnabled { - globals.linkPreviewEnabled = true - mux.HandleFunc(config.ApiPath+"v0/urlpreview", previewLink) - } - if staticMountPoint != "/" { // Serve json-formatted 404 for all other URLs mux.HandleFunc("/", serve404) diff --git a/server/session.go b/server/session.go index a88bb90e..8cf00b7a 100644 --- a/server/session.go +++ b/server/session.go @@ -760,7 +760,6 @@ func (s *Session) hello(msg *ClientComMessage) { "maxTagCount": globals.maxTagCount, "maxFileUploadSize": globals.maxFileUploadSize, "reqCred": globals.validatorClientConfig, - "linkPreviewEnabled": globals.linkPreviewEnabled, } if len(globals.iceServers) > 0 { params["iceServers"] = globals.iceServers diff --git a/server/tinode.conf b/server/tinode.conf index a1c5d0e7..06f5c305 100644 --- a/server/tinode.conf +++ b/server/tinode.conf @@ -68,11 +68,6 @@ // If missing, the server will default to "US". "default_country_code": "", - // Enable service which generates link previews: in response to a GET request with a URL - // /v0/urlpreview?url=https%3A%2F%2Ftinode.co visit the URL, parse HTML, and return JSON like - // {"title": "Page title", description: "This is a page description", image_url: "https://tinode.co/img/logo.png"}. - "link_preview_enabled": false, - // Large media/blob handlers: large files/images included in messages. "media": { // The name of the media handler to use. From 6627fd8e587bc4bd3bfabe45ebc247bf3fddbb18 Mon Sep 17 00:00:00 2001 From: or-else Date: Wed, 15 Jan 2025 21:02:10 +0300 Subject: [PATCH 27/27] remove assignment to a deprecated protobuf field --- server/pbconverter.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/pbconverter.go b/server/pbconverter.go index 4a7ac19c..55f4f2ed 100644 --- a/server/pbconverter.go +++ b/server/pbconverter.go @@ -135,8 +135,6 @@ func pbServSerialize(msg *ServerComMessage) *pbx.ServerMsg { pkt.Message = pbServMetaSerialize(msg.Meta) } - pkt.Topic = msg.RcptTo - return &pkt }