diff options
| author | 2021-05-10 16:29:05 +0200 | |
|---|---|---|
| committer | 2021-05-10 16:29:05 +0200 | |
| commit | 742f985d5b0620ad14015f9a2df9940edc254bf4 (patch) | |
| tree | 5c8d469fd015efc6f8459f2d9df04132877285a3 /internal/api/client | |
| parent | Webfinger + Small fixes (#20) (diff) | |
| download | gotosocial-742f985d5b0620ad14015f9a2df9940edc254bf4.tar.xz | |
Mediahandler (#21)
Media GET and media PUT handlers
Diffstat (limited to 'internal/api/client')
| -rw-r--r-- | internal/api/client/auth/middleware.go | 3 | ||||
| -rw-r--r-- | internal/api/client/fileserver/servefile.go | 11 | ||||
| -rw-r--r-- | internal/api/client/media/media.go | 6 | ||||
| -rw-r--r-- | internal/api/client/media/mediacreate.go | 10 | ||||
| -rw-r--r-- | internal/api/client/media/mediaget.go | 51 | ||||
| -rw-r--r-- | internal/api/client/media/mediaupdate.go | 87 | ||||
| -rw-r--r-- | internal/api/client/status/statuscreate.go | 3 | 
7 files changed, 163 insertions, 8 deletions
| diff --git a/internal/api/client/auth/middleware.go b/internal/api/client/auth/middleware.go index c42ba77fc..8313b7c28 100644 --- a/internal/api/client/auth/middleware.go +++ b/internal/api/client/auth/middleware.go @@ -35,9 +35,10 @@ func (m *Module) OauthTokenMiddleware(c *gin.Context) {  	ti, err := m.server.ValidationBearerToken(c.Request)  	if err != nil { -		l.Trace("no valid token presented: continuing with unauthenticated request") +		l.Tracef("could not validate token: %s", err)  		return  	} +	l.Trace("continuing with unauthenticated request")  	c.Set(oauth.SessionAuthorizedToken, ti)  	l.Tracef("set gin context %s to %+v", oauth.SessionAuthorizedToken, ti) diff --git a/internal/api/client/fileserver/servefile.go b/internal/api/client/fileserver/servefile.go index 9823eb387..1339fbac3 100644 --- a/internal/api/client/fileserver/servefile.go +++ b/internal/api/client/fileserver/servefile.go @@ -78,7 +78,7 @@ func (m *FileServer) ServeFile(c *gin.Context) {  		return  	} -	content, err := m.processor.MediaGet(authed, &model.GetContentRequestForm{ +	content, err := m.processor.FileGet(authed, &model.GetContentRequestForm{  		AccountID: accountID,  		MediaType: mediaType,  		MediaSize: mediaSize, @@ -90,5 +90,14 @@ func (m *FileServer) ServeFile(c *gin.Context) {  		return  	} +	// TODO: do proper content negotiation here -- if the requester only accepts text/html we should try to serve them *something* +	// This is mostly needed because when sharing a link to a gts-hosted file on something like mastodon, the masto servers will +	// attempt to look up the content to provide a preview of the link, and they ask for text/html. +	if c.NegotiateFormat(content.ContentType) == "" { +		l.Debugf("couldn't negotiate content for Accept headers %+v: we have content type %s", c.Request.Header.Get("Accepted"), content.ContentType) +		c.AbortWithStatus(http.StatusNotAcceptable) +		return +	} +  	c.DataFromReader(http.StatusOK, content.ContentLength, content.ContentType, bytes.NewReader(content.Content), nil)  } diff --git a/internal/api/client/media/media.go b/internal/api/client/media/media.go index 2826783d6..e45eec7ea 100644 --- a/internal/api/client/media/media.go +++ b/internal/api/client/media/media.go @@ -33,6 +33,10 @@ import (  // BasePath is the base API path for making media requests  const BasePath = "/api/v1/media" +// IDKey is the key for media attachment IDs +const IDKey = "id" +// BasePathWithID corresponds to a media attachment with the given ID +const BasePathWithID = BasePath + "/:" + IDKey  // Module implements the ClientAPIModule interface for media  type Module struct { @@ -53,6 +57,8 @@ func New(config *config.Config, processor message.Processor, log *logrus.Logger)  // Route satisfies the RESTAPIModule interface  func (m *Module) Route(s router.Router) error {  	s.AttachHandler(http.MethodPost, BasePath, m.MediaCreatePOSTHandler) +	s.AttachHandler(http.MethodGet, BasePathWithID, m.MediaGETHandler) +	s.AttachHandler(http.MethodPut, BasePathWithID, m.MediaPUTHandler)  	return nil  } diff --git a/internal/api/client/media/mediacreate.go b/internal/api/client/media/mediacreate.go index db57e2052..c0b4a80d7 100644 --- a/internal/api/client/media/mediacreate.go +++ b/internal/api/client/media/mediacreate.go @@ -41,8 +41,8 @@ func (m *Module) MediaCreatePOSTHandler(c *gin.Context) {  	// extract the media create form from the request context  	l.Tracef("parsing request form: %s", c.Request.Form) -	form := &model.AttachmentRequest{} -	if err := c.ShouldBind(form); err != nil || form == nil { +	var form model.AttachmentRequest +	if err := c.ShouldBind(&form); err != nil {  		l.Debugf("could not parse form from request: %s", err)  		c.JSON(http.StatusBadRequest, gin.H{"error": "missing one or more required form values"})  		return @@ -50,19 +50,19 @@ func (m *Module) MediaCreatePOSTHandler(c *gin.Context) {  	// Give the fields on the request form a first pass to make sure the request is superficially valid.  	l.Tracef("validating form %+v", form) -	if err := validateCreateMedia(form, m.config.MediaConfig); err != nil { +	if err := validateCreateMedia(&form, m.config.MediaConfig); err != nil {  		l.Debugf("error validating form: %s", err)  		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})  		return  	} -	mastoAttachment, err := m.processor.MediaCreate(authed, form) +	mastoAttachment, err := m.processor.MediaCreate(authed, &form)  	if err != nil {  		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})  		return  	} -	c.JSON(http.StatusAccepted, mastoAttachment) +	c.JSON(http.StatusOK, mastoAttachment)  }  func validateCreateMedia(form *model.AttachmentRequest, config *config.MediaConfig) error { diff --git a/internal/api/client/media/mediaget.go b/internal/api/client/media/mediaget.go new file mode 100644 index 000000000..31c40a5aa --- /dev/null +++ b/internal/api/client/media/mediaget.go @@ -0,0 +1,51 @@ +package media + +import ( +	"net/http" + +	"github.com/gin-gonic/gin" +	"github.com/superseriousbusiness/gotosocial/internal/oauth" +) + +/* +   GoToSocial +   Copyright (C) 2021 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/>. +*/ + +// MediaGETHandler allows the owner of an attachment to get information about that attachment before it's used in a status. +func (m *Module) MediaGETHandler(c *gin.Context) { +	l := m.log.WithField("func", "MediaGETHandler") +	authed, err := oauth.Authed(c, true, true, true, true) +	if err != nil { +		l.Debugf("couldn't auth: %s", err) +		c.JSON(http.StatusForbidden, gin.H{"error": err.Error()}) +		return +	} + +	attachmentID := c.Param(IDKey) +	if attachmentID == "" { +		c.JSON(http.StatusBadRequest, gin.H{"error": "no attachment ID given in request"}) +		return +	} + +	attachment, errWithCode := m.processor.MediaGet(authed, attachmentID) +	if errWithCode != nil { +		c.JSON(errWithCode.Code(),gin.H{"error":  errWithCode.Safe()}) +		return +	} + +	c.JSON(http.StatusOK, attachment) +} diff --git a/internal/api/client/media/mediaupdate.go b/internal/api/client/media/mediaupdate.go new file mode 100644 index 000000000..29ab04947 --- /dev/null +++ b/internal/api/client/media/mediaupdate.go @@ -0,0 +1,87 @@ +package media + +import ( +	"errors" +	"fmt" +	"net/http" + +	"github.com/gin-gonic/gin" +	"github.com/superseriousbusiness/gotosocial/internal/api/model" +	"github.com/superseriousbusiness/gotosocial/internal/config" +	"github.com/superseriousbusiness/gotosocial/internal/oauth" +) + +/* +   GoToSocial +   Copyright (C) 2021 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/>. +*/ + +// MediaPUTHandler allows the owner of an attachment to update information about that attachment before it's used in a status. +func (m *Module) MediaPUTHandler(c *gin.Context) { +	l := m.log.WithField("func", "MediaGETHandler") +	authed, err := oauth.Authed(c, true, true, true, true) +	if err != nil { +		l.Debugf("couldn't auth: %s", err) +		c.JSON(http.StatusForbidden, gin.H{"error": err.Error()}) +		return +	} + +	attachmentID := c.Param(IDKey) +	if attachmentID == "" { +		c.JSON(http.StatusBadRequest, gin.H{"error": "no attachment ID given in request"}) +		return +	} + +	// extract the media update form from the request context +	l.Tracef("parsing request form: %s", c.Request.Form) +	var form model.AttachmentUpdateRequest +	if err := c.ShouldBind(&form); err != nil { +		l.Debugf("could not parse form from request: %s", err) +		c.JSON(http.StatusBadRequest, gin.H{"error": "missing one or more required form values"}) +		return +	} + +	// Give the fields on the request form a first pass to make sure the request is superficially valid. +	l.Tracef("validating form %+v", form) +	if err := validateUpdateMedia(&form, m.config.MediaConfig); err != nil { +		l.Debugf("error validating form: %s", err) +		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) +		return +	} + +	attachment, errWithCode := m.processor.MediaUpdate(authed, attachmentID, &form) +	if errWithCode != nil { +		c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()}) +		return +	} + +	c.JSON(http.StatusOK, attachment) +} + +func validateUpdateMedia(form *model.AttachmentUpdateRequest, config *config.MediaConfig) error { + +	if form.Description != nil { +		if len(*form.Description) < config.MinDescriptionChars || len(*form.Description) > config.MaxDescriptionChars { +			return fmt.Errorf("image description length must be between %d and %d characters (inclusive), but provided image description was %d chars", config.MinDescriptionChars, config.MaxDescriptionChars, len(*form.Description)) +		} +	} + +	if form.Focus == nil && form.Description == nil { +		return errors.New("focus and description were both nil, there's nothing to update") +	} + +	return nil +} diff --git a/internal/api/client/status/statuscreate.go b/internal/api/client/status/statuscreate.go index 02080b042..178901536 100644 --- a/internal/api/client/status/statuscreate.go +++ b/internal/api/client/status/statuscreate.go @@ -49,13 +49,14 @@ func (m *Module) StatusCreatePOSTHandler(c *gin.Context) {  	}  	// extract the status create form from the request context -	l.Tracef("parsing request form: %s", c.Request.Form) +	l.Debugf("parsing request form: %s", c.Request.Form)  	form := &model.AdvancedStatusCreateForm{}  	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": "missing one or more required form values"})  		return  	} +	l.Debugf("handling status request form: %+v", form)  	// Give the fields on the request form a first pass to make sure the request is superficially valid.  	l.Tracef("validating form %+v", form) | 
