continue with instance patch stuff
This commit is contained in:
parent
c08dc0937b
commit
7974d5e2e6
|
@ -0,0 +1,50 @@
|
|||
package instance
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
func (m *Module) InstanceUpdatePATCHHandler(c *gin.Context) {
|
||||
l := m.log.WithField("func", "InstanceUpdatePATCHHandler")
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
l.Debugf("couldn't auth: %s", err)
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// only admins can update instance settings
|
||||
if !authed.User.Admin {
|
||||
l.Debug("user is not an admin so cannot update instance settings")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "not an admin"})
|
||||
return
|
||||
}
|
||||
|
||||
l.Debugf("parsing request form %s", c.Request.Form)
|
||||
form := &model.InstanceSettingsUpdateRequest{}
|
||||
if err := c.ShouldBind(&form); err != nil || form == nil {
|
||||
l.Debugf("could not parse form from request: %s", err)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// if everything on the form is nil, then nothing has been set and we shouldn't continue
|
||||
if form.SiteTitle == nil && form.RegistrationsMode == nil && form.SiteContactUsername == nil && form.SiteContactEmail == nil && form.SiteShortDescription == nil && form.SiteDescription == nil && form.SiteTerms == nil && form.Avatar == nil && form.Header == nil {
|
||||
l.Debugf("could not parse form from request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "empty form submitted"})
|
||||
return
|
||||
}
|
||||
|
||||
i, errWithCode := m.processor.InstancePatch(form)
|
||||
if errWithCode != nil {
|
||||
l.Debugf("error with instance patch request: %s", errWithCode.Error())
|
||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, i)
|
||||
}
|
|
@ -18,8 +18,6 @@
|
|||
|
||||
package model
|
||||
|
||||
import "mime/multipart"
|
||||
|
||||
// AdminAccountInfo represents the *admin* view of an account's details. See here: https://docs.joinmastodon.org/entities/admin-account/
|
||||
type AdminAccountInfo struct {
|
||||
// The ID of the account in the database.
|
||||
|
@ -81,24 +79,3 @@ type AdminReportInfo struct {
|
|||
// Statuses attached to the report, for context.
|
||||
Statuses []Status `json:"statuses"`
|
||||
}
|
||||
|
||||
// AdminSiteSettings is the form to be parsed on a POST or PATCH to /admin/settings
|
||||
type AdminSiteSettings struct {
|
||||
FormAdminSettings FormAdminSettings `form:"form_admin_settings" json:"form_admin_settings" xml:"form_admin_settings"`
|
||||
}
|
||||
|
||||
// FormAdminSettings wraps a whole bunch of instance settings
|
||||
type FormAdminSettings struct {
|
||||
SiteTitle *string `form:"site_title" json:"site_title" xml:"site_title"`
|
||||
RegistrationsMode *string `form:"registrations_mode" json:"registrations_mode" xml:"registrations_mode"`
|
||||
SiteContactUsername *string `form:"site_contact_username" json:"site_contact_username" xml:"site_contact_username"`
|
||||
SiteContactEmail *string `form:"site_contact_email" json:"site_contact_email" xml:"site_contact_email"`
|
||||
SiteShortDescription *string `form:"site_short_description" json:"site_short_description" xml:"site_short_description"`
|
||||
SiteDescription *string `form:"site_description" json:"site_description" xml:"site_description"`
|
||||
SiteExtendedDescription *string `form:"site_extended_description" json:"site_extended_description" xml:"site_extended_description"`
|
||||
SiteTerms *string `form:"site_terms" json:"site_terms" xml:"site_terms"`
|
||||
Thumbnail *multipart.FileHeader `form:"thumbnail" json:"thumbnail" xml:"thumbnail"`
|
||||
Hero *multipart.FileHeader `form:"hero" json:"hero" xml:"hero"`
|
||||
Mascot *multipart.FileHeader `form:"mascot" json:"mascot" xml:"mascot"`
|
||||
RequireInviteText *bool `form:"require_invite_text" json:"require_invite_text" xml:"require_invite_text"`
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
package model
|
||||
|
||||
import "mime/multipart"
|
||||
|
||||
// Instance represents the software instance of Mastodon running on this domain. See https://docs.joinmastodon.org/entities/instance/
|
||||
type Instance struct {
|
||||
// REQUIRED
|
||||
|
@ -70,3 +72,16 @@ type InstanceStats struct {
|
|||
// Domains federated with this instance.
|
||||
DomainCount int `json:"domain_count"`
|
||||
}
|
||||
|
||||
// InstanceSettingsUpdateRequest is the form to be parsed on a PATCH to /api/v1/instance
|
||||
type InstanceSettingsUpdateRequest struct {
|
||||
SiteTitle *string `form:"site_title" json:"site_title" xml:"site_title"`
|
||||
RegistrationsMode *string `form:"registrations_mode" json:"registrations_mode" xml:"registrations_mode"`
|
||||
SiteContactUsername *string `form:"site_contact_username" json:"site_contact_username" xml:"site_contact_username"`
|
||||
SiteContactEmail *string `form:"site_contact_email" json:"site_contact_email" xml:"site_contact_email"`
|
||||
SiteShortDescription *string `form:"site_short_description" json:"site_short_description" xml:"site_short_description"`
|
||||
SiteDescription *string `form:"site_description" json:"site_description" xml:"site_description"`
|
||||
SiteTerms *string `form:"site_terms" json:"site_terms" xml:"site_terms"`
|
||||
Avatar *multipart.FileHeader `form:"avatar" json:"avatar" xml:"avatar"`
|
||||
Header *multipart.FileHeader `form:"header" json:"header" xml:"header"`
|
||||
}
|
||||
|
|
|
@ -40,3 +40,17 @@ func (p *processor) InstanceGet(domain string) (*apimodel.Instance, gtserror.Wit
|
|||
|
||||
return ai, nil
|
||||
}
|
||||
|
||||
func (p *processor) InstancePatch(form *apimodel.InstanceSettingsUpdateRequest) (*apimodel.Instance, gtserror.WithCode) {
|
||||
i := >smodel.Instance{}
|
||||
if err := p.db.GetWhere([]db.Where{{Key: "domain", Value: p.config.Host}}, i); err != nil {
|
||||
return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error fetching instance %s: %s", p.config.Host, err))
|
||||
}
|
||||
|
||||
ai, err := p.tc.InstanceToMasto(i)
|
||||
if err != nil {
|
||||
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting instance to api representation: %s", err))
|
||||
}
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
return ai, nil
|
||||
}
|
||||
|
|
|
@ -95,6 +95,10 @@ type Processor interface {
|
|||
|
||||
// InstanceGet retrieves instance information for serving at api/v1/instance
|
||||
InstanceGet(domain string) (*apimodel.Instance, gtserror.WithCode)
|
||||
// InstancePatch updates this instance according to the given form.
|
||||
//
|
||||
// It should already be ascertained that the requesting account is authenticated and an admin.
|
||||
InstancePatch(form *apimodel.InstanceSettingsUpdateRequest) (*apimodel.Instance, gtserror.WithCode)
|
||||
|
||||
// MediaCreate handles the creation of a media attachment, using the given form.
|
||||
MediaCreate(authed *oauth.Auth, form *apimodel.AttachmentRequest) (*apimodel.Attachment, error)
|
||||
|
|
|
@ -24,12 +24,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
minimumPasswordEntropy = 60 // dictates password strength. See https://github.com/wagslane/go-password-validator
|
||||
minimumReasonLength = 40
|
||||
maximumReasonLength = 500
|
||||
maximumEmailLength = 256
|
||||
maximumUsernameLength = 64
|
||||
maximumPasswordLength = 64
|
||||
maximumEmojiShortcodeLength = 30
|
||||
maximumHashtagLength = 30
|
||||
)
|
||||
|
|
|
@ -27,6 +27,17 @@ import (
|
|||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
const (
|
||||
maximumPasswordLength = 64
|
||||
minimumPasswordEntropy = 60 // dictates password strength. See https://github.com/wagslane/go-password-validator
|
||||
minimumReasonLength = 40
|
||||
maximumReasonLength = 500
|
||||
maximumSiteTitleLength = 40
|
||||
maximumShortDescriptionLength = 500
|
||||
maximumDescriptionLength = 5000
|
||||
maximumSiteTermsLength = 5000
|
||||
)
|
||||
|
||||
// ValidateNewPassword returns an error if the given password is not sufficiently strong, or nil if it's ok.
|
||||
func ValidateNewPassword(password string) error {
|
||||
if password == "" {
|
||||
|
@ -47,12 +58,8 @@ func ValidateUsername(username string) error {
|
|||
return errors.New("no username provided")
|
||||
}
|
||||
|
||||
if len(username) > maximumUsernameLength {
|
||||
return fmt.Errorf("username should be no more than %d chars but '%s' was %d", maximumUsernameLength, username, len(username))
|
||||
}
|
||||
|
||||
if !usernameValidationRegex.MatchString(username) {
|
||||
return fmt.Errorf("given username %s was invalid: must contain only lowercase letters, numbers, and underscores", username)
|
||||
return fmt.Errorf("given username %s was invalid: must contain only lowercase letters, numbers, and underscores, max %d characters", username, maximumUsernameLength)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -65,10 +72,6 @@ func ValidateEmail(email string) error {
|
|||
return errors.New("no email provided")
|
||||
}
|
||||
|
||||
if len(email) > maximumEmailLength {
|
||||
return fmt.Errorf("email address should be no more than %d chars but '%s' was %d", maximumEmailLength, email, len(email))
|
||||
}
|
||||
|
||||
_, err := mail.ParseAddress(email)
|
||||
return err
|
||||
}
|
||||
|
@ -132,3 +135,39 @@ func ValidateEmojiShortcode(shortcode string) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateSiteTitle ensures that the given site title is within spec.
|
||||
func ValidateSiteTitle(siteTitle string) error {
|
||||
if len(siteTitle) > maximumSiteTitleLength {
|
||||
return fmt.Errorf("site title should be no more than %d chars but given title was %d", maximumSiteTitleLength, len(siteTitle))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateSiteShortDescription ensures that the given site short description is within spec.
|
||||
func ValidateSiteShortDescription(d string) error {
|
||||
if len(d) > maximumShortDescriptionLength {
|
||||
return fmt.Errorf("short description should be no more than %d chars but given description was %d", maximumShortDescriptionLength, len(d))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateSiteDescription ensures that the given site description is within spec.
|
||||
func ValidateSiteDescription(d string) error {
|
||||
if len(d) > maximumDescriptionLength {
|
||||
return fmt.Errorf("description should be no more than %d chars but given description was %d", maximumDescriptionLength, len(d))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateSiteTerms ensures that the given site terms string is within spec.
|
||||
func ValidateSiteTerms(t string) error {
|
||||
if len(t) > maximumSiteTermsLength {
|
||||
return fmt.Errorf("terms should be no more than %d chars but given terms was %d", maximumSiteTermsLength, len(t))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue