Skip to content

Commit

Permalink
[feature] Implement /api/v1/instance/peers endpoint (#660)
Browse files Browse the repository at this point in the history
* add missing license headers

* start adding instance peers get

* rename domainblock.go

* embed domain in domainblock so it can be reused

* update swagger docs

* add test instances to db

* update tests

* add/update instancepeersget

* update domain model

* add getinstancepeers to db

* instance-expose-peers, instance-expose-suspended

* add auth checks for both current filters

* attach endpoint to router

* include public comment

* obfuscate domain if required

* go mod tidy

* update swagger docs

* remove unnecessary comment

* return 'flat' peerlist if no query params provided
  • Loading branch information
tsmethurst committed Jun 23, 2022
1 parent 604600c commit 5f00d49
Show file tree
Hide file tree
Showing 27 changed files with 819 additions and 29 deletions.
101 changes: 98 additions & 3 deletions docs/api/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,35 @@ definitions:
type: object
x-go-name: Card
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
domain:
description: Domain represents a remote domain
properties:
domain:
description: The hostname of the domain.
example: example.org
type: string
x-go-name: Domain
public_comment:
description: If the domain is blocked, what's the publicly-stated reason for
the block.
example: they smell
type: string
x-go-name: PublicComment
silenced_at:
description: Time at which this domain was silenced. Key will not be present
on open domains.
example: "2021-07-30T09:20:25+00:00"
type: string
x-go-name: SilencedAt
suspended_at:
description: Time at which this domain was suspended. Key will not be present
on open domains.
example: "2021-07-30T09:20:25+00:00"
type: string
x-go-name: SuspendedAt
type: object
x-go-name: Domain
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
domainBlock:
description: DomainBlock represents a block on one domain
properties:
Expand All @@ -798,7 +827,7 @@ definitions:
type: string
x-go-name: CreatedBy
domain:
description: The hostname of the blocked domain.
description: The hostname of the domain.
example: example.org
type: string
x-go-name: Domain
Expand All @@ -822,16 +851,28 @@ definitions:
type: string
x-go-name: PrivateComment
public_comment:
description: Public comment for this block, visible if domain blocks are served
publicly.
description: If the domain is blocked, what's the publicly-stated reason for
the block.
example: they smell
type: string
x-go-name: PublicComment
silenced_at:
description: Time at which this domain was silenced. Key will not be present
on open domains.
example: "2021-07-30T09:20:25+00:00"
type: string
x-go-name: SilencedAt
subscription_id:
description: The ID of the subscription that created/caused this domain block.
example: 01FBW25TF5J67JW3HFHZCSD23K
type: string
x-go-name: SubscriptionID
suspended_at:
description: Time at which this domain was suspended. Key will not be present
on open domains.
example: "2021-07-30T09:20:25+00:00"
type: string
x-go-name: SuspendedAt
type: object
x-go-name: DomainBlock
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
Expand Down Expand Up @@ -3038,6 +3079,60 @@ paths:
for the instance.
tags:
- instance
/api/v1/instance/peers:
get:
operationId: instancePeersGet
parameters:
- description: |-
Comma-separated list of filters to apply to results. Recognized values are:
'open' -- include peers that are not suspended or silenced
'suspended' -- include peers that have been suspended.
If filter is 'open', only instances that haven't been suspended or silenced will be returned.
If filter is 'suspended', only suspended instances will be shown.
If filter is 'open,suspended', then all known instances will be returned.
If filter is an empty string or not set, then 'open' will be assumed as the default.
in: query
name: filter
type: string
produces:
- application/json
responses:
"200":
description: |-
If no filter parameter is provided, or filter is empty, then a legacy,
Mastodon-API compatible response will be returned. This will consist of
just a 'flat' array of strings like `["example.com", "example.org"]`.
If a filter parameter is provided, then an array of objects with at least
a `domain` key set on each object will be returned.
Domains that are silenced or suspended will also have a key
'suspended_at' or 'silenced_at' that contains an iso8601 date string.
If one of these keys is not present on the domain object, it is open.
Suspended instances may in some cases be obfuscated, which means they
will have some letters replaced by '*' to make it more difficult for
bad actors to target instances with harassment.
Whether a flat response or a more detailed response is returned, domains
will be sorted alphabetically by hostname.
schema:
items:
$ref: '#/definitions/domain'
type: array
"400":
description: bad request
"401":
description: unauthorized
"403":
description: forbidden
"404":
description: not found
"406":
description: not acceptable
"500":
description: internal server error
tags:
- instance
/api/v1/media:
post:
consumes:
Expand Down
26 changes: 26 additions & 0 deletions docs/configuration/instance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Instance

## Settings

```yaml
###########################
##### INSTANCE CONFIG #####
###########################

# Config pertaining to instance federation settings, pages to hide/expose, etc.

# Bool. Allow unauthenticated users to make queries to /api/v1/instance/peers?filter=open in order
# to see a list of instances that this instance 'peers' with. Even if set to 'false', then authenticated
# users (members of the instance) will still be able to query the endpoint.
# Options: [true, false]
# Default: false
instance-expose-peers: false

# Bool. Allow unauthenticated users to make queries to /api/v1/instance/peers?filter=suspended in order
# to see a list of instances that this instance blocks/suspends. This will also allow unauthenticated
# users to see the list through the web UI. Even if set to 'false', then authenticated users (members
# of the instance) will still be able to query the endpoint.
# Options: [true, false]
# Default: false
instance-expose-suspended: false
```
21 changes: 21 additions & 0 deletions example/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,27 @@ web-template-base-dir: "./web/template/"
# Default: "./web/assets/"
web-asset-base-dir: "./web/assets/"

###########################
##### INSTANCE CONFIG #####
###########################

# Config pertaining to instance federation settings, pages to hide/expose, etc.

# Bool. Allow unauthenticated users to make queries to /api/v1/instance/peers?filter=open in order
# to see a list of instances that this instance 'peers' with. Even if set to 'false', then authenticated
# users (members of the instance) will still be able to query the endpoint.
# Options: [true, false]
# Default: false
instance-expose-peers: false

# Bool. Allow unauthenticated users to make queries to /api/v1/instance/peers?filter=suspended in order
# to see a list of instances that this instance blocks/suspends. This will also allow unauthenticated
# users to see the list through the web UI. Even if set to 'false', then authenticated users (members
# of the instance) will still be able to query the endpoint.
# Options: [true, false]
# Default: false
instance-expose-suspended: false

###########################
##### ACCOUNTS CONFIG #####
###########################
Expand Down
23 changes: 23 additions & 0 deletions internal/api/client/instance/instance.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
/*
GoToSocial
Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package instance

import (
Expand All @@ -11,6 +29,10 @@ import (
const (
// InstanceInformationPath is for serving instance info requests
InstanceInformationPath = "api/v1/instance"
// InstancePeersPath is for serving instance peers requests.
InstancePeersPath = InstanceInformationPath + "/peers"
// PeersFilterKey is used to provide filters to /api/v1/instance/peers
PeersFilterKey = "filter"
)

// Module implements the ClientModule interface
Expand All @@ -29,5 +51,6 @@ func New(processor processing.Processor) api.ClientModule {
func (m *Module) Route(s router.Router) error {
s.AttachHandler(http.MethodGet, InstanceInformationPath, m.InstanceInformationGETHandler)
s.AttachHandler(http.MethodPatch, InstanceInformationPath, m.InstanceUpdatePATCHHandler)
s.AttachHandler(http.MethodGet, InstancePeersPath, m.InstancePeersGETHandler)
return nil
}
3 changes: 1 addition & 2 deletions internal/api/client/instance/instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ package instance_test
import (
"bytes"
"fmt"
"net/http"
"net/http/httptest"

"codeberg.org/gruf/go-store/kv"
Expand Down Expand Up @@ -113,7 +112,7 @@ func (suite *InstanceStandardTestSuite) newContext(recorder *httptest.ResponseRe
baseURI := fmt.Sprintf("%s://%s", protocol, host)
requestURI := fmt.Sprintf("%s/%s", baseURI, requestPath)

ctx.Request = httptest.NewRequest(http.MethodPatch, requestURI, bytes.NewReader(requestBody)) // the endpoint we're hitting
ctx.Request = httptest.NewRequest(requestMethod, requestURI, bytes.NewReader(requestBody)) // the endpoint we're hitting

if bodyContentType != "" {
ctx.Request.Header.Set("Content-Type", bodyContentType)
Expand Down
18 changes: 18 additions & 0 deletions internal/api/client/instance/instanceget.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
/*
GoToSocial
Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package instance

import (
Expand Down
18 changes: 18 additions & 0 deletions internal/api/client/instance/instancepatch.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
/*
GoToSocial
Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package instance

import (
Expand Down
6 changes: 3 additions & 3 deletions internal/api/client/instance/instancepatch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch1() {
b, err := io.ReadAll(result.Body)
suite.NoError(err)

suite.Equal(`{"uri":"http://localhost:8080","title":"Example Instance","description":"","short_description":"","email":"someone@example.org","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":0,"status_count":16,"user_count":4},"thumbnail":"","contact_account":{"id":"01F8MH17FWEB39HZJ76B6VXSKF","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2022-05-17T13:10:59.000Z","note":"","url":"http://localhost:8080/@admin","avatar":"","avatar_static":"","header":"","header_static":"","followers_count":1,"following_count":1,"statuses_count":4,"last_status_at":"2021-10-20T10:41:37.000Z","emojis":[],"fields":[]},"max_toot_chars":5000}`, string(b))
suite.Equal(`{"uri":"http://localhost:8080","title":"Example Instance","description":"","short_description":"","email":"someone@example.org","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"","contact_account":{"id":"01F8MH17FWEB39HZJ76B6VXSKF","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2022-05-17T13:10:59.000Z","note":"","url":"http://localhost:8080/@admin","avatar":"","avatar_static":"","header":"","header_static":"","followers_count":1,"following_count":1,"statuses_count":4,"last_status_at":"2021-10-20T10:41:37.000Z","emojis":[],"fields":[]},"max_toot_chars":5000}`, string(b))
}

func (suite *InstancePatchTestSuite) TestInstancePatch2() {
Expand Down Expand Up @@ -93,7 +93,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch2() {
b, err := io.ReadAll(result.Body)
suite.NoError(err)

suite.Equal(`{"uri":"http://localhost:8080","title":"Geoff's Instance","description":"","short_description":"","email":"","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":0,"status_count":16,"user_count":4},"thumbnail":"","max_toot_chars":5000}`, string(b))
suite.Equal(`{"uri":"http://localhost:8080","title":"Geoff's Instance","description":"","short_description":"","email":"","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"","max_toot_chars":5000}`, string(b))
}

func (suite *InstancePatchTestSuite) TestInstancePatch3() {
Expand Down Expand Up @@ -123,7 +123,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch3() {
b, err := io.ReadAll(result.Body)
suite.NoError(err)

suite.Equal(`{"uri":"http://localhost:8080","title":"localhost:8080","description":"","short_description":"\u003cp\u003eThis is some html, which is \u003cem\u003eallowed\u003c/em\u003e in short descriptions.\u003c/p\u003e","email":"","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":0,"status_count":16,"user_count":4},"thumbnail":"","max_toot_chars":5000}`, string(b))
suite.Equal(`{"uri":"http://localhost:8080","title":"localhost:8080","description":"","short_description":"\u003cp\u003eThis is some html, which is \u003cem\u003eallowed\u003c/em\u003e in short descriptions.\u003c/p\u003e","email":"","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"","max_toot_chars":5000}`, string(b))
}

func (suite *InstancePatchTestSuite) TestInstancePatch4() {
Expand Down
Loading

0 comments on commit 5f00d49

Please sign in to comment.