From 3c940c428d529476b6fa2cbf1ba28d53ec011584 Mon Sep 17 00:00:00 2001 From: Till <2353100+S7evinK@users.noreply.github.com> Date: Wed, 4 May 2022 19:04:28 +0200 Subject: [PATCH 01/12] Add opt-in anonymous stats reporting (#2249) * Initial phone home stats queries * Add userAgent to UpdateDeviceLastSeen Add new Table for tracking daily user vists * Add user_daily_visits table * Fix queries * userapi stats tables & queries * userapi interface and internal api * sycnapi stats queries * testing phone home stats * Add complete config to syncapi * add missing files * Fix queries * Send empty request * Add version & monolith stats * Add configuration for phone home stats * Move WASM to its own file, add config and comments * Add tracing methods * Add total rooms * Add more fields, actually send data somewhere * Move stats to the userapi * Move phone home stats to util package * Cleanup * Linter & parts of GH comments * More GH comments changes - Move comments to SQL statements - Shrink interface, add struct for stats - No fatal errors, use defaults * Be more explicit when querying * Fix wrong calculation & wrong query params Add tests * Add Windows stats * ADd build constraint * Use new testing structure Fix issues with getting values when using SQLite Fix wrong AddDate value Export UpdateUserDailyVisits * Fix query params * Fix test * Add comment about countR30UsersSQL and countR30UsersV2SQL; fix test * Update config * Also update example config file * Use OS level proxy, update logging Co-authored-by: kegsay --- cmd/dendrite-polylith-multi/main.go | 2 +- dendrite-config.yaml | 9 + setup/config/config.go | 3 + setup/config/config_global.go | 25 ++ syncapi/sync/requestpool.go | 1 + userapi/api/api.go | 1 + userapi/internal/api.go | 2 +- userapi/storage/interface.go | 8 +- userapi/storage/postgres/devices_table.go | 6 +- userapi/storage/postgres/stats_table.go | 437 ++++++++++++++++++++ userapi/storage/postgres/storage.go | 5 + userapi/storage/shared/storage.go | 13 +- userapi/storage/sqlite3/devices_table.go | 6 +- userapi/storage/sqlite3/stats_table.go | 452 +++++++++++++++++++++ userapi/storage/sqlite3/storage.go | 5 + userapi/storage/storage_test.go | 2 +- userapi/storage/tables/interface.go | 9 +- userapi/storage/tables/stats_table_test.go | 319 +++++++++++++++ userapi/types/statistics.go | 30 ++ userapi/userapi.go | 5 + userapi/util/phonehomestats.go | 160 ++++++++ userapi/util/stats.go | 47 +++ userapi/util/stats_wasm.go | 20 + userapi/util/stats_windows.go | 29 ++ 24 files changed, 1582 insertions(+), 14 deletions(-) create mode 100644 userapi/storage/postgres/stats_table.go create mode 100644 userapi/storage/sqlite3/stats_table.go create mode 100644 userapi/storage/tables/stats_table_test.go create mode 100644 userapi/types/statistics.go create mode 100644 userapi/util/phonehomestats.go create mode 100644 userapi/util/stats.go create mode 100644 userapi/util/stats_wasm.go create mode 100644 userapi/util/stats_windows.go diff --git a/cmd/dendrite-polylith-multi/main.go b/cmd/dendrite-polylith-multi/main.go index 6226cc328..4fccaa922 100644 --- a/cmd/dendrite-polylith-multi/main.go +++ b/cmd/dendrite-polylith-multi/main.go @@ -31,7 +31,7 @@ import ( type entrypoint func(base *base.BaseDendrite, cfg *config.Dendrite) func main() { - cfg := setup.ParseFlags(true) + cfg := setup.ParseFlags(false) component := "" if flag.NFlag() > 0 { diff --git a/dendrite-config.yaml b/dendrite-config.yaml index 1647af15d..7709e0c87 100644 --- a/dendrite-config.yaml +++ b/dendrite-config.yaml @@ -85,6 +85,15 @@ global: # Whether outbound presence events are allowed, e.g. sending presence events to other servers enable_outbound: false + # Configures opt-in anonymous stats reporting. + report_stats: + # Whether this instance sends anonymous usage stats + enabled: false + + # The endpoint to report the anonymized homeserver usage statistics to. + # Defaults to https://matrix.org/report-usage-stats/push + endpoint: https://matrix.org/report-usage-stats/push + # Server notices allows server admins to send messages to all users. server_notices: enabled: false diff --git a/setup/config/config.go b/setup/config/config.go index e03518e24..9b9000a62 100644 --- a/setup/config/config.go +++ b/setup/config/config.go @@ -78,6 +78,8 @@ type Dendrite struct { // Any information derived from the configuration options for later use. Derived Derived `yaml:"-"` + + IsMonolith bool `yaml:"-"` } // TODO: Kill Derived @@ -210,6 +212,7 @@ func loadConfig( ) (*Dendrite, error) { var c Dendrite c.Defaults(false) + c.IsMonolith = monolithic var err error if err = yaml.Unmarshal(configData, &c); err != nil { diff --git a/setup/config/config_global.go b/setup/config/config_global.go index d609e2460..9d4c1485e 100644 --- a/setup/config/config_global.go +++ b/setup/config/config_global.go @@ -70,6 +70,9 @@ type Global struct { // ServerNotices configuration used for sending server notices ServerNotices ServerNotices `yaml:"server_notices"` + + // ReportStats configures opt-in anonymous stats reporting. + ReportStats ReportStats `yaml:"report_stats"` } func (c *Global) Defaults(generate bool) { @@ -86,6 +89,7 @@ func (c *Global) Defaults(generate bool) { c.DNSCache.Defaults() c.Sentry.Defaults() c.ServerNotices.Defaults(generate) + c.ReportStats.Defaults() } func (c *Global) Verify(configErrs *ConfigErrors, isMonolith bool) { @@ -97,6 +101,7 @@ func (c *Global) Verify(configErrs *ConfigErrors, isMonolith bool) { c.Sentry.Verify(configErrs, isMonolith) c.DNSCache.Verify(configErrs, isMonolith) c.ServerNotices.Verify(configErrs, isMonolith) + c.ReportStats.Verify(configErrs, isMonolith) } type OldVerifyKeys struct { @@ -163,6 +168,26 @@ func (c *ServerNotices) Defaults(generate bool) { func (c *ServerNotices) Verify(errors *ConfigErrors, isMonolith bool) {} +// ReportStats configures opt-in anonymous stats reporting. +type ReportStats struct { + // Enabled configures anonymous usage stats of the server + Enabled bool `yaml:"enabled"` + + // Endpoint the endpoint to report stats to + Endpoint string `yaml:"endpoint"` +} + +func (c *ReportStats) Defaults() { + c.Enabled = false + c.Endpoint = "https://matrix.org/report-usage-stats/push" +} + +func (c *ReportStats) Verify(configErrs *ConfigErrors, isMonolith bool) { + if c.Enabled { + checkNotEmpty(configErrs, "global.report_stats.endpoint", c.Endpoint) + } +} + // The configuration to use for Sentry error reporting type Sentry struct { Enabled bool `yaml:"enabled"` diff --git a/syncapi/sync/requestpool.go b/syncapi/sync/requestpool.go index 76d550a65..f8e502d2c 100644 --- a/syncapi/sync/requestpool.go +++ b/syncapi/sync/requestpool.go @@ -182,6 +182,7 @@ func (rp *RequestPool) updateLastSeen(req *http.Request, device *userapi.Device) UserID: device.UserID, DeviceID: device.ID, RemoteAddr: remoteAddr, + UserAgent: req.UserAgent(), } lsres := &userapi.PerformLastSeenUpdateResponse{} go rp.userAPI.PerformLastSeenUpdate(req.Context(), lsreq, lsres) // nolint:errcheck diff --git a/userapi/api/api.go b/userapi/api/api.go index 6aa6a6842..6ab68fa08 100644 --- a/userapi/api/api.go +++ b/userapi/api/api.go @@ -320,6 +320,7 @@ type PerformLastSeenUpdateRequest struct { UserID string DeviceID string RemoteAddr string + UserAgent string } // PerformLastSeenUpdateResponse is the response for PerformLastSeenUpdate. diff --git a/userapi/internal/api.go b/userapi/internal/api.go index be58e2d8d..394bfa224 100644 --- a/userapi/internal/api.go +++ b/userapi/internal/api.go @@ -210,7 +210,7 @@ func (a *UserInternalAPI) PerformLastSeenUpdate( if err != nil { return fmt.Errorf("gomatrixserverlib.SplitID: %w", err) } - if err := a.DB.UpdateDeviceLastSeen(ctx, localpart, req.DeviceID, req.RemoteAddr); err != nil { + if err := a.DB.UpdateDeviceLastSeen(ctx, localpart, req.DeviceID, req.RemoteAddr, req.UserAgent); err != nil { return fmt.Errorf("a.DeviceDB.UpdateDeviceLastSeen: %w", err) } return nil diff --git a/userapi/storage/interface.go b/userapi/storage/interface.go index a4562cf19..f7cd1810a 100644 --- a/userapi/storage/interface.go +++ b/userapi/storage/interface.go @@ -22,6 +22,7 @@ import ( "github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/storage/tables" + "github.com/matrix-org/dendrite/userapi/types" ) type Profile interface { @@ -67,7 +68,7 @@ type Device interface { // Returns the device on success. CreateDevice(ctx context.Context, localpart string, deviceID *string, accessToken string, displayName *string, ipAddr, userAgent string) (dev *api.Device, returnErr error) UpdateDevice(ctx context.Context, localpart, deviceID string, displayName *string) error - UpdateDeviceLastSeen(ctx context.Context, localpart, deviceID, ipAddr string) error + UpdateDeviceLastSeen(ctx context.Context, localpart, deviceID, ipAddr, userAgent string) error RemoveDevices(ctx context.Context, localpart string, devices []string) error // RemoveAllDevices deleted all devices for this user. Returns the devices deleted. RemoveAllDevices(ctx context.Context, localpart, exceptDeviceID string) (devices []api.Device, err error) @@ -135,9 +136,14 @@ type Database interface { OpenID Profile Pusher + Statistics ThreePID } +type Statistics interface { + UserStatistics(ctx context.Context) (*types.UserStatistics, *types.DatabaseEngine, error) +} + // Err3PIDInUse is the error returned when trying to save an association involving // a third-party identifier which is already associated to a local user. var Err3PIDInUse = errors.New("this third-party identifier is already in use") diff --git a/userapi/storage/postgres/devices_table.go b/userapi/storage/postgres/devices_table.go index 6c777982f..ccb776672 100644 --- a/userapi/storage/postgres/devices_table.go +++ b/userapi/storage/postgres/devices_table.go @@ -96,7 +96,7 @@ const selectDevicesByIDSQL = "" + "SELECT device_id, localpart, display_name, last_seen_ts FROM device_devices WHERE device_id = ANY($1) ORDER BY last_seen_ts DESC" const updateDeviceLastSeen = "" + - "UPDATE device_devices SET last_seen_ts = $1, ip = $2 WHERE localpart = $3 AND device_id = $4" + "UPDATE device_devices SET last_seen_ts = $1, ip = $2, user_agent = $3 WHERE localpart = $4 AND device_id = $5" type devicesStatements struct { insertDeviceStmt *sql.Stmt @@ -304,9 +304,9 @@ func (s *devicesStatements) SelectDevicesByLocalpart( return devices, rows.Err() } -func (s *devicesStatements) UpdateDeviceLastSeen(ctx context.Context, txn *sql.Tx, localpart, deviceID, ipAddr string) error { +func (s *devicesStatements) UpdateDeviceLastSeen(ctx context.Context, txn *sql.Tx, localpart, deviceID, ipAddr, userAgent string) error { lastSeenTs := time.Now().UnixNano() / 1000000 stmt := sqlutil.TxStmt(txn, s.updateDeviceLastSeenStmt) - _, err := stmt.ExecContext(ctx, lastSeenTs, ipAddr, localpart, deviceID) + _, err := stmt.ExecContext(ctx, lastSeenTs, ipAddr, userAgent, localpart, deviceID) return err } diff --git a/userapi/storage/postgres/stats_table.go b/userapi/storage/postgres/stats_table.go new file mode 100644 index 000000000..f71900015 --- /dev/null +++ b/userapi/storage/postgres/stats_table.go @@ -0,0 +1,437 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package postgres + +import ( + "context" + "database/sql" + "time" + + "github.com/lib/pq" + "github.com/matrix-org/dendrite/internal" + "github.com/matrix-org/dendrite/internal/sqlutil" + "github.com/matrix-org/dendrite/userapi/api" + "github.com/matrix-org/dendrite/userapi/storage/tables" + "github.com/matrix-org/dendrite/userapi/types" + "github.com/matrix-org/gomatrixserverlib" + "github.com/sirupsen/logrus" +) + +const userDailyVisitsSchema = ` +CREATE TABLE IF NOT EXISTS user_daily_visits ( + localpart TEXT NOT NULL, + device_id TEXT NOT NULL, + timestamp BIGINT NOT NULL, + user_agent TEXT +); + +-- Device IDs and timestamp must be unique for a given user per day +CREATE UNIQUE INDEX IF NOT EXISTS localpart_device_timestamp_idx ON user_daily_visits(localpart, device_id, timestamp); +CREATE INDEX IF NOT EXISTS timestamp_idx ON user_daily_visits(timestamp); +CREATE INDEX IF NOT EXISTS localpart_timestamp_idx ON user_daily_visits(localpart, timestamp); +` + +const countUsersLastSeenAfterSQL = "" + + "SELECT COUNT(*) FROM (" + + " SELECT localpart FROM device_devices WHERE last_seen_ts > $1 " + + " GROUP BY localpart" + + " ) u" + +// Note on the following countR30UsersSQL and countR30UsersV2SQL: The different checks are intentional. +// This is to ensure the values reported by Dendrite are the same as by Synapse. +// Queries are taken from: https://github.com/matrix-org/synapse/blob/9ce51a47f6e37abd0a1275281806399d874eb026/synapse/storage/databases/main/stats.py + +/* +R30Users counts the number of 30 day retained users, defined as: +- Users who have created their accounts more than 30 days ago +- Where last seen at most 30 days ago +- Where account creation and last_seen are > 30 days apart +*/ +const countR30UsersSQL = ` +SELECT platform, COUNT(*) FROM ( + SELECT users.localpart, platform, users.created_ts, MAX(uip.last_seen_ts) + FROM account_accounts users + INNER JOIN + (SELECT + localpart, last_seen_ts, + CASE + WHEN user_agent LIKE '%%Android%%' THEN 'android' + WHEN user_agent LIKE '%%iOS%%' THEN 'ios' + WHEN user_agent LIKE '%%Electron%%' THEN 'electron' + WHEN user_agent LIKE '%%Mozilla%%' THEN 'web' + WHEN user_agent LIKE '%%Gecko%%' THEN 'web' + ELSE 'unknown' + END + AS platform + FROM device_devices + ) uip + ON users.localpart = uip.localpart + AND users.account_type <> 4 + AND users.created_ts < $1 + AND uip.last_seen_ts > $1 + AND (uip.last_seen_ts) - users.created_ts > $2 + GROUP BY users.localpart, platform, users.created_ts + ) u GROUP BY PLATFORM +` + +/* +R30UsersV2 counts the number of 30 day retained users, defined as users that: +- Appear more than once in the past 60 days +- Have more than 30 days between the most and least recent appearances that occurred in the past 60 days. +*/ +const countR30UsersV2SQL = ` +SELECT + client_type, + count(client_type) +FROM + ( + SELECT + localpart, + CASE + WHEN + LOWER(user_agent) LIKE '%%riot%%' OR + LOWER(user_agent) LIKE '%%element%%' + THEN CASE + WHEN LOWER(user_agent) LIKE '%%electron%%' THEN 'electron' + WHEN LOWER(user_agent) LIKE '%%android%%' THEN 'android' + WHEN LOWER(user_agent) LIKE '%%ios%%' THEN 'ios' + ELSE 'unknown' + END + WHEN LOWER(user_agent) LIKE '%%mozilla%%' OR LOWER(user_agent) LIKE '%%gecko%%' THEN 'web' + ELSE 'unknown' + END as client_type + FROM user_daily_visits + WHERE timestamp > $1 AND timestamp < $2 + GROUP BY localpart, client_type + HAVING max(timestamp) - min(timestamp) > $3 + ) AS temp +GROUP BY client_type +` + +const countUserByAccountTypeSQL = ` +SELECT COUNT(*) FROM account_accounts WHERE account_type = ANY($1) +` + +// $1 = All non guest AccountType IDs +// $2 = Guest AccountType +const countRegisteredUserByTypeStmt = ` +SELECT user_type, COUNT(*) AS count FROM ( + SELECT + CASE + WHEN account_type = ANY($1) AND appservice_id IS NULL THEN 'native' + WHEN account_type = $2 AND appservice_id IS NULL THEN 'guest' + WHEN account_type = ANY($1) AND appservice_id IS NOT NULL THEN 'bridged' + END AS user_type + FROM account_accounts + WHERE created_ts > $3 +) AS t GROUP BY user_type +` + +// account_type 1 = users; 3 = admins +const updateUserDailyVisitsSQL = ` +INSERT INTO user_daily_visits(localpart, device_id, timestamp, user_agent) + SELECT u.localpart, u.device_id, $1, MAX(u.user_agent) + FROM device_devices AS u + LEFT JOIN ( + SELECT localpart, device_id, timestamp FROM user_daily_visits + WHERE timestamp = $1 + ) udv + ON u.localpart = udv.localpart AND u.device_id = udv.device_id + INNER JOIN device_devices d ON d.localpart = u.localpart + INNER JOIN account_accounts a ON a.localpart = u.localpart + WHERE $2 <= d.last_seen_ts AND d.last_seen_ts < $3 + AND a.account_type in (1, 3) + GROUP BY u.localpart, u.device_id +ON CONFLICT (localpart, device_id, timestamp) DO NOTHING +; +` + +const queryDBEngineVersion = "SHOW server_version;" + +type statsStatements struct { + serverName gomatrixserverlib.ServerName + lastUpdate time.Time + countUsersLastSeenAfterStmt *sql.Stmt + countR30UsersStmt *sql.Stmt + countR30UsersV2Stmt *sql.Stmt + updateUserDailyVisitsStmt *sql.Stmt + countUserByAccountTypeStmt *sql.Stmt + countRegisteredUserByTypeStmt *sql.Stmt + dbEngineVersionStmt *sql.Stmt +} + +func NewPostgresStatsTable(db *sql.DB, serverName gomatrixserverlib.ServerName) (tables.StatsTable, error) { + s := &statsStatements{ + serverName: serverName, + lastUpdate: time.Now(), + } + + _, err := db.Exec(userDailyVisitsSchema) + if err != nil { + return nil, err + } + go s.startTimers() + return s, sqlutil.StatementList{ + {&s.countUsersLastSeenAfterStmt, countUsersLastSeenAfterSQL}, + {&s.countR30UsersStmt, countR30UsersSQL}, + {&s.countR30UsersV2Stmt, countR30UsersV2SQL}, + {&s.updateUserDailyVisitsStmt, updateUserDailyVisitsSQL}, + {&s.countUserByAccountTypeStmt, countUserByAccountTypeSQL}, + {&s.countRegisteredUserByTypeStmt, countRegisteredUserByTypeStmt}, + {&s.dbEngineVersionStmt, queryDBEngineVersion}, + }.Prepare(db) +} + +func (s *statsStatements) startTimers() { + var updateStatsFunc func() + updateStatsFunc = func() { + logrus.Infof("Executing UpdateUserDailyVisits") + if err := s.UpdateUserDailyVisits(context.Background(), nil, time.Now(), s.lastUpdate); err != nil { + logrus.WithError(err).Error("failed to update daily user visits") + } + time.AfterFunc(time.Hour*3, updateStatsFunc) + } + time.AfterFunc(time.Minute*5, updateStatsFunc) +} + +func (s *statsStatements) allUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { + stmt := sqlutil.TxStmt(txn, s.countUserByAccountTypeStmt) + err = stmt.QueryRowContext(ctx, + pq.Int64Array{ + int64(api.AccountTypeUser), + int64(api.AccountTypeGuest), + int64(api.AccountTypeAdmin), + int64(api.AccountTypeAppService), + }, + ).Scan(&result) + return +} + +func (s *statsStatements) nonBridgedUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { + stmt := sqlutil.TxStmt(txn, s.countUserByAccountTypeStmt) + err = stmt.QueryRowContext(ctx, + pq.Int64Array{ + int64(api.AccountTypeUser), + int64(api.AccountTypeGuest), + int64(api.AccountTypeAdmin), + }, + ).Scan(&result) + return +} + +func (s *statsStatements) registeredUserByType(ctx context.Context, txn *sql.Tx) (map[string]int64, error) { + stmt := sqlutil.TxStmt(txn, s.countRegisteredUserByTypeStmt) + registeredAfter := time.Now().AddDate(0, 0, -30) + + rows, err := stmt.QueryContext(ctx, + pq.Int64Array{ + int64(api.AccountTypeUser), + int64(api.AccountTypeAdmin), + int64(api.AccountTypeAppService), + }, + api.AccountTypeGuest, + gomatrixserverlib.AsTimestamp(registeredAfter), + ) + if err != nil { + return nil, err + } + defer internal.CloseAndLogIfError(ctx, rows, "RegisteredUserByType: failed to close rows") + + var userType string + var count int64 + var result = make(map[string]int64) + for rows.Next() { + if err = rows.Scan(&userType, &count); err != nil { + return nil, err + } + result[userType] = count + } + + return result, rows.Err() +} + +func (s *statsStatements) dailyUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { + stmt := sqlutil.TxStmt(txn, s.countUsersLastSeenAfterStmt) + lastSeenAfter := time.Now().AddDate(0, 0, -1) + err = stmt.QueryRowContext(ctx, + gomatrixserverlib.AsTimestamp(lastSeenAfter), + ).Scan(&result) + return +} + +func (s *statsStatements) monthlyUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { + stmt := sqlutil.TxStmt(txn, s.countUsersLastSeenAfterStmt) + lastSeenAfter := time.Now().AddDate(0, 0, -30) + err = stmt.QueryRowContext(ctx, + gomatrixserverlib.AsTimestamp(lastSeenAfter), + ).Scan(&result) + return +} + +/* +R30Users counts the number of 30 day retained users, defined as: +- Users who have created their accounts more than 30 days ago +- Where last seen at most 30 days ago +- Where account creation and last_seen are > 30 days apart +*/ +func (s *statsStatements) r30Users(ctx context.Context, txn *sql.Tx) (map[string]int64, error) { + stmt := sqlutil.TxStmt(txn, s.countR30UsersStmt) + lastSeenAfter := time.Now().AddDate(0, 0, -30) + diff := time.Hour * 24 * 30 + + rows, err := stmt.QueryContext(ctx, + gomatrixserverlib.AsTimestamp(lastSeenAfter), + diff.Milliseconds(), + ) + if err != nil { + return nil, err + } + defer internal.CloseAndLogIfError(ctx, rows, "R30Users: failed to close rows") + + var platform string + var count int64 + var result = make(map[string]int64) + for rows.Next() { + if err = rows.Scan(&platform, &count); err != nil { + return nil, err + } + if platform == "unknown" { + continue + } + result["all"] += count + result[platform] = count + } + + return result, rows.Err() +} + +/* +R30UsersV2 counts the number of 30 day retained users, defined as users that: +- Appear more than once in the past 60 days +- Have more than 30 days between the most and least recent appearances that occurred in the past 60 days. +*/ +func (s *statsStatements) r30UsersV2(ctx context.Context, txn *sql.Tx) (map[string]int64, error) { + stmt := sqlutil.TxStmt(txn, s.countR30UsersV2Stmt) + sixtyDaysAgo := time.Now().AddDate(0, 0, -60) + diff := time.Hour * 24 * 30 + tomorrow := time.Now().Add(time.Hour * 24) + + rows, err := stmt.QueryContext(ctx, + gomatrixserverlib.AsTimestamp(sixtyDaysAgo), + gomatrixserverlib.AsTimestamp(tomorrow), + diff.Milliseconds(), + ) + if err != nil { + return nil, err + } + defer internal.CloseAndLogIfError(ctx, rows, "R30UsersV2: failed to close rows") + + var platform string + var count int64 + var result = map[string]int64{ + "ios": 0, + "android": 0, + "web": 0, + "electron": 0, + "all": 0, + } + for rows.Next() { + if err = rows.Scan(&platform, &count); err != nil { + return nil, err + } + if _, ok := result[platform]; !ok { + continue + } + result["all"] += count + result[platform] = count + } + + return result, rows.Err() +} + +// UserStatistics collects some information about users on this instance. +// Returns the stats itself as well as the database engine version and type. +// On error, returns the stats collected up to the error. +func (s *statsStatements) UserStatistics(ctx context.Context, txn *sql.Tx) (*types.UserStatistics, *types.DatabaseEngine, error) { + var ( + stats = &types.UserStatistics{ + R30UsersV2: map[string]int64{ + "ios": 0, + "android": 0, + "web": 0, + "electron": 0, + "all": 0, + }, + R30Users: map[string]int64{}, + RegisteredUsersByType: map[string]int64{}, + } + dbEngine = &types.DatabaseEngine{Engine: "Postgres", Version: "unknown"} + err error + ) + stats.AllUsers, err = s.allUsers(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.DailyUsers, err = s.dailyUsers(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.MonthlyUsers, err = s.monthlyUsers(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.R30Users, err = s.r30Users(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.R30UsersV2, err = s.r30UsersV2(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.NonBridgedUsers, err = s.nonBridgedUsers(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.RegisteredUsersByType, err = s.registeredUserByType(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + + stmt := sqlutil.TxStmt(txn, s.dbEngineVersionStmt) + err = stmt.QueryRowContext(ctx).Scan(&dbEngine.Version) + return stats, dbEngine, err +} + +func (s *statsStatements) UpdateUserDailyVisits( + ctx context.Context, txn *sql.Tx, + startTime, lastUpdate time.Time, +) error { + stmt := sqlutil.TxStmt(txn, s.updateUserDailyVisitsStmt) + startTime = startTime.Truncate(time.Hour * 24) + + // edge case + if startTime.After(s.lastUpdate) { + startTime = startTime.AddDate(0, 0, -1) + } + _, err := stmt.ExecContext(ctx, + gomatrixserverlib.AsTimestamp(startTime), + gomatrixserverlib.AsTimestamp(lastUpdate), + gomatrixserverlib.AsTimestamp(time.Now()), + ) + if err == nil { + s.lastUpdate = time.Now() + } + return err +} diff --git a/userapi/storage/postgres/storage.go b/userapi/storage/postgres/storage.go index 74100a728..b9afb5a56 100644 --- a/userapi/storage/postgres/storage.go +++ b/userapi/storage/postgres/storage.go @@ -94,6 +94,10 @@ func NewDatabase(base *base.BaseDendrite, dbProperties *config.DatabaseOptions, if err != nil { return nil, fmt.Errorf("NewPostgresNotificationTable: %w", err) } + statsTable, err := NewPostgresStatsTable(db, serverName) + if err != nil { + return nil, fmt.Errorf("NewPostgresStatsTable: %w", err) + } return &shared.Database{ AccountDatas: accountDataTable, Accounts: accountsTable, @@ -106,6 +110,7 @@ func NewDatabase(base *base.BaseDendrite, dbProperties *config.DatabaseOptions, ThreePIDs: threePIDTable, Pushers: pusherTable, Notifications: notificationsTable, + Stats: statsTable, ServerName: serverName, DB: db, Writer: writer, diff --git a/userapi/storage/shared/storage.go b/userapi/storage/shared/storage.go index f7212e030..0cf713dac 100644 --- a/userapi/storage/shared/storage.go +++ b/userapi/storage/shared/storage.go @@ -26,6 +26,7 @@ import ( "strings" "time" + "github.com/matrix-org/dendrite/userapi/types" "github.com/matrix-org/gomatrixserverlib" "golang.org/x/crypto/bcrypt" @@ -51,6 +52,7 @@ type Database struct { LoginTokens tables.LoginTokenTable Notifications tables.NotificationTable Pushers tables.PusherTable + Stats tables.StatsTable LoginTokenLifetime time.Duration ServerName gomatrixserverlib.ServerName BcryptCost int @@ -611,10 +613,10 @@ func (d *Database) RemoveAllDevices( return } -// UpdateDeviceLastSeen updates a the last seen timestamp and the ip address -func (d *Database) UpdateDeviceLastSeen(ctx context.Context, localpart, deviceID, ipAddr string) error { +// UpdateDeviceLastSeen updates a last seen timestamp and the ip address. +func (d *Database) UpdateDeviceLastSeen(ctx context.Context, localpart, deviceID, ipAddr, userAgent string) error { return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error { - return d.Devices.UpdateDeviceLastSeen(ctx, txn, localpart, deviceID, ipAddr) + return d.Devices.UpdateDeviceLastSeen(ctx, txn, localpart, deviceID, ipAddr, userAgent) }) } @@ -756,3 +758,8 @@ func (d *Database) RemovePushers( return d.Pushers.DeletePushers(ctx, txn, appid, pushkey) }) } + +// UserStatistics populates types.UserStatistics, used in reports. +func (d *Database) UserStatistics(ctx context.Context) (*types.UserStatistics, *types.DatabaseEngine, error) { + return d.Stats.UserStatistics(ctx, nil) +} diff --git a/userapi/storage/sqlite3/devices_table.go b/userapi/storage/sqlite3/devices_table.go index b86ed1cc2..93291e6ad 100644 --- a/userapi/storage/sqlite3/devices_table.go +++ b/userapi/storage/sqlite3/devices_table.go @@ -81,7 +81,7 @@ const selectDevicesByIDSQL = "" + "SELECT device_id, localpart, display_name, last_seen_ts FROM device_devices WHERE device_id IN ($1) ORDER BY last_seen_ts DESC" const updateDeviceLastSeen = "" + - "UPDATE device_devices SET last_seen_ts = $1, ip = $2 WHERE localpart = $3 AND device_id = $4" + "UPDATE device_devices SET last_seen_ts = $1, ip = $2, user_agent = $3 WHERE localpart = $4 AND device_id = $5" type devicesStatements struct { db *sql.DB @@ -306,9 +306,9 @@ func (s *devicesStatements) SelectDevicesByID(ctx context.Context, deviceIDs []s return devices, rows.Err() } -func (s *devicesStatements) UpdateDeviceLastSeen(ctx context.Context, txn *sql.Tx, localpart, deviceID, ipAddr string) error { +func (s *devicesStatements) UpdateDeviceLastSeen(ctx context.Context, txn *sql.Tx, localpart, deviceID, ipAddr, userAgent string) error { lastSeenTs := time.Now().UnixNano() / 1000000 stmt := sqlutil.TxStmt(txn, s.updateDeviceLastSeenStmt) - _, err := stmt.ExecContext(ctx, lastSeenTs, ipAddr, localpart, deviceID) + _, err := stmt.ExecContext(ctx, lastSeenTs, ipAddr, userAgent, localpart, deviceID) return err } diff --git a/userapi/storage/sqlite3/stats_table.go b/userapi/storage/sqlite3/stats_table.go new file mode 100644 index 000000000..af4c7ff98 --- /dev/null +++ b/userapi/storage/sqlite3/stats_table.go @@ -0,0 +1,452 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sqlite3 + +import ( + "context" + "database/sql" + "strings" + "time" + + "github.com/matrix-org/dendrite/internal" + "github.com/matrix-org/dendrite/internal/sqlutil" + "github.com/matrix-org/dendrite/userapi/api" + "github.com/matrix-org/dendrite/userapi/storage/tables" + "github.com/matrix-org/dendrite/userapi/types" + "github.com/matrix-org/gomatrixserverlib" + "github.com/sirupsen/logrus" +) + +const userDailyVisitsSchema = ` +CREATE TABLE IF NOT EXISTS user_daily_visits ( + localpart TEXT NOT NULL, + device_id TEXT NOT NULL, + timestamp BIGINT NOT NULL, + user_agent TEXT +); + +-- Device IDs and timestamp must be unique for a given user per day +CREATE UNIQUE INDEX IF NOT EXISTS localpart_device_timestamp_idx ON user_daily_visits(localpart, device_id, timestamp); +CREATE INDEX IF NOT EXISTS timestamp_idx ON user_daily_visits(timestamp); +CREATE INDEX IF NOT EXISTS localpart_timestamp_idx ON user_daily_visits(localpart, timestamp); +` + +const countUsersLastSeenAfterSQL = "" + + "SELECT COUNT(*) FROM (" + + " SELECT localpart FROM device_devices WHERE last_seen_ts > $1 " + + " GROUP BY localpart" + + " ) u" + +// Note on the following countR30UsersSQL and countR30UsersV2SQL: The different checks are intentional. +// This is to ensure the values reported by Dendrite are the same as by Synapse. +// Queries are taken from: https://github.com/matrix-org/synapse/blob/9ce51a47f6e37abd0a1275281806399d874eb026/synapse/storage/databases/main/stats.py + +/* +R30Users counts the number of 30 day retained users, defined as: +- Users who have created their accounts more than 30 days ago +- Where last seen at most 30 days ago +- Where account creation and last_seen are > 30 days apart +*/ +const countR30UsersSQL = ` +SELECT platform, COUNT(*) FROM ( + SELECT users.localpart, platform, users.created_ts, MAX(uip.last_seen_ts) + FROM account_accounts users + INNER JOIN + (SELECT + localpart, last_seen_ts, + CASE + WHEN user_agent LIKE '%%Android%%' THEN 'android' + WHEN user_agent LIKE '%%iOS%%' THEN 'ios' + WHEN user_agent LIKE '%%Electron%%' THEN 'electron' + WHEN user_agent LIKE '%%Mozilla%%' THEN 'web' + WHEN user_agent LIKE '%%Gecko%%' THEN 'web' + ELSE 'unknown' + END + AS platform + FROM device_devices + ) uip + ON users.localpart = uip.localpart + AND users.account_type <> 4 + AND users.created_ts < $1 + AND uip.last_seen_ts > $2 + AND (uip.last_seen_ts) - users.created_ts > $3 + GROUP BY users.localpart, platform, users.created_ts + ) u GROUP BY PLATFORM +` + +// Note on the following countR30UsersSQL and countR30UsersV2SQL: The different checks are intentional. +// This is to ensure the values reported are the same as Synapse reports. +// Queries are taken from: https://github.com/matrix-org/synapse/blob/9ce51a47f6e37abd0a1275281806399d874eb026/synapse/storage/databases/main/stats.py + +/* +R30UsersV2 counts the number of 30 day retained users, defined as users that: +- Appear more than once in the past 60 days +- Have more than 30 days between the most and least recent appearances that occurred in the past 60 days. +*/ +const countR30UsersV2SQL = ` +SELECT + client_type, + count(client_type) +FROM + ( + SELECT + localpart, + CASE + WHEN + LOWER(user_agent) LIKE '%%riot%%' OR + LOWER(user_agent) LIKE '%%element%%' + THEN CASE + WHEN LOWER(user_agent) LIKE '%%electron%%' THEN 'electron' + WHEN LOWER(user_agent) LIKE '%%android%%' THEN 'android' + WHEN LOWER(user_agent) LIKE '%%ios%%' THEN 'ios' + ELSE 'unknown' + END + WHEN LOWER(user_agent) LIKE '%%mozilla%%' OR LOWER(user_agent) LIKE '%%gecko%%' THEN 'web' + ELSE 'unknown' + END as client_type + FROM user_daily_visits + WHERE timestamp > $1 AND timestamp < $2 + GROUP BY localpart, client_type + HAVING max(timestamp) - min(timestamp) > $3 + ) AS temp +GROUP BY client_type +` + +const countUserByAccountTypeSQL = ` +SELECT COUNT(*) FROM account_accounts WHERE account_type IN ($1) +` + +// $1 = Guest AccountType +// $3 & $4 = All non guest AccountType IDs +const countRegisteredUserByTypeSQL = ` +SELECT user_type, COUNT(*) AS count FROM ( + SELECT + CASE + WHEN account_type IN ($1) AND appservice_id IS NULL THEN 'native' + WHEN account_type = $4 AND appservice_id IS NULL THEN 'guest' + WHEN account_type IN ($5) AND appservice_id IS NOT NULL THEN 'bridged' + END AS user_type + FROM account_accounts + WHERE created_ts > $8 +) AS t GROUP BY user_type +` + +// account_type 1 = users; 3 = admins +const updateUserDailyVisitsSQL = ` +INSERT INTO user_daily_visits(localpart, device_id, timestamp, user_agent) + SELECT u.localpart, u.device_id, $1, MAX(u.user_agent) + FROM device_devices AS u + LEFT JOIN ( + SELECT localpart, device_id, timestamp FROM user_daily_visits + WHERE timestamp = $1 + ) udv + ON u.localpart = udv.localpart AND u.device_id = udv.device_id + INNER JOIN device_devices d ON d.localpart = u.localpart + INNER JOIN account_accounts a ON a.localpart = u.localpart + WHERE $2 <= d.last_seen_ts AND d.last_seen_ts < $3 + AND a.account_type in (1, 3) + GROUP BY u.localpart, u.device_id +ON CONFLICT (localpart, device_id, timestamp) DO NOTHING +; +` + +const queryDBEngineVersion = "select sqlite_version();" + +type statsStatements struct { + serverName gomatrixserverlib.ServerName + db *sql.DB + lastUpdate time.Time + countUsersLastSeenAfterStmt *sql.Stmt + countR30UsersStmt *sql.Stmt + countR30UsersV2Stmt *sql.Stmt + updateUserDailyVisitsStmt *sql.Stmt + countUserByAccountTypeStmt *sql.Stmt + countRegisteredUserByTypeStmt *sql.Stmt + dbEngineVersionStmt *sql.Stmt +} + +func NewSQLiteStatsTable(db *sql.DB, serverName gomatrixserverlib.ServerName) (tables.StatsTable, error) { + s := &statsStatements{ + serverName: serverName, + lastUpdate: time.Now(), + db: db, + } + + _, err := db.Exec(userDailyVisitsSchema) + if err != nil { + return nil, err + } + go s.startTimers() + return s, sqlutil.StatementList{ + {&s.countUsersLastSeenAfterStmt, countUsersLastSeenAfterSQL}, + {&s.countR30UsersStmt, countR30UsersSQL}, + {&s.countR30UsersV2Stmt, countR30UsersV2SQL}, + {&s.updateUserDailyVisitsStmt, updateUserDailyVisitsSQL}, + {&s.countUserByAccountTypeStmt, countUserByAccountTypeSQL}, + {&s.countRegisteredUserByTypeStmt, countRegisteredUserByTypeSQL}, + {&s.dbEngineVersionStmt, queryDBEngineVersion}, + }.Prepare(db) +} + +func (s *statsStatements) startTimers() { + var updateStatsFunc func() + updateStatsFunc = func() { + logrus.Infof("Executing UpdateUserDailyVisits") + if err := s.UpdateUserDailyVisits(context.Background(), nil, time.Now(), s.lastUpdate); err != nil { + logrus.WithError(err).Error("failed to update daily user visits") + } + time.AfterFunc(time.Hour*3, updateStatsFunc) + } + time.AfterFunc(time.Minute*5, updateStatsFunc) +} + +func (s *statsStatements) allUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { + query := strings.Replace(countUserByAccountTypeSQL, "($1)", sqlutil.QueryVariadic(4), 1) + queryStmt, err := s.db.Prepare(query) + if err != nil { + return 0, err + } + stmt := sqlutil.TxStmt(txn, queryStmt) + err = stmt.QueryRowContext(ctx, + 1, 2, 3, 4, + ).Scan(&result) + return +} + +func (s *statsStatements) nonBridgedUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { + query := strings.Replace(countUserByAccountTypeSQL, "($1)", sqlutil.QueryVariadic(3), 1) + queryStmt, err := s.db.Prepare(query) + if err != nil { + return 0, err + } + stmt := sqlutil.TxStmt(txn, queryStmt) + err = stmt.QueryRowContext(ctx, + 1, 2, 3, + ).Scan(&result) + return +} + +func (s *statsStatements) registeredUserByType(ctx context.Context, txn *sql.Tx) (map[string]int64, error) { + // $1 = Guest AccountType; $2 = timestamp + // $3 & $4 = All non guest AccountType IDs + nonGuests := []api.AccountType{api.AccountTypeUser, api.AccountTypeAdmin, api.AccountTypeAppService} + countSQL := strings.Replace(countRegisteredUserByTypeSQL, "($1)", sqlutil.QueryVariadicOffset(len(nonGuests), 0), 1) + countSQL = strings.Replace(countSQL, "($5)", sqlutil.QueryVariadicOffset(len(nonGuests), 1+len(nonGuests)), 1) + queryStmt, err := s.db.Prepare(countSQL) + if err != nil { + return nil, err + } + stmt := sqlutil.TxStmt(txn, queryStmt) + registeredAfter := time.Now().AddDate(0, 0, -30) + + params := make([]interface{}, len(nonGuests)*2+2) + // nonGuests is used twice + for i, v := range nonGuests { + params[i] = v // i: 0 1 2 => ($1, $2, $3) + params[i+1+len(nonGuests)] = v // i: 4 5 6 => ($5, $6, $7) + } + params[3] = api.AccountTypeGuest // $4 + params[7] = gomatrixserverlib.AsTimestamp(registeredAfter) // $8 + + rows, err := stmt.QueryContext(ctx, params...) + if err != nil { + return nil, err + } + defer internal.CloseAndLogIfError(ctx, rows, "RegisteredUserByType: failed to close rows") + + var userType string + var count int64 + var result = make(map[string]int64) + for rows.Next() { + if err = rows.Scan(&userType, &count); err != nil { + return nil, err + } + result[userType] = count + } + + return result, rows.Err() +} + +func (s *statsStatements) dailyUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { + stmt := sqlutil.TxStmt(txn, s.countUsersLastSeenAfterStmt) + lastSeenAfter := time.Now().AddDate(0, 0, -1) + err = stmt.QueryRowContext(ctx, + gomatrixserverlib.AsTimestamp(lastSeenAfter), + ).Scan(&result) + return +} + +func (s *statsStatements) monthlyUsers(ctx context.Context, txn *sql.Tx) (result int64, err error) { + stmt := sqlutil.TxStmt(txn, s.countUsersLastSeenAfterStmt) + lastSeenAfter := time.Now().AddDate(0, 0, -30) + err = stmt.QueryRowContext(ctx, + gomatrixserverlib.AsTimestamp(lastSeenAfter), + ).Scan(&result) + return +} + +/* R30Users counts the number of 30 day retained users, defined as: +- Users who have created their accounts more than 30 days ago +- Where last seen at most 30 days ago +- Where account creation and last_seen are > 30 days apart +*/ +func (s *statsStatements) r30Users(ctx context.Context, txn *sql.Tx) (map[string]int64, error) { + stmt := sqlutil.TxStmt(txn, s.countR30UsersStmt) + lastSeenAfter := time.Now().AddDate(0, 0, -30) + diff := time.Hour * 24 * 30 + + rows, err := stmt.QueryContext(ctx, + gomatrixserverlib.AsTimestamp(lastSeenAfter), + gomatrixserverlib.AsTimestamp(lastSeenAfter), + diff.Milliseconds(), + ) + if err != nil { + return nil, err + } + defer internal.CloseAndLogIfError(ctx, rows, "R30Users: failed to close rows") + + var platform string + var count int64 + var result = make(map[string]int64) + for rows.Next() { + if err = rows.Scan(&platform, &count); err != nil { + return nil, err + } + if platform == "unknown" { + continue + } + result["all"] += count + result[platform] = count + } + + return result, rows.Err() +} + +/* R30UsersV2 counts the number of 30 day retained users, defined as users that: +- Appear more than once in the past 60 days +- Have more than 30 days between the most and least recent appearances that occurred in the past 60 days. +*/ +func (s *statsStatements) r30UsersV2(ctx context.Context, txn *sql.Tx) (map[string]int64, error) { + stmt := sqlutil.TxStmt(txn, s.countR30UsersV2Stmt) + sixtyDaysAgo := time.Now().AddDate(0, 0, -60) + diff := time.Hour * 24 * 30 + tomorrow := time.Now().Add(time.Hour * 24) + + rows, err := stmt.QueryContext(ctx, + gomatrixserverlib.AsTimestamp(sixtyDaysAgo), + gomatrixserverlib.AsTimestamp(tomorrow), + diff.Milliseconds(), + ) + if err != nil { + return nil, err + } + defer internal.CloseAndLogIfError(ctx, rows, "R30UsersV2: failed to close rows") + + var platform string + var count int64 + var result = map[string]int64{ + "ios": 0, + "android": 0, + "web": 0, + "electron": 0, + "all": 0, + } + for rows.Next() { + if err = rows.Scan(&platform, &count); err != nil { + return nil, err + } + if _, ok := result[platform]; !ok { + continue + } + result["all"] += count + result[platform] = count + } + return result, rows.Err() +} + +// UserStatistics collects some information about users on this instance. +// Returns the stats itself as well as the database engine version and type. +// On error, returns the stats collected up to the error. +func (s *statsStatements) UserStatistics(ctx context.Context, txn *sql.Tx) (*types.UserStatistics, *types.DatabaseEngine, error) { + var ( + stats = &types.UserStatistics{ + R30UsersV2: map[string]int64{ + "ios": 0, + "android": 0, + "web": 0, + "electron": 0, + "all": 0, + }, + R30Users: map[string]int64{}, + RegisteredUsersByType: map[string]int64{}, + } + dbEngine = &types.DatabaseEngine{Engine: "SQLite", Version: "unknown"} + err error + ) + stats.AllUsers, err = s.allUsers(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.DailyUsers, err = s.dailyUsers(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.MonthlyUsers, err = s.monthlyUsers(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.R30Users, err = s.r30Users(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.R30UsersV2, err = s.r30UsersV2(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.NonBridgedUsers, err = s.nonBridgedUsers(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + stats.RegisteredUsersByType, err = s.registeredUserByType(ctx, txn) + if err != nil { + return stats, dbEngine, err + } + + stmt := sqlutil.TxStmt(txn, s.dbEngineVersionStmt) + err = stmt.QueryRowContext(ctx).Scan(&dbEngine.Version) + return stats, dbEngine, err +} + +func (s *statsStatements) UpdateUserDailyVisits( + ctx context.Context, txn *sql.Tx, + startTime, lastUpdate time.Time, +) error { + stmt := sqlutil.TxStmt(txn, s.updateUserDailyVisitsStmt) + startTime = startTime.Truncate(time.Hour * 24) + + // edge case + if startTime.After(s.lastUpdate) { + startTime = startTime.AddDate(0, 0, -1) + } + _, err := stmt.ExecContext(ctx, + gomatrixserverlib.AsTimestamp(startTime), + gomatrixserverlib.AsTimestamp(lastUpdate), + gomatrixserverlib.AsTimestamp(time.Now()), + ) + if err == nil { + s.lastUpdate = time.Now() + } + return err +} diff --git a/userapi/storage/sqlite3/storage.go b/userapi/storage/sqlite3/storage.go index 6858d3d15..a822f687d 100644 --- a/userapi/storage/sqlite3/storage.go +++ b/userapi/storage/sqlite3/storage.go @@ -95,6 +95,10 @@ func NewDatabase(base *base.BaseDendrite, dbProperties *config.DatabaseOptions, if err != nil { return nil, fmt.Errorf("NewPostgresNotificationTable: %w", err) } + statsTable, err := NewSQLiteStatsTable(db, serverName) + if err != nil { + return nil, fmt.Errorf("NewSQLiteStatsTable: %w", err) + } return &shared.Database{ AccountDatas: accountDataTable, Accounts: accountsTable, @@ -107,6 +111,7 @@ func NewDatabase(base *base.BaseDendrite, dbProperties *config.DatabaseOptions, ThreePIDs: threePIDTable, Pushers: pusherTable, Notifications: notificationsTable, + Stats: statsTable, ServerName: serverName, DB: db, Writer: writer, diff --git a/userapi/storage/storage_test.go b/userapi/storage/storage_test.go index 79d5a8dae..5683fe067 100644 --- a/userapi/storage/storage_test.go +++ b/userapi/storage/storage_test.go @@ -174,7 +174,7 @@ func Test_Devices(t *testing.T) { newName := "new display name" err = db.UpdateDevice(ctx, localpart, deviceWithID.ID, &newName) assert.NoError(t, err, "unable to update device displayname") - err = db.UpdateDeviceLastSeen(ctx, localpart, deviceWithID.ID, "127.0.0.1") + err = db.UpdateDeviceLastSeen(ctx, localpart, deviceWithID.ID, "127.0.0.1", "Element Web") assert.NoError(t, err, "unable to update device last seen") deviceWithID.DisplayName = newName diff --git a/userapi/storage/tables/interface.go b/userapi/storage/tables/interface.go index eb0cae314..2fe955670 100644 --- a/userapi/storage/tables/interface.go +++ b/userapi/storage/tables/interface.go @@ -18,9 +18,11 @@ import ( "context" "database/sql" "encoding/json" + "time" "github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/userapi/api" + "github.com/matrix-org/dendrite/userapi/types" ) type AccountDataTable interface { @@ -48,7 +50,7 @@ type DevicesTable interface { SelectDeviceByID(ctx context.Context, localpart, deviceID string) (*api.Device, error) SelectDevicesByLocalpart(ctx context.Context, txn *sql.Tx, localpart, exceptDeviceID string) ([]api.Device, error) SelectDevicesByID(ctx context.Context, deviceIDs []string) ([]api.Device, error) - UpdateDeviceLastSeen(ctx context.Context, txn *sql.Tx, localpart, deviceID, ipAddr string) error + UpdateDeviceLastSeen(ctx context.Context, txn *sql.Tx, localpart, deviceID, ipAddr, userAgent string) error } type KeyBackupTable interface { @@ -111,6 +113,11 @@ type NotificationTable interface { SelectRoomCounts(ctx context.Context, txn *sql.Tx, localpart, roomID string) (total int64, highlight int64, _ error) } +type StatsTable interface { + UserStatistics(ctx context.Context, txn *sql.Tx) (*types.UserStatistics, *types.DatabaseEngine, error) + UpdateUserDailyVisits(ctx context.Context, txn *sql.Tx, startTime, lastUpdate time.Time) error +} + type NotificationFilter uint32 const ( diff --git a/userapi/storage/tables/stats_table_test.go b/userapi/storage/tables/stats_table_test.go new file mode 100644 index 000000000..11521c8b0 --- /dev/null +++ b/userapi/storage/tables/stats_table_test.go @@ -0,0 +1,319 @@ +package tables_test + +import ( + "context" + "database/sql" + "fmt" + "reflect" + "testing" + "time" + + "github.com/matrix-org/dendrite/internal/sqlutil" + "github.com/matrix-org/dendrite/setup/config" + "github.com/matrix-org/dendrite/test" + "github.com/matrix-org/dendrite/userapi/api" + "github.com/matrix-org/dendrite/userapi/storage/postgres" + "github.com/matrix-org/dendrite/userapi/storage/sqlite3" + "github.com/matrix-org/dendrite/userapi/storage/tables" + "github.com/matrix-org/dendrite/userapi/types" + "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/util" +) + +func mustMakeDBs(t *testing.T, dbType test.DBType) ( + *sql.DB, tables.AccountsTable, tables.DevicesTable, tables.StatsTable, func(), +) { + t.Helper() + + var ( + accTable tables.AccountsTable + devTable tables.DevicesTable + statsTable tables.StatsTable + err error + ) + + connStr, close := test.PrepareDBConnectionString(t, dbType) + db, err := sqlutil.Open(&config.DatabaseOptions{ + ConnectionString: config.DataSource(connStr), + }, nil) + if err != nil { + t.Fatalf("failed to open db: %s", err) + } + + switch dbType { + case test.DBTypeSQLite: + accTable, err = sqlite3.NewSQLiteAccountsTable(db, "localhost") + if err != nil { + t.Fatalf("unable to create acc db: %v", err) + } + devTable, err = sqlite3.NewSQLiteDevicesTable(db, "localhost") + if err != nil { + t.Fatalf("unable to open device db: %v", err) + } + statsTable, err = sqlite3.NewSQLiteStatsTable(db, "localhost") + if err != nil { + t.Fatalf("unable to open stats db: %v", err) + } + case test.DBTypePostgres: + accTable, err = postgres.NewPostgresAccountsTable(db, "localhost") + if err != nil { + t.Fatalf("unable to create acc db: %v", err) + } + devTable, err = postgres.NewPostgresDevicesTable(db, "localhost") + if err != nil { + t.Fatalf("unable to open device db: %v", err) + } + statsTable, err = postgres.NewPostgresStatsTable(db, "localhost") + if err != nil { + t.Fatalf("unable to open stats db: %v", err) + } + } + + return db, accTable, devTable, statsTable, close +} + +func mustMakeAccountAndDevice( + t *testing.T, + ctx context.Context, + accDB tables.AccountsTable, + devDB tables.DevicesTable, + localpart string, + accType api.AccountType, + userAgent string, +) { + t.Helper() + + appServiceID := "" + if accType == api.AccountTypeAppService { + appServiceID = util.RandomString(16) + } + + _, err := accDB.InsertAccount(ctx, nil, localpart, "", appServiceID, accType) + if err != nil { + t.Fatalf("unable to create account: %v", err) + } + _, err = devDB.InsertDevice(ctx, nil, "deviceID", localpart, util.RandomString(16), nil, "", userAgent) + if err != nil { + t.Fatalf("unable to create device: %v", err) + } +} + +func mustUpdateDeviceLastSeen( + t *testing.T, + ctx context.Context, + db *sql.DB, + localpart string, + timestamp time.Time, +) { + t.Helper() + _, err := db.ExecContext(ctx, "UPDATE device_devices SET last_seen_ts = $1 WHERE localpart = $2", gomatrixserverlib.AsTimestamp(timestamp), localpart) + if err != nil { + t.Fatalf("unable to update device last seen") + } +} + +func mustUserUpdateRegistered( + t *testing.T, + ctx context.Context, + db *sql.DB, + localpart string, + timestamp time.Time, +) { + _, err := db.ExecContext(ctx, "UPDATE account_accounts SET created_ts = $1 WHERE localpart = $2", gomatrixserverlib.AsTimestamp(timestamp), localpart) + if err != nil { + t.Fatalf("unable to update device last seen") + } +} + +// These tests must run sequentially, as they build up on each other +func Test_UserStatistics(t *testing.T) { + + ctx := context.Background() + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + db, accDB, devDB, statsDB, close := mustMakeDBs(t, dbType) + defer close() + wantType := "SQLite" + if dbType == test.DBTypePostgres { + wantType = "Postgres" + } + + t.Run(fmt.Sprintf("want %s database engine", wantType), func(t *testing.T) { + _, gotDB, err := statsDB.UserStatistics(ctx, nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if wantType != gotDB.Engine { // can't use DeepEqual, as the Version might differ + t.Errorf("UserStatistics() got DB engine = %+v, want %s", gotDB.Engine, wantType) + } + }) + + t.Run("Want Users", func(t *testing.T) { + mustMakeAccountAndDevice(t, ctx, accDB, devDB, "user1", api.AccountTypeUser, "Element Android") + mustMakeAccountAndDevice(t, ctx, accDB, devDB, "user2", api.AccountTypeUser, "Element iOS") + mustMakeAccountAndDevice(t, ctx, accDB, devDB, "user3", api.AccountTypeUser, "Element web") + mustMakeAccountAndDevice(t, ctx, accDB, devDB, "user4", api.AccountTypeGuest, "Element Electron") + mustMakeAccountAndDevice(t, ctx, accDB, devDB, "user5", api.AccountTypeAdmin, "gecko") + mustMakeAccountAndDevice(t, ctx, accDB, devDB, "user6", api.AccountTypeAppService, "gecko") + gotStats, _, err := statsDB.UserStatistics(ctx, nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + wantStats := &types.UserStatistics{ + RegisteredUsersByType: map[string]int64{ + "native": 4, + "guest": 1, + "bridged": 1, + }, + R30Users: map[string]int64{}, + R30UsersV2: map[string]int64{ + "ios": 0, + "android": 0, + "web": 0, + "electron": 0, + "all": 0, + }, + AllUsers: 6, + NonBridgedUsers: 5, + DailyUsers: 6, + MonthlyUsers: 6, + } + if !reflect.DeepEqual(gotStats, wantStats) { + t.Errorf("UserStatistics() gotStats = \n%+v\nwant\n%+v", gotStats, wantStats) + } + }) + + t.Run("Users not active for one/two month", func(t *testing.T) { + mustUpdateDeviceLastSeen(t, ctx, db, "user1", time.Now().AddDate(0, -2, 0)) + mustUpdateDeviceLastSeen(t, ctx, db, "user2", time.Now().AddDate(0, -1, 0)) + gotStats, _, err := statsDB.UserStatistics(ctx, nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + wantStats := &types.UserStatistics{ + RegisteredUsersByType: map[string]int64{ + "native": 4, + "guest": 1, + "bridged": 1, + }, + R30Users: map[string]int64{}, + R30UsersV2: map[string]int64{ + "ios": 0, + "android": 0, + "web": 0, + "electron": 0, + "all": 0, + }, + AllUsers: 6, + NonBridgedUsers: 5, + DailyUsers: 4, + MonthlyUsers: 4, + } + if !reflect.DeepEqual(gotStats, wantStats) { + t.Errorf("UserStatistics() gotStats = \n%+v\nwant\n%+v", gotStats, wantStats) + } + }) + + /* R30Users counts the number of 30 day retained users, defined as: + - Users who have created their accounts more than 30 days ago + - Where last seen at most 30 days ago + - Where account creation and last_seen are > 30 days apart + */ + t.Run("R30Users tests", func(t *testing.T) { + mustUserUpdateRegistered(t, ctx, db, "user1", time.Now().AddDate(0, -2, 0)) + mustUpdateDeviceLastSeen(t, ctx, db, "user1", time.Now()) + mustUserUpdateRegistered(t, ctx, db, "user4", time.Now().AddDate(0, -2, 0)) + mustUpdateDeviceLastSeen(t, ctx, db, "user4", time.Now()) + startTime := time.Now().AddDate(0, 0, -2) + err := statsDB.UpdateUserDailyVisits(ctx, nil, startTime, startTime.Truncate(time.Hour*24).Add(time.Hour)) + if err != nil { + t.Fatalf("unable to update daily visits stats: %v", err) + } + + gotStats, _, err := statsDB.UserStatistics(ctx, nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + wantStats := &types.UserStatistics{ + RegisteredUsersByType: map[string]int64{ + "native": 3, + "bridged": 1, + }, + R30Users: map[string]int64{ + "all": 2, + "android": 1, + "electron": 1, + }, + R30UsersV2: map[string]int64{ + "ios": 0, + "android": 0, + "web": 0, + "electron": 0, + "all": 0, + }, + AllUsers: 6, + NonBridgedUsers: 5, + DailyUsers: 5, + MonthlyUsers: 5, + } + if !reflect.DeepEqual(gotStats, wantStats) { + t.Errorf("UserStatistics() gotStats = \n%+v\nwant\n%+v", gotStats, wantStats) + } + }) + + /* + R30UsersV2 counts the number of 30 day retained users, defined as users that: + - Appear more than once in the past 60 days + - Have more than 30 days between the most and least recent appearances that occurred in the past 60 days. + most recent -> neueste + least recent -> älteste + + */ + t.Run("R30UsersV2 tests", func(t *testing.T) { + // generate some data + for i := 100; i > 0; i-- { + mustUpdateDeviceLastSeen(t, ctx, db, "user1", time.Now().AddDate(0, 0, -i)) + mustUpdateDeviceLastSeen(t, ctx, db, "user5", time.Now().AddDate(0, 0, -i)) + startTime := time.Now().AddDate(0, 0, -i) + err := statsDB.UpdateUserDailyVisits(ctx, nil, startTime, startTime.Truncate(time.Hour*24).Add(time.Hour)) + if err != nil { + t.Fatalf("unable to update daily visits stats: %v", err) + } + } + gotStats, _, err := statsDB.UserStatistics(ctx, nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + wantStats := &types.UserStatistics{ + RegisteredUsersByType: map[string]int64{ + "native": 3, + "bridged": 1, + }, + R30Users: map[string]int64{ + "all": 2, + "android": 1, + "electron": 1, + }, + R30UsersV2: map[string]int64{ + "ios": 0, + "android": 1, + "web": 1, + "electron": 0, + "all": 2, + }, + AllUsers: 6, + NonBridgedUsers: 5, + DailyUsers: 3, + MonthlyUsers: 5, + } + if !reflect.DeepEqual(gotStats, wantStats) { + t.Errorf("UserStatistics() gotStats = \n%+v\nwant\n%+v", gotStats, wantStats) + } + }) + }) + +} diff --git a/userapi/types/statistics.go b/userapi/types/statistics.go new file mode 100644 index 000000000..09564f78f --- /dev/null +++ b/userapi/types/statistics.go @@ -0,0 +1,30 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +type UserStatistics struct { + RegisteredUsersByType map[string]int64 + R30Users map[string]int64 + R30UsersV2 map[string]int64 + AllUsers int64 + NonBridgedUsers int64 + DailyUsers int64 + MonthlyUsers int64 +} + +type DatabaseEngine struct { + Engine string + Version string +} diff --git a/userapi/userapi.go b/userapi/userapi.go index 9174119e1..5b11665db 100644 --- a/userapi/userapi.go +++ b/userapi/userapi.go @@ -30,6 +30,7 @@ import ( "github.com/matrix-org/dendrite/userapi/inthttp" "github.com/matrix-org/dendrite/userapi/producers" "github.com/matrix-org/dendrite/userapi/storage" + "github.com/matrix-org/dendrite/userapi/util" "github.com/sirupsen/logrus" ) @@ -104,5 +105,9 @@ func NewInternalAPI( } time.AfterFunc(time.Minute, cleanOldNotifs) + if base.Cfg.Global.ReportStats.Enabled { + go util.StartPhoneHomeCollector(time.Now(), base.Cfg, db) + } + return userAPI } diff --git a/userapi/util/phonehomestats.go b/userapi/util/phonehomestats.go new file mode 100644 index 000000000..ad93a50e3 --- /dev/null +++ b/userapi/util/phonehomestats.go @@ -0,0 +1,160 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package util + +import ( + "bytes" + "context" + "encoding/json" + "math" + "net/http" + "runtime" + "syscall" + "time" + + "github.com/matrix-org/dendrite/internal" + "github.com/matrix-org/dendrite/setup/config" + "github.com/matrix-org/dendrite/userapi/storage" + "github.com/matrix-org/gomatrixserverlib" + "github.com/sirupsen/logrus" +) + +type phoneHomeStats struct { + prevData timestampToRUUsage + stats map[string]interface{} + serverName gomatrixserverlib.ServerName + startTime time.Time + cfg *config.Dendrite + db storage.Statistics + isMonolith bool + client *http.Client +} + +type timestampToRUUsage struct { + timestamp int64 + usage syscall.Rusage +} + +func StartPhoneHomeCollector(startTime time.Time, cfg *config.Dendrite, statsDB storage.Statistics) { + + p := phoneHomeStats{ + startTime: startTime, + serverName: cfg.Global.ServerName, + cfg: cfg, + db: statsDB, + isMonolith: cfg.IsMonolith, + client: &http.Client{ + Timeout: time.Second * 30, + Transport: http.DefaultTransport, + }, + } + + // start initial run after 5min + time.AfterFunc(time.Minute*5, p.collect) + + // run every 3 hours + ticker := time.NewTicker(time.Hour * 3) + for range ticker.C { + p.collect() + } +} + +func (p *phoneHomeStats) collect() { + p.stats = make(map[string]interface{}) + // general information + p.stats["homeserver"] = p.serverName + p.stats["monolith"] = p.isMonolith + p.stats["version"] = internal.VersionString() + p.stats["timestamp"] = time.Now().Unix() + p.stats["go_version"] = runtime.Version() + p.stats["go_arch"] = runtime.GOARCH + p.stats["go_os"] = runtime.GOOS + p.stats["num_cpu"] = runtime.NumCPU() + p.stats["num_go_routine"] = runtime.NumGoroutine() + p.stats["uptime_seconds"] = math.Floor(time.Since(p.startTime).Seconds()) + + ctx, cancel := context.WithTimeout(context.TODO(), time.Minute*1) + defer cancel() + + // cpu and memory usage information + err := getMemoryStats(p) + if err != nil { + logrus.WithError(err).Warn("unable to get memory/cpu stats, using defaults") + } + + // configuration information + p.stats["federation_disabled"] = p.cfg.Global.DisableFederation + p.stats["nats_embedded"] = true + p.stats["nats_in_memory"] = p.cfg.Global.JetStream.InMemory + if len(p.cfg.Global.JetStream.Addresses) > 0 { + p.stats["nats_embedded"] = false + p.stats["nats_in_memory"] = false // probably + } + if len(p.cfg.Logging) > 0 { + p.stats["log_level"] = p.cfg.Logging[0].Level + } else { + p.stats["log_level"] = "info" + } + + // message and room stats + // TODO: Find a solution to actually set these values + p.stats["total_room_count"] = 0 + p.stats["daily_messages"] = 0 + p.stats["daily_sent_messages"] = 0 + p.stats["daily_e2ee_messages"] = 0 + p.stats["daily_sent_e2ee_messages"] = 0 + + // user stats and DB engine + userStats, db, err := p.db.UserStatistics(ctx) + if err != nil { + logrus.WithError(err).Warn("unable to query userstats, using default values") + } + p.stats["database_engine"] = db.Engine + p.stats["database_server_version"] = db.Version + p.stats["total_users"] = userStats.AllUsers + p.stats["total_nonbridged_users"] = userStats.NonBridgedUsers + p.stats["daily_active_users"] = userStats.DailyUsers + p.stats["monthly_active_users"] = userStats.MonthlyUsers + for t, c := range userStats.RegisteredUsersByType { + p.stats["daily_user_type_"+t] = c + } + for t, c := range userStats.R30Users { + p.stats["r30_users_"+t] = c + } + for t, c := range userStats.R30UsersV2 { + p.stats["r30v2_users_"+t] = c + } + + output := bytes.Buffer{} + if err = json.NewEncoder(&output).Encode(p.stats); err != nil { + logrus.WithError(err).Error("unable to encode anonymous stats") + return + } + + logrus.Infof("Reporting stats to %s: %s", p.cfg.Global.ReportStats.Endpoint, output.String()) + + request, err := http.NewRequestWithContext(ctx, http.MethodPost, p.cfg.Global.ReportStats.Endpoint, &output) + if err != nil { + logrus.WithError(err).Error("unable to create anonymous stats request") + return + } + request.Header.Set("User-Agent", "Dendrite/"+internal.VersionString()) + + _, err = p.client.Do(request) + if err != nil { + logrus.WithError(err).Error("unable to send anonymous stats") + return + } +} diff --git a/userapi/util/stats.go b/userapi/util/stats.go new file mode 100644 index 000000000..22ef12aad --- /dev/null +++ b/userapi/util/stats.go @@ -0,0 +1,47 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !wasm && !windows +// +build !wasm,!windows + +package util + +import ( + "syscall" + "time" + + "github.com/sirupsen/logrus" +) + +func getMemoryStats(p *phoneHomeStats) error { + oldUsage := p.prevData + newUsage := syscall.Rusage{} + if err := syscall.Getrusage(syscall.RUSAGE_SELF, &newUsage); err != nil { + logrus.WithError(err).Error("unable to get usage") + return err + } + newData := timestampToRUUsage{timestamp: time.Now().Unix(), usage: newUsage} + p.prevData = newData + + usedCPUTime := (newUsage.Utime.Sec + newUsage.Stime.Sec) - (oldUsage.usage.Utime.Sec + oldUsage.usage.Stime.Sec) + + if usedCPUTime == 0 || newData.timestamp == oldUsage.timestamp { + p.stats["cpu_average"] = 0 + } else { + // conversion to int64 required for GOARCH=386 + p.stats["cpu_average"] = int64(usedCPUTime) / (newData.timestamp - oldUsage.timestamp) * 100 + } + p.stats["memory_rss"] = newUsage.Maxrss + return nil +} diff --git a/userapi/util/stats_wasm.go b/userapi/util/stats_wasm.go new file mode 100644 index 000000000..a182e4e6e --- /dev/null +++ b/userapi/util/stats_wasm.go @@ -0,0 +1,20 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package util + +// stub, since WASM doesn't support syscall.Getrusage +func getMemoryStats(p *phoneHomeStats) error { + return nil +} diff --git a/userapi/util/stats_windows.go b/userapi/util/stats_windows.go new file mode 100644 index 000000000..0b3f8d013 --- /dev/null +++ b/userapi/util/stats_windows.go @@ -0,0 +1,29 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !wasm +// +build !wasm + +package util + +import ( + "runtime" +) + +func getMemoryStats(p *phoneHomeStats) error { + var memStats runtime.MemStats + runtime.ReadMemStats(&memStats) + p.stats["memory_rss"] = memStats.Alloc + return nil +} From d86dcbef66dad344bc38c58762a9634ff126d5c7 Mon Sep 17 00:00:00 2001 From: kegsay Date: Thu, 5 May 2022 09:56:03 +0100 Subject: [PATCH 02/12] syncapi: define specific interfaces for internal HTTP communications (#2416) * syncapi: use finer-grained interfaces when making the syncapi * Use specific interfaces for syncapi-roomserver interactions * Define query access token api for shared http auth code --- clientapi/auth/auth.go | 2 +- .../personalities/syncapi.go | 2 - internal/httputil/httpapi.go | 2 +- keyserver/api/api.go | 9 ++- roomserver/api/api.go | 81 ++++++++++--------- setup/monolith.go | 2 +- syncapi/consumers/keychange.go | 7 +- syncapi/consumers/presence.go | 4 +- syncapi/consumers/roomserver.go | 4 +- syncapi/internal/keychange.go | 8 +- syncapi/routing/context.go | 2 +- syncapi/routing/messages.go | 9 +-- syncapi/routing/routing.go | 6 +- syncapi/streams/stream_accountdata.go | 2 +- syncapi/streams/stream_devicelist.go | 4 +- syncapi/streams/stream_pdu.go | 2 +- syncapi/streams/streams.go | 4 +- syncapi/sync/requestpool.go | 10 +-- syncapi/syncapi.go | 12 ++- userapi/api/api.go | 14 +++- 20 files changed, 95 insertions(+), 91 deletions(-) diff --git a/clientapi/auth/auth.go b/clientapi/auth/auth.go index 575c5377f..93345f4b9 100644 --- a/clientapi/auth/auth.go +++ b/clientapi/auth/auth.go @@ -51,7 +51,7 @@ type AccountDatabase interface { // Note: For an AS user, AS dummy device is returned. // On failure returns an JSON error response which can be sent to the client. func VerifyUserFromRequest( - req *http.Request, userAPI api.UserInternalAPI, + req *http.Request, userAPI api.QueryAcccessTokenAPI, ) (*api.Device, *util.JSONResponse) { // Try to find the Application Service user token, err := ExtractAccessToken(req) diff --git a/cmd/dendrite-polylith-multi/personalities/syncapi.go b/cmd/dendrite-polylith-multi/personalities/syncapi.go index 2245b9b54..41637fe1d 100644 --- a/cmd/dendrite-polylith-multi/personalities/syncapi.go +++ b/cmd/dendrite-polylith-multi/personalities/syncapi.go @@ -22,7 +22,6 @@ import ( func SyncAPI(base *basepkg.BaseDendrite, cfg *config.Dendrite) { userAPI := base.UserAPIClient() - federation := base.CreateFederationClient() rsAPI := base.RoomserverHTTPClient() @@ -30,7 +29,6 @@ func SyncAPI(base *basepkg.BaseDendrite, cfg *config.Dendrite) { base, userAPI, rsAPI, base.KeyServerHTTPClient(), - federation, ) base.SetupAndServeHTTP( diff --git a/internal/httputil/httpapi.go b/internal/httputil/httpapi.go index 5fcacd2ad..3a818cc5e 100644 --- a/internal/httputil/httpapi.go +++ b/internal/httputil/httpapi.go @@ -49,7 +49,7 @@ type BasicAuth struct { // MakeAuthAPI turns a util.JSONRequestHandler function into an http.Handler which authenticates the request. func MakeAuthAPI( - metricsName string, userAPI userapi.UserInternalAPI, + metricsName string, userAPI userapi.QueryAcccessTokenAPI, f func(*http.Request, *userapi.Device) util.JSONResponse, ) http.Handler { h := func(req *http.Request) util.JSONResponse { diff --git a/keyserver/api/api.go b/keyserver/api/api.go index 429617b10..ce651ba4e 100644 --- a/keyserver/api/api.go +++ b/keyserver/api/api.go @@ -27,6 +27,7 @@ import ( ) type KeyInternalAPI interface { + SyncKeyAPI // SetUserAPI assigns a user API to query when extracting device names. SetUserAPI(i userapi.UserInternalAPI) // InputDeviceListUpdate from a federated server EDU @@ -38,12 +39,16 @@ type KeyInternalAPI interface { PerformUploadDeviceKeys(ctx context.Context, req *PerformUploadDeviceKeysRequest, res *PerformUploadDeviceKeysResponse) PerformUploadDeviceSignatures(ctx context.Context, req *PerformUploadDeviceSignaturesRequest, res *PerformUploadDeviceSignaturesResponse) QueryKeys(ctx context.Context, req *QueryKeysRequest, res *QueryKeysResponse) - QueryKeyChanges(ctx context.Context, req *QueryKeyChangesRequest, res *QueryKeyChangesResponse) - QueryOneTimeKeys(ctx context.Context, req *QueryOneTimeKeysRequest, res *QueryOneTimeKeysResponse) QueryDeviceMessages(ctx context.Context, req *QueryDeviceMessagesRequest, res *QueryDeviceMessagesResponse) QuerySignatures(ctx context.Context, req *QuerySignaturesRequest, res *QuerySignaturesResponse) } +// API functions required by the syncapi +type SyncKeyAPI interface { + QueryKeyChanges(ctx context.Context, req *QueryKeyChangesRequest, res *QueryKeyChangesResponse) + QueryOneTimeKeys(ctx context.Context, req *QueryOneTimeKeysRequest, res *QueryOneTimeKeysResponse) +} + // KeyError is returned if there was a problem performing/querying the server type KeyError struct { Err string `json:"error"` diff --git a/roomserver/api/api.go b/roomserver/api/api.go index f0ca8a615..2e4ec3ffd 100644 --- a/roomserver/api/api.go +++ b/roomserver/api/api.go @@ -12,6 +12,8 @@ import ( // RoomserverInputAPI is used to write events to the room server. type RoomserverInternalAPI interface { + SyncRoomserverAPI + // needed to avoid chicken and egg scenario when setting up the // interdependencies between the roomserver and other input APIs SetFederationAPI(fsAPI fsAPI.FederationInternalAPI, keyRing *gomatrixserverlib.KeyRing) @@ -78,34 +80,6 @@ type RoomserverInternalAPI interface { res *QueryPublishedRoomsResponse, ) error - // Query the latest events and state for a room from the room server. - QueryLatestEventsAndState( - ctx context.Context, - request *QueryLatestEventsAndStateRequest, - response *QueryLatestEventsAndStateResponse, - ) error - - // Query the state after a list of events in a room from the room server. - QueryStateAfterEvents( - ctx context.Context, - request *QueryStateAfterEventsRequest, - response *QueryStateAfterEventsResponse, - ) error - - // Query a list of events by event ID. - QueryEventsByID( - ctx context.Context, - request *QueryEventsByIDRequest, - response *QueryEventsByIDResponse, - ) error - - // Query the membership event for an user for a room. - QueryMembershipForUser( - ctx context.Context, - request *QueryMembershipForUserRequest, - response *QueryMembershipForUserResponse, - ) error - // Query a list of membership events for a room QueryMembershipsForRoom( ctx context.Context, @@ -157,22 +131,11 @@ type RoomserverInternalAPI interface { QueryCurrentState(ctx context.Context, req *QueryCurrentStateRequest, res *QueryCurrentStateResponse) error // QueryRoomsForUser retrieves a list of room IDs matching the given query. QueryRoomsForUser(ctx context.Context, req *QueryRoomsForUserRequest, res *QueryRoomsForUserResponse) error - // QueryBulkStateContent does a bulk query for state event content in the given rooms. - QueryBulkStateContent(ctx context.Context, req *QueryBulkStateContentRequest, res *QueryBulkStateContentResponse) error - // QuerySharedUsers returns a list of users who share at least 1 room in common with the given user. - QuerySharedUsers(ctx context.Context, req *QuerySharedUsersRequest, res *QuerySharedUsersResponse) error // QueryKnownUsers returns a list of users that we know about from our joined rooms. QueryKnownUsers(ctx context.Context, req *QueryKnownUsersRequest, res *QueryKnownUsersResponse) error // QueryServerBannedFromRoom returns whether a server is banned from a room by server ACLs. QueryServerBannedFromRoom(ctx context.Context, req *QueryServerBannedFromRoomRequest, res *QueryServerBannedFromRoomResponse) error - // Query a given amount (or less) of events prior to a given set of events. - PerformBackfill( - ctx context.Context, - request *PerformBackfillRequest, - response *PerformBackfillResponse, - ) error - // PerformForget forgets a rooms history for a specific user PerformForget(ctx context.Context, req *PerformForgetRequest, resp *PerformForgetResponse) error @@ -228,3 +191,43 @@ type RoomserverInternalAPI interface { response *RemoveRoomAliasResponse, ) error } + +// API functions required by the syncapi +type SyncRoomserverAPI interface { + // Query the latest events and state for a room from the room server. + QueryLatestEventsAndState( + ctx context.Context, + request *QueryLatestEventsAndStateRequest, + response *QueryLatestEventsAndStateResponse, + ) error + // QueryBulkStateContent does a bulk query for state event content in the given rooms. + QueryBulkStateContent(ctx context.Context, req *QueryBulkStateContentRequest, res *QueryBulkStateContentResponse) error + // QuerySharedUsers returns a list of users who share at least 1 room in common with the given user. + QuerySharedUsers(ctx context.Context, req *QuerySharedUsersRequest, res *QuerySharedUsersResponse) error + // Query a list of events by event ID. + QueryEventsByID( + ctx context.Context, + request *QueryEventsByIDRequest, + response *QueryEventsByIDResponse, + ) error + // Query the membership event for an user for a room. + QueryMembershipForUser( + ctx context.Context, + request *QueryMembershipForUserRequest, + response *QueryMembershipForUserResponse, + ) error + + // Query the state after a list of events in a room from the room server. + QueryStateAfterEvents( + ctx context.Context, + request *QueryStateAfterEventsRequest, + response *QueryStateAfterEventsResponse, + ) error + + // Query a given amount (or less) of events prior to a given set of events. + PerformBackfill( + ctx context.Context, + request *PerformBackfillRequest, + response *PerformBackfillResponse, + ) error +} diff --git a/setup/monolith.go b/setup/monolith.go index 23bd2fb52..e033c14d7 100644 --- a/setup/monolith.go +++ b/setup/monolith.go @@ -69,6 +69,6 @@ func (m *Monolith) AddAllPublicRoutes(base *base.BaseDendrite) { base, m.UserAPI, m.Client, ) syncapi.AddPublicRoutes( - base, m.UserAPI, m.RoomserverAPI, m.KeyAPI, m.FedClient, + base, m.UserAPI, m.RoomserverAPI, m.KeyAPI, ) } diff --git a/syncapi/consumers/keychange.go b/syncapi/consumers/keychange.go index e806f76e6..c8d88ddac 100644 --- a/syncapi/consumers/keychange.go +++ b/syncapi/consumers/keychange.go @@ -42,8 +42,7 @@ type OutputKeyChangeEventConsumer struct { notifier *notifier.Notifier stream types.StreamProvider serverName gomatrixserverlib.ServerName // our server name - rsAPI roomserverAPI.RoomserverInternalAPI - keyAPI api.KeyInternalAPI + rsAPI roomserverAPI.SyncRoomserverAPI } // NewOutputKeyChangeEventConsumer creates a new OutputKeyChangeEventConsumer. @@ -53,8 +52,7 @@ func NewOutputKeyChangeEventConsumer( cfg *config.SyncAPI, topic string, js nats.JetStreamContext, - keyAPI api.KeyInternalAPI, - rsAPI roomserverAPI.RoomserverInternalAPI, + rsAPI roomserverAPI.SyncRoomserverAPI, store storage.Database, notifier *notifier.Notifier, stream types.StreamProvider, @@ -66,7 +64,6 @@ func NewOutputKeyChangeEventConsumer( topic: topic, db: store, serverName: cfg.Matrix.ServerName, - keyAPI: keyAPI, rsAPI: rsAPI, notifier: notifier, stream: stream, diff --git a/syncapi/consumers/presence.go b/syncapi/consumers/presence.go index 6bcca48f4..388c08ff4 100644 --- a/syncapi/consumers/presence.go +++ b/syncapi/consumers/presence.go @@ -41,7 +41,7 @@ type PresenceConsumer struct { db storage.Database stream types.StreamProvider notifier *notifier.Notifier - deviceAPI api.UserDeviceAPI + deviceAPI api.SyncUserAPI cfg *config.SyncAPI } @@ -55,7 +55,7 @@ func NewPresenceConsumer( db storage.Database, notifier *notifier.Notifier, stream types.StreamProvider, - deviceAPI api.UserDeviceAPI, + deviceAPI api.SyncUserAPI, ) *PresenceConsumer { return &PresenceConsumer{ ctx: process.Context(), diff --git a/syncapi/consumers/roomserver.go b/syncapi/consumers/roomserver.go index 5bdc0fad7..7712c8403 100644 --- a/syncapi/consumers/roomserver.go +++ b/syncapi/consumers/roomserver.go @@ -38,7 +38,7 @@ import ( type OutputRoomEventConsumer struct { ctx context.Context cfg *config.SyncAPI - rsAPI api.RoomserverInternalAPI + rsAPI api.SyncRoomserverAPI jetstream nats.JetStreamContext durable string topic string @@ -58,7 +58,7 @@ func NewOutputRoomEventConsumer( notifier *notifier.Notifier, pduStream types.StreamProvider, inviteStream types.StreamProvider, - rsAPI api.RoomserverInternalAPI, + rsAPI api.SyncRoomserverAPI, producer *producers.UserAPIStreamEventProducer, ) *OutputRoomEventConsumer { return &OutputRoomEventConsumer{ diff --git a/syncapi/internal/keychange.go b/syncapi/internal/keychange.go index dc4acd8da..d96718d20 100644 --- a/syncapi/internal/keychange.go +++ b/syncapi/internal/keychange.go @@ -29,7 +29,7 @@ import ( const DeviceListLogName = "dl" // DeviceOTKCounts adds one-time key counts to the /sync response -func DeviceOTKCounts(ctx context.Context, keyAPI keyapi.KeyInternalAPI, userID, deviceID string, res *types.Response) error { +func DeviceOTKCounts(ctx context.Context, keyAPI keyapi.SyncKeyAPI, userID, deviceID string, res *types.Response) error { var queryRes keyapi.QueryOneTimeKeysResponse keyAPI.QueryOneTimeKeys(ctx, &keyapi.QueryOneTimeKeysRequest{ UserID: userID, @@ -46,7 +46,7 @@ func DeviceOTKCounts(ctx context.Context, keyAPI keyapi.KeyInternalAPI, userID, // was filled in, else false if there are no new device list changes because there is nothing to catch up on. The response MUST // be already filled in with join/leave information. func DeviceListCatchup( - ctx context.Context, keyAPI keyapi.KeyInternalAPI, rsAPI roomserverAPI.RoomserverInternalAPI, + ctx context.Context, keyAPI keyapi.SyncKeyAPI, rsAPI roomserverAPI.SyncRoomserverAPI, userID string, res *types.Response, from, to types.StreamPosition, ) (newPos types.StreamPosition, hasNew bool, err error) { @@ -130,7 +130,7 @@ func DeviceListCatchup( // TrackChangedUsers calculates the values of device_lists.changed|left in the /sync response. func TrackChangedUsers( - ctx context.Context, rsAPI roomserverAPI.RoomserverInternalAPI, userID string, newlyJoinedRooms, newlyLeftRooms []string, + ctx context.Context, rsAPI roomserverAPI.SyncRoomserverAPI, userID string, newlyJoinedRooms, newlyLeftRooms []string, ) (changed, left []string, err error) { // process leaves first, then joins afterwards so if we join/leave/join/leave we err on the side of including users. @@ -216,7 +216,7 @@ func TrackChangedUsers( } func filterSharedUsers( - ctx context.Context, rsAPI roomserverAPI.RoomserverInternalAPI, userID string, usersWithChangedKeys []string, + ctx context.Context, rsAPI roomserverAPI.SyncRoomserverAPI, userID string, usersWithChangedKeys []string, ) (map[string]int, []string) { var result []string var sharedUsersRes roomserverAPI.QuerySharedUsersResponse diff --git a/syncapi/routing/context.go b/syncapi/routing/context.go index 17215b669..f5f4b2dd0 100644 --- a/syncapi/routing/context.go +++ b/syncapi/routing/context.go @@ -42,7 +42,7 @@ type ContextRespsonse struct { func Context( req *http.Request, device *userapi.Device, - rsAPI roomserver.RoomserverInternalAPI, + rsAPI roomserver.SyncRoomserverAPI, syncDB storage.Database, roomID, eventID string, lazyLoadCache *caching.LazyLoadCache, diff --git a/syncapi/routing/messages.go b/syncapi/routing/messages.go index f34901bf2..f19dfaed3 100644 --- a/syncapi/routing/messages.go +++ b/syncapi/routing/messages.go @@ -36,8 +36,7 @@ import ( type messagesReq struct { ctx context.Context db storage.Database - rsAPI api.RoomserverInternalAPI - federation *gomatrixserverlib.FederationClient + rsAPI api.SyncRoomserverAPI cfg *config.SyncAPI roomID string from *types.TopologyToken @@ -61,8 +60,7 @@ type messagesResp struct { // See: https://matrix.org/docs/spec/client_server/latest.html#get-matrix-client-r0-rooms-roomid-messages func OnIncomingMessagesRequest( req *http.Request, db storage.Database, roomID string, device *userapi.Device, - federation *gomatrixserverlib.FederationClient, - rsAPI api.RoomserverInternalAPI, + rsAPI api.SyncRoomserverAPI, cfg *config.SyncAPI, srp *sync.RequestPool, lazyLoadCache *caching.LazyLoadCache, @@ -180,7 +178,6 @@ func OnIncomingMessagesRequest( ctx: req.Context(), db: db, rsAPI: rsAPI, - federation: federation, cfg: cfg, roomID: roomID, from: &from, @@ -247,7 +244,7 @@ func OnIncomingMessagesRequest( } } -func checkIsRoomForgotten(ctx context.Context, roomID, userID string, rsAPI api.RoomserverInternalAPI) (bool, error) { +func checkIsRoomForgotten(ctx context.Context, roomID, userID string, rsAPI api.SyncRoomserverAPI) (bool, error) { req := api.QueryMembershipForUserRequest{ RoomID: roomID, UserID: userID, diff --git a/syncapi/routing/routing.go b/syncapi/routing/routing.go index 4102cf073..245ee5b66 100644 --- a/syncapi/routing/routing.go +++ b/syncapi/routing/routing.go @@ -36,8 +36,8 @@ import ( // nolint: gocyclo func Setup( csMux *mux.Router, srp *sync.RequestPool, syncDB storage.Database, - userAPI userapi.UserInternalAPI, federation *gomatrixserverlib.FederationClient, - rsAPI api.RoomserverInternalAPI, + userAPI userapi.SyncUserAPI, + rsAPI api.SyncRoomserverAPI, cfg *config.SyncAPI, lazyLoadCache *caching.LazyLoadCache, ) { @@ -53,7 +53,7 @@ func Setup( if err != nil { return util.ErrorResponse(err) } - return OnIncomingMessagesRequest(req, syncDB, vars["roomID"], device, federation, rsAPI, cfg, srp, lazyLoadCache) + return OnIncomingMessagesRequest(req, syncDB, vars["roomID"], device, rsAPI, cfg, srp, lazyLoadCache) })).Methods(http.MethodGet, http.MethodOptions) v3mux.Handle("/user/{userId}/filter", diff --git a/syncapi/streams/stream_accountdata.go b/syncapi/streams/stream_accountdata.go index 2cddbcf04..9c19b846b 100644 --- a/syncapi/streams/stream_accountdata.go +++ b/syncapi/streams/stream_accountdata.go @@ -10,7 +10,7 @@ import ( type AccountDataStreamProvider struct { StreamProvider - userAPI userapi.UserInternalAPI + userAPI userapi.SyncUserAPI } func (p *AccountDataStreamProvider) Setup() { diff --git a/syncapi/streams/stream_devicelist.go b/syncapi/streams/stream_devicelist.go index 6ff8a7fd5..f42099510 100644 --- a/syncapi/streams/stream_devicelist.go +++ b/syncapi/streams/stream_devicelist.go @@ -11,8 +11,8 @@ import ( type DeviceListStreamProvider struct { StreamProvider - rsAPI api.RoomserverInternalAPI - keyAPI keyapi.KeyInternalAPI + rsAPI api.SyncRoomserverAPI + keyAPI keyapi.SyncKeyAPI } func (p *DeviceListStreamProvider) CompleteSync( diff --git a/syncapi/streams/stream_pdu.go b/syncapi/streams/stream_pdu.go index 0d033095d..f774a1af8 100644 --- a/syncapi/streams/stream_pdu.go +++ b/syncapi/streams/stream_pdu.go @@ -33,7 +33,7 @@ type PDUStreamProvider struct { workers atomic.Int32 // userID+deviceID -> lazy loading cache lazyLoadCache *caching.LazyLoadCache - rsAPI roomserverAPI.RoomserverInternalAPI + rsAPI roomserverAPI.SyncRoomserverAPI } func (p *PDUStreamProvider) worker() { diff --git a/syncapi/streams/streams.go b/syncapi/streams/streams.go index a18a0cc41..af2a0387e 100644 --- a/syncapi/streams/streams.go +++ b/syncapi/streams/streams.go @@ -25,8 +25,8 @@ type Streams struct { } func NewSyncStreamProviders( - d storage.Database, userAPI userapi.UserInternalAPI, - rsAPI rsapi.RoomserverInternalAPI, keyAPI keyapi.KeyInternalAPI, + d storage.Database, userAPI userapi.SyncUserAPI, + rsAPI rsapi.SyncRoomserverAPI, keyAPI keyapi.SyncKeyAPI, eduCache *caching.EDUCache, lazyLoadCache *caching.LazyLoadCache, notifier *notifier.Notifier, ) *Streams { streams := &Streams{ diff --git a/syncapi/sync/requestpool.go b/syncapi/sync/requestpool.go index f8e502d2c..99d1e40c3 100644 --- a/syncapi/sync/requestpool.go +++ b/syncapi/sync/requestpool.go @@ -45,9 +45,9 @@ import ( type RequestPool struct { db storage.Database cfg *config.SyncAPI - userAPI userapi.UserInternalAPI - keyAPI keyapi.KeyInternalAPI - rsAPI roomserverAPI.RoomserverInternalAPI + userAPI userapi.SyncUserAPI + keyAPI keyapi.SyncKeyAPI + rsAPI roomserverAPI.SyncRoomserverAPI lastseen *sync.Map presence *sync.Map streams *streams.Streams @@ -62,8 +62,8 @@ type PresencePublisher interface { // NewRequestPool makes a new RequestPool func NewRequestPool( db storage.Database, cfg *config.SyncAPI, - userAPI userapi.UserInternalAPI, keyAPI keyapi.KeyInternalAPI, - rsAPI roomserverAPI.RoomserverInternalAPI, + userAPI userapi.SyncUserAPI, keyAPI keyapi.SyncKeyAPI, + rsAPI roomserverAPI.SyncRoomserverAPI, streams *streams.Streams, notifier *notifier.Notifier, producer PresencePublisher, ) *RequestPool { diff --git a/syncapi/syncapi.go b/syncapi/syncapi.go index d8becb6ed..686e2044f 100644 --- a/syncapi/syncapi.go +++ b/syncapi/syncapi.go @@ -25,7 +25,6 @@ import ( "github.com/matrix-org/dendrite/setup/base" "github.com/matrix-org/dendrite/setup/jetstream" userapi "github.com/matrix-org/dendrite/userapi/api" - "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/dendrite/syncapi/consumers" "github.com/matrix-org/dendrite/syncapi/notifier" @@ -40,10 +39,9 @@ import ( // component. func AddPublicRoutes( base *base.BaseDendrite, - userAPI userapi.UserInternalAPI, - rsAPI api.RoomserverInternalAPI, - keyAPI keyapi.KeyInternalAPI, - federation *gomatrixserverlib.FederationClient, + userAPI userapi.SyncUserAPI, + rsAPI api.SyncRoomserverAPI, + keyAPI keyapi.SyncKeyAPI, ) { cfg := &base.Cfg.SyncAPI @@ -85,7 +83,7 @@ func AddPublicRoutes( keyChangeConsumer := consumers.NewOutputKeyChangeEventConsumer( base.ProcessContext, cfg, cfg.Matrix.JetStream.Prefixed(jetstream.OutputKeyChangeEvent), - js, keyAPI, rsAPI, syncDB, notifier, + js, rsAPI, syncDB, notifier, streams.DeviceListStreamProvider, ) if err = keyChangeConsumer.Start(); err != nil { @@ -148,6 +146,6 @@ func AddPublicRoutes( routing.Setup( base.PublicClientAPIMux, requestPool, syncDB, userAPI, - federation, rsAPI, cfg, lazyLoadCache, + rsAPI, cfg, lazyLoadCache, ) } diff --git a/userapi/api/api.go b/userapi/api/api.go index 6ab68fa08..6f00fe44f 100644 --- a/userapi/api/api.go +++ b/userapi/api/api.go @@ -31,7 +31,8 @@ type UserInternalAPI interface { UserRegisterAPI UserAccountAPI UserThreePIDAPI - UserDeviceAPI + QueryAcccessTokenAPI + SyncUserAPI InputAccountData(ctx context.Context, req *InputAccountDataRequest, res *InputAccountDataResponse) error @@ -42,15 +43,20 @@ type UserInternalAPI interface { PerformPushRulesPut(ctx context.Context, req *PerformPushRulesPutRequest, res *struct{}) error QueryKeyBackup(ctx context.Context, req *QueryKeyBackupRequest, res *QueryKeyBackupResponse) - QueryAccessToken(ctx context.Context, req *QueryAccessTokenRequest, res *QueryAccessTokenResponse) error - QueryAccountData(ctx context.Context, req *QueryAccountDataRequest, res *QueryAccountDataResponse) error + QueryOpenIDToken(ctx context.Context, req *QueryOpenIDTokenRequest, res *QueryOpenIDTokenResponse) error QueryPushers(ctx context.Context, req *QueryPushersRequest, res *QueryPushersResponse) error QueryPushRules(ctx context.Context, req *QueryPushRulesRequest, res *QueryPushRulesResponse) error QueryNotifications(ctx context.Context, req *QueryNotificationsRequest, res *QueryNotificationsResponse) error } -type UserDeviceAPI interface { +type QueryAcccessTokenAPI interface { + QueryAccessToken(ctx context.Context, req *QueryAccessTokenRequest, res *QueryAccessTokenResponse) error +} + +type SyncUserAPI interface { + QueryAccountData(ctx context.Context, req *QueryAccountDataRequest, res *QueryAccountDataResponse) error + QueryAccessToken(ctx context.Context, req *QueryAccessTokenRequest, res *QueryAccessTokenResponse) error PerformDeviceDeletion(ctx context.Context, req *PerformDeviceDeletionRequest, res *PerformDeviceDeletionResponse) error PerformLastSeenUpdate(ctx context.Context, req *PerformLastSeenUpdateRequest, res *PerformLastSeenUpdateResponse) error PerformDeviceUpdate(ctx context.Context, req *PerformDeviceUpdateRequest, res *PerformDeviceUpdateResponse) error From 5c37f165ae5301ee1f41c1491bed7fcc7c439c40 Mon Sep 17 00:00:00 2001 From: kegsay Date: Thu, 5 May 2022 10:53:52 +0100 Subject: [PATCH 03/12] Errors from createdb are non-fatal (#2420) As they are expected if the database already exists. --- test/db.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/db.go b/test/db.go index fecae5d48..a1754cd08 100644 --- a/test/db.go +++ b/test/db.go @@ -53,8 +53,8 @@ func createLocalDB(t *testing.T, dbName string) { createDB.Stderr = os.Stderr } err := createDB.Run() - if err != nil { - fatalError(t, "createLocalDB returned error: %s", err) + if err != nil && !Quiet { + fmt.Println("createLocalDB returned error:", err) } } From 658e82f8bcfc78d8489f9687e88fb712af0ea75f Mon Sep 17 00:00:00 2001 From: Till <2353100+S7evinK@users.noreply.github.com> Date: Thu, 5 May 2022 12:00:18 +0200 Subject: [PATCH 04/12] Don't use in-memory db for userapi tests (#2417) * Don't use in-memory db * Use WithAllDatabases where possible --- userapi/userapi_test.go | 205 +++++++++++++++++++++------------------- 1 file changed, 109 insertions(+), 96 deletions(-) diff --git a/userapi/userapi_test.go b/userapi/userapi_test.go index 64e23909a..e614765a2 100644 --- a/userapi/userapi_test.go +++ b/userapi/userapi_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package userapi +package userapi_test import ( "context" @@ -23,15 +23,17 @@ import ( "time" "github.com/gorilla/mux" + "github.com/matrix-org/dendrite/internal/httputil" + internalTest "github.com/matrix-org/dendrite/internal/test" + "github.com/matrix-org/dendrite/test" + "github.com/matrix-org/dendrite/userapi" + "github.com/matrix-org/dendrite/userapi/inthttp" "github.com/matrix-org/gomatrixserverlib" "golang.org/x/crypto/bcrypt" - "github.com/matrix-org/dendrite/internal/httputil" - "github.com/matrix-org/dendrite/internal/test" "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/internal" - "github.com/matrix-org/dendrite/userapi/inthttp" "github.com/matrix-org/dendrite/userapi/storage" ) @@ -43,16 +45,15 @@ type apiTestOpts struct { loginTokenLifetime time.Duration } -func MustMakeInternalAPI(t *testing.T, opts apiTestOpts) (api.UserInternalAPI, storage.Database) { +func MustMakeInternalAPI(t *testing.T, opts apiTestOpts, dbType test.DBType) (api.UserInternalAPI, storage.Database, func()) { if opts.loginTokenLifetime == 0 { opts.loginTokenLifetime = api.DefaultLoginTokenLifetime * time.Millisecond } - dbopts := &config.DatabaseOptions{ - ConnectionString: "file::memory:", - MaxOpenConnections: 1, - MaxIdleConnections: 1, - } - accountDB, err := storage.NewUserAPIDatabase(nil, dbopts, serverName, bcrypt.MinCost, config.DefaultOpenIDTokenLifetimeMS, opts.loginTokenLifetime, "") + connStr, close := test.PrepareDBConnectionString(t, dbType) + + accountDB, err := storage.NewUserAPIDatabase(nil, &config.DatabaseOptions{ + ConnectionString: config.DataSource(connStr), + }, serverName, bcrypt.MinCost, config.DefaultOpenIDTokenLifetimeMS, opts.loginTokenLifetime, "") if err != nil { t.Fatalf("failed to create account DB: %s", err) } @@ -66,13 +67,15 @@ func MustMakeInternalAPI(t *testing.T, opts apiTestOpts) (api.UserInternalAPI, s return &internal.UserInternalAPI{ DB: accountDB, ServerName: cfg.Matrix.ServerName, - }, accountDB + }, accountDB, close } func TestQueryProfile(t *testing.T) { aliceAvatarURL := "mxc://example.com/alice" aliceDisplayName := "Alice" - userAPI, accountDB := MustMakeInternalAPI(t, apiTestOpts{}) + // only one DBType, since userapi.AddInternalRoutes complains about multiple prometheus counters added + userAPI, accountDB, close := MustMakeInternalAPI(t, apiTestOpts{}, test.DBTypeSQLite) + defer close() _, err := accountDB.CreateAccount(context.TODO(), "alice", "foobar", "", api.AccountTypeUser) if err != nil { t.Fatalf("failed to make account: %s", err) @@ -131,8 +134,8 @@ func TestQueryProfile(t *testing.T) { t.Run("HTTP API", func(t *testing.T) { router := mux.NewRouter().PathPrefix(httputil.InternalPathPrefix).Subrouter() - AddInternalRoutes(router, userAPI) - apiURL, cancel := test.ListenAndServe(t, router, false) + userapi.AddInternalRoutes(router, userAPI) + apiURL, cancel := internalTest.ListenAndServe(t, router, false) defer cancel() httpAPI, err := inthttp.NewUserAPIClient(apiURL, &http.Client{}) if err != nil { @@ -149,110 +152,120 @@ func TestLoginToken(t *testing.T) { ctx := context.Background() t.Run("tokenLoginFlow", func(t *testing.T) { - userAPI, accountDB := MustMakeInternalAPI(t, apiTestOpts{}) + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + userAPI, accountDB, close := MustMakeInternalAPI(t, apiTestOpts{}, dbType) + defer close() + _, err := accountDB.CreateAccount(ctx, "auser", "apassword", "", api.AccountTypeUser) + if err != nil { + t.Fatalf("failed to make account: %s", err) + } - _, err := accountDB.CreateAccount(ctx, "auser", "apassword", "", api.AccountTypeUser) - if err != nil { - t.Fatalf("failed to make account: %s", err) - } + t.Log("Creating a login token like the SSO callback would...") - t.Log("Creating a login token like the SSO callback would...") + creq := api.PerformLoginTokenCreationRequest{ + Data: api.LoginTokenData{UserID: "@auser:example.com"}, + } + var cresp api.PerformLoginTokenCreationResponse + if err := userAPI.PerformLoginTokenCreation(ctx, &creq, &cresp); err != nil { + t.Fatalf("PerformLoginTokenCreation failed: %v", err) + } - creq := api.PerformLoginTokenCreationRequest{ - Data: api.LoginTokenData{UserID: "@auser:example.com"}, - } - var cresp api.PerformLoginTokenCreationResponse - if err := userAPI.PerformLoginTokenCreation(ctx, &creq, &cresp); err != nil { - t.Fatalf("PerformLoginTokenCreation failed: %v", err) - } + if cresp.Metadata.Token == "" { + t.Errorf("PerformLoginTokenCreation Token: got %q, want non-empty", cresp.Metadata.Token) + } + if cresp.Metadata.Expiration.Before(time.Now()) { + t.Errorf("PerformLoginTokenCreation Expiration: got %v, want non-expired", cresp.Metadata.Expiration) + } - if cresp.Metadata.Token == "" { - t.Errorf("PerformLoginTokenCreation Token: got %q, want non-empty", cresp.Metadata.Token) - } - if cresp.Metadata.Expiration.Before(time.Now()) { - t.Errorf("PerformLoginTokenCreation Expiration: got %v, want non-expired", cresp.Metadata.Expiration) - } + t.Log("Querying the login token like /login with m.login.token would...") - t.Log("Querying the login token like /login with m.login.token would...") + qreq := api.QueryLoginTokenRequest{Token: cresp.Metadata.Token} + var qresp api.QueryLoginTokenResponse + if err := userAPI.QueryLoginToken(ctx, &qreq, &qresp); err != nil { + t.Fatalf("QueryLoginToken failed: %v", err) + } - qreq := api.QueryLoginTokenRequest{Token: cresp.Metadata.Token} - var qresp api.QueryLoginTokenResponse - if err := userAPI.QueryLoginToken(ctx, &qreq, &qresp); err != nil { - t.Fatalf("QueryLoginToken failed: %v", err) - } + if qresp.Data == nil { + t.Errorf("QueryLoginToken Data: got %v, want non-nil", qresp.Data) + } else if want := "@auser:example.com"; qresp.Data.UserID != want { + t.Errorf("QueryLoginToken UserID: got %q, want %q", qresp.Data.UserID, want) + } - if qresp.Data == nil { - t.Errorf("QueryLoginToken Data: got %v, want non-nil", qresp.Data) - } else if want := "@auser:example.com"; qresp.Data.UserID != want { - t.Errorf("QueryLoginToken UserID: got %q, want %q", qresp.Data.UserID, want) - } + t.Log("Deleting the login token like /login with m.login.token would...") - t.Log("Deleting the login token like /login with m.login.token would...") - - dreq := api.PerformLoginTokenDeletionRequest{Token: cresp.Metadata.Token} - var dresp api.PerformLoginTokenDeletionResponse - if err := userAPI.PerformLoginTokenDeletion(ctx, &dreq, &dresp); err != nil { - t.Fatalf("PerformLoginTokenDeletion failed: %v", err) - } + dreq := api.PerformLoginTokenDeletionRequest{Token: cresp.Metadata.Token} + var dresp api.PerformLoginTokenDeletionResponse + if err := userAPI.PerformLoginTokenDeletion(ctx, &dreq, &dresp); err != nil { + t.Fatalf("PerformLoginTokenDeletion failed: %v", err) + } + }) }) t.Run("expiredTokenIsNotReturned", func(t *testing.T) { - userAPI, _ := MustMakeInternalAPI(t, apiTestOpts{loginTokenLifetime: -1 * time.Second}) + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + userAPI, _, close := MustMakeInternalAPI(t, apiTestOpts{loginTokenLifetime: -1 * time.Second}, dbType) + defer close() - creq := api.PerformLoginTokenCreationRequest{ - Data: api.LoginTokenData{UserID: "@auser:example.com"}, - } - var cresp api.PerformLoginTokenCreationResponse - if err := userAPI.PerformLoginTokenCreation(ctx, &creq, &cresp); err != nil { - t.Fatalf("PerformLoginTokenCreation failed: %v", err) - } + creq := api.PerformLoginTokenCreationRequest{ + Data: api.LoginTokenData{UserID: "@auser:example.com"}, + } + var cresp api.PerformLoginTokenCreationResponse + if err := userAPI.PerformLoginTokenCreation(ctx, &creq, &cresp); err != nil { + t.Fatalf("PerformLoginTokenCreation failed: %v", err) + } - qreq := api.QueryLoginTokenRequest{Token: cresp.Metadata.Token} - var qresp api.QueryLoginTokenResponse - if err := userAPI.QueryLoginToken(ctx, &qreq, &qresp); err != nil { - t.Fatalf("QueryLoginToken failed: %v", err) - } + qreq := api.QueryLoginTokenRequest{Token: cresp.Metadata.Token} + var qresp api.QueryLoginTokenResponse + if err := userAPI.QueryLoginToken(ctx, &qreq, &qresp); err != nil { + t.Fatalf("QueryLoginToken failed: %v", err) + } - if qresp.Data != nil { - t.Errorf("QueryLoginToken Data: got %v, want nil", qresp.Data) - } + if qresp.Data != nil { + t.Errorf("QueryLoginToken Data: got %v, want nil", qresp.Data) + } + }) }) t.Run("deleteWorks", func(t *testing.T) { - userAPI, _ := MustMakeInternalAPI(t, apiTestOpts{}) + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + userAPI, _, close := MustMakeInternalAPI(t, apiTestOpts{}, dbType) + defer close() - creq := api.PerformLoginTokenCreationRequest{ - Data: api.LoginTokenData{UserID: "@auser:example.com"}, - } - var cresp api.PerformLoginTokenCreationResponse - if err := userAPI.PerformLoginTokenCreation(ctx, &creq, &cresp); err != nil { - t.Fatalf("PerformLoginTokenCreation failed: %v", err) - } + creq := api.PerformLoginTokenCreationRequest{ + Data: api.LoginTokenData{UserID: "@auser:example.com"}, + } + var cresp api.PerformLoginTokenCreationResponse + if err := userAPI.PerformLoginTokenCreation(ctx, &creq, &cresp); err != nil { + t.Fatalf("PerformLoginTokenCreation failed: %v", err) + } - dreq := api.PerformLoginTokenDeletionRequest{Token: cresp.Metadata.Token} - var dresp api.PerformLoginTokenDeletionResponse - if err := userAPI.PerformLoginTokenDeletion(ctx, &dreq, &dresp); err != nil { - t.Fatalf("PerformLoginTokenDeletion failed: %v", err) - } + dreq := api.PerformLoginTokenDeletionRequest{Token: cresp.Metadata.Token} + var dresp api.PerformLoginTokenDeletionResponse + if err := userAPI.PerformLoginTokenDeletion(ctx, &dreq, &dresp); err != nil { + t.Fatalf("PerformLoginTokenDeletion failed: %v", err) + } - qreq := api.QueryLoginTokenRequest{Token: cresp.Metadata.Token} - var qresp api.QueryLoginTokenResponse - if err := userAPI.QueryLoginToken(ctx, &qreq, &qresp); err != nil { - t.Fatalf("QueryLoginToken failed: %v", err) - } + qreq := api.QueryLoginTokenRequest{Token: cresp.Metadata.Token} + var qresp api.QueryLoginTokenResponse + if err := userAPI.QueryLoginToken(ctx, &qreq, &qresp); err != nil { + t.Fatalf("QueryLoginToken failed: %v", err) + } - if qresp.Data != nil { - t.Errorf("QueryLoginToken Data: got %v, want nil", qresp.Data) - } + if qresp.Data != nil { + t.Errorf("QueryLoginToken Data: got %v, want nil", qresp.Data) + } + }) }) t.Run("deleteUnknownIsNoOp", func(t *testing.T) { - userAPI, _ := MustMakeInternalAPI(t, apiTestOpts{}) - - dreq := api.PerformLoginTokenDeletionRequest{Token: "non-existent token"} - var dresp api.PerformLoginTokenDeletionResponse - if err := userAPI.PerformLoginTokenDeletion(ctx, &dreq, &dresp); err != nil { - t.Fatalf("PerformLoginTokenDeletion failed: %v", err) - } + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + userAPI, _, close := MustMakeInternalAPI(t, apiTestOpts{}, dbType) + defer close() + dreq := api.PerformLoginTokenDeletionRequest{Token: "non-existent token"} + var dresp api.PerformLoginTokenDeletionResponse + if err := userAPI.PerformLoginTokenDeletion(ctx, &dreq, &dresp); err != nil { + t.Fatalf("PerformLoginTokenDeletion failed: %v", err) + } + }) }) } From 1bfe87aa5614d68cbb0cad127b375048cdc70ca9 Mon Sep 17 00:00:00 2001 From: Till <2353100+S7evinK@users.noreply.github.com> Date: Thu, 5 May 2022 12:01:28 +0200 Subject: [PATCH 05/12] Fix user already joined when using server notices (#2364) --- clientapi/routing/server_notices.go | 49 +++++++++++++---------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/clientapi/routing/server_notices.go b/clientapi/routing/server_notices.go index eec3d7e38..47b0da7bb 100644 --- a/clientapi/routing/server_notices.go +++ b/clientapi/routing/server_notices.go @@ -21,6 +21,7 @@ import ( "net/http" "time" + "github.com/matrix-org/dendrite/roomserver/version" "github.com/matrix-org/gomatrix" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib/tokens" @@ -95,29 +96,16 @@ func SendServerNotice( // get rooms for specified user allUserRooms := []string{} userRooms := api.QueryRoomsForUserResponse{} - if err := rsAPI.QueryRoomsForUser(ctx, &api.QueryRoomsForUserRequest{ - UserID: r.UserID, - WantMembership: "join", - }, &userRooms); err != nil { - return util.ErrorResponse(err) + // Get rooms the user is either joined, invited or has left. + for _, membership := range []string{"join", "invite", "leave"} { + if err := rsAPI.QueryRoomsForUser(ctx, &api.QueryRoomsForUserRequest{ + UserID: r.UserID, + WantMembership: membership, + }, &userRooms); err != nil { + return util.ErrorResponse(err) + } + allUserRooms = append(allUserRooms, userRooms.RoomIDs...) } - allUserRooms = append(allUserRooms, userRooms.RoomIDs...) - // get invites for specified user - if err := rsAPI.QueryRoomsForUser(ctx, &api.QueryRoomsForUserRequest{ - UserID: r.UserID, - WantMembership: "invite", - }, &userRooms); err != nil { - return util.ErrorResponse(err) - } - allUserRooms = append(allUserRooms, userRooms.RoomIDs...) - // get left rooms for specified user - if err := rsAPI.QueryRoomsForUser(ctx, &api.QueryRoomsForUserRequest{ - UserID: r.UserID, - WantMembership: "leave", - }, &userRooms); err != nil { - return util.ErrorResponse(err) - } - allUserRooms = append(allUserRooms, userRooms.RoomIDs...) // get rooms of the sender senderUserID := fmt.Sprintf("@%s:%s", cfgNotices.LocalPart, cfgClient.Matrix.ServerName) @@ -145,7 +133,7 @@ func SendServerNotice( var ( roomID string - roomVersion = gomatrixserverlib.RoomVersionV6 + roomVersion = version.DefaultRoomVersion() ) // create a new room for the user @@ -194,14 +182,21 @@ func SendServerNotice( // if we didn't get a createRoomResponse, we probably received an error, so return that. return roomRes } - } else { // we've found a room in common, check the membership roomID = commonRooms[0] - // re-invite the user - res, err := sendInvite(ctx, userAPI, senderDevice, roomID, r.UserID, "Server notice room", cfgClient, rsAPI, asAPI, time.Now()) + membershipRes := api.QueryMembershipForUserResponse{} + err := rsAPI.QueryMembershipForUser(ctx, &api.QueryMembershipForUserRequest{UserID: r.UserID, RoomID: roomID}, &membershipRes) if err != nil { - return res + util.GetLogger(ctx).WithError(err).Error("unable to query membership for user") + return jsonerror.InternalServerError() + } + if !membershipRes.IsInRoom { + // re-invite the user + res, err := sendInvite(ctx, userAPI, senderDevice, roomID, r.UserID, "Server notice room", cfgClient, rsAPI, asAPI, time.Now()) + if err != nil { + return res + } } } From d9e71b93b68efb57582d02448883b8a1259205e8 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 5 May 2022 11:33:16 +0100 Subject: [PATCH 06/12] Use `gomatrixserverlib.Client` instead of `http.Client` (#2421) * Update to matrix-org/gomatrixserverlib#303 * Use `gomatrixserverlib.Client` for phone-home stats * Use `gomatrixserverlib.Client` for push notifications * Use `gomatrixserverlib.Client` for appservices * Use `gomatrixserverlib.Client` for three-PID invites --- appservice/appservice.go | 18 +++++++----------- appservice/query/query.go | 8 ++++---- appservice/workers/transaction_scheduler.go | 8 ++++---- clientapi/threepid/invites.go | 4 ++-- go.mod | 4 ++-- go.sum | 4 ++-- internal/pushgateway/client.go | 21 +++++++++------------ userapi/util/phonehomestats.go | 12 +++++------- 8 files changed, 35 insertions(+), 44 deletions(-) diff --git a/appservice/appservice.go b/appservice/appservice.go index 0db2c1009..ac4ed967e 100644 --- a/appservice/appservice.go +++ b/appservice/appservice.go @@ -16,8 +16,6 @@ package appservice import ( "context" - "crypto/tls" - "net/http" "sync" "time" @@ -36,6 +34,7 @@ import ( "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/jetstream" userapi "github.com/matrix-org/dendrite/userapi/api" + "github.com/matrix-org/gomatrixserverlib" ) // AddInternalRoutes registers HTTP handlers for internal API calls @@ -50,15 +49,12 @@ func NewInternalAPI( userAPI userapi.UserInternalAPI, rsAPI roomserverAPI.RoomserverInternalAPI, ) appserviceAPI.AppServiceQueryAPI { - client := &http.Client{ - Timeout: time.Second * 30, - Transport: &http.Transport{ - DisableKeepAlives: true, - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: base.Cfg.AppServiceAPI.DisableTLSValidation, - }, - }, - } + client := gomatrixserverlib.NewClient( + gomatrixserverlib.WithTimeout(time.Second*30), + gomatrixserverlib.WithKeepAlives(false), + gomatrixserverlib.WithSkipVerify(base.Cfg.AppServiceAPI.DisableTLSValidation), + ) + js, _ := jetstream.Prepare(base.ProcessContext, &base.Cfg.Global.JetStream) // Create a connection to the appservice postgres DB diff --git a/appservice/query/query.go b/appservice/query/query.go index dacd3caa8..b7b0b335a 100644 --- a/appservice/query/query.go +++ b/appservice/query/query.go @@ -23,6 +23,7 @@ import ( "github.com/matrix-org/dendrite/appservice/api" "github.com/matrix-org/dendrite/setup/config" + "github.com/matrix-org/gomatrixserverlib" opentracing "github.com/opentracing/opentracing-go" log "github.com/sirupsen/logrus" ) @@ -32,7 +33,7 @@ const userIDExistsPath = "/users/" // AppServiceQueryAPI is an implementation of api.AppServiceQueryAPI type AppServiceQueryAPI struct { - HTTPClient *http.Client + HTTPClient *gomatrixserverlib.Client Cfg *config.Dendrite } @@ -64,9 +65,8 @@ func (a *AppServiceQueryAPI) RoomAliasExists( if err != nil { return err } - req = req.WithContext(ctx) - resp, err := a.HTTPClient.Do(req) + resp, err := a.HTTPClient.DoHTTPRequest(ctx, req) if resp != nil { defer func() { err = resp.Body.Close() @@ -130,7 +130,7 @@ func (a *AppServiceQueryAPI) UserIDExists( if err != nil { return err } - resp, err := a.HTTPClient.Do(req.WithContext(ctx)) + resp, err := a.HTTPClient.DoHTTPRequest(ctx, req) if resp != nil { defer func() { err = resp.Body.Close() diff --git a/appservice/workers/transaction_scheduler.go b/appservice/workers/transaction_scheduler.go index 4dab00bd7..47d447c2c 100644 --- a/appservice/workers/transaction_scheduler.go +++ b/appservice/workers/transaction_scheduler.go @@ -42,7 +42,7 @@ var ( // size), then send that off to the AS's /transactions/{txnID} endpoint. It also // handles exponentially backing off in case the AS isn't currently available. func SetupTransactionWorkers( - client *http.Client, + client *gomatrixserverlib.Client, appserviceDB storage.Database, workerStates []types.ApplicationServiceWorkerState, ) error { @@ -58,7 +58,7 @@ func SetupTransactionWorkers( // worker is a goroutine that sends any queued events to the application service // it is given. -func worker(client *http.Client, db storage.Database, ws types.ApplicationServiceWorkerState) { +func worker(client *gomatrixserverlib.Client, db storage.Database, ws types.ApplicationServiceWorkerState) { log.WithFields(log.Fields{ "appservice": ws.AppService.ID, }).Info("Starting application service") @@ -200,7 +200,7 @@ func createTransaction( // send sends events to an application service. Returns an error if an OK was not // received back from the application service or the request timed out. func send( - client *http.Client, + client *gomatrixserverlib.Client, appservice config.ApplicationService, txnID int, transaction []byte, @@ -213,7 +213,7 @@ func send( return err } req.Header.Set("Content-Type", "application/json") - resp, err := client.Do(req) + resp, err := client.DoHTTPRequest(context.TODO(), req) if err != nil { return err } diff --git a/clientapi/threepid/invites.go b/clientapi/threepid/invites.go index 6b750199b..eee6992f8 100644 --- a/clientapi/threepid/invites.go +++ b/clientapi/threepid/invites.go @@ -231,7 +231,7 @@ func queryIDServerStoreInvite( profile = &authtypes.Profile{} } - client := http.Client{} + client := gomatrixserverlib.NewClient() data := url.Values{} data.Add("medium", body.Medium) @@ -253,7 +253,7 @@ func queryIDServerStoreInvite( } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - resp, err := client.Do(req.WithContext(ctx)) + resp, err := client.DoHTTPRequest(ctx, req) if err != nil { return nil, err } diff --git a/go.mod b/go.mod index a7caadfb5..56f5dcbbb 100644 --- a/go.mod +++ b/go.mod @@ -25,12 +25,12 @@ require ( github.com/h2non/filetype v1.1.3 // indirect github.com/hashicorp/golang-lru v0.5.4 github.com/juju/testing v0.0.0-20220203020004-a0ff61f03494 // indirect - github.com/kardianos/minwinsvc v1.0.0 // indirect + github.com/kardianos/minwinsvc v1.0.0 github.com/lib/pq v1.10.5 github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91 github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 - github.com/matrix-org/gomatrixserverlib v0.0.0-20220408160933-cf558306b56f + github.com/matrix-org/gomatrixserverlib v0.0.0-20220505092512-c4ceb4751ac2 github.com/matrix-org/pinecone v0.0.0-20220408153826-2999ea29ed48 github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4 github.com/mattn/go-sqlite3 v1.14.10 diff --git a/go.sum b/go.sum index f8daca79e..fccda40c4 100644 --- a/go.sum +++ b/go.sum @@ -795,8 +795,8 @@ github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91/go.mod h1 github.com/matrix-org/gomatrix v0.0.0-20190528120928-7df988a63f26/go.mod h1:3fxX6gUjWyI/2Bt7J1OLhpCzOfO/bB3AiX0cJtEKud0= github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 h1:ZtO5uywdd5dLDCud4r0r55eP4j9FuUNpl60Gmntcop4= github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s= -github.com/matrix-org/gomatrixserverlib v0.0.0-20220408160933-cf558306b56f h1:MZrl4TgTnlaOn2Cu9gJCoJ3oyW5mT4/3QIZGgZXzKl4= -github.com/matrix-org/gomatrixserverlib v0.0.0-20220408160933-cf558306b56f/go.mod h1:V5eO8rn/C3rcxig37A/BCeKerLFS+9Avg/77FIeTZ48= +github.com/matrix-org/gomatrixserverlib v0.0.0-20220505092512-c4ceb4751ac2 h1:5/Y4BpiMk1D/l/HkJz8Ng8bLBz1BHwV6V4e+yMNySzk= +github.com/matrix-org/gomatrixserverlib v0.0.0-20220505092512-c4ceb4751ac2/go.mod h1:V5eO8rn/C3rcxig37A/BCeKerLFS+9Avg/77FIeTZ48= github.com/matrix-org/pinecone v0.0.0-20220408153826-2999ea29ed48 h1:W0sjjC6yjskHX4mb0nk3p0fXAlbU5bAFUFeEtlrPASE= github.com/matrix-org/pinecone v0.0.0-20220408153826-2999ea29ed48/go.mod h1:ulJzsVOTssIVp1j/m5eI//4VpAGDkMt5NrRuAVX7wpc= github.com/matrix-org/util v0.0.0-20190711121626-527ce5ddefc7/go.mod h1:vVQlW/emklohkZnOPwD3LrZUBqdfsbiyO3p1lNV8F6U= diff --git a/internal/pushgateway/client.go b/internal/pushgateway/client.go index 49907cee8..231327a1e 100644 --- a/internal/pushgateway/client.go +++ b/internal/pushgateway/client.go @@ -3,31 +3,28 @@ package pushgateway import ( "bytes" "context" - "crypto/tls" "encoding/json" "fmt" "net/http" "time" + "github.com/matrix-org/gomatrixserverlib" "github.com/opentracing/opentracing-go" ) type httpClient struct { - hc *http.Client + hc *gomatrixserverlib.Client } // NewHTTPClient creates a new Push Gateway client. func NewHTTPClient(disableTLSValidation bool) Client { - hc := &http.Client{ - Timeout: 30 * time.Second, - Transport: &http.Transport{ - DisableKeepAlives: true, - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: disableTLSValidation, - }, - }, + return &httpClient{ + hc: gomatrixserverlib.NewClient( + gomatrixserverlib.WithTimeout(time.Second*30), + gomatrixserverlib.WithKeepAlives(false), + gomatrixserverlib.WithSkipVerify(disableTLSValidation), + ), } - return &httpClient{hc: hc} } func (h *httpClient) Notify(ctx context.Context, url string, req *NotifyRequest, resp *NotifyResponse) error { @@ -44,7 +41,7 @@ func (h *httpClient) Notify(ctx context.Context, url string, req *NotifyRequest, } hreq.Header.Set("Content-Type", "application/json") - hresp, err := h.hc.Do(hreq) + hresp, err := h.hc.DoHTTPRequest(ctx, hreq) if err != nil { return err } diff --git a/userapi/util/phonehomestats.go b/userapi/util/phonehomestats.go index ad93a50e3..e24daba6b 100644 --- a/userapi/util/phonehomestats.go +++ b/userapi/util/phonehomestats.go @@ -39,7 +39,7 @@ type phoneHomeStats struct { cfg *config.Dendrite db storage.Statistics isMonolith bool - client *http.Client + client *gomatrixserverlib.Client } type timestampToRUUsage struct { @@ -55,10 +55,9 @@ func StartPhoneHomeCollector(startTime time.Time, cfg *config.Dendrite, statsDB cfg: cfg, db: statsDB, isMonolith: cfg.IsMonolith, - client: &http.Client{ - Timeout: time.Second * 30, - Transport: http.DefaultTransport, - }, + client: gomatrixserverlib.NewClient( + gomatrixserverlib.WithTimeout(time.Second * 30), + ), } // start initial run after 5min @@ -152,8 +151,7 @@ func (p *phoneHomeStats) collect() { } request.Header.Set("User-Agent", "Dendrite/"+internal.VersionString()) - _, err = p.client.Do(request) - if err != nil { + if _, err = p.client.DoHTTPRequest(ctx, request); err != nil { logrus.WithError(err).Error("unable to send anonymous stats") return } From 506de4bb3d69fef27e60d09d06712a72e588a198 Mon Sep 17 00:00:00 2001 From: kegsay Date: Thu, 5 May 2022 13:17:38 +0100 Subject: [PATCH 07/12] Define component interfaces based on consumers (1/2) (#2423) * Specify interfaces used by appservice, do half of clientapi * convert more deps of clientapi to finer-grained interfaces * Convert mediaapi and rest of clientapi * Somehow this got missed --- appservice/api/query.go | 2 +- appservice/appservice.go | 6 +- appservice/consumers/roomserver.go | 4 +- clientapi/auth/login.go | 2 +- clientapi/auth/login_test.go | 5 +- clientapi/auth/user_interactive.go | 2 +- clientapi/auth/user_interactive_test.go | 4 +- clientapi/clientapi.go | 8 +- clientapi/producers/syncapi.go | 2 +- clientapi/routing/account_data.go | 6 +- clientapi/routing/admin.go | 2 +- clientapi/routing/admin_whois.go | 2 +- clientapi/routing/aliases.go | 2 +- clientapi/routing/capabilities.go | 2 +- clientapi/routing/createroom.go | 30 ++-- clientapi/routing/deactivate.go | 2 +- clientapi/routing/device.go | 10 +- clientapi/routing/directory.go | 12 +- clientapi/routing/directory_public.go | 6 +- clientapi/routing/getevent.go | 5 +- clientapi/routing/joinroom.go | 4 +- clientapi/routing/key_backup.go | 12 +- clientapi/routing/key_crosssigning.go | 6 +- clientapi/routing/keys.go | 6 +- clientapi/routing/leaveroom.go | 2 +- clientapi/routing/login.go | 4 +- clientapi/routing/logout.go | 4 +- clientapi/routing/membership.go | 74 ++++----- clientapi/routing/memberships.go | 4 +- clientapi/routing/notification.go | 2 +- clientapi/routing/openid.go | 2 +- clientapi/routing/password.go | 2 +- clientapi/routing/peekroom.go | 4 +- clientapi/routing/profile.go | 18 +-- clientapi/routing/pusher.go | 4 +- clientapi/routing/pushrules.go | 20 +-- clientapi/routing/redaction.go | 2 +- clientapi/routing/register.go | 16 +- clientapi/routing/room_tagging.go | 10 +- clientapi/routing/routing.go | 12 +- clientapi/routing/sendevent.go | 4 +- clientapi/routing/sendtyping.go | 2 +- clientapi/routing/server_notices.go | 6 +- clientapi/routing/state.go | 4 +- clientapi/routing/threepid.go | 8 +- clientapi/routing/upgrade_room.go | 4 +- clientapi/routing/userdirectory.go | 14 +- clientapi/threepid/invites.go | 14 +- federationapi/api/api.go | 5 + federationapi/routing/invite.go | 49 +++--- internal/eventutil/events.go | 4 +- keyserver/api/api.go | 18 ++- mediaapi/mediaapi.go | 2 +- mediaapi/routing/routing.go | 2 +- roomserver/api/api.go | 190 +++++++++++++++--------- roomserver/api/wrapper.go | 42 +----- userapi/api/api.go | 96 ++++++------ 57 files changed, 414 insertions(+), 372 deletions(-) diff --git a/appservice/api/query.go b/appservice/api/query.go index cf25a9616..6db8be85b 100644 --- a/appservice/api/query.go +++ b/appservice/api/query.go @@ -84,7 +84,7 @@ func RetrieveUserProfile( ctx context.Context, userID string, asAPI AppServiceQueryAPI, - profileAPI userapi.UserProfileAPI, + profileAPI userapi.ClientUserAPI, ) (*authtypes.Profile, error) { localpart, _, err := gomatrixserverlib.SplitID('@', userID) if err != nil { diff --git a/appservice/appservice.go b/appservice/appservice.go index ac4ed967e..e026a7875 100644 --- a/appservice/appservice.go +++ b/appservice/appservice.go @@ -46,8 +46,8 @@ func AddInternalRoutes(router *mux.Router, queryAPI appserviceAPI.AppServiceQuer // can call functions directly on the returned API or via an HTTP interface using AddInternalRoutes. func NewInternalAPI( base *base.BaseDendrite, - userAPI userapi.UserInternalAPI, - rsAPI roomserverAPI.RoomserverInternalAPI, + userAPI userapi.AppserviceUserAPI, + rsAPI roomserverAPI.AppserviceRoomserverAPI, ) appserviceAPI.AppServiceQueryAPI { client := gomatrixserverlib.NewClient( gomatrixserverlib.WithTimeout(time.Second*30), @@ -113,7 +113,7 @@ func NewInternalAPI( // `sender_localpart` field of each application service if it doesn't // exist already func generateAppServiceAccount( - userAPI userapi.UserInternalAPI, + userAPI userapi.AppserviceUserAPI, as config.ApplicationService, ) error { var accRes userapi.PerformAccountCreationResponse diff --git a/appservice/consumers/roomserver.go b/appservice/consumers/roomserver.go index 31e05caa0..e406e88a7 100644 --- a/appservice/consumers/roomserver.go +++ b/appservice/consumers/roomserver.go @@ -37,7 +37,7 @@ type OutputRoomEventConsumer struct { durable string topic string asDB storage.Database - rsAPI api.RoomserverInternalAPI + rsAPI api.AppserviceRoomserverAPI serverName string workerStates []types.ApplicationServiceWorkerState } @@ -49,7 +49,7 @@ func NewOutputRoomEventConsumer( cfg *config.Dendrite, js nats.JetStreamContext, appserviceDB storage.Database, - rsAPI api.RoomserverInternalAPI, + rsAPI api.AppserviceRoomserverAPI, workerStates []types.ApplicationServiceWorkerState, ) *OutputRoomEventConsumer { return &OutputRoomEventConsumer{ diff --git a/clientapi/auth/login.go b/clientapi/auth/login.go index 020731c9f..5f51c662a 100644 --- a/clientapi/auth/login.go +++ b/clientapi/auth/login.go @@ -33,7 +33,7 @@ import ( // called after authorization has completed, with the result of the authorization. // If the final return value is non-nil, an error occurred and the cleanup function // is nil. -func LoginFromJSONReader(ctx context.Context, r io.Reader, useraccountAPI uapi.UserAccountAPI, userAPI UserInternalAPIForLogin, cfg *config.ClientAPI) (*Login, LoginCleanupFunc, *util.JSONResponse) { +func LoginFromJSONReader(ctx context.Context, r io.Reader, useraccountAPI uapi.UserLoginAPI, userAPI UserInternalAPIForLogin, cfg *config.ClientAPI) (*Login, LoginCleanupFunc, *util.JSONResponse) { reqBytes, err := ioutil.ReadAll(r) if err != nil { err := &util.JSONResponse{ diff --git a/clientapi/auth/login_test.go b/clientapi/auth/login_test.go index d401469c1..5085f0170 100644 --- a/clientapi/auth/login_test.go +++ b/clientapi/auth/login_test.go @@ -160,7 +160,6 @@ func TestBadLoginFromJSONReader(t *testing.T) { type fakeUserInternalAPI struct { UserInternalAPIForLogin - uapi.UserAccountAPI DeletedTokens []string } @@ -179,6 +178,10 @@ func (ua *fakeUserInternalAPI) PerformLoginTokenDeletion(ctx context.Context, re return nil } +func (ua *fakeUserInternalAPI) PerformLoginTokenCreation(ctx context.Context, req *uapi.PerformLoginTokenCreationRequest, res *uapi.PerformLoginTokenCreationResponse) error { + return nil +} + func (*fakeUserInternalAPI) QueryLoginToken(ctx context.Context, req *uapi.QueryLoginTokenRequest, res *uapi.QueryLoginTokenResponse) error { if req.Token == "invalidtoken" { return nil diff --git a/clientapi/auth/user_interactive.go b/clientapi/auth/user_interactive.go index 22c430f97..6caf7dcdc 100644 --- a/clientapi/auth/user_interactive.go +++ b/clientapi/auth/user_interactive.go @@ -110,7 +110,7 @@ type UserInteractive struct { Sessions map[string][]string } -func NewUserInteractive(userAccountAPI api.UserAccountAPI, cfg *config.ClientAPI) *UserInteractive { +func NewUserInteractive(userAccountAPI api.UserLoginAPI, cfg *config.ClientAPI) *UserInteractive { typePassword := &LoginTypePassword{ GetAccountByPassword: userAccountAPI.QueryAccountByPassword, Config: cfg, diff --git a/clientapi/auth/user_interactive_test.go b/clientapi/auth/user_interactive_test.go index a4b4587a3..262e48103 100644 --- a/clientapi/auth/user_interactive_test.go +++ b/clientapi/auth/user_interactive_test.go @@ -24,9 +24,7 @@ var ( } ) -type fakeAccountDatabase struct { - api.UserAccountAPI -} +type fakeAccountDatabase struct{} func (d *fakeAccountDatabase) PerformPasswordUpdate(ctx context.Context, req *api.PerformPasswordUpdateRequest, res *api.PerformPasswordUpdateResponse) error { return nil diff --git a/clientapi/clientapi.go b/clientapi/clientapi.go index 0d16e4c14..957d082a6 100644 --- a/clientapi/clientapi.go +++ b/clientapi/clientapi.go @@ -33,13 +33,13 @@ import ( func AddPublicRoutes( base *base.BaseDendrite, federation *gomatrixserverlib.FederationClient, - rsAPI roomserverAPI.RoomserverInternalAPI, + rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceQueryAPI, transactionsCache *transactions.Cache, - fsAPI federationAPI.FederationInternalAPI, - userAPI userapi.UserInternalAPI, + fsAPI federationAPI.ClientFederationAPI, + userAPI userapi.ClientUserAPI, userDirectoryProvider userapi.UserDirectoryProvider, - keyAPI keyserverAPI.KeyInternalAPI, + keyAPI keyserverAPI.ClientKeyAPI, extRoomsProvider api.ExtraPublicRoomsProvider, ) { cfg := &base.Cfg.ClientAPI diff --git a/clientapi/producers/syncapi.go b/clientapi/producers/syncapi.go index 187e3412d..48b1ae88d 100644 --- a/clientapi/producers/syncapi.go +++ b/clientapi/producers/syncapi.go @@ -38,7 +38,7 @@ type SyncAPIProducer struct { TopicPresenceEvent string JetStream nats.JetStreamContext ServerName gomatrixserverlib.ServerName - UserAPI userapi.UserInternalAPI + UserAPI userapi.ClientUserAPI } // SendData sends account data to the sync API server diff --git a/clientapi/routing/account_data.go b/clientapi/routing/account_data.go index d0dd3ab8d..a5a3014ab 100644 --- a/clientapi/routing/account_data.go +++ b/clientapi/routing/account_data.go @@ -33,7 +33,7 @@ import ( // GetAccountData implements GET /user/{userId}/[rooms/{roomid}/]account_data/{type} func GetAccountData( - req *http.Request, userAPI api.UserInternalAPI, device *api.Device, + req *http.Request, userAPI api.ClientUserAPI, device *api.Device, userID string, roomID string, dataType string, ) util.JSONResponse { if userID != device.UserID { @@ -76,7 +76,7 @@ func GetAccountData( // SaveAccountData implements PUT /user/{userId}/[rooms/{roomId}/]account_data/{type} func SaveAccountData( - req *http.Request, userAPI api.UserInternalAPI, device *api.Device, + req *http.Request, userAPI api.ClientUserAPI, device *api.Device, userID string, roomID string, dataType string, syncProducer *producers.SyncAPIProducer, ) util.JSONResponse { if userID != device.UserID { @@ -152,7 +152,7 @@ type fullyReadEvent struct { // SaveReadMarker implements POST /rooms/{roomId}/read_markers func SaveReadMarker( req *http.Request, - userAPI api.UserInternalAPI, rsAPI roomserverAPI.RoomserverInternalAPI, + userAPI api.ClientUserAPI, rsAPI roomserverAPI.ClientRoomserverAPI, syncProducer *producers.SyncAPIProducer, device *api.Device, roomID string, ) util.JSONResponse { // Verify that the user is a member of this room diff --git a/clientapi/routing/admin.go b/clientapi/routing/admin.go index 31e431c78..125b3847d 100644 --- a/clientapi/routing/admin.go +++ b/clientapi/routing/admin.go @@ -11,7 +11,7 @@ import ( "github.com/matrix-org/util" ) -func AdminEvacuateRoom(req *http.Request, device *userapi.Device, rsAPI roomserverAPI.RoomserverInternalAPI) util.JSONResponse { +func AdminEvacuateRoom(req *http.Request, device *userapi.Device, rsAPI roomserverAPI.ClientRoomserverAPI) util.JSONResponse { if device.AccountType != userapi.AccountTypeAdmin { return util.JSONResponse{ Code: http.StatusForbidden, diff --git a/clientapi/routing/admin_whois.go b/clientapi/routing/admin_whois.go index 87bb79366..f1cbd3467 100644 --- a/clientapi/routing/admin_whois.go +++ b/clientapi/routing/admin_whois.go @@ -44,7 +44,7 @@ type connectionInfo struct { // GetAdminWhois implements GET /admin/whois/{userId} func GetAdminWhois( - req *http.Request, userAPI api.UserInternalAPI, device *api.Device, + req *http.Request, userAPI api.ClientUserAPI, device *api.Device, userID string, ) util.JSONResponse { allowed := device.AccountType == api.AccountTypeAdmin || userID == device.UserID diff --git a/clientapi/routing/aliases.go b/clientapi/routing/aliases.go index 8c4830532..504d60265 100644 --- a/clientapi/routing/aliases.go +++ b/clientapi/routing/aliases.go @@ -28,7 +28,7 @@ import ( // GetAliases implements GET /_matrix/client/r0/rooms/{roomId}/aliases func GetAliases( - req *http.Request, rsAPI api.RoomserverInternalAPI, device *userapi.Device, roomID string, + req *http.Request, rsAPI api.ClientRoomserverAPI, device *userapi.Device, roomID string, ) util.JSONResponse { stateTuple := gomatrixserverlib.StateKeyTuple{ EventType: gomatrixserverlib.MRoomHistoryVisibility, diff --git a/clientapi/routing/capabilities.go b/clientapi/routing/capabilities.go index 72668fa5a..b7d47e916 100644 --- a/clientapi/routing/capabilities.go +++ b/clientapi/routing/capabilities.go @@ -26,7 +26,7 @@ import ( // GetCapabilities returns information about the server's supported feature set // and other relevant capabilities to an authenticated user. func GetCapabilities( - req *http.Request, rsAPI roomserverAPI.RoomserverInternalAPI, + req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, ) util.JSONResponse { roomVersionsQueryReq := roomserverAPI.QueryRoomVersionCapabilitiesRequest{} roomVersionsQueryRes := roomserverAPI.QueryRoomVersionCapabilitiesResponse{} diff --git a/clientapi/routing/createroom.go b/clientapi/routing/createroom.go index 4976b3e50..a21abb0eb 100644 --- a/clientapi/routing/createroom.go +++ b/clientapi/routing/createroom.go @@ -137,7 +137,7 @@ type fledglingEvent struct { func CreateRoom( req *http.Request, device *api.Device, cfg *config.ClientAPI, - profileAPI api.UserProfileAPI, rsAPI roomserverAPI.RoomserverInternalAPI, + profileAPI api.ClientUserAPI, rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceQueryAPI, ) util.JSONResponse { var r createRoomRequest @@ -164,7 +164,7 @@ func createRoom( ctx context.Context, r createRoomRequest, device *api.Device, cfg *config.ClientAPI, - profileAPI api.UserProfileAPI, rsAPI roomserverAPI.RoomserverInternalAPI, + profileAPI api.ClientUserAPI, rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceQueryAPI, evTime time.Time, ) util.JSONResponse { @@ -531,25 +531,23 @@ func createRoom( gomatrixserverlib.NewInviteV2StrippedState(inviteEvent.Event), ) // Send the invite event to the roomserver. - err = roomserverAPI.SendInvite( - ctx, - rsAPI, - inviteEvent.Headered(roomVersion), - inviteStrippedState, // invite room state - cfg.Matrix.ServerName, // send as server - nil, // transaction ID - ) - switch e := err.(type) { - case *roomserverAPI.PerformError: - return e.JSONResponse() - case nil: - default: - util.GetLogger(ctx).WithError(err).Error("roomserverAPI.SendInvite failed") + var inviteRes roomserverAPI.PerformInviteResponse + event := inviteEvent.Headered(roomVersion) + if err := rsAPI.PerformInvite(ctx, &roomserverAPI.PerformInviteRequest{ + Event: event, + InviteRoomState: inviteStrippedState, + RoomVersion: event.RoomVersion, + SendAsServer: string(cfg.Matrix.ServerName), + }, &inviteRes); err != nil { + util.GetLogger(ctx).WithError(err).Error("PerformInvite failed") return util.JSONResponse{ Code: http.StatusInternalServerError, JSON: jsonerror.InternalServerError(), } } + if inviteRes.Error != nil { + return inviteRes.Error.JSONResponse() + } } } diff --git a/clientapi/routing/deactivate.go b/clientapi/routing/deactivate.go index da1b6dcf9..c8aa6a3bc 100644 --- a/clientapi/routing/deactivate.go +++ b/clientapi/routing/deactivate.go @@ -15,7 +15,7 @@ import ( func Deactivate( req *http.Request, userInteractiveAuth *auth.UserInteractive, - accountAPI api.UserAccountAPI, + accountAPI api.ClientUserAPI, deviceAPI *api.Device, ) util.JSONResponse { ctx := req.Context() diff --git a/clientapi/routing/device.go b/clientapi/routing/device.go index 161bc2731..bb1cf47bd 100644 --- a/clientapi/routing/device.go +++ b/clientapi/routing/device.go @@ -50,7 +50,7 @@ type devicesDeleteJSON struct { // GetDeviceByID handles /devices/{deviceID} func GetDeviceByID( - req *http.Request, userAPI api.UserInternalAPI, device *api.Device, + req *http.Request, userAPI api.ClientUserAPI, device *api.Device, deviceID string, ) util.JSONResponse { var queryRes api.QueryDevicesResponse @@ -88,7 +88,7 @@ func GetDeviceByID( // GetDevicesByLocalpart handles /devices func GetDevicesByLocalpart( - req *http.Request, userAPI api.UserInternalAPI, device *api.Device, + req *http.Request, userAPI api.ClientUserAPI, device *api.Device, ) util.JSONResponse { var queryRes api.QueryDevicesResponse err := userAPI.QueryDevices(req.Context(), &api.QueryDevicesRequest{ @@ -118,7 +118,7 @@ func GetDevicesByLocalpart( // UpdateDeviceByID handles PUT on /devices/{deviceID} func UpdateDeviceByID( - req *http.Request, userAPI api.UserInternalAPI, device *api.Device, + req *http.Request, userAPI api.ClientUserAPI, device *api.Device, deviceID string, ) util.JSONResponse { @@ -161,7 +161,7 @@ func UpdateDeviceByID( // DeleteDeviceById handles DELETE requests to /devices/{deviceId} func DeleteDeviceById( - req *http.Request, userInteractiveAuth *auth.UserInteractive, userAPI api.UserInternalAPI, device *api.Device, + req *http.Request, userInteractiveAuth *auth.UserInteractive, userAPI api.ClientUserAPI, device *api.Device, deviceID string, ) util.JSONResponse { var ( @@ -242,7 +242,7 @@ func DeleteDeviceById( // DeleteDevices handles POST requests to /delete_devices func DeleteDevices( - req *http.Request, userAPI api.UserInternalAPI, device *api.Device, + req *http.Request, userAPI api.ClientUserAPI, device *api.Device, ) util.JSONResponse { ctx := req.Context() payload := devicesDeleteJSON{} diff --git a/clientapi/routing/directory.go b/clientapi/routing/directory.go index ac355b5d4..53ba3f190 100644 --- a/clientapi/routing/directory.go +++ b/clientapi/routing/directory.go @@ -46,8 +46,8 @@ func DirectoryRoom( roomAlias string, federation *gomatrixserverlib.FederationClient, cfg *config.ClientAPI, - rsAPI roomserverAPI.RoomserverInternalAPI, - fedSenderAPI federationAPI.FederationInternalAPI, + rsAPI roomserverAPI.ClientRoomserverAPI, + fedSenderAPI federationAPI.ClientFederationAPI, ) util.JSONResponse { _, domain, err := gomatrixserverlib.SplitID('#', roomAlias) if err != nil { @@ -117,7 +117,7 @@ func SetLocalAlias( device *userapi.Device, alias string, cfg *config.ClientAPI, - rsAPI roomserverAPI.RoomserverInternalAPI, + rsAPI roomserverAPI.ClientRoomserverAPI, ) util.JSONResponse { _, domain, err := gomatrixserverlib.SplitID('#', alias) if err != nil { @@ -199,7 +199,7 @@ func RemoveLocalAlias( req *http.Request, device *userapi.Device, alias string, - rsAPI roomserverAPI.RoomserverInternalAPI, + rsAPI roomserverAPI.ClientRoomserverAPI, ) util.JSONResponse { queryReq := roomserverAPI.RemoveRoomAliasRequest{ Alias: alias, @@ -237,7 +237,7 @@ type roomVisibility struct { // GetVisibility implements GET /directory/list/room/{roomID} func GetVisibility( - req *http.Request, rsAPI roomserverAPI.RoomserverInternalAPI, + req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, roomID string, ) util.JSONResponse { var res roomserverAPI.QueryPublishedRoomsResponse @@ -265,7 +265,7 @@ func GetVisibility( // SetVisibility implements PUT /directory/list/room/{roomID} // TODO: Allow admin users to edit the room visibility func SetVisibility( - req *http.Request, rsAPI roomserverAPI.RoomserverInternalAPI, dev *userapi.Device, + req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, dev *userapi.Device, roomID string, ) util.JSONResponse { resErr := checkMemberInRoom(req.Context(), rsAPI, dev.UserID, roomID) diff --git a/clientapi/routing/directory_public.go b/clientapi/routing/directory_public.go index 0dacfced5..c3e6141b2 100644 --- a/clientapi/routing/directory_public.go +++ b/clientapi/routing/directory_public.go @@ -50,7 +50,7 @@ type filter struct { // GetPostPublicRooms implements GET and POST /publicRooms func GetPostPublicRooms( - req *http.Request, rsAPI roomserverAPI.RoomserverInternalAPI, + req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, extRoomsProvider api.ExtraPublicRoomsProvider, federation *gomatrixserverlib.FederationClient, cfg *config.ClientAPI, @@ -91,7 +91,7 @@ func GetPostPublicRooms( } func publicRooms( - ctx context.Context, request PublicRoomReq, rsAPI roomserverAPI.RoomserverInternalAPI, extRoomsProvider api.ExtraPublicRoomsProvider, + ctx context.Context, request PublicRoomReq, rsAPI roomserverAPI.ClientRoomserverAPI, extRoomsProvider api.ExtraPublicRoomsProvider, ) (*gomatrixserverlib.RespPublicRooms, error) { response := gomatrixserverlib.RespPublicRooms{ @@ -229,7 +229,7 @@ func sliceInto(slice []gomatrixserverlib.PublicRoom, since int64, limit int16) ( } func refreshPublicRoomCache( - ctx context.Context, rsAPI roomserverAPI.RoomserverInternalAPI, extRoomsProvider api.ExtraPublicRoomsProvider, + ctx context.Context, rsAPI roomserverAPI.ClientRoomserverAPI, extRoomsProvider api.ExtraPublicRoomsProvider, ) []gomatrixserverlib.PublicRoom { cacheMu.Lock() defer cacheMu.Unlock() diff --git a/clientapi/routing/getevent.go b/clientapi/routing/getevent.go index 36f3ee9e3..7f5842800 100644 --- a/clientapi/routing/getevent.go +++ b/clientapi/routing/getevent.go @@ -31,7 +31,6 @@ type getEventRequest struct { roomID string eventID string cfg *config.ClientAPI - federation *gomatrixserverlib.FederationClient requestedEvent *gomatrixserverlib.Event } @@ -43,8 +42,7 @@ func GetEvent( roomID string, eventID string, cfg *config.ClientAPI, - rsAPI api.RoomserverInternalAPI, - federation *gomatrixserverlib.FederationClient, + rsAPI api.ClientRoomserverAPI, ) util.JSONResponse { eventsReq := api.QueryEventsByIDRequest{ EventIDs: []string{eventID}, @@ -72,7 +70,6 @@ func GetEvent( roomID: roomID, eventID: eventID, cfg: cfg, - federation: federation, requestedEvent: requestedEvent, } diff --git a/clientapi/routing/joinroom.go b/clientapi/routing/joinroom.go index dc15f4bda..4e6acebc3 100644 --- a/clientapi/routing/joinroom.go +++ b/clientapi/routing/joinroom.go @@ -29,8 +29,8 @@ import ( func JoinRoomByIDOrAlias( req *http.Request, device *api.Device, - rsAPI roomserverAPI.RoomserverInternalAPI, - profileAPI api.UserProfileAPI, + rsAPI roomserverAPI.ClientRoomserverAPI, + profileAPI api.ClientUserAPI, roomIDOrAlias string, ) util.JSONResponse { // Prepare to ask the roomserver to perform the room join. diff --git a/clientapi/routing/key_backup.go b/clientapi/routing/key_backup.go index 9d2ff87fd..28c80415b 100644 --- a/clientapi/routing/key_backup.go +++ b/clientapi/routing/key_backup.go @@ -55,7 +55,7 @@ type keyBackupSessionResponse struct { // Create a new key backup. Request must contain a `keyBackupVersion`. Returns a `keyBackupVersionCreateResponse`. // Implements POST /_matrix/client/r0/room_keys/version -func CreateKeyBackupVersion(req *http.Request, userAPI userapi.UserInternalAPI, device *userapi.Device) util.JSONResponse { +func CreateKeyBackupVersion(req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device) util.JSONResponse { var kb keyBackupVersion resErr := httputil.UnmarshalJSONRequest(req, &kb) if resErr != nil { @@ -89,7 +89,7 @@ func CreateKeyBackupVersion(req *http.Request, userAPI userapi.UserInternalAPI, // KeyBackupVersion returns the key backup version specified. If `version` is empty, the latest `keyBackupVersionResponse` is returned. // Implements GET /_matrix/client/r0/room_keys/version and GET /_matrix/client/r0/room_keys/version/{version} -func KeyBackupVersion(req *http.Request, userAPI userapi.UserInternalAPI, device *userapi.Device, version string) util.JSONResponse { +func KeyBackupVersion(req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version string) util.JSONResponse { var queryResp userapi.QueryKeyBackupResponse userAPI.QueryKeyBackup(req.Context(), &userapi.QueryKeyBackupRequest{ UserID: device.UserID, @@ -118,7 +118,7 @@ func KeyBackupVersion(req *http.Request, userAPI userapi.UserInternalAPI, device // Modify the auth data of a key backup. Version must not be empty. Request must contain a `keyBackupVersion` // Implements PUT /_matrix/client/r0/room_keys/version/{version} -func ModifyKeyBackupVersionAuthData(req *http.Request, userAPI userapi.UserInternalAPI, device *userapi.Device, version string) util.JSONResponse { +func ModifyKeyBackupVersionAuthData(req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version string) util.JSONResponse { var kb keyBackupVersion resErr := httputil.UnmarshalJSONRequest(req, &kb) if resErr != nil { @@ -159,7 +159,7 @@ func ModifyKeyBackupVersionAuthData(req *http.Request, userAPI userapi.UserInter // Delete a version of key backup. Version must not be empty. If the key backup was previously deleted, will return 200 OK. // Implements DELETE /_matrix/client/r0/room_keys/version/{version} -func DeleteKeyBackupVersion(req *http.Request, userAPI userapi.UserInternalAPI, device *userapi.Device, version string) util.JSONResponse { +func DeleteKeyBackupVersion(req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version string) util.JSONResponse { var performKeyBackupResp userapi.PerformKeyBackupResponse if err := userAPI.PerformKeyBackup(req.Context(), &userapi.PerformKeyBackupRequest{ UserID: device.UserID, @@ -194,7 +194,7 @@ func DeleteKeyBackupVersion(req *http.Request, userAPI userapi.UserInternalAPI, // Upload a bunch of session keys for a given `version`. func UploadBackupKeys( - req *http.Request, userAPI userapi.UserInternalAPI, device *userapi.Device, version string, keys *keyBackupSessionRequest, + req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version string, keys *keyBackupSessionRequest, ) util.JSONResponse { var performKeyBackupResp userapi.PerformKeyBackupResponse if err := userAPI.PerformKeyBackup(req.Context(), &userapi.PerformKeyBackupRequest{ @@ -230,7 +230,7 @@ func UploadBackupKeys( // Get keys from a given backup version. Response returned varies depending on if roomID and sessionID are set. func GetBackupKeys( - req *http.Request, userAPI userapi.UserInternalAPI, device *userapi.Device, version, roomID, sessionID string, + req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version, roomID, sessionID string, ) util.JSONResponse { var queryResp userapi.QueryKeyBackupResponse userAPI.QueryKeyBackup(req.Context(), &userapi.QueryKeyBackupRequest{ diff --git a/clientapi/routing/key_crosssigning.go b/clientapi/routing/key_crosssigning.go index c73e0a10d..8fbb86f7a 100644 --- a/clientapi/routing/key_crosssigning.go +++ b/clientapi/routing/key_crosssigning.go @@ -34,8 +34,8 @@ type crossSigningRequest struct { func UploadCrossSigningDeviceKeys( req *http.Request, userInteractiveAuth *auth.UserInteractive, - keyserverAPI api.KeyInternalAPI, device *userapi.Device, - accountAPI userapi.UserAccountAPI, cfg *config.ClientAPI, + keyserverAPI api.ClientKeyAPI, device *userapi.Device, + accountAPI userapi.ClientUserAPI, cfg *config.ClientAPI, ) util.JSONResponse { uploadReq := &crossSigningRequest{} uploadRes := &api.PerformUploadDeviceKeysResponse{} @@ -105,7 +105,7 @@ func UploadCrossSigningDeviceKeys( } } -func UploadCrossSigningDeviceSignatures(req *http.Request, keyserverAPI api.KeyInternalAPI, device *userapi.Device) util.JSONResponse { +func UploadCrossSigningDeviceSignatures(req *http.Request, keyserverAPI api.ClientKeyAPI, device *userapi.Device) util.JSONResponse { uploadReq := &api.PerformUploadDeviceSignaturesRequest{} uploadRes := &api.PerformUploadDeviceSignaturesResponse{} diff --git a/clientapi/routing/keys.go b/clientapi/routing/keys.go index 2d65ac353..fdda34a53 100644 --- a/clientapi/routing/keys.go +++ b/clientapi/routing/keys.go @@ -31,7 +31,7 @@ type uploadKeysRequest struct { OneTimeKeys map[string]json.RawMessage `json:"one_time_keys"` } -func UploadKeys(req *http.Request, keyAPI api.KeyInternalAPI, device *userapi.Device) util.JSONResponse { +func UploadKeys(req *http.Request, keyAPI api.ClientKeyAPI, device *userapi.Device) util.JSONResponse { var r uploadKeysRequest resErr := httputil.UnmarshalJSONRequest(req, &r) if resErr != nil { @@ -100,7 +100,7 @@ func (r *queryKeysRequest) GetTimeout() time.Duration { return time.Duration(r.Timeout) * time.Millisecond } -func QueryKeys(req *http.Request, keyAPI api.KeyInternalAPI, device *userapi.Device) util.JSONResponse { +func QueryKeys(req *http.Request, keyAPI api.ClientKeyAPI, device *userapi.Device) util.JSONResponse { var r queryKeysRequest resErr := httputil.UnmarshalJSONRequest(req, &r) if resErr != nil { @@ -138,7 +138,7 @@ func (r *claimKeysRequest) GetTimeout() time.Duration { return time.Duration(r.TimeoutMS) * time.Millisecond } -func ClaimKeys(req *http.Request, keyAPI api.KeyInternalAPI) util.JSONResponse { +func ClaimKeys(req *http.Request, keyAPI api.ClientKeyAPI) util.JSONResponse { var r claimKeysRequest resErr := httputil.UnmarshalJSONRequest(req, &r) if resErr != nil { diff --git a/clientapi/routing/leaveroom.go b/clientapi/routing/leaveroom.go index a34dd02d3..a71661851 100644 --- a/clientapi/routing/leaveroom.go +++ b/clientapi/routing/leaveroom.go @@ -26,7 +26,7 @@ import ( func LeaveRoomByID( req *http.Request, device *api.Device, - rsAPI roomserverAPI.RoomserverInternalAPI, + rsAPI roomserverAPI.ClientRoomserverAPI, roomID string, ) util.JSONResponse { // Prepare to ask the roomserver to perform the room join. diff --git a/clientapi/routing/login.go b/clientapi/routing/login.go index 2329df504..6017b5840 100644 --- a/clientapi/routing/login.go +++ b/clientapi/routing/login.go @@ -53,7 +53,7 @@ func passwordLogin() flows { // Login implements GET and POST /login func Login( - req *http.Request, userAPI userapi.UserInternalAPI, + req *http.Request, userAPI userapi.ClientUserAPI, cfg *config.ClientAPI, ) util.JSONResponse { if req.Method == http.MethodGet { @@ -79,7 +79,7 @@ func Login( } func completeAuth( - ctx context.Context, serverName gomatrixserverlib.ServerName, userAPI userapi.UserInternalAPI, login *auth.Login, + ctx context.Context, serverName gomatrixserverlib.ServerName, userAPI userapi.ClientUserAPI, login *auth.Login, ipAddr, userAgent string, ) util.JSONResponse { token, err := auth.GenerateAccessToken() diff --git a/clientapi/routing/logout.go b/clientapi/routing/logout.go index cfbb6f9f2..73bae7af7 100644 --- a/clientapi/routing/logout.go +++ b/clientapi/routing/logout.go @@ -24,7 +24,7 @@ import ( // Logout handles POST /logout func Logout( - req *http.Request, userAPI api.UserInternalAPI, device *api.Device, + req *http.Request, userAPI api.ClientUserAPI, device *api.Device, ) util.JSONResponse { var performRes api.PerformDeviceDeletionResponse err := userAPI.PerformDeviceDeletion(req.Context(), &api.PerformDeviceDeletionRequest{ @@ -44,7 +44,7 @@ func Logout( // LogoutAll handles POST /logout/all func LogoutAll( - req *http.Request, userAPI api.UserInternalAPI, device *api.Device, + req *http.Request, userAPI api.ClientUserAPI, device *api.Device, ) util.JSONResponse { var performRes api.PerformDeviceDeletionResponse err := userAPI.PerformDeviceDeletion(req.Context(), &api.PerformDeviceDeletionRequest{ diff --git a/clientapi/routing/membership.go b/clientapi/routing/membership.go index df8447b14..7d91c7b03 100644 --- a/clientapi/routing/membership.go +++ b/clientapi/routing/membership.go @@ -27,6 +27,7 @@ import ( "github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/clientapi/threepid" "github.com/matrix-org/dendrite/internal/eventutil" + "github.com/matrix-org/dendrite/roomserver/api" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/setup/config" userapi "github.com/matrix-org/dendrite/userapi/api" @@ -38,9 +39,9 @@ import ( var errMissingUserID = errors.New("'user_id' must be supplied") func SendBan( - req *http.Request, profileAPI userapi.UserProfileAPI, device *userapi.Device, + req *http.Request, profileAPI userapi.ClientUserAPI, device *userapi.Device, roomID string, cfg *config.ClientAPI, - rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI, + rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceQueryAPI, ) util.JSONResponse { body, evTime, roomVer, reqErr := extractRequestData(req, roomID, rsAPI) if reqErr != nil { @@ -80,10 +81,10 @@ func SendBan( return sendMembership(req.Context(), profileAPI, device, roomID, "ban", body.Reason, cfg, body.UserID, evTime, roomVer, rsAPI, asAPI) } -func sendMembership(ctx context.Context, profileAPI userapi.UserProfileAPI, device *userapi.Device, +func sendMembership(ctx context.Context, profileAPI userapi.ClientUserAPI, device *userapi.Device, roomID, membership, reason string, cfg *config.ClientAPI, targetUserID string, evTime time.Time, roomVer gomatrixserverlib.RoomVersion, - rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI) util.JSONResponse { + rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceQueryAPI) util.JSONResponse { event, err := buildMembershipEvent( ctx, targetUserID, reason, profileAPI, device, membership, @@ -124,9 +125,9 @@ func sendMembership(ctx context.Context, profileAPI userapi.UserProfileAPI, devi } func SendKick( - req *http.Request, profileAPI userapi.UserProfileAPI, device *userapi.Device, + req *http.Request, profileAPI userapi.ClientUserAPI, device *userapi.Device, roomID string, cfg *config.ClientAPI, - rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI, + rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceQueryAPI, ) util.JSONResponse { body, evTime, roomVer, reqErr := extractRequestData(req, roomID, rsAPI) if reqErr != nil { @@ -164,9 +165,9 @@ func SendKick( } func SendUnban( - req *http.Request, profileAPI userapi.UserProfileAPI, device *userapi.Device, + req *http.Request, profileAPI userapi.ClientUserAPI, device *userapi.Device, roomID string, cfg *config.ClientAPI, - rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI, + rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceQueryAPI, ) util.JSONResponse { body, evTime, roomVer, reqErr := extractRequestData(req, roomID, rsAPI) if reqErr != nil { @@ -199,9 +200,9 @@ func SendUnban( } func SendInvite( - req *http.Request, profileAPI userapi.UserProfileAPI, device *userapi.Device, + req *http.Request, profileAPI userapi.ClientUserAPI, device *userapi.Device, roomID string, cfg *config.ClientAPI, - rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI, + rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceQueryAPI, ) util.JSONResponse { body, evTime, _, reqErr := extractRequestData(req, roomID, rsAPI) if reqErr != nil { @@ -233,11 +234,11 @@ func SendInvite( // sendInvite sends an invitation to a user. Returns a JSONResponse and an error func sendInvite( ctx context.Context, - profileAPI userapi.UserProfileAPI, + profileAPI userapi.ClientUserAPI, device *userapi.Device, roomID, userID, reason string, cfg *config.ClientAPI, - rsAPI roomserverAPI.RoomserverInternalAPI, + rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceQueryAPI, evTime time.Time, ) (util.JSONResponse, error) { event, err := buildMembershipEvent( @@ -259,37 +260,36 @@ func sendInvite( return jsonerror.InternalServerError(), err } - err = roomserverAPI.SendInvite( - ctx, rsAPI, - event, - nil, // ask the roomserver to draw up invite room state for us - cfg.Matrix.ServerName, - nil, - ) - switch e := err.(type) { - case *roomserverAPI.PerformError: - return e.JSONResponse(), err - case nil: - return util.JSONResponse{ - Code: http.StatusOK, - JSON: struct{}{}, - }, nil - default: - util.GetLogger(ctx).WithError(err).Error("roomserverAPI.SendInvite failed") + var inviteRes api.PerformInviteResponse + if err := rsAPI.PerformInvite(ctx, &api.PerformInviteRequest{ + Event: event, + InviteRoomState: nil, // ask the roomserver to draw up invite room state for us + RoomVersion: event.RoomVersion, + SendAsServer: string(cfg.Matrix.ServerName), + }, &inviteRes); err != nil { + util.GetLogger(ctx).WithError(err).Error("PerformInvite failed") return util.JSONResponse{ Code: http.StatusInternalServerError, JSON: jsonerror.InternalServerError(), }, err } + if inviteRes.Error != nil { + return inviteRes.Error.JSONResponse(), inviteRes.Error + } + + return util.JSONResponse{ + Code: http.StatusOK, + JSON: struct{}{}, + }, nil } func buildMembershipEvent( ctx context.Context, - targetUserID, reason string, profileAPI userapi.UserProfileAPI, + targetUserID, reason string, profileAPI userapi.ClientUserAPI, device *userapi.Device, membership, roomID string, isDirect bool, cfg *config.ClientAPI, evTime time.Time, - rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI, + rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceQueryAPI, ) (*gomatrixserverlib.HeaderedEvent, error) { profile, err := loadProfile(ctx, targetUserID, cfg, profileAPI, asAPI) if err != nil { @@ -326,7 +326,7 @@ func loadProfile( ctx context.Context, userID string, cfg *config.ClientAPI, - profileAPI userapi.UserProfileAPI, + profileAPI userapi.ClientUserAPI, asAPI appserviceAPI.AppServiceQueryAPI, ) (*authtypes.Profile, error) { _, serverName, err := gomatrixserverlib.SplitID('@', userID) @@ -344,7 +344,7 @@ func loadProfile( return profile, err } -func extractRequestData(req *http.Request, roomID string, rsAPI roomserverAPI.RoomserverInternalAPI) ( +func extractRequestData(req *http.Request, roomID string, rsAPI roomserverAPI.ClientRoomserverAPI) ( body *threepid.MembershipRequest, evTime time.Time, roomVer gomatrixserverlib.RoomVersion, resErr *util.JSONResponse, ) { verReq := roomserverAPI.QueryRoomVersionForRoomRequest{RoomID: roomID} @@ -379,8 +379,8 @@ func checkAndProcessThreepid( device *userapi.Device, body *threepid.MembershipRequest, cfg *config.ClientAPI, - rsAPI roomserverAPI.RoomserverInternalAPI, - profileAPI userapi.UserProfileAPI, + rsAPI roomserverAPI.ClientRoomserverAPI, + profileAPI userapi.ClientUserAPI, roomID string, evTime time.Time, ) (inviteStored bool, errRes *util.JSONResponse) { @@ -418,7 +418,7 @@ func checkAndProcessThreepid( return } -func checkMemberInRoom(ctx context.Context, rsAPI roomserverAPI.RoomserverInternalAPI, userID, roomID string) *util.JSONResponse { +func checkMemberInRoom(ctx context.Context, rsAPI roomserverAPI.ClientRoomserverAPI, userID, roomID string) *util.JSONResponse { tuple := gomatrixserverlib.StateKeyTuple{ EventType: gomatrixserverlib.MRoomMember, StateKey: userID, @@ -457,7 +457,7 @@ func checkMemberInRoom(ctx context.Context, rsAPI roomserverAPI.RoomserverIntern func SendForget( req *http.Request, device *userapi.Device, - roomID string, rsAPI roomserverAPI.RoomserverInternalAPI, + roomID string, rsAPI roomserverAPI.ClientRoomserverAPI, ) util.JSONResponse { ctx := req.Context() logger := util.GetLogger(ctx).WithField("roomID", roomID).WithField("userID", device.UserID) diff --git a/clientapi/routing/memberships.go b/clientapi/routing/memberships.go index 6ddcf1be3..9bdd8a4f4 100644 --- a/clientapi/routing/memberships.go +++ b/clientapi/routing/memberships.go @@ -55,7 +55,7 @@ type databaseJoinedMember struct { func GetMemberships( req *http.Request, device *userapi.Device, roomID string, joinedOnly bool, _ *config.ClientAPI, - rsAPI api.RoomserverInternalAPI, + rsAPI api.ClientRoomserverAPI, ) util.JSONResponse { queryReq := api.QueryMembershipsForRoomRequest{ JoinedOnly: joinedOnly, @@ -100,7 +100,7 @@ func GetMemberships( func GetJoinedRooms( req *http.Request, device *userapi.Device, - rsAPI api.RoomserverInternalAPI, + rsAPI api.ClientRoomserverAPI, ) util.JSONResponse { var res api.QueryRoomsForUserResponse err := rsAPI.QueryRoomsForUser(req.Context(), &api.QueryRoomsForUserRequest{ diff --git a/clientapi/routing/notification.go b/clientapi/routing/notification.go index ee715d323..8a424a141 100644 --- a/clientapi/routing/notification.go +++ b/clientapi/routing/notification.go @@ -27,7 +27,7 @@ import ( // GetNotifications handles /_matrix/client/r0/notifications func GetNotifications( req *http.Request, device *userapi.Device, - userAPI userapi.UserInternalAPI, + userAPI userapi.ClientUserAPI, ) util.JSONResponse { var limit int64 if limitStr := req.URL.Query().Get("limit"); limitStr != "" { diff --git a/clientapi/routing/openid.go b/clientapi/routing/openid.go index 13656e288..cfb440bea 100644 --- a/clientapi/routing/openid.go +++ b/clientapi/routing/openid.go @@ -34,7 +34,7 @@ type openIDTokenResponse struct { // can supply to an OpenID Relying Party to verify their identity func CreateOpenIDToken( req *http.Request, - userAPI api.UserInternalAPI, + userAPI api.ClientUserAPI, device *api.Device, userID string, cfg *config.ClientAPI, diff --git a/clientapi/routing/password.go b/clientapi/routing/password.go index 08ce1ffa1..6dc9af508 100644 --- a/clientapi/routing/password.go +++ b/clientapi/routing/password.go @@ -28,7 +28,7 @@ type newPasswordAuth struct { func Password( req *http.Request, - userAPI api.UserInternalAPI, + userAPI api.ClientUserAPI, device *api.Device, cfg *config.ClientAPI, ) util.JSONResponse { diff --git a/clientapi/routing/peekroom.go b/clientapi/routing/peekroom.go index 41d1ff004..d0eeccf17 100644 --- a/clientapi/routing/peekroom.go +++ b/clientapi/routing/peekroom.go @@ -26,7 +26,7 @@ import ( func PeekRoomByIDOrAlias( req *http.Request, device *api.Device, - rsAPI roomserverAPI.RoomserverInternalAPI, + rsAPI roomserverAPI.ClientRoomserverAPI, roomIDOrAlias string, ) util.JSONResponse { // if this is a remote roomIDOrAlias, we have to ask the roomserver (or federation sender?) to @@ -79,7 +79,7 @@ func PeekRoomByIDOrAlias( func UnpeekRoomByID( req *http.Request, device *api.Device, - rsAPI roomserverAPI.RoomserverInternalAPI, + rsAPI roomserverAPI.ClientRoomserverAPI, roomID string, ) util.JSONResponse { unpeekReq := roomserverAPI.PerformUnpeekRequest{ diff --git a/clientapi/routing/profile.go b/clientapi/routing/profile.go index 3f91b4c93..97f86afe2 100644 --- a/clientapi/routing/profile.go +++ b/clientapi/routing/profile.go @@ -35,7 +35,7 @@ import ( // GetProfile implements GET /profile/{userID} func GetProfile( - req *http.Request, profileAPI userapi.UserProfileAPI, cfg *config.ClientAPI, + req *http.Request, profileAPI userapi.ClientUserAPI, cfg *config.ClientAPI, userID string, asAPI appserviceAPI.AppServiceQueryAPI, federation *gomatrixserverlib.FederationClient, @@ -64,7 +64,7 @@ func GetProfile( // GetAvatarURL implements GET /profile/{userID}/avatar_url func GetAvatarURL( - req *http.Request, profileAPI userapi.UserProfileAPI, cfg *config.ClientAPI, + req *http.Request, profileAPI userapi.ClientUserAPI, cfg *config.ClientAPI, userID string, asAPI appserviceAPI.AppServiceQueryAPI, federation *gomatrixserverlib.FederationClient, ) util.JSONResponse { @@ -91,8 +91,8 @@ func GetAvatarURL( // SetAvatarURL implements PUT /profile/{userID}/avatar_url func SetAvatarURL( - req *http.Request, profileAPI userapi.UserProfileAPI, - device *userapi.Device, userID string, cfg *config.ClientAPI, rsAPI api.RoomserverInternalAPI, + req *http.Request, profileAPI userapi.ClientUserAPI, + device *userapi.Device, userID string, cfg *config.ClientAPI, rsAPI api.ClientRoomserverAPI, ) util.JSONResponse { if userID != device.UserID { return util.JSONResponse{ @@ -193,7 +193,7 @@ func SetAvatarURL( // GetDisplayName implements GET /profile/{userID}/displayname func GetDisplayName( - req *http.Request, profileAPI userapi.UserProfileAPI, cfg *config.ClientAPI, + req *http.Request, profileAPI userapi.ClientUserAPI, cfg *config.ClientAPI, userID string, asAPI appserviceAPI.AppServiceQueryAPI, federation *gomatrixserverlib.FederationClient, ) util.JSONResponse { @@ -220,8 +220,8 @@ func GetDisplayName( // SetDisplayName implements PUT /profile/{userID}/displayname func SetDisplayName( - req *http.Request, profileAPI userapi.UserProfileAPI, - device *userapi.Device, userID string, cfg *config.ClientAPI, rsAPI api.RoomserverInternalAPI, + req *http.Request, profileAPI userapi.ClientUserAPI, + device *userapi.Device, userID string, cfg *config.ClientAPI, rsAPI api.ClientRoomserverAPI, ) util.JSONResponse { if userID != device.UserID { return util.JSONResponse{ @@ -325,7 +325,7 @@ func SetDisplayName( // Returns an error when something goes wrong or specifically // eventutil.ErrProfileNoExists when the profile doesn't exist. func getProfile( - ctx context.Context, profileAPI userapi.UserProfileAPI, cfg *config.ClientAPI, + ctx context.Context, profileAPI userapi.ClientUserAPI, cfg *config.ClientAPI, userID string, asAPI appserviceAPI.AppServiceQueryAPI, federation *gomatrixserverlib.FederationClient, @@ -366,7 +366,7 @@ func buildMembershipEvents( ctx context.Context, roomIDs []string, newProfile authtypes.Profile, userID string, cfg *config.ClientAPI, - evTime time.Time, rsAPI api.RoomserverInternalAPI, + evTime time.Time, rsAPI api.ClientRoomserverAPI, ) ([]*gomatrixserverlib.HeaderedEvent, error) { evs := []*gomatrixserverlib.HeaderedEvent{} diff --git a/clientapi/routing/pusher.go b/clientapi/routing/pusher.go index 9d6bef8bd..d6a6eb936 100644 --- a/clientapi/routing/pusher.go +++ b/clientapi/routing/pusher.go @@ -28,7 +28,7 @@ import ( // GetPushers handles /_matrix/client/r0/pushers func GetPushers( req *http.Request, device *userapi.Device, - userAPI userapi.UserInternalAPI, + userAPI userapi.ClientUserAPI, ) util.JSONResponse { var queryRes userapi.QueryPushersResponse localpart, _, err := gomatrixserverlib.SplitID('@', device.UserID) @@ -57,7 +57,7 @@ func GetPushers( // The behaviour of this endpoint varies depending on the values in the JSON body. func SetPusher( req *http.Request, device *userapi.Device, - userAPI userapi.UserInternalAPI, + userAPI userapi.ClientUserAPI, ) util.JSONResponse { localpart, _, err := gomatrixserverlib.SplitID('@', device.UserID) if err != nil { diff --git a/clientapi/routing/pushrules.go b/clientapi/routing/pushrules.go index 81a33b25a..856f52c75 100644 --- a/clientapi/routing/pushrules.go +++ b/clientapi/routing/pushrules.go @@ -30,7 +30,7 @@ func errorResponse(ctx context.Context, err error, msg string, args ...interface return jsonerror.InternalServerError() } -func GetAllPushRules(ctx context.Context, device *userapi.Device, userAPI userapi.UserInternalAPI) util.JSONResponse { +func GetAllPushRules(ctx context.Context, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) if err != nil { return errorResponse(ctx, err, "queryPushRulesJSON failed") @@ -41,7 +41,7 @@ func GetAllPushRules(ctx context.Context, device *userapi.Device, userAPI userap } } -func GetPushRulesByScope(ctx context.Context, scope string, device *userapi.Device, userAPI userapi.UserInternalAPI) util.JSONResponse { +func GetPushRulesByScope(ctx context.Context, scope string, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) if err != nil { return errorResponse(ctx, err, "queryPushRulesJSON failed") @@ -56,7 +56,7 @@ func GetPushRulesByScope(ctx context.Context, scope string, device *userapi.Devi } } -func GetPushRulesByKind(ctx context.Context, scope, kind string, device *userapi.Device, userAPI userapi.UserInternalAPI) util.JSONResponse { +func GetPushRulesByKind(ctx context.Context, scope, kind string, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) if err != nil { return errorResponse(ctx, err, "queryPushRules failed") @@ -75,7 +75,7 @@ func GetPushRulesByKind(ctx context.Context, scope, kind string, device *userapi } } -func GetPushRuleByRuleID(ctx context.Context, scope, kind, ruleID string, device *userapi.Device, userAPI userapi.UserInternalAPI) util.JSONResponse { +func GetPushRuleByRuleID(ctx context.Context, scope, kind, ruleID string, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) if err != nil { return errorResponse(ctx, err, "queryPushRules failed") @@ -98,7 +98,7 @@ func GetPushRuleByRuleID(ctx context.Context, scope, kind, ruleID string, device } } -func PutPushRuleByRuleID(ctx context.Context, scope, kind, ruleID, afterRuleID, beforeRuleID string, body io.Reader, device *userapi.Device, userAPI userapi.UserInternalAPI) util.JSONResponse { +func PutPushRuleByRuleID(ctx context.Context, scope, kind, ruleID, afterRuleID, beforeRuleID string, body io.Reader, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { var newRule pushrules.Rule if err := json.NewDecoder(body).Decode(&newRule); err != nil { return errorResponse(ctx, err, "JSON Decode failed") @@ -160,7 +160,7 @@ func PutPushRuleByRuleID(ctx context.Context, scope, kind, ruleID, afterRuleID, return util.JSONResponse{Code: http.StatusOK, JSON: struct{}{}} } -func DeletePushRuleByRuleID(ctx context.Context, scope, kind, ruleID string, device *userapi.Device, userAPI userapi.UserInternalAPI) util.JSONResponse { +func DeletePushRuleByRuleID(ctx context.Context, scope, kind, ruleID string, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { ruleSets, err := queryPushRules(ctx, device.UserID, userAPI) if err != nil { return errorResponse(ctx, err, "queryPushRules failed") @@ -187,7 +187,7 @@ func DeletePushRuleByRuleID(ctx context.Context, scope, kind, ruleID string, dev return util.JSONResponse{Code: http.StatusOK, JSON: struct{}{}} } -func GetPushRuleAttrByRuleID(ctx context.Context, scope, kind, ruleID, attr string, device *userapi.Device, userAPI userapi.UserInternalAPI) util.JSONResponse { +func GetPushRuleAttrByRuleID(ctx context.Context, scope, kind, ruleID, attr string, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { attrGet, err := pushRuleAttrGetter(attr) if err != nil { return errorResponse(ctx, err, "pushRuleAttrGetter failed") @@ -216,7 +216,7 @@ func GetPushRuleAttrByRuleID(ctx context.Context, scope, kind, ruleID, attr stri } } -func PutPushRuleAttrByRuleID(ctx context.Context, scope, kind, ruleID, attr string, body io.Reader, device *userapi.Device, userAPI userapi.UserInternalAPI) util.JSONResponse { +func PutPushRuleAttrByRuleID(ctx context.Context, scope, kind, ruleID, attr string, body io.Reader, device *userapi.Device, userAPI userapi.ClientUserAPI) util.JSONResponse { var newPartialRule pushrules.Rule if err := json.NewDecoder(body).Decode(&newPartialRule); err != nil { return util.JSONResponse{ @@ -266,7 +266,7 @@ func PutPushRuleAttrByRuleID(ctx context.Context, scope, kind, ruleID, attr stri return util.JSONResponse{Code: http.StatusOK, JSON: struct{}{}} } -func queryPushRules(ctx context.Context, userID string, userAPI userapi.UserInternalAPI) (*pushrules.AccountRuleSets, error) { +func queryPushRules(ctx context.Context, userID string, userAPI userapi.ClientUserAPI) (*pushrules.AccountRuleSets, error) { var res userapi.QueryPushRulesResponse if err := userAPI.QueryPushRules(ctx, &userapi.QueryPushRulesRequest{UserID: userID}, &res); err != nil { util.GetLogger(ctx).WithError(err).Error("userAPI.QueryPushRules failed") @@ -275,7 +275,7 @@ func queryPushRules(ctx context.Context, userID string, userAPI userapi.UserInte return res.RuleSets, nil } -func putPushRules(ctx context.Context, userID string, ruleSets *pushrules.AccountRuleSets, userAPI userapi.UserInternalAPI) error { +func putPushRules(ctx context.Context, userID string, ruleSets *pushrules.AccountRuleSets, userAPI userapi.ClientUserAPI) error { req := userapi.PerformPushRulesPutRequest{ UserID: userID, RuleSets: ruleSets, diff --git a/clientapi/routing/redaction.go b/clientapi/routing/redaction.go index e8d14ce34..27f0ba5d0 100644 --- a/clientapi/routing/redaction.go +++ b/clientapi/routing/redaction.go @@ -40,7 +40,7 @@ type redactionResponse struct { func SendRedaction( req *http.Request, device *userapi.Device, roomID, eventID string, cfg *config.ClientAPI, - rsAPI roomserverAPI.RoomserverInternalAPI, + rsAPI roomserverAPI.ClientRoomserverAPI, txnID *string, txnCache *transactions.Cache, ) util.JSONResponse { diff --git a/clientapi/routing/register.go b/clientapi/routing/register.go index 8253f3155..eba4920c6 100644 --- a/clientapi/routing/register.go +++ b/clientapi/routing/register.go @@ -518,7 +518,7 @@ func validateApplicationService( // http://matrix.org/speculator/spec/HEAD/client_server/unstable.html#post-matrix-client-unstable-register func Register( req *http.Request, - userAPI userapi.UserRegisterAPI, + userAPI userapi.ClientUserAPI, cfg *config.ClientAPI, ) util.JSONResponse { defer req.Body.Close() // nolint: errcheck @@ -614,7 +614,7 @@ func handleGuestRegistration( req *http.Request, r registerRequest, cfg *config.ClientAPI, - userAPI userapi.UserRegisterAPI, + userAPI userapi.ClientUserAPI, ) util.JSONResponse { if cfg.RegistrationDisabled || cfg.GuestsDisabled { return util.JSONResponse{ @@ -679,7 +679,7 @@ func handleRegistrationFlow( r registerRequest, sessionID string, cfg *config.ClientAPI, - userAPI userapi.UserRegisterAPI, + userAPI userapi.ClientUserAPI, accessToken string, accessTokenErr error, ) util.JSONResponse { @@ -768,7 +768,7 @@ func handleApplicationServiceRegistration( req *http.Request, r registerRequest, cfg *config.ClientAPI, - userAPI userapi.UserRegisterAPI, + userAPI userapi.ClientUserAPI, ) util.JSONResponse { // Check if we previously had issues extracting the access token from the // request. @@ -806,7 +806,7 @@ func checkAndCompleteFlow( r registerRequest, sessionID string, cfg *config.ClientAPI, - userAPI userapi.UserRegisterAPI, + userAPI userapi.ClientUserAPI, ) util.JSONResponse { if checkFlowCompleted(flow, cfg.Derived.Registration.Flows) { // This flow was completed, registration can continue @@ -833,7 +833,7 @@ func checkAndCompleteFlow( // not all func completeRegistration( ctx context.Context, - userAPI userapi.UserRegisterAPI, + userAPI userapi.ClientUserAPI, username, password, appserviceID, ipAddr, userAgent, sessionID string, inhibitLogin eventutil.WeakBoolean, displayName, deviceID *string, @@ -992,7 +992,7 @@ type availableResponse struct { func RegisterAvailable( req *http.Request, cfg *config.ClientAPI, - registerAPI userapi.UserRegisterAPI, + registerAPI userapi.ClientUserAPI, ) util.JSONResponse { username := req.URL.Query().Get("username") @@ -1040,7 +1040,7 @@ func RegisterAvailable( } } -func handleSharedSecretRegistration(userAPI userapi.UserInternalAPI, sr *SharedSecretRegistration, req *http.Request) util.JSONResponse { +func handleSharedSecretRegistration(userAPI userapi.ClientUserAPI, sr *SharedSecretRegistration, req *http.Request) util.JSONResponse { ssrr, err := NewSharedSecretRegistrationRequest(req.Body) if err != nil { return util.JSONResponse{ diff --git a/clientapi/routing/room_tagging.go b/clientapi/routing/room_tagging.go index ce173613e..039289569 100644 --- a/clientapi/routing/room_tagging.go +++ b/clientapi/routing/room_tagging.go @@ -31,7 +31,7 @@ import ( // GetTags implements GET /_matrix/client/r0/user/{userID}/rooms/{roomID}/tags func GetTags( req *http.Request, - userAPI api.UserInternalAPI, + userAPI api.ClientUserAPI, device *api.Device, userID string, roomID string, @@ -62,7 +62,7 @@ func GetTags( // the tag to the "map" and saving the new "map" to the DB func PutTag( req *http.Request, - userAPI api.UserInternalAPI, + userAPI api.ClientUserAPI, device *api.Device, userID string, roomID string, @@ -113,7 +113,7 @@ func PutTag( // the "map" and then saving the new "map" in the DB func DeleteTag( req *http.Request, - userAPI api.UserInternalAPI, + userAPI api.ClientUserAPI, device *api.Device, userID string, roomID string, @@ -167,7 +167,7 @@ func obtainSavedTags( req *http.Request, userID string, roomID string, - userAPI api.UserInternalAPI, + userAPI api.ClientUserAPI, ) (tags gomatrix.TagContent, err error) { dataReq := api.QueryAccountDataRequest{ UserID: userID, @@ -194,7 +194,7 @@ func saveTagData( req *http.Request, userID string, roomID string, - userAPI api.UserInternalAPI, + userAPI api.ClientUserAPI, Tag gomatrix.TagContent, ) error { newTagData, err := json.Marshal(Tag) diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go index ba1c76b81..6da467073 100644 --- a/clientapi/routing/routing.go +++ b/clientapi/routing/routing.go @@ -50,15 +50,15 @@ import ( func Setup( publicAPIMux, synapseAdminRouter, dendriteAdminRouter *mux.Router, cfg *config.ClientAPI, - rsAPI roomserverAPI.RoomserverInternalAPI, + rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceQueryAPI, - userAPI userapi.UserInternalAPI, + userAPI userapi.ClientUserAPI, userDirectoryProvider userapi.UserDirectoryProvider, federation *gomatrixserverlib.FederationClient, syncProducer *producers.SyncAPIProducer, transactionsCache *transactions.Cache, - federationSender federationAPI.FederationInternalAPI, - keyAPI keyserverAPI.KeyInternalAPI, + federationSender federationAPI.ClientFederationAPI, + keyAPI keyserverAPI.ClientKeyAPI, extRoomsProvider api.ExtraPublicRoomsProvider, mscCfg *config.MSCs, natsClient *nats.Conn, ) { @@ -325,7 +325,7 @@ func Setup( if err != nil { return util.ErrorResponse(err) } - return GetEvent(req, device, vars["roomID"], vars["eventID"], cfg, rsAPI, federation) + return GetEvent(req, device, vars["roomID"], vars["eventID"], cfg, rsAPI) }), ).Methods(http.MethodGet, http.MethodOptions) @@ -897,7 +897,7 @@ func Setup( if resErr := clientutil.UnmarshalJSONRequest(req, &postContent); resErr != nil { return *resErr } - return *SearchUserDirectory( + return SearchUserDirectory( req.Context(), device, userAPI, diff --git a/clientapi/routing/sendevent.go b/clientapi/routing/sendevent.go index 1211fa72d..5f84739d0 100644 --- a/clientapi/routing/sendevent.go +++ b/clientapi/routing/sendevent.go @@ -70,7 +70,7 @@ func SendEvent( device *userapi.Device, roomID, eventType string, txnID, stateKey *string, cfg *config.ClientAPI, - rsAPI api.RoomserverInternalAPI, + rsAPI api.ClientRoomserverAPI, txnCache *transactions.Cache, ) util.JSONResponse { verReq := api.QueryRoomVersionForRoomRequest{RoomID: roomID} @@ -207,7 +207,7 @@ func generateSendEvent( device *userapi.Device, roomID, eventType string, stateKey *string, cfg *config.ClientAPI, - rsAPI api.RoomserverInternalAPI, + rsAPI api.ClientRoomserverAPI, evTime time.Time, ) (*gomatrixserverlib.Event, *util.JSONResponse) { // parse the incoming http request diff --git a/clientapi/routing/sendtyping.go b/clientapi/routing/sendtyping.go index 6a27ee615..3f92e4227 100644 --- a/clientapi/routing/sendtyping.go +++ b/clientapi/routing/sendtyping.go @@ -32,7 +32,7 @@ type typingContentJSON struct { // sends the typing events to client API typingProducer func SendTyping( req *http.Request, device *userapi.Device, roomID string, - userID string, rsAPI roomserverAPI.RoomserverInternalAPI, + userID string, rsAPI roomserverAPI.ClientRoomserverAPI, syncProducer *producers.SyncAPIProducer, ) util.JSONResponse { if device.UserID != userID { diff --git a/clientapi/routing/server_notices.go b/clientapi/routing/server_notices.go index 47b0da7bb..9c34f2e1c 100644 --- a/clientapi/routing/server_notices.go +++ b/clientapi/routing/server_notices.go @@ -56,8 +56,8 @@ func SendServerNotice( req *http.Request, cfgNotices *config.ServerNotices, cfgClient *config.ClientAPI, - userAPI userapi.UserInternalAPI, - rsAPI api.RoomserverInternalAPI, + userAPI userapi.ClientUserAPI, + rsAPI api.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceQueryAPI, device *userapi.Device, senderDevice *userapi.Device, @@ -276,7 +276,7 @@ func (r sendServerNoticeRequest) valid() (ok bool) { // It returns an userapi.Device, which is used for building the event func getSenderDevice( ctx context.Context, - userAPI userapi.UserInternalAPI, + userAPI userapi.ClientUserAPI, cfg *config.ClientAPI, ) (*userapi.Device, error) { var accRes userapi.PerformAccountCreationResponse diff --git a/clientapi/routing/state.go b/clientapi/routing/state.go index d25ee8237..c6e9e91d0 100644 --- a/clientapi/routing/state.go +++ b/clientapi/routing/state.go @@ -41,7 +41,7 @@ type stateEventInStateResp struct { // TODO: Check if the user is in the room. If not, check if the room's history // is publicly visible. Current behaviour is returning an empty array if the // user cannot see the room's history. -func OnIncomingStateRequest(ctx context.Context, device *userapi.Device, rsAPI api.RoomserverInternalAPI, roomID string) util.JSONResponse { +func OnIncomingStateRequest(ctx context.Context, device *userapi.Device, rsAPI api.ClientRoomserverAPI, roomID string) util.JSONResponse { var worldReadable bool var wantLatestState bool @@ -162,7 +162,7 @@ func OnIncomingStateRequest(ctx context.Context, device *userapi.Device, rsAPI a // is then (by default) we return the content, otherwise a 404. // If eventFormat=true, sends the whole event else just the content. func OnIncomingStateTypeRequest( - ctx context.Context, device *userapi.Device, rsAPI api.RoomserverInternalAPI, + ctx context.Context, device *userapi.Device, rsAPI api.ClientRoomserverAPI, roomID, evType, stateKey string, eventFormat bool, ) util.JSONResponse { var worldReadable bool diff --git a/clientapi/routing/threepid.go b/clientapi/routing/threepid.go index a4898ca46..94b658ee3 100644 --- a/clientapi/routing/threepid.go +++ b/clientapi/routing/threepid.go @@ -40,7 +40,7 @@ type threePIDsResponse struct { // RequestEmailToken implements: // POST /account/3pid/email/requestToken // POST /register/email/requestToken -func RequestEmailToken(req *http.Request, threePIDAPI api.UserThreePIDAPI, cfg *config.ClientAPI) util.JSONResponse { +func RequestEmailToken(req *http.Request, threePIDAPI api.ClientUserAPI, cfg *config.ClientAPI) util.JSONResponse { var body threepid.EmailAssociationRequest if reqErr := httputil.UnmarshalJSONRequest(req, &body); reqErr != nil { return *reqErr @@ -90,7 +90,7 @@ func RequestEmailToken(req *http.Request, threePIDAPI api.UserThreePIDAPI, cfg * // CheckAndSave3PIDAssociation implements POST /account/3pid func CheckAndSave3PIDAssociation( - req *http.Request, threePIDAPI api.UserThreePIDAPI, device *api.Device, + req *http.Request, threePIDAPI api.ClientUserAPI, device *api.Device, cfg *config.ClientAPI, ) util.JSONResponse { var body threepid.EmailAssociationCheckRequest @@ -158,7 +158,7 @@ func CheckAndSave3PIDAssociation( // GetAssociated3PIDs implements GET /account/3pid func GetAssociated3PIDs( - req *http.Request, threepidAPI api.UserThreePIDAPI, device *api.Device, + req *http.Request, threepidAPI api.ClientUserAPI, device *api.Device, ) util.JSONResponse { localpart, _, err := gomatrixserverlib.SplitID('@', device.UserID) if err != nil { @@ -182,7 +182,7 @@ func GetAssociated3PIDs( } // Forget3PID implements POST /account/3pid/delete -func Forget3PID(req *http.Request, threepidAPI api.UserThreePIDAPI) util.JSONResponse { +func Forget3PID(req *http.Request, threepidAPI api.ClientUserAPI) util.JSONResponse { var body authtypes.ThreePID if reqErr := httputil.UnmarshalJSONRequest(req, &body); reqErr != nil { return *reqErr diff --git a/clientapi/routing/upgrade_room.go b/clientapi/routing/upgrade_room.go index 00bde36b3..505bf8f53 100644 --- a/clientapi/routing/upgrade_room.go +++ b/clientapi/routing/upgrade_room.go @@ -40,8 +40,8 @@ type upgradeRoomResponse struct { func UpgradeRoom( req *http.Request, device *userapi.Device, cfg *config.ClientAPI, - roomID string, profileAPI userapi.UserProfileAPI, - rsAPI roomserverAPI.RoomserverInternalAPI, + roomID string, profileAPI userapi.ClientUserAPI, + rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceQueryAPI, ) util.JSONResponse { var r upgradeRoomRequest diff --git a/clientapi/routing/userdirectory.go b/clientapi/routing/userdirectory.go index ab73cf430..4d41da734 100644 --- a/clientapi/routing/userdirectory.go +++ b/clientapi/routing/userdirectory.go @@ -34,13 +34,13 @@ type UserDirectoryResponse struct { func SearchUserDirectory( ctx context.Context, device *userapi.Device, - userAPI userapi.UserInternalAPI, - rsAPI api.RoomserverInternalAPI, + userAPI userapi.ClientUserAPI, + rsAPI api.ClientRoomserverAPI, provider userapi.UserDirectoryProvider, serverName gomatrixserverlib.ServerName, searchString string, limit int, -) *util.JSONResponse { +) util.JSONResponse { if limit < 10 { limit = 10 } @@ -58,8 +58,7 @@ func SearchUserDirectory( } userRes := &userapi.QuerySearchProfilesResponse{} if err := provider.QuerySearchProfiles(ctx, userReq, userRes); err != nil { - errRes := util.ErrorResponse(fmt.Errorf("userAPI.QuerySearchProfiles: %w", err)) - return &errRes + return util.ErrorResponse(fmt.Errorf("userAPI.QuerySearchProfiles: %w", err)) } for _, user := range userRes.Profiles { @@ -94,8 +93,7 @@ func SearchUserDirectory( } stateRes := &api.QueryKnownUsersResponse{} if err := rsAPI.QueryKnownUsers(ctx, stateReq, stateRes); err != nil && err != sql.ErrNoRows { - errRes := util.ErrorResponse(fmt.Errorf("rsAPI.QueryKnownUsers: %w", err)) - return &errRes + return util.ErrorResponse(fmt.Errorf("rsAPI.QueryKnownUsers: %w", err)) } for _, user := range stateRes.Users { @@ -114,7 +112,7 @@ func SearchUserDirectory( response.Results = append(response.Results, result) } - return &util.JSONResponse{ + return util.JSONResponse{ Code: 200, JSON: response, } diff --git a/clientapi/threepid/invites.go b/clientapi/threepid/invites.go index eee6992f8..6e7426a7f 100644 --- a/clientapi/threepid/invites.go +++ b/clientapi/threepid/invites.go @@ -86,7 +86,7 @@ var ( func CheckAndProcessInvite( ctx context.Context, device *userapi.Device, body *MembershipRequest, cfg *config.ClientAPI, - rsAPI api.RoomserverInternalAPI, db userapi.UserProfileAPI, + rsAPI api.ClientRoomserverAPI, db userapi.ClientUserAPI, roomID string, evTime time.Time, ) (inviteStoredOnIDServer bool, err error) { @@ -136,7 +136,7 @@ func CheckAndProcessInvite( // Returns an error if a check or a request failed. func queryIDServer( ctx context.Context, - db userapi.UserProfileAPI, cfg *config.ClientAPI, device *userapi.Device, + userAPI userapi.ClientUserAPI, cfg *config.ClientAPI, device *userapi.Device, body *MembershipRequest, roomID string, ) (lookupRes *idServerLookupResponse, storeInviteRes *idServerStoreInviteResponse, err error) { if err = isTrusted(body.IDServer, cfg); err != nil { @@ -152,7 +152,7 @@ func queryIDServer( if lookupRes.MXID == "" { // No Matrix ID matches with the given 3PID, ask the server to store the // invite and return a token - storeInviteRes, err = queryIDServerStoreInvite(ctx, db, cfg, device, body, roomID) + storeInviteRes, err = queryIDServerStoreInvite(ctx, userAPI, cfg, device, body, roomID) return } @@ -163,7 +163,7 @@ func queryIDServer( if lookupRes.NotBefore > now || now > lookupRes.NotAfter { // If the current timestamp isn't in the time frame in which the association // is known to be valid, re-run the query - return queryIDServer(ctx, db, cfg, device, body, roomID) + return queryIDServer(ctx, userAPI, cfg, device, body, roomID) } // Check the request signatures and send an error if one isn't valid @@ -205,7 +205,7 @@ func queryIDServerLookup(ctx context.Context, body *MembershipRequest) (*idServe // Returns an error if the request failed to send or if the response couldn't be parsed. func queryIDServerStoreInvite( ctx context.Context, - db userapi.UserProfileAPI, cfg *config.ClientAPI, device *userapi.Device, + userAPI userapi.ClientUserAPI, cfg *config.ClientAPI, device *userapi.Device, body *MembershipRequest, roomID string, ) (*idServerStoreInviteResponse, error) { // Retrieve the sender's profile to get their display name @@ -217,7 +217,7 @@ func queryIDServerStoreInvite( var profile *authtypes.Profile if serverName == cfg.Matrix.ServerName { res := &userapi.QueryProfileResponse{} - err = db.QueryProfile(ctx, &userapi.QueryProfileRequest{UserID: device.UserID}, res) + err = userAPI.QueryProfile(ctx, &userapi.QueryProfileRequest{UserID: device.UserID}, res) if err != nil { return nil, err } @@ -337,7 +337,7 @@ func emit3PIDInviteEvent( ctx context.Context, body *MembershipRequest, res *idServerStoreInviteResponse, device *userapi.Device, roomID string, cfg *config.ClientAPI, - rsAPI api.RoomserverInternalAPI, + rsAPI api.ClientRoomserverAPI, evTime time.Time, ) error { builder := &gomatrixserverlib.EventBuilder{ diff --git a/federationapi/api/api.go b/federationapi/api/api.go index 4d6b0211c..ce6a0f2ed 100644 --- a/federationapi/api/api.go +++ b/federationapi/api/api.go @@ -42,6 +42,7 @@ func (e *FederationClientError) Error() string { type FederationInternalAPI interface { FederationClient gomatrixserverlib.KeyDatabase + ClientFederationAPI KeyRing() *gomatrixserverlib.KeyRing @@ -100,6 +101,10 @@ type FederationInternalAPI interface { ) error } +type ClientFederationAPI interface { + QueryJoinedHostServerNamesInRoom(ctx context.Context, request *QueryJoinedHostServerNamesInRoomRequest, response *QueryJoinedHostServerNamesInRoomResponse) error +} + type QueryServerKeysRequest struct { ServerName gomatrixserverlib.ServerName KeyIDToCriteria map[gomatrixserverlib.KeyID]gomatrixserverlib.PublicKeyNotaryQueryCriteria diff --git a/federationapi/routing/invite.go b/federationapi/routing/invite.go index 58bf99f4a..25faff0cb 100644 --- a/federationapi/routing/invite.go +++ b/federationapi/routing/invite.go @@ -166,31 +166,36 @@ func processInvite( ) // Add the invite event to the roomserver. - err = api.SendInvite( - ctx, rsAPI, signedEvent.Headered(roomVer), strippedState, api.DoNotSendToOtherServers, nil, - ) - switch e := err.(type) { - case *api.PerformError: - return e.JSONResponse() - case nil: - // Return the signed event to the originating server, it should then tell - // the other servers in the room that we have been invited. - if isInviteV2 { - return util.JSONResponse{ - Code: http.StatusOK, - JSON: gomatrixserverlib.RespInviteV2{Event: signedEvent.JSON()}, - } - } else { - return util.JSONResponse{ - Code: http.StatusOK, - JSON: gomatrixserverlib.RespInvite{Event: signedEvent.JSON()}, - } - } - default: - util.GetLogger(ctx).WithError(err).Error("api.SendInvite failed") + inviteEvent := signedEvent.Headered(roomVer) + request := &api.PerformInviteRequest{ + Event: inviteEvent, + InviteRoomState: strippedState, + RoomVersion: inviteEvent.RoomVersion, + SendAsServer: string(api.DoNotSendToOtherServers), + TransactionID: nil, + } + response := &api.PerformInviteResponse{} + if err := rsAPI.PerformInvite(ctx, request, response); err != nil { + util.GetLogger(ctx).WithError(err).Error("PerformInvite failed") return util.JSONResponse{ Code: http.StatusInternalServerError, JSON: jsonerror.InternalServerError(), } } + if response.Error != nil { + return response.Error.JSONResponse() + } + // Return the signed event to the originating server, it should then tell + // the other servers in the room that we have been invited. + if isInviteV2 { + return util.JSONResponse{ + Code: http.StatusOK, + JSON: gomatrixserverlib.RespInviteV2{Event: signedEvent.JSON()}, + } + } else { + return util.JSONResponse{ + Code: http.StatusOK, + JSON: gomatrixserverlib.RespInvite{Event: signedEvent.JSON()}, + } + } } diff --git a/internal/eventutil/events.go b/internal/eventutil/events.go index 47c83d515..ee67a6daf 100644 --- a/internal/eventutil/events.go +++ b/internal/eventutil/events.go @@ -39,7 +39,7 @@ var ErrRoomNoExists = errors.New("room does not exist") func QueryAndBuildEvent( ctx context.Context, builder *gomatrixserverlib.EventBuilder, cfg *config.Global, evTime time.Time, - rsAPI api.RoomserverInternalAPI, queryRes *api.QueryLatestEventsAndStateResponse, + rsAPI api.QueryLatestEventsAndStateAPI, queryRes *api.QueryLatestEventsAndStateResponse, ) (*gomatrixserverlib.HeaderedEvent, error) { if queryRes == nil { queryRes = &api.QueryLatestEventsAndStateResponse{} @@ -80,7 +80,7 @@ func BuildEvent( func queryRequiredEventsForBuilder( ctx context.Context, builder *gomatrixserverlib.EventBuilder, - rsAPI api.RoomserverInternalAPI, queryRes *api.QueryLatestEventsAndStateResponse, + rsAPI api.QueryLatestEventsAndStateAPI, queryRes *api.QueryLatestEventsAndStateResponse, ) (*gomatrixserverlib.StateNeeded, error) { eventsNeeded, err := gomatrixserverlib.StateNeededForEventBuilder(builder) if err != nil { diff --git a/keyserver/api/api.go b/keyserver/api/api.go index ce651ba4e..5564eb271 100644 --- a/keyserver/api/api.go +++ b/keyserver/api/api.go @@ -28,21 +28,27 @@ import ( type KeyInternalAPI interface { SyncKeyAPI + ClientKeyAPI // SetUserAPI assigns a user API to query when extracting device names. SetUserAPI(i userapi.UserInternalAPI) // InputDeviceListUpdate from a federated server EDU InputDeviceListUpdate(ctx context.Context, req *InputDeviceListUpdateRequest, res *InputDeviceListUpdateResponse) - PerformUploadKeys(ctx context.Context, req *PerformUploadKeysRequest, res *PerformUploadKeysResponse) - // PerformClaimKeys claims one-time keys for use in pre-key messages - PerformClaimKeys(ctx context.Context, req *PerformClaimKeysRequest, res *PerformClaimKeysResponse) + PerformDeleteKeys(ctx context.Context, req *PerformDeleteKeysRequest, res *PerformDeleteKeysResponse) - PerformUploadDeviceKeys(ctx context.Context, req *PerformUploadDeviceKeysRequest, res *PerformUploadDeviceKeysResponse) - PerformUploadDeviceSignatures(ctx context.Context, req *PerformUploadDeviceSignaturesRequest, res *PerformUploadDeviceSignaturesResponse) - QueryKeys(ctx context.Context, req *QueryKeysRequest, res *QueryKeysResponse) QueryDeviceMessages(ctx context.Context, req *QueryDeviceMessagesRequest, res *QueryDeviceMessagesResponse) QuerySignatures(ctx context.Context, req *QuerySignaturesRequest, res *QuerySignaturesResponse) } +// API functions required by the clientapi +type ClientKeyAPI interface { + QueryKeys(ctx context.Context, req *QueryKeysRequest, res *QueryKeysResponse) + PerformUploadKeys(ctx context.Context, req *PerformUploadKeysRequest, res *PerformUploadKeysResponse) + PerformUploadDeviceKeys(ctx context.Context, req *PerformUploadDeviceKeysRequest, res *PerformUploadDeviceKeysResponse) + PerformUploadDeviceSignatures(ctx context.Context, req *PerformUploadDeviceSignaturesRequest, res *PerformUploadDeviceSignaturesResponse) + // PerformClaimKeys claims one-time keys for use in pre-key messages + PerformClaimKeys(ctx context.Context, req *PerformClaimKeysRequest, res *PerformClaimKeysResponse) +} + // API functions required by the syncapi type SyncKeyAPI interface { QueryKeyChanges(ctx context.Context, req *QueryKeyChangesRequest, res *QueryKeyChangesResponse) diff --git a/mediaapi/mediaapi.go b/mediaapi/mediaapi.go index 5976957ca..4792c996d 100644 --- a/mediaapi/mediaapi.go +++ b/mediaapi/mediaapi.go @@ -26,7 +26,7 @@ import ( // AddPublicRoutes sets up and registers HTTP handlers for the MediaAPI component. func AddPublicRoutes( base *base.BaseDendrite, - userAPI userapi.UserInternalAPI, + userAPI userapi.MediaUserAPI, client *gomatrixserverlib.Client, ) { cfg := &base.Cfg.MediaAPI diff --git a/mediaapi/routing/routing.go b/mediaapi/routing/routing.go index 97dfd3341..76f07415b 100644 --- a/mediaapi/routing/routing.go +++ b/mediaapi/routing/routing.go @@ -48,7 +48,7 @@ func Setup( cfg *config.MediaAPI, rateLimit *config.RateLimiting, db storage.Database, - userAPI userapi.UserInternalAPI, + userAPI userapi.MediaUserAPI, client *gomatrixserverlib.Client, ) { rateLimits := httputil.NewRateLimits(rateLimit) diff --git a/roomserver/api/api.go b/roomserver/api/api.go index 2e4ec3ffd..33c3d157b 100644 --- a/roomserver/api/api.go +++ b/roomserver/api/api.go @@ -12,7 +12,13 @@ import ( // RoomserverInputAPI is used to write events to the room server. type RoomserverInternalAPI interface { + InputRoomEventsAPI + QueryLatestEventsAndStateAPI + QueryEventsAPI + SyncRoomserverAPI + AppserviceRoomserverAPI + ClientRoomserverAPI // needed to avoid chicken and egg scenario when setting up the // interdependencies between the roomserver and other input APIs @@ -20,12 +26,6 @@ type RoomserverInternalAPI interface { SetAppserviceAPI(asAPI asAPI.AppServiceQueryAPI) SetUserAPI(userAPI userapi.UserInternalAPI) - InputRoomEvents( - ctx context.Context, - request *InputRoomEventsRequest, - response *InputRoomEventsResponse, - ) - PerformInvite( ctx context.Context, req *PerformInviteRequest, @@ -68,44 +68,31 @@ type RoomserverInternalAPI interface { res *PerformInboundPeekResponse, ) error - PerformAdminEvacuateRoom( - ctx context.Context, - req *PerformAdminEvacuateRoomRequest, - res *PerformAdminEvacuateRoomResponse, - ) - QueryPublishedRooms( ctx context.Context, req *QueryPublishedRoomsRequest, res *QueryPublishedRoomsResponse, ) error - // Query a list of membership events for a room - QueryMembershipsForRoom( - ctx context.Context, - request *QueryMembershipsForRoomRequest, - response *QueryMembershipsForRoomResponse, - ) error - // Query if we think we're still in a room. QueryServerJoinedToRoom( ctx context.Context, - request *QueryServerJoinedToRoomRequest, - response *QueryServerJoinedToRoomResponse, + req *QueryServerJoinedToRoomRequest, + res *QueryServerJoinedToRoomResponse, ) error // Query whether a server is allowed to see an event QueryServerAllowedToSeeEvent( ctx context.Context, - request *QueryServerAllowedToSeeEventRequest, - response *QueryServerAllowedToSeeEventResponse, + req *QueryServerAllowedToSeeEventRequest, + res *QueryServerAllowedToSeeEventResponse, ) error // Query missing events for a room from roomserver QueryMissingEvents( ctx context.Context, - request *QueryMissingEventsRequest, - response *QueryMissingEventsResponse, + req *QueryMissingEventsRequest, + res *QueryMissingEventsResponse, ) error // Query to get state and auth chain for a (potentially hypothetical) event. @@ -113,8 +100,8 @@ type RoomserverInternalAPI interface { // the state and auth chain to return. QueryStateAndAuthChain( ctx context.Context, - request *QueryStateAndAuthChainRequest, - response *QueryStateAndAuthChainResponse, + req *QueryStateAndAuthChainRequest, + res *QueryStateAndAuthChainResponse, ) error // QueryAuthChain returns the entire auth chain for the event IDs given. @@ -122,112 +109,179 @@ type RoomserverInternalAPI interface { // Omits without error for any missing auth events. There will be no duplicates. QueryAuthChain( ctx context.Context, - request *QueryAuthChainRequest, - response *QueryAuthChainResponse, + req *QueryAuthChainRequest, + res *QueryAuthChainResponse, ) error - // QueryCurrentState retrieves the requested state events. If state events are not found, they will be missing from - // the response. - QueryCurrentState(ctx context.Context, req *QueryCurrentStateRequest, res *QueryCurrentStateResponse) error // QueryRoomsForUser retrieves a list of room IDs matching the given query. QueryRoomsForUser(ctx context.Context, req *QueryRoomsForUserRequest, res *QueryRoomsForUserResponse) error - // QueryKnownUsers returns a list of users that we know about from our joined rooms. - QueryKnownUsers(ctx context.Context, req *QueryKnownUsersRequest, res *QueryKnownUsersResponse) error // QueryServerBannedFromRoom returns whether a server is banned from a room by server ACLs. QueryServerBannedFromRoom(ctx context.Context, req *QueryServerBannedFromRoomRequest, res *QueryServerBannedFromRoomResponse) error - // PerformForget forgets a rooms history for a specific user - PerformForget(ctx context.Context, req *PerformForgetRequest, resp *PerformForgetResponse) error - // PerformRoomUpgrade upgrades a room to a newer version PerformRoomUpgrade(ctx context.Context, req *PerformRoomUpgradeRequest, resp *PerformRoomUpgradeResponse) // Asks for the default room version as preferred by the server. QueryRoomVersionCapabilities( ctx context.Context, - request *QueryRoomVersionCapabilitiesRequest, - response *QueryRoomVersionCapabilitiesResponse, + req *QueryRoomVersionCapabilitiesRequest, + res *QueryRoomVersionCapabilitiesResponse, ) error // Asks for the room version for a given room. QueryRoomVersionForRoom( ctx context.Context, - request *QueryRoomVersionForRoomRequest, - response *QueryRoomVersionForRoomResponse, + req *QueryRoomVersionForRoomRequest, + res *QueryRoomVersionForRoomResponse, ) error // Set a room alias SetRoomAlias( ctx context.Context, req *SetRoomAliasRequest, - response *SetRoomAliasResponse, + res *SetRoomAliasResponse, ) error // Get the room ID for an alias GetRoomIDForAlias( ctx context.Context, req *GetRoomIDForAliasRequest, - response *GetRoomIDForAliasResponse, - ) error - - // Get all known aliases for a room ID - GetAliasesForRoomID( - ctx context.Context, - req *GetAliasesForRoomIDRequest, - response *GetAliasesForRoomIDResponse, + res *GetRoomIDForAliasResponse, ) error // Get the user ID of the creator of an alias GetCreatorIDForAlias( ctx context.Context, req *GetCreatorIDForAliasRequest, - response *GetCreatorIDForAliasResponse, + res *GetCreatorIDForAliasResponse, ) error // Remove a room alias RemoveRoomAlias( ctx context.Context, req *RemoveRoomAliasRequest, - response *RemoveRoomAliasResponse, + res *RemoveRoomAliasResponse, ) error } +type InputRoomEventsAPI interface { + InputRoomEvents( + ctx context.Context, + req *InputRoomEventsRequest, + res *InputRoomEventsResponse, + ) +} + +// Query the latest events and state for a room from the room server. +type QueryLatestEventsAndStateAPI interface { + QueryLatestEventsAndState(ctx context.Context, req *QueryLatestEventsAndStateRequest, res *QueryLatestEventsAndStateResponse) error +} + +// QueryBulkStateContent does a bulk query for state event content in the given rooms. +type QueryBulkStateContentAPI interface { + QueryBulkStateContent(ctx context.Context, req *QueryBulkStateContentRequest, res *QueryBulkStateContentResponse) error +} + +type QueryEventsAPI interface { + // Query a list of events by event ID. + QueryEventsByID( + ctx context.Context, + req *QueryEventsByIDRequest, + res *QueryEventsByIDResponse, + ) error + // QueryCurrentState retrieves the requested state events. If state events are not found, they will be missing from + // the response. + QueryCurrentState(ctx context.Context, req *QueryCurrentStateRequest, res *QueryCurrentStateResponse) error +} + // API functions required by the syncapi type SyncRoomserverAPI interface { - // Query the latest events and state for a room from the room server. - QueryLatestEventsAndState( - ctx context.Context, - request *QueryLatestEventsAndStateRequest, - response *QueryLatestEventsAndStateResponse, - ) error - // QueryBulkStateContent does a bulk query for state event content in the given rooms. - QueryBulkStateContent(ctx context.Context, req *QueryBulkStateContentRequest, res *QueryBulkStateContentResponse) error + QueryLatestEventsAndStateAPI + QueryBulkStateContentAPI // QuerySharedUsers returns a list of users who share at least 1 room in common with the given user. QuerySharedUsers(ctx context.Context, req *QuerySharedUsersRequest, res *QuerySharedUsersResponse) error // Query a list of events by event ID. QueryEventsByID( ctx context.Context, - request *QueryEventsByIDRequest, - response *QueryEventsByIDResponse, + req *QueryEventsByIDRequest, + res *QueryEventsByIDResponse, ) error // Query the membership event for an user for a room. QueryMembershipForUser( ctx context.Context, - request *QueryMembershipForUserRequest, - response *QueryMembershipForUserResponse, + req *QueryMembershipForUserRequest, + res *QueryMembershipForUserResponse, ) error // Query the state after a list of events in a room from the room server. QueryStateAfterEvents( ctx context.Context, - request *QueryStateAfterEventsRequest, - response *QueryStateAfterEventsResponse, + req *QueryStateAfterEventsRequest, + res *QueryStateAfterEventsResponse, ) error // Query a given amount (or less) of events prior to a given set of events. PerformBackfill( ctx context.Context, - request *PerformBackfillRequest, - response *PerformBackfillResponse, + req *PerformBackfillRequest, + res *PerformBackfillResponse, ) error } + +type AppserviceRoomserverAPI interface { + // Query a list of events by event ID. + QueryEventsByID( + ctx context.Context, + req *QueryEventsByIDRequest, + res *QueryEventsByIDResponse, + ) error + // Query a list of membership events for a room + QueryMembershipsForRoom( + ctx context.Context, + req *QueryMembershipsForRoomRequest, + res *QueryMembershipsForRoomResponse, + ) error + // Get all known aliases for a room ID + GetAliasesForRoomID( + ctx context.Context, + req *GetAliasesForRoomIDRequest, + res *GetAliasesForRoomIDResponse, + ) error +} + +type ClientRoomserverAPI interface { + InputRoomEventsAPI + QueryLatestEventsAndStateAPI + QueryBulkStateContentAPI + QueryEventsAPI + QueryMembershipForUser(ctx context.Context, req *QueryMembershipForUserRequest, res *QueryMembershipForUserResponse) error + QueryMembershipsForRoom(ctx context.Context, req *QueryMembershipsForRoomRequest, res *QueryMembershipsForRoomResponse) error + QueryRoomsForUser(ctx context.Context, req *QueryRoomsForUserRequest, res *QueryRoomsForUserResponse) error + QueryStateAfterEvents(ctx context.Context, req *QueryStateAfterEventsRequest, res *QueryStateAfterEventsResponse) error + // QueryKnownUsers returns a list of users that we know about from our joined rooms. + QueryKnownUsers(ctx context.Context, req *QueryKnownUsersRequest, res *QueryKnownUsersResponse) error + QueryRoomVersionForRoom(ctx context.Context, req *QueryRoomVersionForRoomRequest, res *QueryRoomVersionForRoomResponse) error + QueryPublishedRooms(ctx context.Context, req *QueryPublishedRoomsRequest, res *QueryPublishedRoomsResponse) error + QueryRoomVersionCapabilities(ctx context.Context, req *QueryRoomVersionCapabilitiesRequest, res *QueryRoomVersionCapabilitiesResponse) error + + GetRoomIDForAlias(ctx context.Context, req *GetRoomIDForAliasRequest, res *GetRoomIDForAliasResponse) error + GetAliasesForRoomID(ctx context.Context, req *GetAliasesForRoomIDRequest, res *GetAliasesForRoomIDResponse) error + + // PerformRoomUpgrade upgrades a room to a newer version + PerformRoomUpgrade(ctx context.Context, req *PerformRoomUpgradeRequest, resp *PerformRoomUpgradeResponse) + PerformAdminEvacuateRoom( + ctx context.Context, + req *PerformAdminEvacuateRoomRequest, + res *PerformAdminEvacuateRoomResponse, + ) + PerformPeek(ctx context.Context, req *PerformPeekRequest, res *PerformPeekResponse) + PerformUnpeek(ctx context.Context, req *PerformUnpeekRequest, res *PerformUnpeekResponse) + PerformInvite(ctx context.Context, req *PerformInviteRequest, res *PerformInviteResponse) error + PerformJoin(ctx context.Context, req *PerformJoinRequest, res *PerformJoinResponse) + PerformLeave(ctx context.Context, req *PerformLeaveRequest, res *PerformLeaveResponse) error + PerformPublish(ctx context.Context, req *PerformPublishRequest, res *PerformPublishResponse) + // PerformForget forgets a rooms history for a specific user + PerformForget(ctx context.Context, req *PerformForgetRequest, resp *PerformForgetResponse) error + SetRoomAlias(ctx context.Context, req *SetRoomAliasRequest, res *SetRoomAliasResponse) error + RemoveRoomAlias(ctx context.Context, req *RemoveRoomAliasRequest, res *RemoveRoomAliasResponse) error +} diff --git a/roomserver/api/wrapper.go b/roomserver/api/wrapper.go index 5491d36b3..9f7a09ddd 100644 --- a/roomserver/api/wrapper.go +++ b/roomserver/api/wrapper.go @@ -16,7 +16,6 @@ package api import ( "context" - "fmt" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" @@ -24,7 +23,7 @@ import ( // SendEvents to the roomserver The events are written with KindNew. func SendEvents( - ctx context.Context, rsAPI RoomserverInternalAPI, + ctx context.Context, rsAPI InputRoomEventsAPI, kind Kind, events []*gomatrixserverlib.HeaderedEvent, origin gomatrixserverlib.ServerName, sendAsServer gomatrixserverlib.ServerName, txnID *TransactionID, @@ -47,7 +46,7 @@ func SendEvents( // with the state at the event as KindOutlier before it. Will not send any event that is // marked as `true` in haveEventIDs. func SendEventWithState( - ctx context.Context, rsAPI RoomserverInternalAPI, kind Kind, + ctx context.Context, rsAPI InputRoomEventsAPI, kind Kind, state *gomatrixserverlib.RespState, event *gomatrixserverlib.HeaderedEvent, origin gomatrixserverlib.ServerName, haveEventIDs map[string]bool, async bool, ) error { @@ -83,7 +82,7 @@ func SendEventWithState( // SendInputRoomEvents to the roomserver. func SendInputRoomEvents( - ctx context.Context, rsAPI RoomserverInternalAPI, + ctx context.Context, rsAPI InputRoomEventsAPI, ires []InputRoomEvent, async bool, ) error { request := InputRoomEventsRequest{ @@ -95,37 +94,8 @@ func SendInputRoomEvents( return response.Err() } -// SendInvite event to the roomserver. -// This should only be needed for invite events that occur outside of a known room. -// If we are in the room then the event should be sent using the SendEvents method. -func SendInvite( - ctx context.Context, - rsAPI RoomserverInternalAPI, inviteEvent *gomatrixserverlib.HeaderedEvent, - inviteRoomState []gomatrixserverlib.InviteV2StrippedState, - sendAsServer gomatrixserverlib.ServerName, txnID *TransactionID, -) error { - // Start by sending the invite request into the roomserver. This will - // trigger the federation request amongst other things if needed. - request := &PerformInviteRequest{ - Event: inviteEvent, - InviteRoomState: inviteRoomState, - RoomVersion: inviteEvent.RoomVersion, - SendAsServer: string(sendAsServer), - TransactionID: txnID, - } - response := &PerformInviteResponse{} - if err := rsAPI.PerformInvite(ctx, request, response); err != nil { - return fmt.Errorf("rsAPI.PerformInvite: %w", err) - } - if response.Error != nil { - return response.Error - } - - return nil -} - // GetEvent returns the event or nil, even on errors. -func GetEvent(ctx context.Context, rsAPI RoomserverInternalAPI, eventID string) *gomatrixserverlib.HeaderedEvent { +func GetEvent(ctx context.Context, rsAPI QueryEventsAPI, eventID string) *gomatrixserverlib.HeaderedEvent { var res QueryEventsByIDResponse err := rsAPI.QueryEventsByID(ctx, &QueryEventsByIDRequest{ EventIDs: []string{eventID}, @@ -141,7 +111,7 @@ func GetEvent(ctx context.Context, rsAPI RoomserverInternalAPI, eventID string) } // GetStateEvent returns the current state event in the room or nil. -func GetStateEvent(ctx context.Context, rsAPI RoomserverInternalAPI, roomID string, tuple gomatrixserverlib.StateKeyTuple) *gomatrixserverlib.HeaderedEvent { +func GetStateEvent(ctx context.Context, rsAPI QueryEventsAPI, roomID string, tuple gomatrixserverlib.StateKeyTuple) *gomatrixserverlib.HeaderedEvent { var res QueryCurrentStateResponse err := rsAPI.QueryCurrentState(ctx, &QueryCurrentStateRequest{ RoomID: roomID, @@ -175,7 +145,7 @@ func IsServerBannedFromRoom(ctx context.Context, rsAPI RoomserverInternalAPI, ro // PopulatePublicRooms extracts PublicRoom information for all the provided room IDs. The IDs are not checked to see if they are visible in the // published room directory. // due to lots of switches -func PopulatePublicRooms(ctx context.Context, roomIDs []string, rsAPI RoomserverInternalAPI) ([]gomatrixserverlib.PublicRoom, error) { +func PopulatePublicRooms(ctx context.Context, roomIDs []string, rsAPI QueryBulkStateContentAPI) ([]gomatrixserverlib.PublicRoom, error) { avatarTuple := gomatrixserverlib.StateKeyTuple{EventType: "m.room.avatar", StateKey: ""} nameTuple := gomatrixserverlib.StateKeyTuple{EventType: "m.room.name", StateKey: ""} canonicalTuple := gomatrixserverlib.StateKeyTuple{EventType: gomatrixserverlib.MRoomCanonicalAlias, StateKey: ""} diff --git a/userapi/api/api.go b/userapi/api/api.go index 6f00fe44f..928b91e6d 100644 --- a/userapi/api/api.go +++ b/userapi/api/api.go @@ -26,44 +26,77 @@ import ( // UserInternalAPI is the internal API for information about users and devices. type UserInternalAPI interface { - LoginTokenInternalAPI UserProfileAPI - UserRegisterAPI - UserAccountAPI - UserThreePIDAPI QueryAcccessTokenAPI + + AppserviceUserAPI SyncUserAPI - - InputAccountData(ctx context.Context, req *InputAccountDataRequest, res *InputAccountDataResponse) error - - PerformOpenIDTokenCreation(ctx context.Context, req *PerformOpenIDTokenCreationRequest, res *PerformOpenIDTokenCreationResponse) error - PerformKeyBackup(ctx context.Context, req *PerformKeyBackupRequest, res *PerformKeyBackupResponse) error - PerformPusherSet(ctx context.Context, req *PerformPusherSetRequest, res *struct{}) error - PerformPusherDeletion(ctx context.Context, req *PerformPusherDeletionRequest, res *struct{}) error - PerformPushRulesPut(ctx context.Context, req *PerformPushRulesPutRequest, res *struct{}) error - - QueryKeyBackup(ctx context.Context, req *QueryKeyBackupRequest, res *QueryKeyBackupResponse) + ClientUserAPI + MediaUserAPI QueryOpenIDToken(ctx context.Context, req *QueryOpenIDTokenRequest, res *QueryOpenIDTokenResponse) error - QueryPushers(ctx context.Context, req *QueryPushersRequest, res *QueryPushersResponse) error - QueryPushRules(ctx context.Context, req *QueryPushRulesRequest, res *QueryPushRulesResponse) error - QueryNotifications(ctx context.Context, req *QueryNotificationsRequest, res *QueryNotificationsResponse) error } type QueryAcccessTokenAPI interface { QueryAccessToken(ctx context.Context, req *QueryAccessTokenRequest, res *QueryAccessTokenResponse) error } +type UserLoginAPI interface { + QueryAccountByPassword(ctx context.Context, req *QueryAccountByPasswordRequest, res *QueryAccountByPasswordResponse) error +} + +type AppserviceUserAPI interface { + PerformAccountCreation(ctx context.Context, req *PerformAccountCreationRequest, res *PerformAccountCreationResponse) error + PerformDeviceCreation(ctx context.Context, req *PerformDeviceCreationRequest, res *PerformDeviceCreationResponse) error +} + +type MediaUserAPI interface { + QueryAcccessTokenAPI +} + type SyncUserAPI interface { + QueryAcccessTokenAPI QueryAccountData(ctx context.Context, req *QueryAccountDataRequest, res *QueryAccountDataResponse) error - QueryAccessToken(ctx context.Context, req *QueryAccessTokenRequest, res *QueryAccessTokenResponse) error - PerformDeviceDeletion(ctx context.Context, req *PerformDeviceDeletionRequest, res *PerformDeviceDeletionResponse) error PerformLastSeenUpdate(ctx context.Context, req *PerformLastSeenUpdateRequest, res *PerformLastSeenUpdateResponse) error PerformDeviceUpdate(ctx context.Context, req *PerformDeviceUpdateRequest, res *PerformDeviceUpdateResponse) error QueryDevices(ctx context.Context, req *QueryDevicesRequest, res *QueryDevicesResponse) error QueryDeviceInfos(ctx context.Context, req *QueryDeviceInfosRequest, res *QueryDeviceInfosResponse) error } +type ClientUserAPI interface { + QueryAcccessTokenAPI + LoginTokenInternalAPI + UserLoginAPI + QueryNumericLocalpart(ctx context.Context, res *QueryNumericLocalpartResponse) error + QueryDevices(ctx context.Context, req *QueryDevicesRequest, res *QueryDevicesResponse) error + QueryProfile(ctx context.Context, req *QueryProfileRequest, res *QueryProfileResponse) error + QueryAccountData(ctx context.Context, req *QueryAccountDataRequest, res *QueryAccountDataResponse) error + QueryPushers(ctx context.Context, req *QueryPushersRequest, res *QueryPushersResponse) error + QueryPushRules(ctx context.Context, req *QueryPushRulesRequest, res *QueryPushRulesResponse) error + QueryAccountAvailability(ctx context.Context, req *QueryAccountAvailabilityRequest, res *QueryAccountAvailabilityResponse) error + PerformAccountCreation(ctx context.Context, req *PerformAccountCreationRequest, res *PerformAccountCreationResponse) error + PerformDeviceCreation(ctx context.Context, req *PerformDeviceCreationRequest, res *PerformDeviceCreationResponse) error + PerformDeviceUpdate(ctx context.Context, req *PerformDeviceUpdateRequest, res *PerformDeviceUpdateResponse) error + PerformDeviceDeletion(ctx context.Context, req *PerformDeviceDeletionRequest, res *PerformDeviceDeletionResponse) error + PerformPasswordUpdate(ctx context.Context, req *PerformPasswordUpdateRequest, res *PerformPasswordUpdateResponse) error + PerformPusherDeletion(ctx context.Context, req *PerformPusherDeletionRequest, res *struct{}) error + PerformPusherSet(ctx context.Context, req *PerformPusherSetRequest, res *struct{}) error + PerformPushRulesPut(ctx context.Context, req *PerformPushRulesPutRequest, res *struct{}) error + PerformAccountDeactivation(ctx context.Context, req *PerformAccountDeactivationRequest, res *PerformAccountDeactivationResponse) error + PerformOpenIDTokenCreation(ctx context.Context, req *PerformOpenIDTokenCreationRequest, res *PerformOpenIDTokenCreationResponse) error + SetAvatarURL(ctx context.Context, req *PerformSetAvatarURLRequest, res *PerformSetAvatarURLResponse) error + SetDisplayName(ctx context.Context, req *PerformUpdateDisplayNameRequest, res *struct{}) error + QueryNotifications(ctx context.Context, req *QueryNotificationsRequest, res *QueryNotificationsResponse) error + InputAccountData(ctx context.Context, req *InputAccountDataRequest, res *InputAccountDataResponse) error + PerformKeyBackup(ctx context.Context, req *PerformKeyBackupRequest, res *PerformKeyBackupResponse) error + QueryKeyBackup(ctx context.Context, req *QueryKeyBackupRequest, res *QueryKeyBackupResponse) + + QueryThreePIDsForLocalpart(ctx context.Context, req *QueryThreePIDsForLocalpartRequest, res *QueryThreePIDsForLocalpartResponse) error + QueryLocalpartForThreePID(ctx context.Context, req *QueryLocalpartForThreePIDRequest, res *QueryLocalpartForThreePIDResponse) error + PerformForgetThreePID(ctx context.Context, req *PerformForgetThreePIDRequest, res *struct{}) error + PerformSaveThreePIDAssociation(ctx context.Context, req *PerformSaveThreePIDAssociationRequest, res *struct{}) error +} + type UserDirectoryProvider interface { QuerySearchProfiles(ctx context.Context, req *QuerySearchProfilesRequest, res *QuerySearchProfilesResponse) error } @@ -72,31 +105,6 @@ type UserDirectoryProvider interface { type UserProfileAPI interface { QueryProfile(ctx context.Context, req *QueryProfileRequest, res *QueryProfileResponse) error QuerySearchProfiles(ctx context.Context, req *QuerySearchProfilesRequest, res *QuerySearchProfilesResponse) error - SetAvatarURL(ctx context.Context, req *PerformSetAvatarURLRequest, res *PerformSetAvatarURLResponse) error - SetDisplayName(ctx context.Context, req *PerformUpdateDisplayNameRequest, res *struct{}) error -} - -// UserRegisterAPI defines functions for registering accounts -type UserRegisterAPI interface { - QueryNumericLocalpart(ctx context.Context, res *QueryNumericLocalpartResponse) error - QueryAccountAvailability(ctx context.Context, req *QueryAccountAvailabilityRequest, res *QueryAccountAvailabilityResponse) error - PerformAccountCreation(ctx context.Context, req *PerformAccountCreationRequest, res *PerformAccountCreationResponse) error - PerformDeviceCreation(ctx context.Context, req *PerformDeviceCreationRequest, res *PerformDeviceCreationResponse) error -} - -// UserAccountAPI defines functions for changing an account -type UserAccountAPI interface { - PerformPasswordUpdate(ctx context.Context, req *PerformPasswordUpdateRequest, res *PerformPasswordUpdateResponse) error - PerformAccountDeactivation(ctx context.Context, req *PerformAccountDeactivationRequest, res *PerformAccountDeactivationResponse) error - QueryAccountByPassword(ctx context.Context, req *QueryAccountByPasswordRequest, res *QueryAccountByPasswordResponse) error -} - -// UserThreePIDAPI defines functions for 3PID -type UserThreePIDAPI interface { - QueryLocalpartForThreePID(ctx context.Context, req *QueryLocalpartForThreePIDRequest, res *QueryLocalpartForThreePIDResponse) error - QueryThreePIDsForLocalpart(ctx context.Context, req *QueryThreePIDsForLocalpartRequest, res *QueryThreePIDsForLocalpartResponse) error - PerformForgetThreePID(ctx context.Context, req *PerformForgetThreePIDRequest, res *struct{}) error - PerformSaveThreePIDAssociation(ctx context.Context, req *PerformSaveThreePIDAssociationRequest, res *struct{}) error } type PerformKeyBackupRequest struct { From 530fd488a91dd1644799920328a12810b70b1b49 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 5 May 2022 13:29:39 +0100 Subject: [PATCH 08/12] Don't log consumer errors on shutdown --- roomserver/internal/input/input.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roomserver/internal/input/input.go b/roomserver/internal/input/input.go index 1fea6ef06..10c210220 100644 --- a/roomserver/internal/input/input.go +++ b/roomserver/internal/input/input.go @@ -202,7 +202,7 @@ func (w *worker) _next() { return } - case context.DeadlineExceeded: + case context.DeadlineExceeded, context.Canceled: // The context exceeded, so we've been waiting for more than a // minute for activity in this room. At this point we will shut // down the subscriber to free up resources. It'll get started From 42f35a57ac82e78e7035547504806733089f21a0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 5 May 2022 13:42:12 +0100 Subject: [PATCH 09/12] Update table names for user API stats table --- userapi/storage/postgres/stats_table.go | 14 +++++++------- userapi/storage/sqlite3/stats_table.go | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/userapi/storage/postgres/stats_table.go b/userapi/storage/postgres/stats_table.go index f71900015..c0b317503 100644 --- a/userapi/storage/postgres/stats_table.go +++ b/userapi/storage/postgres/stats_table.go @@ -30,7 +30,7 @@ import ( ) const userDailyVisitsSchema = ` -CREATE TABLE IF NOT EXISTS user_daily_visits ( +CREATE TABLE IF NOT EXISTS userapi_daily_visits ( localpart TEXT NOT NULL, device_id TEXT NOT NULL, timestamp BIGINT NOT NULL, @@ -38,9 +38,9 @@ CREATE TABLE IF NOT EXISTS user_daily_visits ( ); -- Device IDs and timestamp must be unique for a given user per day -CREATE UNIQUE INDEX IF NOT EXISTS localpart_device_timestamp_idx ON user_daily_visits(localpart, device_id, timestamp); -CREATE INDEX IF NOT EXISTS timestamp_idx ON user_daily_visits(timestamp); -CREATE INDEX IF NOT EXISTS localpart_timestamp_idx ON user_daily_visits(localpart, timestamp); +CREATE UNIQUE INDEX IF NOT EXISTS userapi_daily_visits_localpart_device_timestamp_idx ON userapi_daily_visits(localpart, device_id, timestamp); +CREATE INDEX IF NOT EXISTS userapi_daily_visits_timestamp_idx ON userapi_daily_visits(timestamp); +CREATE INDEX IF NOT EXISTS userapi_daily_visits_localpart_timestamp_idx ON userapi_daily_visits(localpart, timestamp); ` const countUsersLastSeenAfterSQL = "" + @@ -112,7 +112,7 @@ FROM WHEN LOWER(user_agent) LIKE '%%mozilla%%' OR LOWER(user_agent) LIKE '%%gecko%%' THEN 'web' ELSE 'unknown' END as client_type - FROM user_daily_visits + FROM userapi_daily_visits WHERE timestamp > $1 AND timestamp < $2 GROUP BY localpart, client_type HAVING max(timestamp) - min(timestamp) > $3 @@ -141,11 +141,11 @@ SELECT user_type, COUNT(*) AS count FROM ( // account_type 1 = users; 3 = admins const updateUserDailyVisitsSQL = ` -INSERT INTO user_daily_visits(localpart, device_id, timestamp, user_agent) +INSERT INTO userapi_daily_visits(localpart, device_id, timestamp, user_agent) SELECT u.localpart, u.device_id, $1, MAX(u.user_agent) FROM device_devices AS u LEFT JOIN ( - SELECT localpart, device_id, timestamp FROM user_daily_visits + SELECT localpart, device_id, timestamp FROM userapi_daily_visits WHERE timestamp = $1 ) udv ON u.localpart = udv.localpart AND u.device_id = udv.device_id diff --git a/userapi/storage/sqlite3/stats_table.go b/userapi/storage/sqlite3/stats_table.go index af4c7ff98..e00ed417b 100644 --- a/userapi/storage/sqlite3/stats_table.go +++ b/userapi/storage/sqlite3/stats_table.go @@ -30,7 +30,7 @@ import ( ) const userDailyVisitsSchema = ` -CREATE TABLE IF NOT EXISTS user_daily_visits ( +CREATE TABLE IF NOT EXISTS userapi_daily_visits ( localpart TEXT NOT NULL, device_id TEXT NOT NULL, timestamp BIGINT NOT NULL, @@ -38,9 +38,9 @@ CREATE TABLE IF NOT EXISTS user_daily_visits ( ); -- Device IDs and timestamp must be unique for a given user per day -CREATE UNIQUE INDEX IF NOT EXISTS localpart_device_timestamp_idx ON user_daily_visits(localpart, device_id, timestamp); -CREATE INDEX IF NOT EXISTS timestamp_idx ON user_daily_visits(timestamp); -CREATE INDEX IF NOT EXISTS localpart_timestamp_idx ON user_daily_visits(localpart, timestamp); +CREATE UNIQUE INDEX IF NOT EXISTS userapi_daily_visits_localpart_device_timestamp_idx ON userapi_daily_visits(localpart, device_id, timestamp); +CREATE INDEX IF NOT EXISTS userapi_daily_visits_timestamp_idx ON userapi_daily_visits(timestamp); +CREATE INDEX IF NOT EXISTS userapi_daily_visits_localpart_timestamp_idx ON userapi_daily_visits(localpart, timestamp); ` const countUsersLastSeenAfterSQL = "" + @@ -116,7 +116,7 @@ FROM WHEN LOWER(user_agent) LIKE '%%mozilla%%' OR LOWER(user_agent) LIKE '%%gecko%%' THEN 'web' ELSE 'unknown' END as client_type - FROM user_daily_visits + FROM userapi_daily_visits WHERE timestamp > $1 AND timestamp < $2 GROUP BY localpart, client_type HAVING max(timestamp) - min(timestamp) > $3 @@ -145,11 +145,11 @@ SELECT user_type, COUNT(*) AS count FROM ( // account_type 1 = users; 3 = admins const updateUserDailyVisitsSQL = ` -INSERT INTO user_daily_visits(localpart, device_id, timestamp, user_agent) +INSERT INTO userapi_daily_visits(localpart, device_id, timestamp, user_agent) SELECT u.localpart, u.device_id, $1, MAX(u.user_agent) FROM device_devices AS u LEFT JOIN ( - SELECT localpart, device_id, timestamp FROM user_daily_visits + SELECT localpart, device_id, timestamp FROM userapi_daily_visits WHERE timestamp = $1 ) udv ON u.localpart = udv.localpart AND u.device_id = udv.device_id From e4da04e75b4cba1c9afb63b9973444e1da12021b Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 5 May 2022 14:06:05 +0100 Subject: [PATCH 10/12] Update to matrix-org/gomatrixserverlib#303 --- go.mod | 2 +- go.sum | 4 ++-- setup/base/base.go | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 56f5dcbbb..b6070b94f 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91 github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 - github.com/matrix-org/gomatrixserverlib v0.0.0-20220505092512-c4ceb4751ac2 + github.com/matrix-org/gomatrixserverlib v0.0.0-20220505130352-f72a63510060 github.com/matrix-org/pinecone v0.0.0-20220408153826-2999ea29ed48 github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4 github.com/mattn/go-sqlite3 v1.14.10 diff --git a/go.sum b/go.sum index fccda40c4..cbea7319e 100644 --- a/go.sum +++ b/go.sum @@ -795,8 +795,8 @@ github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91/go.mod h1 github.com/matrix-org/gomatrix v0.0.0-20190528120928-7df988a63f26/go.mod h1:3fxX6gUjWyI/2Bt7J1OLhpCzOfO/bB3AiX0cJtEKud0= github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 h1:ZtO5uywdd5dLDCud4r0r55eP4j9FuUNpl60Gmntcop4= github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s= -github.com/matrix-org/gomatrixserverlib v0.0.0-20220505092512-c4ceb4751ac2 h1:5/Y4BpiMk1D/l/HkJz8Ng8bLBz1BHwV6V4e+yMNySzk= -github.com/matrix-org/gomatrixserverlib v0.0.0-20220505092512-c4ceb4751ac2/go.mod h1:V5eO8rn/C3rcxig37A/BCeKerLFS+9Avg/77FIeTZ48= +github.com/matrix-org/gomatrixserverlib v0.0.0-20220505130352-f72a63510060 h1:tYi4mCOWgVLt8mpkG1LFRKcMfSTwp5NQ5wBKdtaxO9s= +github.com/matrix-org/gomatrixserverlib v0.0.0-20220505130352-f72a63510060/go.mod h1:V5eO8rn/C3rcxig37A/BCeKerLFS+9Avg/77FIeTZ48= github.com/matrix-org/pinecone v0.0.0-20220408153826-2999ea29ed48 h1:W0sjjC6yjskHX4mb0nk3p0fXAlbU5bAFUFeEtlrPASE= github.com/matrix-org/pinecone v0.0.0-20220408153826-2999ea29ed48/go.mod h1:ulJzsVOTssIVp1j/m5eI//4VpAGDkMt5NrRuAVX7wpc= github.com/matrix-org/util v0.0.0-20190711121626-527ce5ddefc7/go.mod h1:vVQlW/emklohkZnOPwD3LrZUBqdfsbiyO3p1lNV8F6U= diff --git a/setup/base/base.go b/setup/base/base.go index 3641ad780..9326be1c0 100644 --- a/setup/base/base.go +++ b/setup/base/base.go @@ -331,6 +331,7 @@ func (b *BaseDendrite) CreateClient() *gomatrixserverlib.Client { } opts := []gomatrixserverlib.ClientOption{ gomatrixserverlib.WithSkipVerify(b.Cfg.FederationAPI.DisableTLSValidation), + gomatrixserverlib.WithWellKnownSRVLookups(true), } if b.Cfg.Global.DNSCache.Enabled { opts = append(opts, gomatrixserverlib.WithDNSCache(b.DNSCache)) From 9957752a9d60d4519cc0b7e8b9b40a781240c27d Mon Sep 17 00:00:00 2001 From: kegsay Date: Thu, 5 May 2022 19:30:38 +0100 Subject: [PATCH 11/12] Define component interfaces based on consumers (2/2) (#2425) * convert remaining interfaces * Tidy up the userapi interfaces --- clientapi/clientapi.go | 2 +- clientapi/routing/routing.go | 2 +- clientapi/routing/userdirectory.go | 2 +- cmd/dendrite-demo-pinecone/users/users.go | 4 +- federationapi/api/api.go | 62 ++++++-------- federationapi/federationapi.go | 2 +- federationapi/routing/backfill.go | 2 +- federationapi/routing/eventauth.go | 2 +- federationapi/routing/events.go | 6 +- federationapi/routing/invite.go | 6 +- federationapi/routing/join.go | 4 +- federationapi/routing/leave.go | 4 +- federationapi/routing/missingevents.go | 2 +- federationapi/routing/openid.go | 2 +- federationapi/routing/peek.go | 2 +- federationapi/routing/profile.go | 2 +- federationapi/routing/publicrooms.go | 6 +- federationapi/routing/query.go | 2 +- federationapi/routing/routing.go | 6 +- federationapi/routing/send.go | 4 +- federationapi/routing/send_test.go | 2 +- federationapi/routing/state.go | 6 +- federationapi/routing/threepid.go | 12 +-- keyserver/api/api.go | 9 ++- roomserver/api/api.go | 80 ++++++++----------- roomserver/api/api_trace.go | 2 +- roomserver/api/wrapper.go | 2 +- roomserver/internal/api.go | 4 +- roomserver/internal/input/input.go | 2 +- roomserver/internal/input/input_missing.go | 2 +- .../internal/perform/perform_backfill.go | 6 +- roomserver/internal/perform/perform_invite.go | 2 +- roomserver/internal/perform/perform_join.go | 2 +- roomserver/internal/perform/perform_leave.go | 2 +- roomserver/internal/perform/perform_peek.go | 2 +- roomserver/internal/perform/perform_unpeek.go | 2 +- roomserver/inthttp/client.go | 2 +- setup/monolith.go | 2 +- userapi/api/api.go | 38 +++++---- userapi/consumers/syncapi_streamevent.go | 6 +- userapi/internal/api.go | 2 +- userapi/userapi.go | 4 +- 42 files changed, 153 insertions(+), 162 deletions(-) diff --git a/clientapi/clientapi.go b/clientapi/clientapi.go index 957d082a6..ad4609080 100644 --- a/clientapi/clientapi.go +++ b/clientapi/clientapi.go @@ -38,7 +38,7 @@ func AddPublicRoutes( transactionsCache *transactions.Cache, fsAPI federationAPI.ClientFederationAPI, userAPI userapi.ClientUserAPI, - userDirectoryProvider userapi.UserDirectoryProvider, + userDirectoryProvider userapi.QuerySearchProfilesAPI, keyAPI keyserverAPI.ClientKeyAPI, extRoomsProvider api.ExtraPublicRoomsProvider, ) { diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go index 6da467073..f9f71ed7a 100644 --- a/clientapi/routing/routing.go +++ b/clientapi/routing/routing.go @@ -53,7 +53,7 @@ func Setup( rsAPI roomserverAPI.ClientRoomserverAPI, asAPI appserviceAPI.AppServiceQueryAPI, userAPI userapi.ClientUserAPI, - userDirectoryProvider userapi.UserDirectoryProvider, + userDirectoryProvider userapi.QuerySearchProfilesAPI, federation *gomatrixserverlib.FederationClient, syncProducer *producers.SyncAPIProducer, transactionsCache *transactions.Cache, diff --git a/clientapi/routing/userdirectory.go b/clientapi/routing/userdirectory.go index 4d41da734..f311457a0 100644 --- a/clientapi/routing/userdirectory.go +++ b/clientapi/routing/userdirectory.go @@ -36,7 +36,7 @@ func SearchUserDirectory( device *userapi.Device, userAPI userapi.ClientUserAPI, rsAPI api.ClientRoomserverAPI, - provider userapi.UserDirectoryProvider, + provider userapi.QuerySearchProfilesAPI, serverName gomatrixserverlib.ServerName, searchString string, limit int, diff --git a/cmd/dendrite-demo-pinecone/users/users.go b/cmd/dendrite-demo-pinecone/users/users.go index ebfb5cbe3..fc66bf299 100644 --- a/cmd/dendrite-demo-pinecone/users/users.go +++ b/cmd/dendrite-demo-pinecone/users/users.go @@ -37,7 +37,7 @@ import ( type PineconeUserProvider struct { r *pineconeRouter.Router s *pineconeSessions.Sessions - userAPI userapi.UserProfileAPI + userAPI userapi.QuerySearchProfilesAPI fedClient *gomatrixserverlib.FederationClient } @@ -46,7 +46,7 @@ const PublicURL = "/_matrix/p2p/profiles" func NewPineconeUserProvider( r *pineconeRouter.Router, s *pineconeSessions.Sessions, - userAPI userapi.UserProfileAPI, + userAPI userapi.QuerySearchProfilesAPI, fedClient *gomatrixserverlib.FederationClient, ) *PineconeUserProvider { p := &PineconeUserProvider{ diff --git a/federationapi/api/api.go b/federationapi/api/api.go index ce6a0f2ed..87b037187 100644 --- a/federationapi/api/api.go +++ b/federationapi/api/api.go @@ -14,17 +14,13 @@ import ( // implements as proxy calls, with built-in backoff/retries/etc. Errors returned from functions in // this interface are of type FederationClientError type FederationClient interface { - gomatrixserverlib.BackfillClient gomatrixserverlib.FederatedStateClient GetUserDevices(ctx context.Context, s gomatrixserverlib.ServerName, userID string) (res gomatrixserverlib.RespUserDevices, err error) ClaimKeys(ctx context.Context, s gomatrixserverlib.ServerName, oneTimeKeys map[string]map[string]string) (res gomatrixserverlib.RespClaimKeys, err error) QueryKeys(ctx context.Context, s gomatrixserverlib.ServerName, keys map[string][]string) (res gomatrixserverlib.RespQueryKeys, err error) - GetEvent(ctx context.Context, s gomatrixserverlib.ServerName, eventID string) (res gomatrixserverlib.Transaction, err error) MSC2836EventRelationships(ctx context.Context, dst gomatrixserverlib.ServerName, r gomatrixserverlib.MSC2836EventRelationshipsRequest, roomVersion gomatrixserverlib.RoomVersion) (res gomatrixserverlib.MSC2836EventRelationshipsResponse, err error) MSC2946Spaces(ctx context.Context, dst gomatrixserverlib.ServerName, roomID string, suggestedOnly bool) (res gomatrixserverlib.MSC2946SpacesResponse, err error) LookupServerKeys(ctx context.Context, s gomatrixserverlib.ServerName, keyRequests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp) ([]gomatrixserverlib.ServerKeys, error) - GetEventAuth(ctx context.Context, s gomatrixserverlib.ServerName, roomVersion gomatrixserverlib.RoomVersion, roomID, eventID string) (res gomatrixserverlib.RespEventAuth, err error) - LookupMissingEvents(ctx context.Context, s gomatrixserverlib.ServerName, roomID string, missing gomatrixserverlib.MissingEvents, roomVersion gomatrixserverlib.RoomVersion) (res gomatrixserverlib.RespMissingEvents, err error) } // FederationClientError is returned from FederationClient methods in the event of a problem. @@ -43,17 +39,10 @@ type FederationInternalAPI interface { FederationClient gomatrixserverlib.KeyDatabase ClientFederationAPI - - KeyRing() *gomatrixserverlib.KeyRing + RoomserverFederationAPI QueryServerKeys(ctx context.Context, request *QueryServerKeysRequest, response *QueryServerKeysResponse) error - // PerformDirectoryLookup looks up a remote room ID from a room alias. - PerformDirectoryLookup( - ctx context.Context, - request *PerformDirectoryLookupRequest, - response *PerformDirectoryLookupResponse, - ) error // Query the server names of the joined hosts in a room. // Unlike QueryJoinedHostsInRoom, this function returns a de-duplicated slice // containing only the server names (without information for membership events). @@ -63,30 +52,6 @@ type FederationInternalAPI interface { request *QueryJoinedHostServerNamesInRoomRequest, response *QueryJoinedHostServerNamesInRoomResponse, ) error - // Handle an instruction to make_join & send_join with a remote server. - PerformJoin( - ctx context.Context, - request *PerformJoinRequest, - response *PerformJoinResponse, - ) - // Handle an instruction to peek a room on a remote server. - PerformOutboundPeek( - ctx context.Context, - request *PerformOutboundPeekRequest, - response *PerformOutboundPeekResponse, - ) error - // Handle an instruction to make_leave & send_leave with a remote server. - PerformLeave( - ctx context.Context, - request *PerformLeaveRequest, - response *PerformLeaveResponse, - ) error - // Handle sending an invite to a remote server. - PerformInvite( - ctx context.Context, - request *PerformInviteRequest, - response *PerformInviteResponse, - ) error // Notifies the federation sender that these servers may be online and to retry sending messages. PerformServersAlive( ctx context.Context, @@ -105,6 +70,31 @@ type ClientFederationAPI interface { QueryJoinedHostServerNamesInRoom(ctx context.Context, request *QueryJoinedHostServerNamesInRoomRequest, response *QueryJoinedHostServerNamesInRoomResponse) error } +type RoomserverFederationAPI interface { + gomatrixserverlib.BackfillClient + gomatrixserverlib.FederatedStateClient + KeyRing() *gomatrixserverlib.KeyRing + + // PerformDirectoryLookup looks up a remote room ID from a room alias. + PerformDirectoryLookup(ctx context.Context, request *PerformDirectoryLookupRequest, response *PerformDirectoryLookupResponse) error + // Handle an instruction to make_join & send_join with a remote server. + PerformJoin(ctx context.Context, request *PerformJoinRequest, response *PerformJoinResponse) + // Handle an instruction to make_leave & send_leave with a remote server. + PerformLeave(ctx context.Context, request *PerformLeaveRequest, response *PerformLeaveResponse) error + // Handle sending an invite to a remote server. + PerformInvite(ctx context.Context, request *PerformInviteRequest, response *PerformInviteResponse) error + // Handle an instruction to peek a room on a remote server. + PerformOutboundPeek(ctx context.Context, request *PerformOutboundPeekRequest, response *PerformOutboundPeekResponse) error + // Query the server names of the joined hosts in a room. + // Unlike QueryJoinedHostsInRoom, this function returns a de-duplicated slice + // containing only the server names (without information for membership events). + // The response will include this server if they are joined to the room. + QueryJoinedHostServerNamesInRoom(ctx context.Context, request *QueryJoinedHostServerNamesInRoomRequest, response *QueryJoinedHostServerNamesInRoomResponse) error + GetEventAuth(ctx context.Context, s gomatrixserverlib.ServerName, roomVersion gomatrixserverlib.RoomVersion, roomID, eventID string) (res gomatrixserverlib.RespEventAuth, err error) + GetEvent(ctx context.Context, s gomatrixserverlib.ServerName, eventID string) (res gomatrixserverlib.Transaction, err error) + LookupMissingEvents(ctx context.Context, s gomatrixserverlib.ServerName, roomID string, missing gomatrixserverlib.MissingEvents, roomVersion gomatrixserverlib.RoomVersion) (res gomatrixserverlib.RespMissingEvents, err error) +} + type QueryServerKeysRequest struct { ServerName gomatrixserverlib.ServerName KeyIDToCriteria map[gomatrixserverlib.KeyID]gomatrixserverlib.PublicKeyNotaryQueryCriteria diff --git a/federationapi/federationapi.go b/federationapi/federationapi.go index c627aab5c..632994db9 100644 --- a/federationapi/federationapi.go +++ b/federationapi/federationapi.go @@ -49,7 +49,7 @@ func AddPublicRoutes( userAPI userapi.UserInternalAPI, federation *gomatrixserverlib.FederationClient, keyRing gomatrixserverlib.JSONVerifier, - rsAPI roomserverAPI.RoomserverInternalAPI, + rsAPI roomserverAPI.FederationRoomserverAPI, federationAPI federationAPI.FederationInternalAPI, keyAPI keyserverAPI.KeyInternalAPI, servers federationAPI.ServersInRoomProvider, diff --git a/federationapi/routing/backfill.go b/federationapi/routing/backfill.go index 82f6cbabf..7b9ca66f6 100644 --- a/federationapi/routing/backfill.go +++ b/federationapi/routing/backfill.go @@ -33,7 +33,7 @@ import ( func Backfill( httpReq *http.Request, request *gomatrixserverlib.FederationRequest, - rsAPI api.RoomserverInternalAPI, + rsAPI api.FederationRoomserverAPI, roomID string, cfg *config.FederationAPI, ) util.JSONResponse { diff --git a/federationapi/routing/eventauth.go b/federationapi/routing/eventauth.go index e83cb8ad2..868785a9b 100644 --- a/federationapi/routing/eventauth.go +++ b/federationapi/routing/eventauth.go @@ -26,7 +26,7 @@ import ( func GetEventAuth( ctx context.Context, request *gomatrixserverlib.FederationRequest, - rsAPI api.RoomserverInternalAPI, + rsAPI api.FederationRoomserverAPI, roomID string, eventID string, ) util.JSONResponse { diff --git a/federationapi/routing/events.go b/federationapi/routing/events.go index 312ef9f8e..23796edfa 100644 --- a/federationapi/routing/events.go +++ b/federationapi/routing/events.go @@ -29,7 +29,7 @@ import ( func GetEvent( ctx context.Context, request *gomatrixserverlib.FederationRequest, - rsAPI api.RoomserverInternalAPI, + rsAPI api.FederationRoomserverAPI, eventID string, origin gomatrixserverlib.ServerName, ) util.JSONResponse { @@ -56,7 +56,7 @@ func GetEvent( func allowedToSeeEvent( ctx context.Context, origin gomatrixserverlib.ServerName, - rsAPI api.RoomserverInternalAPI, + rsAPI api.FederationRoomserverAPI, eventID string, ) *util.JSONResponse { var authResponse api.QueryServerAllowedToSeeEventResponse @@ -82,7 +82,7 @@ func allowedToSeeEvent( } // fetchEvent fetches the event without auth checks. Returns an error if the event cannot be found. -func fetchEvent(ctx context.Context, rsAPI api.RoomserverInternalAPI, eventID string) (*gomatrixserverlib.Event, *util.JSONResponse) { +func fetchEvent(ctx context.Context, rsAPI api.FederationRoomserverAPI, eventID string) (*gomatrixserverlib.Event, *util.JSONResponse) { var eventsResponse api.QueryEventsByIDResponse err := rsAPI.QueryEventsByID( ctx, diff --git a/federationapi/routing/invite.go b/federationapi/routing/invite.go index 25faff0cb..a5797645e 100644 --- a/federationapi/routing/invite.go +++ b/federationapi/routing/invite.go @@ -35,7 +35,7 @@ func InviteV2( roomID string, eventID string, cfg *config.FederationAPI, - rsAPI api.RoomserverInternalAPI, + rsAPI api.FederationRoomserverAPI, keys gomatrixserverlib.JSONVerifier, ) util.JSONResponse { inviteReq := gomatrixserverlib.InviteV2Request{} @@ -72,7 +72,7 @@ func InviteV1( roomID string, eventID string, cfg *config.FederationAPI, - rsAPI api.RoomserverInternalAPI, + rsAPI api.FederationRoomserverAPI, keys gomatrixserverlib.JSONVerifier, ) util.JSONResponse { roomVer := gomatrixserverlib.RoomVersionV1 @@ -110,7 +110,7 @@ func processInvite( roomID string, eventID string, cfg *config.FederationAPI, - rsAPI api.RoomserverInternalAPI, + rsAPI api.FederationRoomserverAPI, keys gomatrixserverlib.JSONVerifier, ) util.JSONResponse { diff --git a/federationapi/routing/join.go b/federationapi/routing/join.go index 495b8c914..767699728 100644 --- a/federationapi/routing/join.go +++ b/federationapi/routing/join.go @@ -34,7 +34,7 @@ func MakeJoin( httpReq *http.Request, request *gomatrixserverlib.FederationRequest, cfg *config.FederationAPI, - rsAPI api.RoomserverInternalAPI, + rsAPI api.FederationRoomserverAPI, roomID, userID string, remoteVersions []gomatrixserverlib.RoomVersion, ) util.JSONResponse { @@ -165,7 +165,7 @@ func SendJoin( httpReq *http.Request, request *gomatrixserverlib.FederationRequest, cfg *config.FederationAPI, - rsAPI api.RoomserverInternalAPI, + rsAPI api.FederationRoomserverAPI, keys gomatrixserverlib.JSONVerifier, roomID, eventID string, ) util.JSONResponse { diff --git a/federationapi/routing/leave.go b/federationapi/routing/leave.go index 0b83f04ae..54b2c3e84 100644 --- a/federationapi/routing/leave.go +++ b/federationapi/routing/leave.go @@ -30,7 +30,7 @@ func MakeLeave( httpReq *http.Request, request *gomatrixserverlib.FederationRequest, cfg *config.FederationAPI, - rsAPI api.RoomserverInternalAPI, + rsAPI api.FederationRoomserverAPI, roomID, userID string, ) util.JSONResponse { _, domain, err := gomatrixserverlib.SplitID('@', userID) @@ -122,7 +122,7 @@ func SendLeave( httpReq *http.Request, request *gomatrixserverlib.FederationRequest, cfg *config.FederationAPI, - rsAPI api.RoomserverInternalAPI, + rsAPI api.FederationRoomserverAPI, keys gomatrixserverlib.JSONVerifier, roomID, eventID string, ) util.JSONResponse { diff --git a/federationapi/routing/missingevents.go b/federationapi/routing/missingevents.go index b826d69c4..531cb9e28 100644 --- a/federationapi/routing/missingevents.go +++ b/federationapi/routing/missingevents.go @@ -34,7 +34,7 @@ type getMissingEventRequest struct { func GetMissingEvents( httpReq *http.Request, request *gomatrixserverlib.FederationRequest, - rsAPI api.RoomserverInternalAPI, + rsAPI api.FederationRoomserverAPI, roomID string, ) util.JSONResponse { var gme getMissingEventRequest diff --git a/federationapi/routing/openid.go b/federationapi/routing/openid.go index 829dbccad..cbc75a9a7 100644 --- a/federationapi/routing/openid.go +++ b/federationapi/routing/openid.go @@ -30,7 +30,7 @@ type openIDUserInfoResponse struct { // GetOpenIDUserInfo implements GET /_matrix/federation/v1/openid/userinfo func GetOpenIDUserInfo( httpReq *http.Request, - userAPI userapi.UserInternalAPI, + userAPI userapi.FederationUserAPI, ) util.JSONResponse { token := httpReq.URL.Query().Get("access_token") if len(token) == 0 { diff --git a/federationapi/routing/peek.go b/federationapi/routing/peek.go index 827d1116d..bc4dac90f 100644 --- a/federationapi/routing/peek.go +++ b/federationapi/routing/peek.go @@ -29,7 +29,7 @@ func Peek( httpReq *http.Request, request *gomatrixserverlib.FederationRequest, cfg *config.FederationAPI, - rsAPI api.RoomserverInternalAPI, + rsAPI api.FederationRoomserverAPI, roomID, peekID string, remoteVersions []gomatrixserverlib.RoomVersion, ) util.JSONResponse { diff --git a/federationapi/routing/profile.go b/federationapi/routing/profile.go index dbc209ce1..f672811af 100644 --- a/federationapi/routing/profile.go +++ b/federationapi/routing/profile.go @@ -29,7 +29,7 @@ import ( // GetProfile implements GET /_matrix/federation/v1/query/profile func GetProfile( httpReq *http.Request, - userAPI userapi.UserInternalAPI, + userAPI userapi.FederationUserAPI, cfg *config.FederationAPI, ) util.JSONResponse { userID, field := httpReq.FormValue("user_id"), httpReq.FormValue("field") diff --git a/federationapi/routing/publicrooms.go b/federationapi/routing/publicrooms.go index a253f86eb..1a54f5a7d 100644 --- a/federationapi/routing/publicrooms.go +++ b/federationapi/routing/publicrooms.go @@ -23,7 +23,7 @@ type filter struct { } // GetPostPublicRooms implements GET and POST /publicRooms -func GetPostPublicRooms(req *http.Request, rsAPI roomserverAPI.RoomserverInternalAPI) util.JSONResponse { +func GetPostPublicRooms(req *http.Request, rsAPI roomserverAPI.FederationRoomserverAPI) util.JSONResponse { var request PublicRoomReq if fillErr := fillPublicRoomsReq(req, &request); fillErr != nil { return *fillErr @@ -42,7 +42,7 @@ func GetPostPublicRooms(req *http.Request, rsAPI roomserverAPI.RoomserverInterna } func publicRooms( - ctx context.Context, request PublicRoomReq, rsAPI roomserverAPI.RoomserverInternalAPI, + ctx context.Context, request PublicRoomReq, rsAPI roomserverAPI.FederationRoomserverAPI, ) (*gomatrixserverlib.RespPublicRooms, error) { var response gomatrixserverlib.RespPublicRooms @@ -111,7 +111,7 @@ func fillPublicRoomsReq(httpReq *http.Request, request *PublicRoomReq) *util.JSO } // due to lots of switches -func fillInRooms(ctx context.Context, roomIDs []string, rsAPI roomserverAPI.RoomserverInternalAPI) ([]gomatrixserverlib.PublicRoom, error) { +func fillInRooms(ctx context.Context, roomIDs []string, rsAPI roomserverAPI.FederationRoomserverAPI) ([]gomatrixserverlib.PublicRoom, error) { avatarTuple := gomatrixserverlib.StateKeyTuple{EventType: "m.room.avatar", StateKey: ""} nameTuple := gomatrixserverlib.StateKeyTuple{EventType: "m.room.name", StateKey: ""} canonicalTuple := gomatrixserverlib.StateKeyTuple{EventType: gomatrixserverlib.MRoomCanonicalAlias, StateKey: ""} diff --git a/federationapi/routing/query.go b/federationapi/routing/query.go index 47d3b2df9..707b7b019 100644 --- a/federationapi/routing/query.go +++ b/federationapi/routing/query.go @@ -32,7 +32,7 @@ func RoomAliasToID( httpReq *http.Request, federation *gomatrixserverlib.FederationClient, cfg *config.FederationAPI, - rsAPI roomserverAPI.RoomserverInternalAPI, + rsAPI roomserverAPI.FederationRoomserverAPI, senderAPI federationAPI.FederationInternalAPI, ) util.JSONResponse { roomAlias := httpReq.FormValue("room_alias") diff --git a/federationapi/routing/routing.go b/federationapi/routing/routing.go index 6d24c8b40..51adc279c 100644 --- a/federationapi/routing/routing.go +++ b/federationapi/routing/routing.go @@ -47,11 +47,11 @@ import ( func Setup( fedMux, keyMux, wkMux *mux.Router, cfg *config.FederationAPI, - rsAPI roomserverAPI.RoomserverInternalAPI, + rsAPI roomserverAPI.FederationRoomserverAPI, fsAPI federationAPI.FederationInternalAPI, keys gomatrixserverlib.JSONVerifier, federation *gomatrixserverlib.FederationClient, - userAPI userapi.UserInternalAPI, + userAPI userapi.FederationUserAPI, keyAPI keyserverAPI.KeyInternalAPI, mscCfg *config.MSCs, servers federationAPI.ServersInRoomProvider, @@ -497,7 +497,7 @@ func Setup( func ErrorIfLocalServerNotInRoom( ctx context.Context, - rsAPI api.RoomserverInternalAPI, + rsAPI api.FederationRoomserverAPI, roomID string, ) *util.JSONResponse { // Check if we think we're in this room. If we aren't then diff --git a/federationapi/routing/send.go b/federationapi/routing/send.go index 2c01afb1b..b9b6d33b7 100644 --- a/federationapi/routing/send.go +++ b/federationapi/routing/send.go @@ -82,7 +82,7 @@ func Send( request *gomatrixserverlib.FederationRequest, txnID gomatrixserverlib.TransactionID, cfg *config.FederationAPI, - rsAPI api.RoomserverInternalAPI, + rsAPI api.FederationRoomserverAPI, keyAPI keyapi.KeyInternalAPI, keys gomatrixserverlib.JSONVerifier, federation *gomatrixserverlib.FederationClient, @@ -182,7 +182,7 @@ func Send( type txnReq struct { gomatrixserverlib.Transaction - rsAPI api.RoomserverInternalAPI + rsAPI api.FederationRoomserverAPI keyAPI keyapi.KeyInternalAPI ourServerName gomatrixserverlib.ServerName keys gomatrixserverlib.JSONVerifier diff --git a/federationapi/routing/send_test.go b/federationapi/routing/send_test.go index 8d2d85040..011d4e342 100644 --- a/federationapi/routing/send_test.go +++ b/federationapi/routing/send_test.go @@ -183,7 +183,7 @@ func (c *txnFedClient) LookupMissingEvents(ctx context.Context, s gomatrixserver return c.getMissingEvents(missing) } -func mustCreateTransaction(rsAPI api.RoomserverInternalAPI, fedClient txnFederationClient, pdus []json.RawMessage) *txnReq { +func mustCreateTransaction(rsAPI api.FederationRoomserverAPI, fedClient txnFederationClient, pdus []json.RawMessage) *txnReq { t := &txnReq{ rsAPI: rsAPI, keys: &test.NopJSONVerifier{}, diff --git a/federationapi/routing/state.go b/federationapi/routing/state.go index e2b67776a..6fdce20ce 100644 --- a/federationapi/routing/state.go +++ b/federationapi/routing/state.go @@ -27,7 +27,7 @@ import ( func GetState( ctx context.Context, request *gomatrixserverlib.FederationRequest, - rsAPI api.RoomserverInternalAPI, + rsAPI api.FederationRoomserverAPI, roomID string, ) util.JSONResponse { eventID, err := parseEventIDParam(request) @@ -50,7 +50,7 @@ func GetState( func GetStateIDs( ctx context.Context, request *gomatrixserverlib.FederationRequest, - rsAPI api.RoomserverInternalAPI, + rsAPI api.FederationRoomserverAPI, roomID string, ) util.JSONResponse { eventID, err := parseEventIDParam(request) @@ -97,7 +97,7 @@ func parseEventIDParam( func getState( ctx context.Context, request *gomatrixserverlib.FederationRequest, - rsAPI api.RoomserverInternalAPI, + rsAPI api.FederationRoomserverAPI, roomID string, eventID string, ) (stateEvents, authEvents []*gomatrixserverlib.HeaderedEvent, errRes *util.JSONResponse) { diff --git a/federationapi/routing/threepid.go b/federationapi/routing/threepid.go index 8ae7130c3..16f245cee 100644 --- a/federationapi/routing/threepid.go +++ b/federationapi/routing/threepid.go @@ -55,10 +55,10 @@ var ( // CreateInvitesFrom3PIDInvites implements POST /_matrix/federation/v1/3pid/onbind func CreateInvitesFrom3PIDInvites( - req *http.Request, rsAPI api.RoomserverInternalAPI, + req *http.Request, rsAPI api.FederationRoomserverAPI, cfg *config.FederationAPI, federation *gomatrixserverlib.FederationClient, - userAPI userapi.UserInternalAPI, + userAPI userapi.FederationUserAPI, ) util.JSONResponse { var body invites if reqErr := httputil.UnmarshalJSONRequest(req, &body); reqErr != nil { @@ -105,7 +105,7 @@ func ExchangeThirdPartyInvite( httpReq *http.Request, request *gomatrixserverlib.FederationRequest, roomID string, - rsAPI api.RoomserverInternalAPI, + rsAPI api.FederationRoomserverAPI, cfg *config.FederationAPI, federation *gomatrixserverlib.FederationClient, ) util.JSONResponse { @@ -203,10 +203,10 @@ func ExchangeThirdPartyInvite( // Returns an error if there was a problem building the event or fetching the // necessary data to do so. func createInviteFrom3PIDInvite( - ctx context.Context, rsAPI api.RoomserverInternalAPI, + ctx context.Context, rsAPI api.FederationRoomserverAPI, cfg *config.FederationAPI, inv invite, federation *gomatrixserverlib.FederationClient, - userAPI userapi.UserInternalAPI, + userAPI userapi.FederationUserAPI, ) (*gomatrixserverlib.Event, error) { verReq := api.QueryRoomVersionForRoomRequest{RoomID: inv.RoomID} verRes := api.QueryRoomVersionForRoomResponse{} @@ -270,7 +270,7 @@ func createInviteFrom3PIDInvite( // Returns an error if something failed during the process. func buildMembershipEvent( ctx context.Context, - builder *gomatrixserverlib.EventBuilder, rsAPI api.RoomserverInternalAPI, + builder *gomatrixserverlib.EventBuilder, rsAPI api.FederationRoomserverAPI, cfg *config.FederationAPI, ) (*gomatrixserverlib.Event, error) { eventsNeeded, err := gomatrixserverlib.StateNeededForEventBuilder(builder) diff --git a/keyserver/api/api.go b/keyserver/api/api.go index 5564eb271..6cee2c014 100644 --- a/keyserver/api/api.go +++ b/keyserver/api/api.go @@ -29,12 +29,13 @@ import ( type KeyInternalAPI interface { SyncKeyAPI ClientKeyAPI + UserKeyAPI + // SetUserAPI assigns a user API to query when extracting device names. SetUserAPI(i userapi.UserInternalAPI) // InputDeviceListUpdate from a federated server EDU InputDeviceListUpdate(ctx context.Context, req *InputDeviceListUpdateRequest, res *InputDeviceListUpdateResponse) - PerformDeleteKeys(ctx context.Context, req *PerformDeleteKeysRequest, res *PerformDeleteKeysResponse) QueryDeviceMessages(ctx context.Context, req *QueryDeviceMessagesRequest, res *QueryDeviceMessagesResponse) QuerySignatures(ctx context.Context, req *QuerySignaturesRequest, res *QuerySignaturesResponse) } @@ -49,6 +50,12 @@ type ClientKeyAPI interface { PerformClaimKeys(ctx context.Context, req *PerformClaimKeysRequest, res *PerformClaimKeysResponse) } +// API functions required by the userapi +type UserKeyAPI interface { + PerformUploadKeys(ctx context.Context, req *PerformUploadKeysRequest, res *PerformUploadKeysResponse) + PerformDeleteKeys(ctx context.Context, req *PerformDeleteKeysRequest, res *PerformDeleteKeysResponse) +} + // API functions required by the syncapi type SyncKeyAPI interface { QueryKeyChanges(ctx context.Context, req *QueryKeyChangesRequest, res *QueryKeyChangesResponse) diff --git a/roomserver/api/api.go b/roomserver/api/api.go index 33c3d157b..7e1e568ce 100644 --- a/roomserver/api/api.go +++ b/roomserver/api/api.go @@ -19,10 +19,12 @@ type RoomserverInternalAPI interface { SyncRoomserverAPI AppserviceRoomserverAPI ClientRoomserverAPI + UserRoomserverAPI + FederationRoomserverAPI // needed to avoid chicken and egg scenario when setting up the // interdependencies between the roomserver and other input APIs - SetFederationAPI(fsAPI fsAPI.FederationInternalAPI, keyRing *gomatrixserverlib.KeyRing) + SetFederationAPI(fsAPI fsAPI.RoomserverFederationAPI, keyRing *gomatrixserverlib.KeyRing) SetAppserviceAPI(asAPI asAPI.AppServiceQueryAPI) SetUserAPI(userAPI userapi.UserInternalAPI) @@ -62,48 +64,6 @@ type RoomserverInternalAPI interface { res *PerformPublishResponse, ) - PerformInboundPeek( - ctx context.Context, - req *PerformInboundPeekRequest, - res *PerformInboundPeekResponse, - ) error - - QueryPublishedRooms( - ctx context.Context, - req *QueryPublishedRoomsRequest, - res *QueryPublishedRoomsResponse, - ) error - - // Query if we think we're still in a room. - QueryServerJoinedToRoom( - ctx context.Context, - req *QueryServerJoinedToRoomRequest, - res *QueryServerJoinedToRoomResponse, - ) error - - // Query whether a server is allowed to see an event - QueryServerAllowedToSeeEvent( - ctx context.Context, - req *QueryServerAllowedToSeeEventRequest, - res *QueryServerAllowedToSeeEventResponse, - ) error - - // Query missing events for a room from roomserver - QueryMissingEvents( - ctx context.Context, - req *QueryMissingEventsRequest, - res *QueryMissingEventsResponse, - ) error - - // Query to get state and auth chain for a (potentially hypothetical) event. - // Takes lists of PrevEventIDs and AuthEventsIDs and uses them to calculate - // the state and auth chain to return. - QueryStateAndAuthChain( - ctx context.Context, - req *QueryStateAndAuthChainRequest, - res *QueryStateAndAuthChainResponse, - ) error - // QueryAuthChain returns the entire auth chain for the event IDs given. // The response includes the events in the request. // Omits without error for any missing auth events. There will be no duplicates. @@ -115,8 +75,6 @@ type RoomserverInternalAPI interface { // QueryRoomsForUser retrieves a list of room IDs matching the given query. QueryRoomsForUser(ctx context.Context, req *QueryRoomsForUserRequest, res *QueryRoomsForUserResponse) error - // QueryServerBannedFromRoom returns whether a server is banned from a room by server ACLs. - QueryServerBannedFromRoom(ctx context.Context, req *QueryServerBannedFromRoomRequest, res *QueryServerBannedFromRoomResponse) error // PerformRoomUpgrade upgrades a room to a newer version PerformRoomUpgrade(ctx context.Context, req *PerformRoomUpgradeRequest, resp *PerformRoomUpgradeResponse) @@ -285,3 +243,35 @@ type ClientRoomserverAPI interface { SetRoomAlias(ctx context.Context, req *SetRoomAliasRequest, res *SetRoomAliasResponse) error RemoveRoomAlias(ctx context.Context, req *RemoveRoomAliasRequest, res *RemoveRoomAliasResponse) error } + +type UserRoomserverAPI interface { + QueryLatestEventsAndStateAPI + QueryCurrentState(ctx context.Context, req *QueryCurrentStateRequest, res *QueryCurrentStateResponse) error + QueryMembershipsForRoom(ctx context.Context, req *QueryMembershipsForRoomRequest, res *QueryMembershipsForRoomResponse) error +} + +type FederationRoomserverAPI interface { + InputRoomEventsAPI + QueryLatestEventsAndStateAPI + QueryBulkStateContentAPI + // QueryServerBannedFromRoom returns whether a server is banned from a room by server ACLs. + QueryServerBannedFromRoom(ctx context.Context, req *QueryServerBannedFromRoomRequest, res *QueryServerBannedFromRoomResponse) error + QueryRoomVersionForRoom(ctx context.Context, req *QueryRoomVersionForRoomRequest, res *QueryRoomVersionForRoomResponse) error + GetRoomIDForAlias(ctx context.Context, req *GetRoomIDForAliasRequest, res *GetRoomIDForAliasResponse) error + QueryEventsByID(ctx context.Context, req *QueryEventsByIDRequest, res *QueryEventsByIDResponse) error + // Query to get state and auth chain for a (potentially hypothetical) event. + // Takes lists of PrevEventIDs and AuthEventsIDs and uses them to calculate + // the state and auth chain to return. + QueryStateAndAuthChain(ctx context.Context, req *QueryStateAndAuthChainRequest, res *QueryStateAndAuthChainResponse) error + // Query if we think we're still in a room. + QueryServerJoinedToRoom(ctx context.Context, req *QueryServerJoinedToRoomRequest, res *QueryServerJoinedToRoomResponse) error + QueryPublishedRooms(ctx context.Context, req *QueryPublishedRoomsRequest, res *QueryPublishedRoomsResponse) error + // Query missing events for a room from roomserver + QueryMissingEvents(ctx context.Context, req *QueryMissingEventsRequest, res *QueryMissingEventsResponse) error + // Query whether a server is allowed to see an event + QueryServerAllowedToSeeEvent(ctx context.Context, req *QueryServerAllowedToSeeEventRequest, res *QueryServerAllowedToSeeEventResponse) error + PerformInboundPeek(ctx context.Context, req *PerformInboundPeekRequest, res *PerformInboundPeekResponse) error + PerformInvite(ctx context.Context, req *PerformInviteRequest, res *PerformInviteResponse) error + // Query a given amount (or less) of events prior to a given set of events. + PerformBackfill(ctx context.Context, req *PerformBackfillRequest, res *PerformBackfillResponse) error +} diff --git a/roomserver/api/api_trace.go b/roomserver/api/api_trace.go index 61c06e886..bc60999e6 100644 --- a/roomserver/api/api_trace.go +++ b/roomserver/api/api_trace.go @@ -19,7 +19,7 @@ type RoomserverInternalAPITrace struct { Impl RoomserverInternalAPI } -func (t *RoomserverInternalAPITrace) SetFederationAPI(fsAPI fsAPI.FederationInternalAPI, keyRing *gomatrixserverlib.KeyRing) { +func (t *RoomserverInternalAPITrace) SetFederationAPI(fsAPI fsAPI.RoomserverFederationAPI, keyRing *gomatrixserverlib.KeyRing) { t.Impl.SetFederationAPI(fsAPI, keyRing) } diff --git a/roomserver/api/wrapper.go b/roomserver/api/wrapper.go index 9f7a09ddd..344e9b079 100644 --- a/roomserver/api/wrapper.go +++ b/roomserver/api/wrapper.go @@ -129,7 +129,7 @@ func GetStateEvent(ctx context.Context, rsAPI QueryEventsAPI, roomID string, tup } // IsServerBannedFromRoom returns whether the server is banned from a room by server ACLs. -func IsServerBannedFromRoom(ctx context.Context, rsAPI RoomserverInternalAPI, roomID string, serverName gomatrixserverlib.ServerName) bool { +func IsServerBannedFromRoom(ctx context.Context, rsAPI FederationRoomserverAPI, roomID string, serverName gomatrixserverlib.ServerName) bool { req := &QueryServerBannedFromRoomRequest{ ServerName: serverName, RoomID: roomID, diff --git a/roomserver/internal/api.go b/roomserver/internal/api.go index 267cd4099..dc0a0a718 100644 --- a/roomserver/internal/api.go +++ b/roomserver/internal/api.go @@ -43,7 +43,7 @@ type RoomserverInternalAPI struct { ServerName gomatrixserverlib.ServerName KeyRing gomatrixserverlib.JSONVerifier ServerACLs *acls.ServerACLs - fsAPI fsAPI.FederationInternalAPI + fsAPI fsAPI.RoomserverFederationAPI asAPI asAPI.AppServiceQueryAPI NATSClient *nats.Conn JetStream nats.JetStreamContext @@ -87,7 +87,7 @@ func NewRoomserverAPI( // SetFederationInputAPI passes in a federation input API reference so that we can // avoid the chicken-and-egg problem of both the roomserver input API and the // federation input API being interdependent. -func (r *RoomserverInternalAPI) SetFederationAPI(fsAPI fsAPI.FederationInternalAPI, keyRing *gomatrixserverlib.KeyRing) { +func (r *RoomserverInternalAPI) SetFederationAPI(fsAPI fsAPI.RoomserverFederationAPI, keyRing *gomatrixserverlib.KeyRing) { r.fsAPI = fsAPI r.KeyRing = keyRing diff --git a/roomserver/internal/input/input.go b/roomserver/internal/input/input.go index 10c210220..600994c5a 100644 --- a/roomserver/internal/input/input.go +++ b/roomserver/internal/input/input.go @@ -82,7 +82,7 @@ type Inputer struct { JetStream nats.JetStreamContext Durable nats.SubOpt ServerName gomatrixserverlib.ServerName - FSAPI fedapi.FederationInternalAPI + FSAPI fedapi.RoomserverFederationAPI KeyRing gomatrixserverlib.JSONVerifier ACLs *acls.ServerACLs InputRoomEventTopic string diff --git a/roomserver/internal/input/input_missing.go b/roomserver/internal/input/input_missing.go index 2c958335d..9c70076c2 100644 --- a/roomserver/internal/input/input_missing.go +++ b/roomserver/internal/input/input_missing.go @@ -44,7 +44,7 @@ type missingStateReq struct { roomInfo *types.RoomInfo inputer *Inputer keys gomatrixserverlib.JSONVerifier - federation fedapi.FederationInternalAPI + federation fedapi.RoomserverFederationAPI roomsMu *internal.MutexByRoom servers []gomatrixserverlib.ServerName hadEvents map[string]bool diff --git a/roomserver/internal/perform/perform_backfill.go b/roomserver/internal/perform/perform_backfill.go index 081f694a1..1bc4c75ce 100644 --- a/roomserver/internal/perform/perform_backfill.go +++ b/roomserver/internal/perform/perform_backfill.go @@ -38,7 +38,7 @@ const maxBackfillServers = 5 type Backfiller struct { ServerName gomatrixserverlib.ServerName DB storage.Database - FSAPI federationAPI.FederationInternalAPI + FSAPI federationAPI.RoomserverFederationAPI KeyRing gomatrixserverlib.JSONVerifier // The servers which should be preferred above other servers when backfilling @@ -228,7 +228,7 @@ func (r *Backfiller) fetchAndStoreMissingEvents(ctx context.Context, roomVer gom // backfillRequester implements gomatrixserverlib.BackfillRequester type backfillRequester struct { db storage.Database - fsAPI federationAPI.FederationInternalAPI + fsAPI federationAPI.RoomserverFederationAPI thisServer gomatrixserverlib.ServerName preferServer map[gomatrixserverlib.ServerName]bool bwExtrems map[string][]string @@ -240,7 +240,7 @@ type backfillRequester struct { } func newBackfillRequester( - db storage.Database, fsAPI federationAPI.FederationInternalAPI, thisServer gomatrixserverlib.ServerName, + db storage.Database, fsAPI federationAPI.RoomserverFederationAPI, thisServer gomatrixserverlib.ServerName, bwExtrems map[string][]string, preferServers []gomatrixserverlib.ServerName, ) *backfillRequester { preferServer := make(map[gomatrixserverlib.ServerName]bool) diff --git a/roomserver/internal/perform/perform_invite.go b/roomserver/internal/perform/perform_invite.go index 6111372d8..b0148a314 100644 --- a/roomserver/internal/perform/perform_invite.go +++ b/roomserver/internal/perform/perform_invite.go @@ -35,7 +35,7 @@ import ( type Inviter struct { DB storage.Database Cfg *config.RoomServer - FSAPI federationAPI.FederationInternalAPI + FSAPI federationAPI.RoomserverFederationAPI Inputer *input.Inputer } diff --git a/roomserver/internal/perform/perform_join.go b/roomserver/internal/perform/perform_join.go index a40f66d21..61a0206ef 100644 --- a/roomserver/internal/perform/perform_join.go +++ b/roomserver/internal/perform/perform_join.go @@ -38,7 +38,7 @@ import ( type Joiner struct { ServerName gomatrixserverlib.ServerName Cfg *config.RoomServer - FSAPI fsAPI.FederationInternalAPI + FSAPI fsAPI.RoomserverFederationAPI RSAPI rsAPI.RoomserverInternalAPI DB storage.Database diff --git a/roomserver/internal/perform/perform_leave.go b/roomserver/internal/perform/perform_leave.go index 5b4cd3c6f..b006843fb 100644 --- a/roomserver/internal/perform/perform_leave.go +++ b/roomserver/internal/perform/perform_leave.go @@ -37,7 +37,7 @@ import ( type Leaver struct { Cfg *config.RoomServer DB storage.Database - FSAPI fsAPI.FederationInternalAPI + FSAPI fsAPI.RoomserverFederationAPI UserAPI userapi.UserInternalAPI Inputer *input.Inputer } diff --git a/roomserver/internal/perform/perform_peek.go b/roomserver/internal/perform/perform_peek.go index 6a2c329b9..45e63888d 100644 --- a/roomserver/internal/perform/perform_peek.go +++ b/roomserver/internal/perform/perform_peek.go @@ -33,7 +33,7 @@ import ( type Peeker struct { ServerName gomatrixserverlib.ServerName Cfg *config.RoomServer - FSAPI fsAPI.FederationInternalAPI + FSAPI fsAPI.RoomserverFederationAPI DB storage.Database Inputer *input.Inputer diff --git a/roomserver/internal/perform/perform_unpeek.go b/roomserver/internal/perform/perform_unpeek.go index 16b4eeaed..1057499cb 100644 --- a/roomserver/internal/perform/perform_unpeek.go +++ b/roomserver/internal/perform/perform_unpeek.go @@ -30,7 +30,7 @@ import ( type Unpeeker struct { ServerName gomatrixserverlib.ServerName Cfg *config.RoomServer - FSAPI fsAPI.FederationInternalAPI + FSAPI fsAPI.RoomserverFederationAPI DB storage.Database Inputer *input.Inputer diff --git a/roomserver/inthttp/client.go b/roomserver/inthttp/client.go index 3b29001e9..4fc75ff41 100644 --- a/roomserver/inthttp/client.go +++ b/roomserver/inthttp/client.go @@ -87,7 +87,7 @@ func NewRoomserverClient( } // SetFederationInputAPI no-ops in HTTP client mode as there is no chicken/egg scenario -func (h *httpRoomserverInternalAPI) SetFederationAPI(fsAPI fsInputAPI.FederationInternalAPI, keyRing *gomatrixserverlib.KeyRing) { +func (h *httpRoomserverInternalAPI) SetFederationAPI(fsAPI fsInputAPI.RoomserverFederationAPI, keyRing *gomatrixserverlib.KeyRing) { } // SetAppserviceAPI no-ops in HTTP client mode as there is no chicken/egg scenario diff --git a/setup/monolith.go b/setup/monolith.go index e033c14d7..a0e850d83 100644 --- a/setup/monolith.go +++ b/setup/monolith.go @@ -47,7 +47,7 @@ type Monolith struct { // Optional ExtPublicRoomsProvider api.ExtraPublicRoomsProvider - ExtUserDirectoryProvider userapi.UserDirectoryProvider + ExtUserDirectoryProvider userapi.QuerySearchProfilesAPI } // AddAllPublicRoutes attaches all public paths to the given router diff --git a/userapi/api/api.go b/userapi/api/api.go index 928b91e6d..dc8c12b74 100644 --- a/userapi/api/api.go +++ b/userapi/api/api.go @@ -26,34 +26,33 @@ import ( // UserInternalAPI is the internal API for information about users and devices. type UserInternalAPI interface { - UserProfileAPI - QueryAcccessTokenAPI - AppserviceUserAPI SyncUserAPI ClientUserAPI MediaUserAPI + FederationUserAPI - QueryOpenIDToken(ctx context.Context, req *QueryOpenIDTokenRequest, res *QueryOpenIDTokenResponse) error -} - -type QueryAcccessTokenAPI interface { - QueryAccessToken(ctx context.Context, req *QueryAccessTokenRequest, res *QueryAccessTokenResponse) error -} - -type UserLoginAPI interface { - QueryAccountByPassword(ctx context.Context, req *QueryAccountByPasswordRequest, res *QueryAccountByPasswordResponse) error + QuerySearchProfilesAPI // used by p2p demos } +// api functions required by the appservice api type AppserviceUserAPI interface { PerformAccountCreation(ctx context.Context, req *PerformAccountCreationRequest, res *PerformAccountCreationResponse) error PerformDeviceCreation(ctx context.Context, req *PerformDeviceCreationRequest, res *PerformDeviceCreationResponse) error } +// api functions required by the media api type MediaUserAPI interface { QueryAcccessTokenAPI } +// api functions required by the federation api +type FederationUserAPI interface { + QueryOpenIDToken(ctx context.Context, req *QueryOpenIDTokenRequest, res *QueryOpenIDTokenResponse) error + QueryProfile(ctx context.Context, req *QueryProfileRequest, res *QueryProfileResponse) error +} + +// api functions required by the sync api type SyncUserAPI interface { QueryAcccessTokenAPI QueryAccountData(ctx context.Context, req *QueryAccountDataRequest, res *QueryAccountDataResponse) error @@ -63,6 +62,7 @@ type SyncUserAPI interface { QueryDeviceInfos(ctx context.Context, req *QueryDeviceInfosRequest, res *QueryDeviceInfosResponse) error } +// api functions required by the client api type ClientUserAPI interface { QueryAcccessTokenAPI LoginTokenInternalAPI @@ -97,14 +97,18 @@ type ClientUserAPI interface { PerformSaveThreePIDAssociation(ctx context.Context, req *PerformSaveThreePIDAssociationRequest, res *struct{}) error } -type UserDirectoryProvider interface { +// custom api functions required by pinecone / p2p demos +type QuerySearchProfilesAPI interface { QuerySearchProfiles(ctx context.Context, req *QuerySearchProfilesRequest, res *QuerySearchProfilesResponse) error } -// UserProfileAPI provides functions for getting user profiles -type UserProfileAPI interface { - QueryProfile(ctx context.Context, req *QueryProfileRequest, res *QueryProfileResponse) error - QuerySearchProfiles(ctx context.Context, req *QuerySearchProfilesRequest, res *QuerySearchProfilesResponse) error +// common function for creating authenticated endpoints (used in client/media/sync api) +type QueryAcccessTokenAPI interface { + QueryAccessToken(ctx context.Context, req *QueryAccessTokenRequest, res *QueryAccessTokenResponse) error +} + +type UserLoginAPI interface { + QueryAccountByPassword(ctx context.Context, req *QueryAccountByPasswordRequest, res *QueryAccountByPasswordResponse) error } type PerformKeyBackupRequest struct { diff --git a/userapi/consumers/syncapi_streamevent.go b/userapi/consumers/syncapi_streamevent.go index 9ef7b5083..7807c7637 100644 --- a/userapi/consumers/syncapi_streamevent.go +++ b/userapi/consumers/syncapi_streamevent.go @@ -29,7 +29,7 @@ type OutputStreamEventConsumer struct { ctx context.Context cfg *config.UserAPI userAPI api.UserInternalAPI - rsAPI rsapi.RoomserverInternalAPI + rsAPI rsapi.UserRoomserverAPI jetstream nats.JetStreamContext durable string db storage.Database @@ -45,7 +45,7 @@ func NewOutputStreamEventConsumer( store storage.Database, pgClient pushgateway.Client, userAPI api.UserInternalAPI, - rsAPI rsapi.RoomserverInternalAPI, + rsAPI rsapi.UserRoomserverAPI, syncProducer *producers.SyncAPI, ) *OutputStreamEventConsumer { return &OutputStreamEventConsumer{ @@ -455,7 +455,7 @@ func (s *OutputStreamEventConsumer) evaluatePushRules(ctx context.Context, event type ruleSetEvalContext struct { ctx context.Context - rsAPI rsapi.RoomserverInternalAPI + rsAPI rsapi.UserRoomserverAPI mem *localMembership roomID string roomSize int diff --git a/userapi/internal/api.go b/userapi/internal/api.go index 394bfa224..9d2f63c72 100644 --- a/userapi/internal/api.go +++ b/userapi/internal/api.go @@ -48,7 +48,7 @@ type UserInternalAPI struct { ServerName gomatrixserverlib.ServerName // AppServices is the list of all registered AS AppServices []config.ApplicationService - KeyAPI keyapi.KeyInternalAPI + KeyAPI keyapi.UserKeyAPI } func (a *UserInternalAPI) InputAccountData(ctx context.Context, req *api.InputAccountDataRequest, res *api.InputAccountDataResponse) error { diff --git a/userapi/userapi.go b/userapi/userapi.go index 5b11665db..03a46807f 100644 --- a/userapi/userapi.go +++ b/userapi/userapi.go @@ -44,8 +44,8 @@ func AddInternalRoutes(router *mux.Router, intAPI api.UserInternalAPI) { // can call functions directly on the returned API or via an HTTP interface using AddInternalRoutes. func NewInternalAPI( base *base.BaseDendrite, cfg *config.UserAPI, - appServices []config.ApplicationService, keyAPI keyapi.KeyInternalAPI, - rsAPI rsapi.RoomserverInternalAPI, pgClient pushgateway.Client, + appServices []config.ApplicationService, keyAPI keyapi.UserKeyAPI, + rsAPI rsapi.UserRoomserverAPI, pgClient pushgateway.Client, ) api.UserInternalAPI { js, _ := jetstream.Prepare(base.ProcessContext, &cfg.Matrix.JetStream) From 4705f5761e620e7f8a35151eeb2007e884847152 Mon Sep 17 00:00:00 2001 From: Till <2353100+S7evinK@users.noreply.github.com> Date: Fri, 6 May 2022 08:26:24 +0200 Subject: [PATCH 12/12] Add FAQ entry for anonymous stats (#2419) --- docs/FAQ.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/docs/FAQ.md b/docs/FAQ.md index 978212cce..47eaecf0f 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -74,3 +74,46 @@ If you are running with `GODEBUG=madvdontneed=1` and still see hugely inflated m ### Dendrite is running out of PostgreSQL database connections You may need to revisit the connection limit of your PostgreSQL server and/or make changes to the `max_connections` lines in your Dendrite configuration. Be aware that each Dendrite component opens its own database connections and has its own connection limit, even in monolith mode! + +### What is being reported when enabling anonymous stats? + +If anonymous stats reporting is enabled, the following data is send to the defined endpoint. + +```json +{ + "cpu_average": 0, + "daily_active_users": 97, + "daily_e2ee_messages": 0, + "daily_messages": 0, + "daily_sent_e2ee_messages": 0, + "daily_sent_messages": 0, + "daily_user_type_bridged": 2, + "daily_user_type_native": 97, + "database_engine": "Postgres", + "database_server_version": "11.14 (Debian 11.14-0+deb10u1)", + "federation_disabled": false, + "go_arch": "amd64", + "go_os": "linux", + "go_version": "go1.16.13", + "homeserver": "localhost:8800", + "log_level": "trace", + "memory_rss": 93452, + "monolith": true, + "monthly_active_users": 97, + "nats_embedded": true, + "nats_in_memory": true, + "num_cpu": 8, + "num_go_routine": 203, + "r30v2_users_all": 0, + "r30v2_users_android": 0, + "r30v2_users_electron": 0, + "r30v2_users_ios": 0, + "r30v2_users_web": 0, + "timestamp": 1651741851, + "total_nonbridged_users": 97, + "total_room_count": 0, + "total_users": 99, + "uptime_seconds": 30, + "version": "0.8.2" +} +``` \ No newline at end of file