instancepeersget.go (5533B)
1 // GoToSocial 2 // Copyright (C) GoToSocial Authors admin@gotosocial.org 3 // SPDX-License-Identifier: AGPL-3.0-or-later 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package instance 19 20 import ( 21 "fmt" 22 "net/http" 23 "strings" 24 25 apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" 26 "github.com/superseriousbusiness/gotosocial/internal/config" 27 "github.com/superseriousbusiness/gotosocial/internal/gtserror" 28 "github.com/superseriousbusiness/gotosocial/internal/oauth" 29 30 "github.com/gin-gonic/gin" 31 ) 32 33 // InstancePeersGETHandler swagger:operation GET /api/v1/instance/peers instancePeersGet 34 // 35 // --- 36 // tags: 37 // - instance 38 // 39 // produces: 40 // - application/json 41 // 42 // parameters: 43 // - 44 // name: filter 45 // type: string 46 // description: |- 47 // Comma-separated list of filters to apply to results. Recognized filters are: 48 // - `open` -- include peers that are not suspended or silenced 49 // - `suspended` -- include peers that have been suspended. 50 // 51 // If filter is `open`, only instances that haven't been suspended or silenced will be returned. 52 // 53 // If filter is `suspended`, only suspended instances will be shown. 54 // 55 // If filter is `open,suspended`, then all known instances will be returned. 56 // 57 // If filter is an empty string or not set, then `open` will be assumed as the default. 58 // in: query 59 // required: false 60 // default: "open" 61 // 62 // responses: 63 // '200': 64 // description: >- 65 // If no filter parameter is provided, or filter is empty, then a legacy, 66 // Mastodon-API compatible response will be returned. This will consist of 67 // just a 'flat' array of strings like `["example.com", "example.org"]`, 68 // which corresponds to domains this instance peers with. 69 // 70 // 71 // If a filter parameter is provided, then an array of objects with at least 72 // a `domain` key set on each object will be returned. 73 // 74 // 75 // Domains that are silenced or suspended will also have a key 76 // `suspended_at` or `silenced_at` that contains an iso8601 date string. 77 // If one of these keys is not present on the domain object, it is open. 78 // Suspended instances may in some cases be obfuscated, which means they 79 // will have some letters replaced by `*` to make it more difficult for 80 // bad actors to target instances with harassment. 81 // 82 // 83 // Whether a flat response or a more detailed response is returned, domains 84 // will be sorted alphabetically by hostname. 85 // schema: 86 // type: array 87 // items: 88 // "$ref": "#/definitions/domain" 89 // '400': 90 // description: bad request 91 // '401': 92 // description: unauthorized 93 // '403': 94 // description: forbidden 95 // '404': 96 // description: not found 97 // '406': 98 // description: not acceptable 99 // '500': 100 // description: internal server error 101 func (m *Module) InstancePeersGETHandler(c *gin.Context) { 102 authed, err := oauth.Authed(c, false, false, false, false) 103 if err != nil { 104 apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1) 105 return 106 } 107 108 isUnauthenticated := authed.Account == nil || authed.User == nil 109 110 if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil { 111 apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1) 112 return 113 } 114 115 var includeSuspended bool 116 var includeOpen bool 117 var flat bool 118 if filterParam := c.Query(PeersFilterKey); filterParam != "" { 119 filters := strings.Split(filterParam, ",") 120 for _, f := range filters { 121 trimmed := strings.TrimSpace(f) 122 switch { 123 case strings.EqualFold(trimmed, "suspended"): 124 includeSuspended = true 125 case strings.EqualFold(trimmed, "open"): 126 includeOpen = true 127 default: 128 err := fmt.Errorf("filter %s not recognized; accepted values are 'open', 'suspended'", trimmed) 129 apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) 130 return 131 } 132 } 133 } else { 134 // default is to only include open domains, and present 135 // them in a 'flat' manner (just an array of strings), 136 // to maintain compatibility with mastodon API 137 includeOpen = true 138 flat = true 139 } 140 141 if includeOpen && !config.GetInstanceExposePeers() && isUnauthenticated { 142 err := fmt.Errorf("peers open query requires an authenticated account/user") 143 apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1) 144 return 145 } 146 147 if includeSuspended && !config.GetInstanceExposeSuspended() && isUnauthenticated { 148 err := fmt.Errorf("peers suspended query requires an authenticated account/user") 149 apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1) 150 return 151 } 152 153 data, errWithCode := m.processor.InstancePeersGet(c.Request.Context(), includeSuspended, includeOpen, flat) 154 if errWithCode != nil { 155 apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) 156 return 157 } 158 159 c.JSON(http.StatusOK, data) 160 }