Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] Allow users to export data via the settings panel #3140

Merged
merged 5 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 172 additions & 0 deletions docs/api/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,56 @@ definitions:
type: object
x-go-name: Account
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
accountExportStats:
description: |-
AccountExportStats models an account's stats
specifically for the purpose of informing about
export sizes at the /api/v1/exports/stats endpoint.
properties:
blocks_count:
description: Number of accounts blocked by this account.
example: 15
format: int64
type: integer
x-go-name: BlocksCount
followers_count:
description: Number of accounts following this account.
example: 50
format: int64
type: integer
x-go-name: FollowersCount
following_count:
description: Number of accounts followed by this account.
example: 50
format: int64
type: integer
x-go-name: FollowingCount
lists_count:
description: Number of lists created by this account.
example: 10
format: int64
type: integer
x-go-name: ListsCount
media_storage:
description: 'TODO: String representation of media storage size attributed to this account.'
example: 500MB
type: string
x-go-name: MediaStorage
mutes_count:
description: Number of accounts muted by this account.
example: 11
format: int64
type: integer
x-go-name: MutesCount
statuses_count:
description: Number of statuses created by this account.
example: 81986
format: int64
type: integer
x-go-name: StatusesCount
type: object
x-go-name: AccountExportStats
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
accountRelationship:
properties:
blocked_by:
Expand Down Expand Up @@ -6364,6 +6414,128 @@ paths:
summary: Get an array of custom emojis available on the instance.
tags:
- custom_emojis
/api/v1/exports/blocks.csv:
get:
operationId: exportBlocks
produces:
- text/csv
responses:
"200":
description: CSV file of accounts that you block.
"401":
description: unauthorized
"406":
description: not acceptable
"500":
description: internal server error
security:
- OAuth2 Bearer:
- read:blocks
summary: Export a CSV file of accounts that you block.
tags:
- import-export
/api/v1/exports/followers.csv:
get:
operationId: exportFollowers
produces:
- text/csv
responses:
"200":
description: CSV file of accounts that follow you.
"401":
description: unauthorized
"406":
description: not acceptable
"500":
description: internal server error
security:
- OAuth2 Bearer:
- read:follows
summary: Export a CSV file of accounts that follow you.
tags:
- import-export
/api/v1/exports/following.csv:
get:
operationId: exportFollowing
produces:
- text/csv
responses:
"200":
description: CSV file of accounts that you follow.
"401":
description: unauthorized
"406":
description: not acceptable
"500":
description: internal server error
security:
- OAuth2 Bearer:
- read:follows
summary: Export a CSV file of accounts that you follow.
tags:
- import-export
/api/v1/exports/lists.csv:
get:
operationId: exportLists
produces:
- text/csv
responses:
"200":
description: CSV file of lists.
"401":
description: unauthorized
"406":
description: not acceptable
"500":
description: internal server error
security:
- OAuth2 Bearer:
- read:lists
summary: Export a CSV file of lists created by you.
tags:
- import-export
/api/v1/exports/mutes.csv:
get:
operationId: exportMutes
produces:
- text/csv
responses:
"200":
description: CSV file of accounts that you mute.
"401":
description: unauthorized
"406":
description: not acceptable
"500":
description: internal server error
security:
- OAuth2 Bearer:
- read:mutes
summary: Export a CSV file of accounts that you mute.
tags:
- import-export
/api/v1/exports/stats:
get:
operationId: exportStats
produces:
- application/json
responses:
"200":
description: Export stats for the requesting account.
schema:
$ref: '#/definitions/accountExportStats'
"401":
description: unauthorized
"406":
description: not acceptable
"500":
description: internal server error
security:
- OAuth2 Bearer:
- read:account
summary: Returns informational stats on the number of items that can be exported for requesting account.
tags:
- import-export
/api/v1/favourites:
get:
description: |-
Expand Down
8 changes: 8 additions & 0 deletions docs/user_guide/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,11 @@ For more information on the way GoToSocial manages passwords, please see the [Pa
In the migration section you can manage settings related to aliasing and/or migrating your account to or from another account.

Please see the [migration document](./migration.md) for more information on moving your account.

## Export & Import

In the export & import section, you can export data from your GoToSocial account, or import data into it (TODO).

### Export

To export your following, followers, lists, account blocks, or account mutes, you can use the button on this page. All exports will be served in Mastodon-compatible CSV format, so you can import them later into Mastodon or another GoToSocial instance, if you like.
4 changes: 4 additions & 0 deletions internal/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/api/client/bookmarks"
"github.com/superseriousbusiness/gotosocial/internal/api/client/conversations"
"github.com/superseriousbusiness/gotosocial/internal/api/client/customemojis"
"github.com/superseriousbusiness/gotosocial/internal/api/client/exports"
"github.com/superseriousbusiness/gotosocial/internal/api/client/favourites"
"github.com/superseriousbusiness/gotosocial/internal/api/client/featuredtags"
filtersV1 "github.com/superseriousbusiness/gotosocial/internal/api/client/filters/v1"
Expand Down Expand Up @@ -68,6 +69,7 @@ type Client struct {
bookmarks *bookmarks.Module // api/v1/bookmarks
conversations *conversations.Module // api/v1/conversations
customEmojis *customemojis.Module // api/v1/custom_emojis
exports *exports.Module // api/v1/exports
favourites *favourites.Module // api/v1/favourites
featuredTags *featuredtags.Module // api/v1/featured_tags
filtersV1 *filtersV1.Module // api/v1/filters
Expand Down Expand Up @@ -116,6 +118,7 @@ func (c *Client) Route(r *router.Router, m ...gin.HandlerFunc) {
c.bookmarks.Route(h)
c.conversations.Route(h)
c.customEmojis.Route(h)
c.exports.Route(h)
c.favourites.Route(h)
c.featuredTags.Route(h)
c.filtersV1.Route(h)
Expand Down Expand Up @@ -152,6 +155,7 @@ func NewClient(state *state.State, p *processing.Processor) *Client {
bookmarks: bookmarks.New(p),
conversations: conversations.New(p),
customEmojis: customemojis.New(p),
exports: exports.New(p),
favourites: favourites.New(p),
featuredTags: featuredtags.New(p),
filtersV1: filtersV1.New(p),
Expand Down
76 changes: 76 additions & 0 deletions internal/api/client/exports/blocks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// 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 exports

import (
"net/http"

"github.com/gin-gonic/gin"
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)

// ExportBlocksGETHandler swagger:operation GET /api/v1/exports/blocks.csv exportBlocks
//
// Export a CSV file of accounts that you block.
//
// ---
// tags:
// - import-export
//
// produces:
// - text/csv
//
// security:
// - OAuth2 Bearer:
// - read:blocks
//
// responses:
// '200':
// name: accounts
// description: CSV file of accounts that you block.
// '401':
// description: unauthorized
// '406':
// description: not acceptable
// '500':
// description: internal server error
func (m *Module) ExportBlocksGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1)
return
}

if _, err := apiutil.NegotiateAccept(c, apiutil.CSVHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return
}

records, errWithCode := m.processor.Account().ExportBlocks(
c.Request.Context(),
authed.Account,
)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
}

apiutil.EncodeCSVResponse(c.Writer, c.Request, http.StatusOK, records)
}
54 changes: 54 additions & 0 deletions internal/api/client/exports/exports.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// 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 exports

import (
"net/http"

"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/processing"
)

const (
BasePath = "/v1/exports"
StatsPath = BasePath + "/stats"
FollowingPath = BasePath + "/following.csv"
FollowersPath = BasePath + "/followers.csv"
ListsPath = BasePath + "/lists.csv"
BlocksPath = BasePath + "/blocks.csv"
MutesPath = BasePath + "/mutes.csv"
)

type Module struct {
processor *processing.Processor
}

func New(processor *processing.Processor) *Module {
return &Module{
processor: processor,
}
}

func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
attachHandler(http.MethodGet, StatsPath, m.ExportStatsGETHandler)
attachHandler(http.MethodGet, FollowingPath, m.ExportFollowingGETHandler)
attachHandler(http.MethodGet, FollowersPath, m.ExportFollowersGETHandler)
attachHandler(http.MethodGet, ListsPath, m.ExportListsGETHandler)
attachHandler(http.MethodGet, BlocksPath, m.ExportBlocksGETHandler)
attachHandler(http.MethodGet, MutesPath, m.ExportMutesGETHandler)
}
Loading