From 6fed50ce536807934089a740d06db0ad923fff7f Mon Sep 17 00:00:00 2001 From: yqrashawn Date: Thu, 18 May 2023 14:27:16 +0800 Subject: [PATCH] feat: media server and colorhash related change (#3500) --- VERSION | 2 +- api/geth_backend.go | 56 +++++++++++++++++++++++--- mobile/status.go | 40 ------------------- multiaccounts/database.go | 1 - node/get_status_node.go | 42 ++++++++++++++++++-- server/handlers.go | 64 ++++++++++++++++++++++++++---- server/pairing/sync_device_test.go | 3 ++ 7 files changed, 149 insertions(+), 59 deletions(-) diff --git a/VERSION b/VERSION index 0b8a67e57..d650db2f7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.151.8 +0.151.9 diff --git a/api/geth_backend.go b/api/geth_backend.go index c34e995d5..80a10d6f7 100644 --- a/api/geth_backend.go +++ b/api/geth_backend.go @@ -35,6 +35,8 @@ import ( "github.com/status-im/status-go/nodecfg" "github.com/status-im/status-go/params" "github.com/status-im/status-go/protocol" + identityUtils "github.com/status-im/status-go/protocol/identity" + "github.com/status-im/status-go/protocol/identity/colorhash" "github.com/status-im/status-go/protocol/requests" "github.com/status-im/status-go/rpc" "github.com/status-im/status-go/services/ext" @@ -180,6 +182,13 @@ func (b *GethStatusBackend) OpenAccounts() error { b.multiaccountsDB = db // Probably we should iron out a bit better how to create/dispose of the status-service b.statusNode.SetMultiaccountsDB(db) + + err = b.statusNode.StartMediaServerWithoutDB() + if err != nil { + b.log.Error("failed to start media server without app db", "err", err) + return err + } + return nil } @@ -968,12 +977,44 @@ func (b *GethStatusBackend) VerifyDatabasePassword(keyUID string, password strin return nil } -func (b *GethStatusBackend) SaveAccountAndStartNodeWithKey(acc multiaccounts.Account, password string, settings settings.Settings, nodecfg *params.NodeConfig, subaccs []*accounts.Account, keyHex string) error { - err := b.SaveAccount(acc) +func enrichMultiaccountInfo(account *multiaccounts.Account, subaccs []*accounts.Account) error { + if account.ColorHash != nil && account.ColorID != 0 { + return nil + } + + for i, acc := range subaccs { + subaccs[i].KeyUID = account.KeyUID + + if acc.Chat { + colorHash, err := colorhash.GenerateFor(string(acc.PublicKey.Bytes())) + if err != nil { + return err + } + account.ColorHash = colorHash + + colorID, err := identityUtils.ToColorID(string(acc.PublicKey.Bytes())) + if err != nil { + return err + } + account.ColorID = colorID + + break + } + } + + return nil +} + +func (b *GethStatusBackend) SaveAccountAndStartNodeWithKey(account multiaccounts.Account, password string, settings settings.Settings, nodecfg *params.NodeConfig, subaccs []*accounts.Account, keyHex string) error { + err := enrichMultiaccountInfo(&account, subaccs) if err != nil { return err } - err = b.ensureAppDBOpened(acc, password) + err = b.SaveAccount(account) + if err != nil { + return err + } + err = b.ensureAppDBOpened(account, password) if err != nil { return err } @@ -981,7 +1022,7 @@ func (b *GethStatusBackend) SaveAccountAndStartNodeWithKey(acc multiaccounts.Acc if err != nil { return err } - return b.StartNodeWithKey(acc, password, keyHex) + return b.StartNodeWithKey(account, password, keyHex) } // StartNodeWithAccountAndInitialConfig is used after account and config was generated. @@ -995,7 +1036,12 @@ func (b *GethStatusBackend) StartNodeWithAccountAndInitialConfig( subaccs []*accounts.Account, ) error { b.log.Info("node config", "config", nodecfg) - err := b.SaveAccount(account) + + err := enrichMultiaccountInfo(&account, subaccs) + if err != nil { + return err + } + err = b.SaveAccount(account) if err != nil { return err } diff --git a/mobile/status.go b/mobile/status.go index f0446baf7..9e35c1f63 100644 --- a/mobile/status.go +++ b/mobile/status.go @@ -335,26 +335,6 @@ func SaveAccountAndLogin(accountData, password, settingsJSON, configJSON, subacc return makeJSONResponse(err) } - for i, acc := range subaccs { - subaccs[i].KeyUID = account.KeyUID - - if acc.Chat { - colorHash, err := colorhash.GenerateFor(string(acc.PublicKey.Bytes())) - if err != nil { - return makeJSONResponse(err) - } - account.ColorHash = colorHash - - colorID, err := identityUtils.ToColorID(string(acc.PublicKey.Bytes())) - if err != nil { - return makeJSONResponse(err) - } - account.ColorID = colorID - - break - } - } - api.RunAsync(func() error { log.Debug("starting a node, and saving account with configuration", "key-uid", account.KeyUID) err := statusBackend.StartNodeWithAccountAndInitialConfig(account, password, settings, &conf, subaccs) @@ -417,26 +397,6 @@ func SaveAccountAndLoginWithKeycard(accountData, password, settingsJSON, configJ return makeJSONResponse(err) } - for i, acc := range subaccs { - subaccs[i].KeyUID = account.KeyUID - - if acc.Chat { - colorHash, err := colorhash.GenerateFor(string(acc.PublicKey.Bytes())) - if err != nil { - return makeJSONResponse(err) - } - account.ColorHash = colorHash - - colorID, err := identityUtils.ToColorID(string(acc.PublicKey.Bytes())) - if err != nil { - return makeJSONResponse(err) - } - account.ColorID = colorID - - break - } - } - api.RunAsync(func() error { log.Debug("starting a node, and saving account with configuration", "key-uid", account.KeyUID) err := statusBackend.SaveAccountAndStartNodeWithKey(account, password, settings, &conf, subaccs, keyHex) diff --git a/multiaccounts/database.go b/multiaccounts/database.go index 989d80288..5a498df99 100644 --- a/multiaccounts/database.go +++ b/multiaccounts/database.go @@ -362,7 +362,6 @@ func (db *Database) DeleteAccount(keyUID string) error { } // Account images - func (db *Database) GetIdentityImages(keyUID string) (iis []*images.IdentityImage, err error) { rows, err := db.db.Query(`SELECT key_uid, name, image_payload, width, height, file_size, resize_target, clock FROM identity_images WHERE key_uid = ?`, keyUID) if err != nil { diff --git a/node/get_status_node.go b/node/get_status_node.go index 05f10af26..665430e08 100644 --- a/node/get_status_node.go +++ b/node/get_status_node.go @@ -188,6 +188,34 @@ type StartOptions struct { AccountsManager *accounts.Manager } +// StartMediaServerWithoutDB starts media server without starting the node +// The server can only handle requests that don't require appdb or IPFS downloader +func (n *StatusNode) StartMediaServerWithoutDB() error { + if n.isRunning() { + n.log.Debug("node is already running, no need to StartMediaServerWithoutDB") + return nil + } + + if n.httpServer != nil { + if err := n.httpServer.Stop(); err != nil { + return err + } + } + + httpServer, err := server.NewMediaServer(nil, nil, n.multiaccountsDB) + if err != nil { + return err + } + + n.httpServer = httpServer + + if err := n.httpServer.Start(); err != nil { + return err + } + + return nil +} + // StartWithOptions starts current StatusNode, failing if it's already started. // It takes some options that allows to further configure starting process. func (n *StatusNode) StartWithOptions(config *params.NodeConfig, options StartOptions) error { @@ -240,18 +268,24 @@ func (n *StatusNode) startWithDB(config *params.NodeConfig, accs *accounts.Manag n.downloader = ipfs.NewDownloader(config.RootDataDir) + if n.httpServer != nil { + if err := n.httpServer.Stop(); err != nil { + return err + } + } + httpServer, err := server.NewMediaServer(n.appDB, n.downloader, n.multiaccountsDB) if err != nil { return err } - if err := httpServer.Start(); err != nil { + n.httpServer = httpServer + + if err := n.httpServer.Start(); err != nil { return err } - n.httpServer = httpServer - - if err := n.initServices(config, httpServer); err != nil { + if err := n.initServices(config, n.httpServer); err != nil { return err } return n.startGethNode() diff --git a/server/handlers.go b/server/handlers.go index f24310a28..43eff4b5d 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -36,6 +36,18 @@ const ( type HandlerPatternMap map[string]http.HandlerFunc +func handleRequestDBMissing(logger *zap.Logger) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + logger.Error("can't handle media request without appdb") + } +} + +func handleRequestDownloaderMissing(logger *zap.Logger) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + logger.Error("can't handle media request without ipfs downloader") + } +} + func handleAccountImages(multiaccountsDB *multiaccounts.Database, logger *zap.Logger) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { params := r.URL.Query() @@ -60,21 +72,33 @@ func handleAccountImages(multiaccountsDB *multiaccounts.Database, logger *zap.Lo var payload = identityImage.Payload if ringEnabled(params) { - pks, ok := params["publicKey"] - if !ok || len(pks) == 0 { - logger.Error("no publicKey") + account, err := multiaccountsDB.GetAccount(keyUids[0]) + + if err != nil { + logger.Error("handleAccountImages: failed to GetAccount .", zap.String("keyUid", keyUids[0]), zap.Error(err)) return } - colorHash, err := colorhash.GenerateFor(pks[0]) - if err != nil { - logger.Error("could not generate color hash") - return + + accColorHash := account.ColorHash + + if accColorHash == nil { + pks, ok := params["publicKey"] + if !ok || len(pks) == 0 { + logger.Error("no publicKey") + return + } + + accColorHash, err = colorhash.GenerateFor(pks[0]) + if err != nil { + logger.Error("could not generate color hash") + return + } } var theme = getTheme(params, logger) payload, err = ring.DrawRing(&ring.DrawRingParam{ - Theme: theme, ColorHash: colorHash, ImageBytes: identityImage.Payload, Height: identityImage.Height, Width: identityImage.Width, + Theme: theme, ColorHash: accColorHash, ImageBytes: identityImage.Payload, Height: identityImage.Height, Width: identityImage.Width, }) if err != nil { @@ -103,6 +127,10 @@ func handleAccountImages(multiaccountsDB *multiaccounts.Database, logger *zap.Lo } func handleContactImages(db *sql.DB, logger *zap.Logger) http.HandlerFunc { + if db == nil { + return handleRequestDBMissing(logger) + } + return func(w http.ResponseWriter, r *http.Request) { params := r.URL.Query() pks, ok := params["publicKey"] @@ -226,6 +254,10 @@ func handleIdenticon(logger *zap.Logger) http.HandlerFunc { } func handleDiscordAuthorAvatar(db *sql.DB, logger *zap.Logger) http.HandlerFunc { + if db == nil { + return handleRequestDBMissing(logger) + } + return func(w http.ResponseWriter, r *http.Request) { authorIDs, ok := r.URL.Query()["authorId"] if !ok || len(authorIDs) == 0 { @@ -260,6 +292,10 @@ func handleDiscordAuthorAvatar(db *sql.DB, logger *zap.Logger) http.HandlerFunc } func handleDiscordAttachment(db *sql.DB, logger *zap.Logger) http.HandlerFunc { + if db == nil { + return handleRequestDBMissing(logger) + } + return func(w http.ResponseWriter, r *http.Request) { messageIDs, ok := r.URL.Query()["messageId"] if !ok || len(messageIDs) == 0 { @@ -299,6 +335,10 @@ func handleDiscordAttachment(db *sql.DB, logger *zap.Logger) http.HandlerFunc { } func handleImage(db *sql.DB, logger *zap.Logger) http.HandlerFunc { + if db == nil { + return handleRequestDBMissing(logger) + } + return func(w http.ResponseWriter, r *http.Request) { messageIDs, ok := r.URL.Query()["messageId"] if !ok || len(messageIDs) == 0 { @@ -332,6 +372,10 @@ func handleImage(db *sql.DB, logger *zap.Logger) http.HandlerFunc { } func handleAudio(db *sql.DB, logger *zap.Logger) http.HandlerFunc { + if db == nil { + return handleRequestDBMissing(logger) + } + return func(w http.ResponseWriter, r *http.Request) { messageIDs, ok := r.URL.Query()["messageId"] if !ok || len(messageIDs) == 0 { @@ -361,6 +405,10 @@ func handleAudio(db *sql.DB, logger *zap.Logger) http.HandlerFunc { } func handleIPFS(downloader *ipfs.Downloader, logger *zap.Logger) http.HandlerFunc { + if downloader == nil { + return handleRequestDownloaderMissing(logger) + } + return func(w http.ResponseWriter, r *http.Request) { hashes, ok := r.URL.Query()["hash"] if !ok || len(hashes) == 0 { diff --git a/server/pairing/sync_device_test.go b/server/pairing/sync_device_test.go index 4f54ae18b..de6e6094a 100644 --- a/server/pairing/sync_device_test.go +++ b/server/pairing/sync_device_test.go @@ -110,6 +110,9 @@ func (s *SyncDeviceSuite) prepareBackendWithAccount(tmpdir string) *api.GethStat accounts := []*accounts.Account{walletAccount, chatAccount} err = backend.StartNodeWithAccountAndInitialConfig(account, s.password, *settings, nodeConfig, accounts) require.NoError(s.T(), err) + multiaccounts, err := backend.GetAccounts() + require.NoError(s.T(), err) + require.NotEmpty(s.T(), multiaccounts[0].ColorHash) return backend }