diff options
| author | 2025-11-14 10:52:13 +0100 | |
|---|---|---|
| committer | 2025-11-17 14:15:53 +0100 | |
| commit | bc7ff907077b523f33b6bf8cb406d55d7c873628 (patch) | |
| tree | e7e12a10e1c9477e106b709a9732e3c6e14882d2 | |
| parent | [bugfix] Add Swagger docs for the user:notification stream (#4555) (diff) | |
| download | gotosocial-bc7ff907077b523f33b6bf8cb406d55d7c873628.tar.xz | |
[feature] Implement stub `authorize_interaction` route (#4557)
Updates #4548
# Description
This PR implements a route for the `/authorize_interaction` endpoint. This endpoint is used when an interaction request comes from a remote Mastodon instance, e.g. when a user clicks on the reply button and enters their GoToSocial server address into the field.
closes #4548
## Checklist
- [x] I/we have read the [GoToSocial contribution guidelines](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/CONTRIBUTING.md).
- [x] I/we have discussed the proposed changes already, either in an issue on the repository, or in the Matrix chat.
- [x] I/we have not leveraged AI to create the proposed changes.
- [x] I/we have performed a self-review of added code.
- [x] I/we have written code that is legible and maintainable by others.
- [x] I/we have commented the added code, particularly in hard-to-understand areas.
- [x] I/we have made any necessary changes to documentation.
- [ ] I/we have added tests that cover new code.
- [ ] I/we have run tests and they pass locally with the changes.
- [ ] I/we have run `go fmt ./...` and `golangci-lint run`.
Co-authored-by: tobi <tobi.smethurst@protonmail.com>
Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4557
Co-authored-by: Kevin Gimbel <kevin@gimbel.dev>
Co-committed-by: Kevin Gimbel <kevin@gimbel.dev>
| -rw-r--r-- | internal/web/authorize-interaction.go | 91 | ||||
| -rw-r--r-- | internal/web/web.go | 32 | ||||
| -rw-r--r-- | web/template/authorize-interaction.tmpl | 30 |
3 files changed, 138 insertions, 15 deletions
diff --git a/internal/web/authorize-interaction.go b/internal/web/authorize-interaction.go new file mode 100644 index 000000000..46dc00d1d --- /dev/null +++ b/internal/web/authorize-interaction.go @@ -0,0 +1,91 @@ +// 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 web + +import ( + "context" + "net/http" + "net/url" + + apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model" + apiutil "code.superseriousbusiness.org/gotosocial/internal/api/util" + "code.superseriousbusiness.org/gotosocial/internal/gtserror" + "github.com/gin-gonic/gin" +) + +// authorizeInteractionGETHandler handles redirects from remote +// (usually Mastodon) instances when a user tries to do a +// "remote interaction" and gives their GoToSocial account/domain. +// We use this handler instead of serving a generic 404 page. +func (m *Module) authorizeInteractionGETHandler(c *gin.Context) { + instance, errWithCode := m.processor.InstanceGetV1(c.Request.Context()) + if errWithCode != nil { + apiutil.WebErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } + + // Return instance we already got from the db, + // don't try to fetch it again when erroring. + instanceGet := func(ctx context.Context) (*apimodel.InstanceV1, gtserror.WithCode) { + return instance, nil + } + + // We only serve text/html at this endpoint. + if _, err := apiutil.NegotiateAccept(c, apiutil.TextHTML); err != nil { + apiutil.WebErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), instanceGet) + return + } + + // Redirects to the "authorize_interaction" + // endpoint should contain the URI of the + // object that the user is trying to interact + // with in the 'uri' query param. + uriStr := c.Query("uri") + if uriStr == "" { + const text = "no uri query parameter found in string" + errWithCode := gtserror.NewWithCode(http.StatusNotFound, text) + apiutil.WebErrorHandler(c, errWithCode, instanceGet) + } + + // Try to parse the object URI. + interactionURI, err := url.Parse(uriStr) + if err != nil { + err := gtserror.Newf("interaction URI could not be parsed: %w", err) + errWithCode := gtserror.NewErrorBadRequest(err, err.Error()) + apiutil.WebErrorHandler(c, errWithCode, instanceGet) + } + + page := apiutil.WebPage{ + Template: "authorize-interaction.tmpl", + Instance: instance, + OGMeta: apiutil.OGBase(instance), + Stylesheets: []string{cssAbout}, + Javascript: []apiutil.JavascriptEntry{ + { + Src: jsFrontend, + Async: true, + Defer: true, + }, + }, + Extra: map[string]any{ + "interactionURI": interactionURI, + }, + } + + apiutil.TemplateWebPage(c, page) +} diff --git a/internal/web/web.go b/internal/web/web.go index 3468ef63b..72b640fb3 100644 --- a/internal/web/web.go +++ b/internal/web/web.go @@ -33,21 +33,22 @@ import ( ) const ( - confirmEmailPath = "/" + uris.ConfirmEmailPath - profileGroupPath = "/@:username" - statusPath = "/statuses/:" + apiutil.WebStatusIDKey // leave out the '/@:username' prefix as this will be served within the profile group - tagsPath = "/tags/:" + apiutil.TagNameKey - customCSSPath = profileGroupPath + "/custom.css" - instanceCustomCSSPath = "/custom.css" - rssFeedPath = profileGroupPath + "/feed.rss" - assetsPathPrefix = "/assets" - distPathPrefix = assetsPathPrefix + "/dist" - themesPathPrefix = assetsPathPrefix + "/themes" - settingsPathPrefix = "/settings" - settingsPanelGlob = settingsPathPrefix + "/*panel" - userPanelPath = settingsPathPrefix + "/user" - adminPanelPath = settingsPathPrefix + "/admin" - signupPath = "/signup" + confirmEmailPath = "/" + uris.ConfirmEmailPath + profileGroupPath = "/@:username" + statusPath = "/statuses/:" + apiutil.WebStatusIDKey // leave out the '/@:username' prefix as this will be served within the profile group + tagsPath = "/tags/:" + apiutil.TagNameKey + customCSSPath = profileGroupPath + "/custom.css" + instanceCustomCSSPath = "/custom.css" + rssFeedPath = profileGroupPath + "/feed.rss" + assetsPathPrefix = "/assets" + distPathPrefix = assetsPathPrefix + "/dist" + themesPathPrefix = assetsPathPrefix + "/themes" + settingsPathPrefix = "/settings" + settingsPanelGlob = settingsPathPrefix + "/*panel" + userPanelPath = settingsPathPrefix + "/user" + adminPanelPath = settingsPathPrefix + "/admin" + signupPath = "/signup" + authorizeInteractionPath = "/authorize_interaction" cacheControlHeader = "Cache-Control" // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control cacheControlNoCache = "no-cache" // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#response_directives @@ -127,6 +128,7 @@ func (m *Module) Route(r *router.Router, mi ...gin.HandlerFunc) { everythingElseGroup.Handle(http.MethodGet, domainAllowlistPath, m.domainAllowlistGETHandler) everythingElseGroup.Handle(http.MethodGet, tagsPath, m.tagGETHandler) everythingElseGroup.Handle(http.MethodGet, signupPath, m.signupGETHandler) + everythingElseGroup.Handle(http.MethodGet, authorizeInteractionPath, m.authorizeInteractionGETHandler) everythingElseGroup.Handle(http.MethodPost, signupPath, m.signupPOSTHandler) // Redirects from old endpoints for back compat. diff --git a/web/template/authorize-interaction.tmpl b/web/template/authorize-interaction.tmpl new file mode 100644 index 000000000..e2ab397ed --- /dev/null +++ b/web/template/authorize-interaction.tmpl @@ -0,0 +1,30 @@ +{{- /* +// 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/>. +*/ -}} + +{{- with . }} +<main> + <section> + <h2>Authorize Remote Interaction</h2> + <p>It looks like you're trying to interact with a post or account from a different instance.</p> + <p>Because GoToSocial does not come with a web client built in, this is not supported.</p> + <p>Instead, you can interact with the post or account by copying the URL shown below, and pasting it into the search bar of whatever app you normally use for your GoToSocial account:</p> + <div class="prism-highlighted"><pre><code class="language-uri">{{- .interactionURI -}}</code></pre></div> + </section> +</main> +{{- end }}
\ No newline at end of file |
