From d797ddf7043c8205dd52962992a626d9327b34c5 Mon Sep 17 00:00:00 2001 From: tsmethurst Date: Mon, 10 May 2021 09:56:49 +0200 Subject: [PATCH] start updating media handler for proper processing --- internal/api/client/fileserver/servefile.go | 2 +- internal/api/client/media/media.go | 5 ++ internal/api/client/media/mediaget.go | 51 +++++++++++++++++++++ internal/message/mediaprocess.go | 29 +++++++++++- internal/message/processor.go | 7 ++- 5 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 internal/api/client/media/mediaget.go diff --git a/internal/api/client/fileserver/servefile.go b/internal/api/client/fileserver/servefile.go index 9823eb387..f4728d153 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, diff --git a/internal/api/client/media/media.go b/internal/api/client/media/media.go index 2826783d6..8903de579 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,7 @@ 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) return nil } diff --git a/internal/api/client/media/mediaget.go b/internal/api/client/media/mediaget.go new file mode 100644 index 000000000..c1a4f2ece --- /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 . +*/ + +// 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", "statusCreatePOSTHandler") + 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.GetString(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 err != nil { + c.JSON(errWithCode.Code(),gin.H{"error": errWithCode.Safe()}) + return + } + + c.JSON(http.StatusOK, attachment) +} diff --git a/internal/message/mediaprocess.go b/internal/message/mediaprocess.go index 77b387df3..c4ba3e72e 100644 --- a/internal/message/mediaprocess.go +++ b/internal/message/mediaprocess.go @@ -9,6 +9,7 @@ import ( "strings" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" + "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -17,6 +18,8 @@ import ( func (p *processor) MediaCreate(authed *oauth.Auth, form *apimodel.AttachmentRequest) (*apimodel.Attachment, error) { // First check this user/account is permitted to create media // There's no point continuing otherwise. + // + // TODO: move this check to the oauth.Authed function and do it for all accounts if authed.User.Disabled || !authed.User.Approved || !authed.Account.SuspendedAt.IsZero() { return nil, errors.New("not authorized to post new media") } @@ -49,7 +52,7 @@ func (p *processor) MediaCreate(authed *oauth.Auth, form *apimodel.AttachmentReq attachment.Description = form.Description // now parse the focus parameter - // TODO: tidy this up into a separate function and just return an error so all the c.JSON and return calls are obviated + // TODO: tidy this up into a separate function and just return an error var focusx, focusy float32 if form.Focus != "" { spl := strings.Split(form.Focus, ",") @@ -96,7 +99,29 @@ func (p *processor) MediaCreate(authed *oauth.Auth, form *apimodel.AttachmentReq return &mastoAttachment, nil } -func (p *processor) MediaGet(authed *oauth.Auth, form *apimodel.GetContentRequestForm) (*apimodel.Content, error) { +func (p *processor) MediaGet(authed *oauth.Auth, mediaAttachmentID string) (*apimodel.Attachment, ErrorWithCode) { + attachment := >smodel.MediaAttachment{} + if err := p.db.GetByID(mediaAttachmentID, attachment); err != nil { + if _, ok := err.(db.ErrNoEntries); ok { + // attachment doesn't exist + return nil, NewErrorNotFound(errors.New("attachment doesn't exist in the db")) + } + return nil, NewErrorNotFound(fmt.Errorf("db error getting attachment: %s", err)) + } + + if attachment.AccountID != authed.Account.ID { + return nil, NewErrorNotFound(errors.New("attachment not owned by requesting account")) + } + + a, err := p.tc.AttachmentToMasto(attachment) + if err != nil { + return nil, NewErrorNotFound(fmt.Errorf("error converting attachment: %s", err)) + } + + return &a, nil +} + +func (p *processor) FileGet(authed *oauth.Auth, form *apimodel.GetContentRequestForm) (*apimodel.Content, error) { // parse the form fields mediaSize, err := media.ParseMediaSize(form.MediaSize) if err != nil { diff --git a/internal/message/processor.go b/internal/message/processor.go index d150d56e6..0c660caf3 100644 --- a/internal/message/processor.go +++ b/internal/message/processor.go @@ -74,13 +74,16 @@ type Processor interface { // AppCreate processes the creation of a new API application AppCreate(authed *oauth.Auth, form *apimodel.ApplicationCreateRequest) (*apimodel.Application, error) + // FileGet handles the fetching of a media attachment file via the fileserver. + FileGet(authed *oauth.Auth, form *apimodel.GetContentRequestForm) (*apimodel.Content, error) + // InstanceGet retrieves instance information for serving at api/v1/instance InstanceGet(domain string) (*apimodel.Instance, ErrorWithCode) // MediaCreate handles the creation of a media attachment, using the given form. MediaCreate(authed *oauth.Auth, form *apimodel.AttachmentRequest) (*apimodel.Attachment, error) - // MediaGet handles the fetching of a media attachment, using the given request form. - MediaGet(authed *oauth.Auth, form *apimodel.GetContentRequestForm) (*apimodel.Content, error) + // MediaGet handles the GET of a media attachment with the given ID + MediaGet(authed *oauth.Auth, attachmentID string) (*apimodel.Attachment, ErrorWithCode) // StatusCreate processes the given form to create a new status, returning the api model representation of that status if it's OK. StatusCreate(authed *oauth.Auth, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, error)