diff options
| author | 2024-08-24 11:49:37 +0200 | |
|---|---|---|
| committer | 2024-08-24 11:49:37 +0200 | |
| commit | f23f04e0b1d117be714bf91d5266dab219ed741e (patch) | |
| tree | 0b3ddd60d51c8729949c3669993910a7f8f32a7b /internal/api/client/interactionrequests | |
| parent | [performance] ffmpeg ffprobe wrapper improvements (#3225) (diff) | |
| download | gotosocial-f23f04e0b1d117be714bf91d5266dab219ed741e.tar.xz | |
[feature] Interaction requests client api + settings panel (#3215)
* [feature] Interaction requests client api + settings panel
* test accept / reject
* fmt
* don't pin rejected interaction
* use single db model for interaction accept, reject, and request
* swaggor
* env sharting
* append errors
* remove ErrNoEntries checks
* change intReqID to reqID
* rename "pend" to "request"
* markIntsPending -> mark interactionsPending
* use log instead of returning error when rejecting interaction
* empty migration
* jolly renaming
* make interactionURI unique again
* swag grr
* remove unnecessary locks
* invalidate as last step
Diffstat (limited to 'internal/api/client/interactionrequests')
| -rw-r--r-- | internal/api/client/interactionrequests/authorize.go | 104 | ||||
| -rw-r--r-- | internal/api/client/interactionrequests/get.go | 96 | ||||
| -rw-r--r-- | internal/api/client/interactionrequests/getpage.go | 211 | ||||
| -rw-r--r-- | internal/api/client/interactionrequests/interactionrequests.go | 50 | ||||
| -rw-r--r-- | internal/api/client/interactionrequests/reject.go | 104 | 
5 files changed, 565 insertions, 0 deletions
| diff --git a/internal/api/client/interactionrequests/authorize.go b/internal/api/client/interactionrequests/authorize.go new file mode 100644 index 000000000..1e5589f7e --- /dev/null +++ b/internal/api/client/interactionrequests/authorize.go @@ -0,0 +1,104 @@ +// 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 interactionrequests + +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" +) + +// InteractionRequestAuthorizePOSTHandler swagger:operation POST /api/v1/interaction_requests/{id}/authorize authorizeInteractionRequest +// +// Accept/authorize/approve an interaction request with the given ID. +// +//	--- +//	tags: +//	- interaction_requests +// +//	produces: +//	- application/json +// +//	parameters: +//	- +//		name: id +//		type: string +//		description: ID of the interaction request targeting you. +//		in: path +//		required: true +// +//	security: +//	- OAuth2 Bearer: +//		- write:statuses +// +//	responses: +//		'200': +//			name: Approval. +//			description: The now-approved interaction request. +//			schema: +//				"$ref": "#/definitions/interactionRequest" +//		'400': +//			description: bad request +//		'401': +//			description: unauthorized +//		'404': +//			description: not found +//		'406': +//			description: not acceptable +//		'500': +//			description: internal server error +func (m *Module) InteractionRequestAuthorizePOSTHandler(c *gin.Context) { +	authed, err := oauth.Authed(c, true, true, true, true) +	if err != nil { +		errWithCode := gtserror.NewErrorUnauthorized(err, err.Error()) +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return +	} + +	if authed.Account.IsMoving() { +		apiutil.ForbiddenAfterMove(c) +		return +	} + +	if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil { +		errWithCode := gtserror.NewErrorNotAcceptable(err, err.Error()) +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return +	} + +	reqID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey)) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return +	} + +	apiReq, errWithCode := m.processor.InteractionRequests().Accept( +		c.Request.Context(), +		authed.Account, +		reqID, +	) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return +	} + +	apiutil.JSON(c, http.StatusOK, apiReq) +} diff --git a/internal/api/client/interactionrequests/get.go b/internal/api/client/interactionrequests/get.go new file mode 100644 index 000000000..a354a8623 --- /dev/null +++ b/internal/api/client/interactionrequests/get.go @@ -0,0 +1,96 @@ +// 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 interactionrequests + +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" +) + +// InteractionRequestGETHandler swagger:operation GET /api/v1/interaction_requests/{id} getInteractionRequest +// +// Get interaction request with the given ID. +// +//	--- +//	tags: +//	- interaction_requests +// +//	produces: +//	- application/json +// +//	parameters: +//	- +//		name: id +//		type: string +//		description: ID of the interaction request targeting you. +//		in: path +//		required: true +// +//	security: +//	- OAuth2 Bearer: +//		- read:notifications +// +//	responses: +//		'200': +//			description: Interaction request. +//			schema: +//				"$ref": "#/definitions/interactionRequest" +//		'400': +//			description: bad request +//		'401': +//			description: unauthorized +//		'404': +//			description: not found +//		'406': +//			description: not acceptable +//		'500': +//			description: internal server error +func (m *Module) InteractionRequestGETHandler(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.JSONAcceptHeaders...); err != nil { +		apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1) +		return +	} + +	id, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey)) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return +	} + +	intReq, errWithCode := m.processor.InteractionRequests().GetOne( +		c.Request.Context(), +		authed.Account, +		id, +	) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return +	} + +	apiutil.JSON(c, http.StatusOK, intReq) +} diff --git a/internal/api/client/interactionrequests/getpage.go b/internal/api/client/interactionrequests/getpage.go new file mode 100644 index 000000000..1978a055c --- /dev/null +++ b/internal/api/client/interactionrequests/getpage.go @@ -0,0 +1,211 @@ +// 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 interactionrequests + +import ( +	"errors" +	"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" +	"github.com/superseriousbusiness/gotosocial/internal/paging" +) + +// InteractionRequestsGETHandler swagger:operation GET /api/v1/interaction_requests getInteractionRequests +// +// Get an array of interactions requested on your statuses by other accounts, and pending your approval. +// +// ``` +// <https://example.org/api/v1/interaction_requests?limit=80&max_id=01FC0SKA48HNSVR6YKZCQGS2V8>; rel="next", <https://example.org/api/v1/interaction_requests?limit=80&min_id=01FC0SKW5JK2Q4EVAV2B462YY0>; rel="prev" +// ```` +// +//	--- +//	tags: +//	- interaction_requests +// +//	produces: +//	- application/json +// +//	parameters: +//	- +//		name: status_id +//		type: string +//		description: >- +//			If set, then only interactions targeting the given status_id will be included in the results. +//		in: query +//		required: false +//	- +//		name: favourites +//		type: boolean +//		description: >- +//			If true or not set, pending favourites will be included in the results. +//			At least one of favourites, replies, and reblogs must be true. +//		in: query +//		required: false +//		default: true +//	- +//		name: replies +//		type: boolean +//		description: >- +//			If true or not set, pending replies will be included in the results. +//			At least one of favourites, replies, and reblogs must be true. +//		in: query +//		required: false +//		default: true +//	- +//		name: reblogs +//		type: boolean +//		description: >- +//			If true or not set, pending reblogs will be included in the results. +//			At least one of favourites, replies, and reblogs must be true. +//		in: query +//		required: false +//		default: true +//	- +//		name: max_id +//		type: string +//		description: >- +//			Return only interaction requests *OLDER* than the given max ID. +//			The interaction with the specified ID will not be included in the response. +//		in: query +//		required: false +//	- +//		name: since_id +//		type: string +//		description: >- +//			Return only interaction requests *NEWER* than the given since ID. +//			The interaction with the specified ID will not be included in the response. +//		in: query +//		required: false +//	- +//		name: min_id +//		type: string +//		description: >- +//			Return only interaction requests *IMMEDIATELY NEWER* than the given min ID. +//			The interaction with the specified ID will not be included in the response. +//		in: query +//		required: false +//	- +//		name: limit +//		type: integer +//		description: Number of interaction requests to return. +//		default: 40 +//		minimum: 1 +//		maximum: 80 +//		in: query +//		required: false +// +//	security: +//	- OAuth2 Bearer: +//		- read:notifications +// +//	responses: +//		'200': +//			headers: +//				Link: +//					type: string +//					description: Links to the next and previous queries. +//			schema: +//				type: array +//				items: +//					"$ref": "#/definitions/interactionRequest" +//		'400': +//			description: bad request +//		'401': +//			description: unauthorized +//		'404': +//			description: not found +//		'406': +//			description: not acceptable +//		'500': +//			description: internal server error +func (m *Module) InteractionRequestsGETHandler(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.JSONAcceptHeaders...); err != nil { +		apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1) +		return +	} + +	includeLikes, errWithCode := apiutil.ParseInteractionFavourites( +		c.Query(apiutil.InteractionFavouritesKey), true, +	) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return +	} + +	includeReplies, errWithCode := apiutil.ParseInteractionReplies( +		c.Query(apiutil.InteractionRepliesKey), true, +	) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return +	} + +	includeBoosts, errWithCode := apiutil.ParseInteractionReblogs( +		c.Query(apiutil.InteractionReblogsKey), true, +	) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return +	} + +	if !includeLikes && !includeReplies && !includeBoosts { +		const text = "at least one of favourites, replies, or boosts must be true" +		errWithCode := gtserror.NewErrorBadRequest(errors.New(text), text) +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return +	} + +	page, errWithCode := paging.ParseIDPage(c, +		1,  // min limit +		80, // max limit +		40, // default limit +	) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return +	} + +	resp, errWithCode := m.processor.InteractionRequests().GetPage( +		c.Request.Context(), +		authed.Account, +		c.Query(apiutil.InteractionStatusIDKey), +		includeLikes, +		includeReplies, +		includeBoosts, +		page, +	) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return +	} + +	if resp.LinkHeader != "" { +		c.Header("Link", resp.LinkHeader) +	} + +	apiutil.JSON(c, http.StatusOK, resp.Items) +} diff --git a/internal/api/client/interactionrequests/interactionrequests.go b/internal/api/client/interactionrequests/interactionrequests.go new file mode 100644 index 000000000..172951817 --- /dev/null +++ b/internal/api/client/interactionrequests/interactionrequests.go @@ -0,0 +1,50 @@ +// 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 interactionrequests + +import ( +	"net/http" + +	"github.com/gin-gonic/gin" +	apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" +	"github.com/superseriousbusiness/gotosocial/internal/processing" +) + +const ( +	BasePath       = "/v1/interaction_requests" +	BasePathWithID = BasePath + "/:" + apiutil.IDKey +	AuthorizePath  = BasePathWithID + "/authorize" +	RejectPath     = BasePathWithID + "/reject" +) + +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, BasePath, m.InteractionRequestsGETHandler) +	attachHandler(http.MethodGet, BasePathWithID, m.InteractionRequestGETHandler) +	attachHandler(http.MethodPost, AuthorizePath, m.InteractionRequestAuthorizePOSTHandler) +	attachHandler(http.MethodPost, RejectPath, m.InteractionRequestRejectPOSTHandler) +} diff --git a/internal/api/client/interactionrequests/reject.go b/internal/api/client/interactionrequests/reject.go new file mode 100644 index 000000000..33c426462 --- /dev/null +++ b/internal/api/client/interactionrequests/reject.go @@ -0,0 +1,104 @@ +// 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 interactionrequests + +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" +) + +// InteractionRequestRejectPOSTHandler swagger:operation POST /api/v1/interaction_requests/{id}/reject rejectInteractionRequest +// +// Reject an interaction request with the given ID. +// +//	--- +//	tags: +//	- interaction_requests +// +//	produces: +//	- application/json +// +//	parameters: +//	- +//		name: id +//		type: string +//		description: ID of the interaction request targeting you. +//		in: path +//		required: true +// +//	security: +//	- OAuth2 Bearer: +//		- write:statuses +// +//	responses: +//		'200': +//			name: Rejection. +//			description: The now-rejected interaction request. +//			schema: +//				"$ref": "#/definitions/interactionRequest" +//		'400': +//			description: bad request +//		'401': +//			description: unauthorized +//		'404': +//			description: not found +//		'406': +//			description: not acceptable +//		'500': +//			description: internal server error +func (m *Module) InteractionRequestRejectPOSTHandler(c *gin.Context) { +	authed, err := oauth.Authed(c, true, true, true, true) +	if err != nil { +		errWithCode := gtserror.NewErrorUnauthorized(err, err.Error()) +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return +	} + +	if authed.Account.IsMoving() { +		apiutil.ForbiddenAfterMove(c) +		return +	} + +	if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil { +		errWithCode := gtserror.NewErrorNotAcceptable(err, err.Error()) +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return +	} + +	reqID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey)) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return +	} + +	apiReq, errWithCode := m.processor.InteractionRequests().Reject( +		c.Request.Context(), +		authed.Account, +		reqID, +	) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return +	} + +	apiutil.JSON(c, http.StatusOK, apiReq) +} | 
