instancepatch.go (5622B)
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 "errors" 22 "fmt" 23 "net/http" 24 25 "github.com/gin-gonic/gin" 26 apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" 27 apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" 28 "github.com/superseriousbusiness/gotosocial/internal/config" 29 "github.com/superseriousbusiness/gotosocial/internal/gtserror" 30 "github.com/superseriousbusiness/gotosocial/internal/oauth" 31 ) 32 33 // InstanceUpdatePATCHHandler swagger:operation PATCH /api/v1/instance instanceUpdate 34 // 35 // Update your instance information and/or upload a new avatar/header for the instance. 36 // 37 // This requires admin permissions on the instance. 38 // 39 // --- 40 // tags: 41 // - instance 42 // 43 // consumes: 44 // - multipart/form-data 45 // 46 // produces: 47 // - application/json 48 // 49 // parameters: 50 // - 51 // name: title 52 // in: formData 53 // description: Title to use for the instance. 54 // type: string 55 // maximum: 40 56 // allowEmptyValue: true 57 // - 58 // name: contact_username 59 // in: formData 60 // description: >- 61 // Username of the contact account. 62 // This must be the username of an instance admin. 63 // type: string 64 // allowEmptyValue: true 65 // - 66 // name: contact_email 67 // in: formData 68 // description: Email address to use as the instance contact. 69 // type: string 70 // allowEmptyValue: true 71 // - 72 // name: short_description 73 // in: formData 74 // description: Short description of the instance. 75 // type: string 76 // maximum: 500 77 // allowEmptyValue: true 78 // - 79 // name: description 80 // in: formData 81 // description: Longer description of the instance. 82 // type: string 83 // maximum: 5000 84 // allowEmptyValue: true 85 // - 86 // name: terms 87 // in: formData 88 // description: Terms and conditions of the instance. 89 // type: string 90 // maximum: 5000 91 // allowEmptyValue: true 92 // - 93 // name: thumbnail 94 // in: formData 95 // description: Thumbnail image to use for the instance. 96 // type: file 97 // - 98 // name: thumbnail_description 99 // in: formData 100 // description: Image description of the submitted instance thumbnail. 101 // type: string 102 // - 103 // name: header 104 // in: formData 105 // description: Header image to use for the instance. 106 // type: file 107 // 108 // security: 109 // - OAuth2 Bearer: 110 // - admin 111 // 112 // responses: 113 // '200': 114 // description: "The newly updated instance." 115 // schema: 116 // "$ref": "#/definitions/instance" 117 // '400': 118 // description: bad request 119 // '401': 120 // description: unauthorized 121 // '403': 122 // description: forbidden 123 // '404': 124 // description: not found 125 // '406': 126 // description: not acceptable 127 // '500': 128 // description: internal server error 129 func (m *Module) InstanceUpdatePATCHHandler(c *gin.Context) { 130 authed, err := oauth.Authed(c, true, true, true, true) 131 if err != nil { 132 apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1) 133 return 134 } 135 136 if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil { 137 apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1) 138 return 139 } 140 141 if !*authed.User.Admin { 142 err := errors.New("user is not an admin so cannot update instance settings") 143 apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGetV1) 144 return 145 } 146 147 form := &apimodel.InstanceSettingsUpdateRequest{} 148 if err := c.ShouldBind(&form); err != nil { 149 apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) 150 return 151 } 152 153 if err := validateInstanceUpdate(form); err != nil { 154 apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) 155 return 156 } 157 158 i, errWithCode := m.processor.InstancePatch(c.Request.Context(), form) 159 if errWithCode != nil { 160 apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) 161 return 162 } 163 164 c.JSON(http.StatusOK, i) 165 } 166 167 func validateInstanceUpdate(form *apimodel.InstanceSettingsUpdateRequest) error { 168 if form.Title == nil && 169 form.ContactUsername == nil && 170 form.ContactEmail == nil && 171 form.ShortDescription == nil && 172 form.Description == nil && 173 form.Terms == nil && 174 form.Avatar == nil && 175 form.AvatarDescription == nil && 176 form.Header == nil { 177 return errors.New("empty form submitted") 178 } 179 180 if form.Avatar != nil { 181 maxImageSize := config.GetMediaImageMaxSize() 182 if size := form.Avatar.Size; size > int64(maxImageSize) { 183 return fmt.Errorf("file size limit exceeded: limit is %d bytes but desired instance avatar was %d bytes", maxImageSize, size) 184 } 185 } 186 187 if form.AvatarDescription != nil { 188 maxDescriptionChars := config.GetMediaDescriptionMaxChars() 189 if length := len([]rune(*form.AvatarDescription)); length > maxDescriptionChars { 190 return fmt.Errorf("avatar description length must be less than %d characters (inclusive), but provided avatar description was %d chars", maxDescriptionChars, length) 191 } 192 } 193 194 return nil 195 }