2021-05-08 14:25:55 +02:00
/ *
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/>.
* /
package message
import (
2021-05-15 11:58:11 +02:00
"context"
2021-05-08 14:25:55 +02:00
"net/http"
"github.com/sirupsen/logrus"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/federation"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/internal/storage"
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
)
// Processor should be passed to api modules (see internal/apimodule/...). It is used for
// passing messages back and forth from the client API and the federating interface, via channels.
// It also contains logic for filtering which messages should end up where.
// It is designed to be used asynchronously: the client API and the federating API should just be able to
// fire messages into the processor and not wait for a reply before proceeding with other work. This allows
// for clean distribution of messages without slowing down the client API and harming the user experience.
type Processor interface {
// ToClientAPI returns a channel for putting in messages that need to go to the gts client API.
2021-05-17 19:06:58 +02:00
// ToClientAPI() chan gtsmodel.ToClientAPI
2021-05-08 14:25:55 +02:00
// FromClientAPI returns a channel for putting messages in that come from the client api going to the processor
2021-05-17 19:06:58 +02:00
FromClientAPI ( ) chan gtsmodel . FromClientAPI
2021-05-08 14:25:55 +02:00
// ToFederator returns a channel for putting in messages that need to go to the federator (activitypub).
2021-05-17 19:06:58 +02:00
// ToFederator() chan gtsmodel.ToFederator
2021-05-08 14:25:55 +02:00
// FromFederator returns a channel for putting messages in that come from the federator (activitypub) going into the processor
2021-05-17 19:06:58 +02:00
FromFederator ( ) chan gtsmodel . FromFederator
2021-05-08 14:25:55 +02:00
// Start starts the Processor, reading from its channels and passing messages back and forth.
Start ( ) error
// Stop stops the processor cleanly, finishing handling any remaining messages before closing down.
Stop ( ) error
/ *
CLIENT API - FACING PROCESSING FUNCTIONS
These functions are intended to be called when the API client needs an immediate ( ie . , synchronous ) reply
to an HTTP request . As such , they will only do the bare - minimum of work necessary to give a properly
formed reply . For more intensive ( and time - consuming ) calls , where you don ' t require an immediate
response , pass work to the processor using a channel instead .
* /
// AccountCreate processes the given form for creating a new account, returning an oauth token for that account if successful.
AccountCreate ( authed * oauth . Auth , form * apimodel . AccountCreateRequest ) ( * apimodel . Token , error )
// AccountGet processes the given request for account information.
AccountGet ( authed * oauth . Auth , targetAccountID string ) ( * apimodel . Account , error )
// AccountUpdate processes the update of an account with the given form
AccountUpdate ( authed * oauth . Auth , form * apimodel . UpdateCredentialsRequest ) ( * apimodel . Account , error )
2021-05-17 19:06:58 +02:00
// AccountStatusesGet fetches a number of statuses (in time descending order) from the given account, filtered by visibility for
// the account given in authed.
AccountStatusesGet ( authed * oauth . Auth , targetAccountID string , limit int , excludeReplies bool , maxID string , pinned bool , mediaOnly bool ) ( [ ] apimodel . Status , ErrorWithCode )
2021-05-21 15:48:26 +02:00
// AccountFollowersGet fetches a list of the target account's followers.
2021-05-17 19:06:58 +02:00
AccountFollowersGet ( authed * oauth . Auth , targetAccountID string ) ( [ ] apimodel . Account , ErrorWithCode )
2021-05-21 15:48:26 +02:00
// AccountFollowingGet fetches a list of the accounts that target account is following.
AccountFollowingGet ( authed * oauth . Auth , targetAccountID string ) ( [ ] apimodel . Account , ErrorWithCode )
// AccountRelationshipGet returns a relationship model describing the relationship of the targetAccount to the Authed account.
AccountRelationshipGet ( authed * oauth . Auth , targetAccountID string ) ( * apimodel . Relationship , ErrorWithCode )
// AccountFollowCreate handles a follow request to an account, either remote or local.
AccountFollowCreate ( authed * oauth . Auth , form * apimodel . AccountFollowRequest ) ( * apimodel . Relationship , ErrorWithCode )
// AccountFollowRemove handles the removal of a follow/follow request to an account, either remote or local.
AccountFollowRemove ( authed * oauth . Auth , targetAccountID string ) ( * apimodel . Relationship , ErrorWithCode )
2021-05-08 14:25:55 +02:00
2021-05-09 14:06:06 +02:00
// AdminEmojiCreate handles the creation of a new instance emoji by an admin, using the given form.
AdminEmojiCreate ( authed * oauth . Auth , form * apimodel . EmojiCreateRequest ) ( * apimodel . Emoji , error )
2021-05-08 14:25:55 +02:00
// AppCreate processes the creation of a new API application
AppCreate ( authed * oauth . Auth , form * apimodel . ApplicationCreateRequest ) ( * apimodel . Application , error )
2021-05-10 16:29:05 +02:00
// FileGet handles the fetching of a media attachment file via the fileserver.
FileGet ( authed * oauth . Auth , form * apimodel . GetContentRequestForm ) ( * apimodel . Content , error )
2021-05-15 11:58:11 +02:00
// FollowRequestsGet handles the getting of the authed account's incoming follow requests
FollowRequestsGet ( auth * oauth . Auth ) ( [ ] apimodel . Account , ErrorWithCode )
// FollowRequestAccept handles the acceptance of a follow request from the given account ID
2021-05-21 15:48:26 +02:00
FollowRequestAccept ( auth * oauth . Auth , accountID string ) ( * apimodel . Relationship , ErrorWithCode )
2021-05-15 11:58:11 +02:00
2021-05-09 14:06:06 +02:00
// 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 )
2021-05-10 16:29:05 +02:00
// MediaGet handles the GET of a media attachment with the given ID
MediaGet ( authed * oauth . Auth , attachmentID string ) ( * apimodel . Attachment , ErrorWithCode )
// MediaUpdate handles the PUT of a media attachment with the given ID and form
MediaUpdate ( authed * oauth . Auth , attachmentID string , form * apimodel . AttachmentUpdateRequest ) ( * apimodel . Attachment , ErrorWithCode )
2021-05-09 14:06:06 +02:00
2021-05-08 14:25:55 +02:00
// 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 )
// StatusDelete processes the delete of a given status, returning the deleted status if the delete goes through.
StatusDelete ( authed * oauth . Auth , targetStatusID string ) ( * apimodel . Status , error )
// StatusFave processes the faving of a given status, returning the updated status if the fave goes through.
StatusFave ( authed * oauth . Auth , targetStatusID string ) ( * apimodel . Status , error )
2021-05-08 15:16:24 +02:00
// StatusBoost processes the boost/reblog of a given status, returning the newly-created boost if all is well.
StatusBoost ( authed * oauth . Auth , targetStatusID string ) ( * apimodel . Status , ErrorWithCode )
2021-05-08 14:25:55 +02:00
// StatusFavedBy returns a slice of accounts that have liked the given status, filtered according to privacy settings.
StatusFavedBy ( authed * oauth . Auth , targetStatusID string ) ( [ ] * apimodel . Account , error )
// StatusGet gets the given status, taking account of privacy settings and blocks etc.
StatusGet ( authed * oauth . Auth , targetStatusID string ) ( * apimodel . Status , error )
// StatusUnfave processes the unfaving of a given status, returning the updated status if the fave goes through.
StatusUnfave ( authed * oauth . Auth , targetStatusID string ) ( * apimodel . Status , error )
/ *
FEDERATION API - FACING PROCESSING FUNCTIONS
These functions are intended to be called when the federating client needs an immediate ( ie . , synchronous ) reply
to an HTTP request . As such , they will only do the bare - minimum of work necessary to give a properly
formed reply . For more intensive ( and time - consuming ) calls , where you don ' t require an immediate
response , pass work to the processor using a channel instead .
* /
// GetFediUser handles the getting of a fedi/activitypub representation of a user/account, performing appropriate authentication
// before returning a JSON serializable interface to the caller.
GetFediUser ( requestedUsername string , request * http . Request ) ( interface { } , ErrorWithCode )
2021-05-09 20:34:27 +02:00
2021-05-21 15:48:26 +02:00
// GetFediFollowers handles the getting of a fedi/activitypub representation of a user/account's followers, performing appropriate
// authentication before returning a JSON serializable interface to the caller.
GetFediFollowers ( requestedUsername string , request * http . Request ) ( interface { } , ErrorWithCode )
// GetFediStatus handles the getting of a fedi/activitypub representation of a particular status, performing appropriate
// authentication before returning a JSON serializable interface to the caller.
GetFediStatus ( requestedUsername string , requestedStatusID string , request * http . Request ) ( interface { } , ErrorWithCode )
2021-05-09 20:34:27 +02:00
// GetWebfingerAccount handles the GET for a webfinger resource. Most commonly, it will be used for returning account lookups.
GetWebfingerAccount ( requestedUsername string , request * http . Request ) ( * apimodel . WebfingerAccountResponse , ErrorWithCode )
2021-05-15 11:58:11 +02:00
// InboxPost handles POST requests to a user's inbox for new activitypub messages.
//
// InboxPost returns true if the request was handled as an ActivityPub POST to an actor's inbox.
// If false, the request was not an ActivityPub request and may still be handled by the caller in another way, such as serving a web page.
//
// If the error is nil, then the ResponseWriter's headers and response has already been written. If a non-nil error is returned, then no response has been written.
//
// If the Actor was constructed with the Federated Protocol enabled, side effects will occur.
//
// If the Federated Protocol is not enabled, writes the http.StatusMethodNotAllowed status code in the response. No side effects occur.
InboxPost ( ctx context . Context , w http . ResponseWriter , r * http . Request ) ( bool , error )
2021-05-08 14:25:55 +02:00
}
// processor just implements the Processor interface
type processor struct {
// federator pub.FederatingActor
2021-05-17 19:06:58 +02:00
// toClientAPI chan gtsmodel.ToClientAPI
fromClientAPI chan gtsmodel . FromClientAPI
// toFederator chan gtsmodel.ToFederator
fromFederator chan gtsmodel . FromFederator
2021-05-08 14:25:55 +02:00
federator federation . Federator
stop chan interface { }
log * logrus . Logger
config * config . Config
tc typeutils . TypeConverter
oauthServer oauth . Server
mediaHandler media . Handler
storage storage . Storage
db db . DB
}
// NewProcessor returns a new Processor that uses the given federator and logger
func NewProcessor ( config * config . Config , tc typeutils . TypeConverter , federator federation . Federator , oauthServer oauth . Server , mediaHandler media . Handler , storage storage . Storage , db db . DB , log * logrus . Logger ) Processor {
return & processor {
2021-05-17 19:06:58 +02:00
// toClientAPI: make(chan gtsmodel.ToClientAPI, 100),
fromClientAPI : make ( chan gtsmodel . FromClientAPI , 100 ) ,
// toFederator: make(chan gtsmodel.ToFederator, 100),
fromFederator : make ( chan gtsmodel . FromFederator , 100 ) ,
2021-05-08 14:25:55 +02:00
federator : federator ,
stop : make ( chan interface { } ) ,
log : log ,
config : config ,
tc : tc ,
oauthServer : oauthServer ,
mediaHandler : mediaHandler ,
storage : storage ,
db : db ,
}
}
2021-05-17 19:06:58 +02:00
// func (p *processor) ToClientAPI() chan gtsmodel.ToClientAPI {
// return p.toClientAPI
// }
2021-05-08 14:25:55 +02:00
2021-05-17 19:06:58 +02:00
func ( p * processor ) FromClientAPI ( ) chan gtsmodel . FromClientAPI {
2021-05-08 14:25:55 +02:00
return p . fromClientAPI
}
2021-05-17 19:06:58 +02:00
// func (p *processor) ToFederator() chan gtsmodel.ToFederator {
// return p.toFederator
// }
2021-05-08 14:25:55 +02:00
2021-05-17 19:06:58 +02:00
func ( p * processor ) FromFederator ( ) chan gtsmodel . FromFederator {
2021-05-08 14:25:55 +02:00
return p . fromFederator
}
// Start starts the Processor, reading from its channels and passing messages back and forth.
func ( p * processor ) Start ( ) error {
go func ( ) {
DistLoop :
for {
select {
case clientMsg := <- p . fromClientAPI :
p . log . Infof ( "received message FROM client API: %+v" , clientMsg )
2021-05-15 11:58:11 +02:00
if err := p . processFromClientAPI ( clientMsg ) ; err != nil {
p . log . Error ( err )
}
2021-05-08 14:25:55 +02:00
case federatorMsg := <- p . fromFederator :
p . log . Infof ( "received message FROM federator: %+v" , federatorMsg )
2021-05-17 19:06:58 +02:00
if err := p . processFromFederator ( federatorMsg ) ; err != nil {
p . log . Error ( err )
}
2021-05-08 14:25:55 +02:00
case <- p . stop :
break DistLoop
}
}
} ( )
return nil
}
// Stop stops the processor cleanly, finishing handling any remaining messages before closing down.
// TODO: empty message buffer properly before stopping otherwise we'll lose federating messages.
func ( p * processor ) Stop ( ) error {
close ( p . stop )
return nil
}