diff options
| author | 2023-03-14 17:11:04 +0100 | |
|---|---|---|
| committer | 2023-03-14 16:11:04 +0000 | |
| commit | 196cd88b1c7c44a337ca12f6a804f1bb7fa83c4a (patch) | |
| tree | 9607d95b3f4f55a1ebfeded2f7aa9a3c8866bd0a /internal/api | |
| parent | [chore] fix + update swagger docs (#1622) (diff) | |
| download | gotosocial-196cd88b1c7c44a337ca12f6a804f1bb7fa83c4a.tar.xz | |
[feature] Allow admins to send test emails (#1620)
* [feature] Allow admins to send test emails
* implement unwrap on new error type
* add + use gtserror types
* GoToSocial Email Test -> GoToSocial Test Email
* add + use getInstance db call
* removed unused "unknown" error type
Diffstat (limited to 'internal/api')
| -rw-r--r-- | internal/api/client/admin/admin.go | 80 | ||||
| -rw-r--r-- | internal/api/client/admin/emailtest.go | 120 | ||||
| -rw-r--r-- | internal/api/model/admin.go | 6 | 
3 files changed, 156 insertions, 50 deletions
diff --git a/internal/api/client/admin/admin.go b/internal/api/client/admin/admin.go index 2193ce545..4079dd979 100644 --- a/internal/api/client/admin/admin.go +++ b/internal/api/client/admin/admin.go @@ -25,60 +25,37 @@ import (  )  const ( -	// BasePath is the base API path for this module, excluding the api prefix -	BasePath = "/v1/admin" -	// EmojiPath is used for posting/deleting custom emojis. -	EmojiPath = BasePath + "/custom_emojis" -	// EmojiPathWithID is used for interacting with a single emoji. -	EmojiPathWithID = EmojiPath + "/:" + IDKey -	// EmojiCategoriesPath is used for interacting with emoji categories. -	EmojiCategoriesPath = EmojiPath + "/categories" -	// DomainBlocksPath is used for posting domain blocks. -	DomainBlocksPath = BasePath + "/domain_blocks" -	// DomainBlocksPathWithID is used for interacting with a single domain block. +	BasePath               = "/v1/admin" +	EmojiPath              = BasePath + "/custom_emojis" +	EmojiPathWithID        = EmojiPath + "/:" + IDKey +	EmojiCategoriesPath    = EmojiPath + "/categories" +	DomainBlocksPath       = BasePath + "/domain_blocks"  	DomainBlocksPathWithID = DomainBlocksPath + "/:" + IDKey -	// AccountsPath is used for listing + acting on accounts. -	AccountsPath = BasePath + "/accounts" -	// AccountsPathWithID is used for interacting with a single account. -	AccountsPathWithID = AccountsPath + "/:" + IDKey -	// AccountsActionPath is used for taking action on a single account. -	AccountsActionPath = AccountsPathWithID + "/action" -	MediaCleanupPath   = BasePath + "/media_cleanup" -	MediaRefetchPath   = BasePath + "/media_refetch" -	// ReportsPath is for serving admin view of user reports. -	ReportsPath = BasePath + "/reports" -	// ReportsPathWithID is for viewing/acting on one report. -	ReportsPathWithID = ReportsPath + "/:" + IDKey -	// ReportsResolvePath is for marking one report as resolved. -	ReportsResolvePath = ReportsPathWithID + "/resolve" +	AccountsPath           = BasePath + "/accounts" +	AccountsPathWithID     = AccountsPath + "/:" + IDKey +	AccountsActionPath     = AccountsPathWithID + "/action" +	MediaCleanupPath       = BasePath + "/media_cleanup" +	MediaRefetchPath       = BasePath + "/media_refetch" +	ReportsPath            = BasePath + "/reports" +	ReportsPathWithID      = ReportsPath + "/:" + IDKey +	ReportsResolvePath     = ReportsPathWithID + "/resolve" +	EmailPath              = BasePath + "/email" +	EmailTestPath          = EmailPath + "/test" -	// ExportQueryKey is for requesting a public export of some data. -	ExportQueryKey = "export" -	// ImportQueryKey is for submitting an import of some data. -	ImportQueryKey = "import" -	// IDKey specifies the ID of a single item being interacted with. -	IDKey = "id" -	// FilterKey is for applying filters to admin views of accounts, emojis, etc. -	FilterQueryKey = "filter" -	// MaxShortcodeDomainKey is the url query for returning emoji results lower (alphabetically) -	// than the given `[shortcode]@[domain]` parameter. +	ExportQueryKey        = "export" +	ImportQueryKey        = "import" +	IDKey                 = "id" +	FilterQueryKey        = "filter"  	MaxShortcodeDomainKey = "max_shortcode_domain" -	// MaxShortcodeDomainKey is the url query for returning emoji results higher (alphabetically) -	// than the given `[shortcode]@[domain]` parameter.  	MinShortcodeDomainKey = "min_shortcode_domain" -	// LimitKey is for specifying maximum number of results to return. -	LimitKey = "limit" -	// DomainQueryKey is for specifying a domain during admin actions. -	DomainQueryKey = "domain" -	// ResolvedKey is for filtering reports by their resolved status -	ResolvedKey = "resolved" -	// AccountIDKey is for selecting account in API paths. -	AccountIDKey = "account_id" -	// TargetAccountIDKey is for selecting target account in API paths. -	TargetAccountIDKey = "target_account_id" -	MaxIDKey           = "max_id" -	SinceIDKey         = "since_id" -	MinIDKey           = "min_id" +	LimitKey              = "limit" +	DomainQueryKey        = "domain" +	ResolvedKey           = "resolved" +	AccountIDKey          = "account_id" +	TargetAccountIDKey    = "target_account_id" +	MaxIDKey              = "max_id" +	SinceIDKey            = "since_id" +	MinIDKey              = "min_id"  )  type Module struct { @@ -117,4 +94,7 @@ func (m *Module) Route(attachHandler func(method string, path string, f ...gin.H  	attachHandler(http.MethodGet, ReportsPath, m.ReportsGETHandler)  	attachHandler(http.MethodGet, ReportsPathWithID, m.ReportGETHandler)  	attachHandler(http.MethodPost, ReportsResolvePath, m.ReportResolvePOSTHandler) + +	// email stuff +	attachHandler(http.MethodPost, EmailTestPath, m.EmailTestPOSTHandler)  } diff --git a/internal/api/client/admin/emailtest.go b/internal/api/client/admin/emailtest.go new file mode 100644 index 000000000..5c5330679 --- /dev/null +++ b/internal/api/client/admin/emailtest.go @@ -0,0 +1,120 @@ +// 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 admin + +import ( +	"fmt" +	"net/http" +	"net/mail" + +	"github.com/gin-gonic/gin" +	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" +	apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" +	"github.com/superseriousbusiness/gotosocial/internal/gtserror" +	"github.com/superseriousbusiness/gotosocial/internal/oauth" +) + +// EmailTestPostHandler swagger:operation POST /api/v1/admin/email/test testEmailSend +// +// Send a generic test email to a specified email address. +// +// This can be used to validate an instance's SMTP configuration, and to debug any potential issues. +// +// If an error is returned by the SMTP connection, this handler will return code 422 to indicate that +// the request could not be processed, and the SMTP error will be returned to the caller. +// +//	--- +//	tags: +//	- admin +// +//	consumes: +//	- multipart/form-data +// +//	produces: +//	- application/json +// +//	parameters: +//	- +//		name: email +//		in: formData +//		description: The email address that the test email should be sent to. +//		type: string +// +//	security: +//	- OAuth2 Bearer: +//		- admin +// +//	responses: +//		'202': +//			description: Test email was sent. +//		'400': +//			description: bad request +//		'401': +//			description: unauthorized +//		'403': +//			description: forbidden +//		'404': +//			description: not found +//		'406': +//			description: not acceptable +//		'422': +//			description: >- +//				An smtp occurred while the email attempt was in progress. +//				Check the returned json for more information. The smtp error +//				will be included, to help you debug communication with the +//				smtp server. +//		'500': +//			description: internal server error +func (m *Module) EmailTestPOSTHandler(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 !*authed.User.Admin { +		err := fmt.Errorf("user %s not an admin", authed.User.ID) +		apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(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 +	} + +	form := &apimodel.AdminSendTestEmailRequest{} +	if err := c.ShouldBind(form); err != nil { +		apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) +		return +	} + +	email, err := mail.ParseAddress(form.Email) +	if err != nil { +		apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) +		return +	} + +	errWithCode := m.processor.Admin().EmailTest(c.Request.Context(), authed.Account, email.Address) +	if errWithCode != nil { +		apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) +		return +	} + +	c.JSON(http.StatusAccepted, gin.H{"status": "test email sent"}) +} diff --git a/internal/api/model/admin.go b/internal/api/model/admin.go index 7d1590b34..cc449ab82 100644 --- a/internal/api/model/admin.go +++ b/internal/api/model/admin.go @@ -183,3 +183,9 @@ type MediaCleanupRequest struct {  	// If value is not specified, the value of media-remote-cache-days in the server config will be used.  	RemoteCacheDays *int `form:"remote_cache_days" json:"remote_cache_days" xml:"remote_cache_days"`  } + +// AdminSendTestEmailRequest models a test email send request (woah). +type AdminSendTestEmailRequest struct { +	// Email address to send the test email to. +	Email string `form:"email" json:"email" xml:"email"` +}  | 
