gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

collections.go (7003B)


      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 fedi
     19 
     20 import (
     21 	"context"
     22 	"errors"
     23 	"fmt"
     24 	"net/http"
     25 	"net/url"
     26 
     27 	"github.com/superseriousbusiness/gotosocial/internal/ap"
     28 	"github.com/superseriousbusiness/gotosocial/internal/db"
     29 	"github.com/superseriousbusiness/gotosocial/internal/gtserror"
     30 )
     31 
     32 // InboxPost handles POST requests to a user's inbox for new activitypub messages.
     33 //
     34 // InboxPost returns true if the request was handled as an ActivityPub POST to an actor's inbox.
     35 // 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.
     36 //
     37 // 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.
     38 //
     39 // If the Actor was constructed with the Federated Protocol enabled, side effects will occur.
     40 //
     41 // If the Federated Protocol is not enabled, writes the http.StatusMethodNotAllowed status code in the response. No side effects occur.
     42 func (p *Processor) InboxPost(ctx context.Context, w http.ResponseWriter, r *http.Request) (bool, error) {
     43 	return p.federator.FederatingActor().PostInbox(ctx, w, r)
     44 }
     45 
     46 // OutboxGet returns the activitypub representation of a local user's outbox.
     47 // This contains links to PUBLIC posts made by this user.
     48 func (p *Processor) OutboxGet(ctx context.Context, requestedUsername string, page bool, maxID string, minID string) (interface{}, gtserror.WithCode) {
     49 	requestedAccount, _, errWithCode := p.authenticate(ctx, requestedUsername)
     50 	if errWithCode != nil {
     51 		return nil, errWithCode
     52 	}
     53 
     54 	var data map[string]interface{}
     55 	// There are two scenarios:
     56 	// 1. we're asked for the whole collection and not a page -- we can just return the collection, with no items, but a link to 'first' page.
     57 	// 2. we're asked for a specific page; this can be either the first page or any other page
     58 
     59 	if !page {
     60 		/*
     61 			scenario 1: return the collection with no items
     62 			we want something that looks like this:
     63 			{
     64 				"@context": "https://www.w3.org/ns/activitystreams",
     65 				"id": "https://example.org/users/whatever/outbox",
     66 				"type": "OrderedCollection",
     67 				"first": "https://example.org/users/whatever/outbox?page=true",
     68 				"last": "https://example.org/users/whatever/outbox?min_id=0&page=true"
     69 			}
     70 		*/
     71 		collection, err := p.tc.OutboxToASCollection(ctx, requestedAccount.OutboxURI)
     72 		if err != nil {
     73 			return nil, gtserror.NewErrorInternalError(err)
     74 		}
     75 
     76 		data, err = ap.Serialize(collection)
     77 		if err != nil {
     78 			return nil, gtserror.NewErrorInternalError(err)
     79 		}
     80 
     81 		return data, nil
     82 	}
     83 
     84 	// scenario 2 -- get the requested page
     85 	// limit pages to 30 entries per page
     86 	publicStatuses, err := p.state.DB.GetAccountStatuses(ctx, requestedAccount.ID, 30, true, true, maxID, minID, false, true)
     87 	if err != nil && !errors.Is(err, db.ErrNoEntries) {
     88 		return nil, gtserror.NewErrorInternalError(err)
     89 	}
     90 
     91 	outboxPage, err := p.tc.StatusesToASOutboxPage(ctx, requestedAccount.OutboxURI, maxID, minID, publicStatuses)
     92 	if err != nil {
     93 		return nil, gtserror.NewErrorInternalError(err)
     94 	}
     95 	data, err = ap.Serialize(outboxPage)
     96 	if err != nil {
     97 		return nil, gtserror.NewErrorInternalError(err)
     98 	}
     99 
    100 	return data, nil
    101 }
    102 
    103 // FollowersGet handles the getting of a fedi/activitypub representation of a user/account's followers, performing appropriate
    104 // authentication before returning a JSON serializable interface to the caller.
    105 func (p *Processor) FollowersGet(ctx context.Context, requestedUsername string) (interface{}, gtserror.WithCode) {
    106 	requestedAccount, _, errWithCode := p.authenticate(ctx, requestedUsername)
    107 	if errWithCode != nil {
    108 		return nil, errWithCode
    109 	}
    110 
    111 	requestedAccountURI, err := url.Parse(requestedAccount.URI)
    112 	if err != nil {
    113 		return nil, gtserror.NewErrorInternalError(fmt.Errorf("error parsing url %s: %s", requestedAccount.URI, err))
    114 	}
    115 
    116 	requestedFollowers, err := p.federator.FederatingDB().Followers(ctx, requestedAccountURI)
    117 	if err != nil {
    118 		return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching followers for uri %s: %s", requestedAccountURI.String(), err))
    119 	}
    120 
    121 	data, err := ap.Serialize(requestedFollowers)
    122 	if err != nil {
    123 		return nil, gtserror.NewErrorInternalError(err)
    124 	}
    125 
    126 	return data, nil
    127 }
    128 
    129 // FollowingGet handles the getting of a fedi/activitypub representation of a user/account's following, performing appropriate
    130 // authentication before returning a JSON serializable interface to the caller.
    131 func (p *Processor) FollowingGet(ctx context.Context, requestedUsername string) (interface{}, gtserror.WithCode) {
    132 	requestedAccount, _, errWithCode := p.authenticate(ctx, requestedUsername)
    133 	if errWithCode != nil {
    134 		return nil, errWithCode
    135 	}
    136 
    137 	requestedAccountURI, err := url.Parse(requestedAccount.URI)
    138 	if err != nil {
    139 		return nil, gtserror.NewErrorInternalError(fmt.Errorf("error parsing url %s: %s", requestedAccount.URI, err))
    140 	}
    141 
    142 	requestedFollowing, err := p.federator.FederatingDB().Following(ctx, requestedAccountURI)
    143 	if err != nil {
    144 		return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching following for uri %s: %s", requestedAccountURI.String(), err))
    145 	}
    146 
    147 	data, err := ap.Serialize(requestedFollowing)
    148 	if err != nil {
    149 		return nil, gtserror.NewErrorInternalError(err)
    150 	}
    151 
    152 	return data, nil
    153 }
    154 
    155 // FeaturedCollectionGet returns an ordered collection of the requested username's Pinned posts.
    156 // The returned collection have an `items` property which contains an ordered list of status URIs.
    157 func (p *Processor) FeaturedCollectionGet(ctx context.Context, requestedUsername string) (interface{}, gtserror.WithCode) {
    158 	requestedAccount, _, errWithCode := p.authenticate(ctx, requestedUsername)
    159 	if errWithCode != nil {
    160 		return nil, errWithCode
    161 	}
    162 
    163 	statuses, err := p.state.DB.GetAccountPinnedStatuses(ctx, requestedAccount.ID)
    164 	if err != nil {
    165 		if !errors.Is(err, db.ErrNoEntries) {
    166 			return nil, gtserror.NewErrorInternalError(err)
    167 		}
    168 	}
    169 
    170 	collection, err := p.tc.StatusesToASFeaturedCollection(ctx, requestedAccount.FeaturedCollectionURI, statuses)
    171 	if err != nil {
    172 		return nil, gtserror.NewErrorInternalError(err)
    173 	}
    174 
    175 	data, err := ap.Serialize(collection)
    176 	if err != nil {
    177 		return nil, gtserror.NewErrorInternalError(err)
    178 	}
    179 
    180 	return data, nil
    181 }