diff --git a/internal/api/client/search/searchget_test.go b/internal/api/client/search/searchget_test.go index 9adc7a9d2..fe817099f 100644 --- a/internal/api/client/search/searchget_test.go +++ b/internal/api/client/search/searchget_test.go @@ -235,6 +235,23 @@ func (suite *SearchGetTestSuite) TestSearchLocalAccountByURI() { suite.NotNil(gotAccount) } +func (suite *SearchGetTestSuite) TestSearchLocalInstanceAccountByURI() { + query := "http://localhost:8080/users/localhost:8080" + resolve := false + + searchResult, err := suite.testSearch(query, resolve, http.StatusOK) + if err != nil { + suite.FailNow(err.Error()) + } + + if !suite.Len(searchResult.Accounts, 1) { + suite.FailNow("expected 1 account in search results but got 0") + } + + gotAccount := searchResult.Accounts[0] + suite.NotNil(gotAccount) +} + func (suite *SearchGetTestSuite) TestSearchLocalAccountByURL() { query := "http://localhost:8080/@the_mighty_zork" resolve := false diff --git a/internal/gtsmodel/account.go b/internal/gtsmodel/account.go index bae50a749..7956157ff 100644 --- a/internal/gtsmodel/account.go +++ b/internal/gtsmodel/account.go @@ -95,6 +95,12 @@ func (a *Account) IsRemote() bool { // IsInstance returns whether account is an instance internal actor account. func (a *Account) IsInstance() bool { + if a.IsLocal() { + // Check if our instance account. + return a.Username == config.GetHost() + } + + // Check if remote instance account. return a.Username == a.Domain || a.FollowersURI == "" || a.FollowingURI == "" || diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go index 2b53cce48..53c8af047 100644 --- a/internal/typeutils/internaltofrontend.go +++ b/internal/typeutils/internaltofrontend.go @@ -171,22 +171,26 @@ func (c *converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A acct = a.Username + "@" + d } else { - // This is a local user. - acct = a.Username + // This is a local account, try to + // fetch more info. Skip for instance + // accounts since they have no user. + if !a.IsInstance() { + user, err := c.db.GetUserByAccountID(ctx, a.ID) + if err != nil { + return nil, fmt.Errorf("AccountToAPIAccountPublic: error getting user from database for account id %s: %w", a.ID, err) + } - user, err := c.db.GetUserByAccountID(ctx, a.ID) - if err != nil { - return nil, fmt.Errorf("AccountToAPIAccountPublic: error getting user from database for account id %s: %w", a.ID, err) + switch { + case *user.Admin: + role = &apimodel.AccountRole{Name: apimodel.AccountRoleAdmin} + case *user.Moderator: + role = &apimodel.AccountRole{Name: apimodel.AccountRoleModerator} + default: + role = &apimodel.AccountRole{Name: apimodel.AccountRoleUser} + } } - switch { - case *user.Admin: - role = &apimodel.AccountRole{Name: apimodel.AccountRoleAdmin} - case *user.Moderator: - role = &apimodel.AccountRole{Name: apimodel.AccountRoleModerator} - default: - role = &apimodel.AccountRole{Name: apimodel.AccountRoleUser} - } + acct = a.Username // omit domain } // Remaining properties are simple and @@ -257,27 +261,31 @@ func (c *converter) AccountToAPIAccountBlocked(ctx context.Context, a *gtsmodel. // de-punify it just in case. d, err := util.DePunify(a.Domain) if err != nil { - return nil, fmt.Errorf("AccountToAPIAccountPublic: error de-punifying domain %s for account id %s: %w", a.Domain, a.ID, err) + return nil, fmt.Errorf("AccountToAPIAccountBlocked: error de-punifying domain %s for account id %s: %w", a.Domain, a.ID, err) } acct = a.Username + "@" + d } else { - // This is a local user. - acct = a.Username + // This is a local account, try to + // fetch more info. Skip for instance + // accounts since they have no user. + if !a.IsInstance() { + user, err := c.db.GetUserByAccountID(ctx, a.ID) + if err != nil { + return nil, fmt.Errorf("AccountToAPIAccountPublic: error getting user from database for account id %s: %w", a.ID, err) + } - user, err := c.db.GetUserByAccountID(ctx, a.ID) - if err != nil { - return nil, fmt.Errorf("AccountToAPIAccountPublic: error getting user from database for account id %s: %s", a.ID, err) + switch { + case *user.Admin: + role = &apimodel.AccountRole{Name: apimodel.AccountRoleAdmin} + case *user.Moderator: + role = &apimodel.AccountRole{Name: apimodel.AccountRoleModerator} + default: + role = &apimodel.AccountRole{Name: apimodel.AccountRoleUser} + } } - switch { - case *user.Admin: - role = &apimodel.AccountRole{Name: apimodel.AccountRoleAdmin} - case *user.Moderator: - role = &apimodel.AccountRole{Name: apimodel.AccountRoleModerator} - default: - role = &apimodel.AccountRole{Name: apimodel.AccountRoleUser} - } + acct = a.Username // omit domain } return &apimodel.Account{ @@ -307,7 +315,6 @@ func (c *converter) AccountToAdminAPIAccount(ctx context.Context, a *gtsmodel.Ac createdByApplicationID string ) - // take user-level information if possible if a.IsRemote() { // Domain may be in Punycode, // de-punify it just in case. @@ -317,7 +324,9 @@ func (c *converter) AccountToAdminAPIAccount(ctx context.Context, a *gtsmodel.Ac } domain = &d - } else { + } else if !a.IsInstance() { + // This is a local, non-instance + // acct; we can fetch more info. user, err := c.db.GetUserByAccountID(ctx, a.ID) if err != nil { return nil, fmt.Errorf("AccountToAdminAPIAccount: error getting user from database for account id %s: %w", a.ID, err) @@ -337,11 +346,13 @@ func (c *converter) AccountToAdminAPIAccount(ctx context.Context, a *gtsmodel.Ac if user.Account.Reason != "" { inviteRequest = &user.Account.Reason } + if *user.Admin { role.Name = apimodel.AccountRoleAdmin } else if *user.Moderator { role.Name = apimodel.AccountRoleModerator } + confirmed = !user.ConfirmedAt.IsZero() approved = *user.Approved disabled = *user.Disabled diff --git a/internal/typeutils/internaltofrontend_test.go b/internal/typeutils/internaltofrontend_test.go index 558d3acea..5a98303e8 100644 --- a/internal/typeutils/internaltofrontend_test.go +++ b/internal/typeutils/internaltofrontend_test.go @@ -248,6 +248,82 @@ func (suite *InternalToFrontendTestSuite) TestAccountToFrontendPublicPunycode() }`, string(b)) } +func (suite *InternalToFrontendTestSuite) TestLocalInstanceAccountToFrontendPublic() { + ctx := context.Background() + testAccount, err := suite.db.GetInstanceAccount(ctx, "") + if err != nil { + suite.FailNow(err.Error()) + } + + apiAccount, err := suite.typeconverter.AccountToAPIAccountPublic(ctx, testAccount) + suite.NoError(err) + suite.NotNil(apiAccount) + + b, err := json.MarshalIndent(apiAccount, "", " ") + suite.NoError(err) + + suite.Equal(`{ + "id": "01AY6P665V14JJR0AFVRT7311Y", + "username": "localhost:8080", + "acct": "localhost:8080", + "display_name": "", + "locked": false, + "discoverable": true, + "bot": false, + "created_at": "2020-05-17T13:10:59.000Z", + "note": "", + "url": "http://localhost:8080/@localhost:8080", + "avatar": "", + "avatar_static": "", + "header": "http://localhost:8080/assets/default_header.png", + "header_static": "http://localhost:8080/assets/default_header.png", + "followers_count": 0, + "following_count": 0, + "statuses_count": 0, + "last_status_at": null, + "emojis": [], + "fields": [] +}`, string(b)) +} + +func (suite *InternalToFrontendTestSuite) TestLocalInstanceAccountToFrontendBlocked() { + ctx := context.Background() + testAccount, err := suite.db.GetInstanceAccount(ctx, "") + if err != nil { + suite.FailNow(err.Error()) + } + + apiAccount, err := suite.typeconverter.AccountToAPIAccountBlocked(ctx, testAccount) + suite.NoError(err) + suite.NotNil(apiAccount) + + b, err := json.MarshalIndent(apiAccount, "", " ") + suite.NoError(err) + + suite.Equal(`{ + "id": "01AY6P665V14JJR0AFVRT7311Y", + "username": "localhost:8080", + "acct": "localhost:8080", + "display_name": "", + "locked": false, + "discoverable": false, + "bot": false, + "created_at": "2020-05-17T13:10:59.000Z", + "note": "", + "url": "http://localhost:8080/@localhost:8080", + "avatar": "", + "avatar_static": "", + "header": "", + "header_static": "", + "followers_count": 0, + "following_count": 0, + "statuses_count": 0, + "last_status_at": null, + "emojis": null, + "fields": null +}`, string(b)) +} + func (suite *InternalToFrontendTestSuite) TestStatusToFrontend() { testStatus := suite.testStatuses["admin_account_status_1"] requestingAccount := suite.testAccounts["local_account_1"]