mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
[chore] The Big Middleware and API Refactor (tm) (#1250)
* interim commit: start refactoring middlewares into package under router * another interim commit, this is becoming a big job * another fucking massive interim commit * refactor bookmarks to new style * ambassador, wiz zeze commits you are spoiling uz * she compiles, we're getting there * we're just normal men; we're just innocent men * apiutil * whoopsie * i'm glad noone reads commit msgs haha :blob_sweat: * use that weirdo go-bytesize library for maxMultipartMemory * fix media module paths
This commit is contained in:
@@ -20,29 +20,18 @@ package router
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
// AttachHandler attaches the given gin.HandlerFunc to the router with the specified method and path.
|
||||
// If the path is set to ANY, then the handlerfunc will be used for ALL methods at its given path.
|
||||
func (r *router) AttachHandler(method string, path string, handler gin.HandlerFunc) {
|
||||
if method == "ANY" {
|
||||
r.engine.Any(path, handler)
|
||||
} else {
|
||||
r.engine.Handle(method, path, handler)
|
||||
}
|
||||
func (r *router) AttachGlobalMiddleware(handlers ...gin.HandlerFunc) gin.IRoutes {
|
||||
return r.engine.Use(handlers...)
|
||||
}
|
||||
|
||||
// AttachMiddleware attaches a gin middleware to the router that will be used globally
|
||||
func (r *router) AttachMiddleware(middleware gin.HandlerFunc) {
|
||||
r.engine.Use(middleware)
|
||||
}
|
||||
|
||||
// AttachNoRouteHandler attaches a gin.HandlerFunc to NoRoute to handle 404's
|
||||
func (r *router) AttachNoRouteHandler(handler gin.HandlerFunc) {
|
||||
r.engine.NoRoute(handler)
|
||||
}
|
||||
|
||||
// AttachGroup attaches the given handlers into a group with the given relativePath as
|
||||
// base path for that group. It then returns the *gin.RouterGroup so that the caller
|
||||
// can add any extra middlewares etc specific to that group, as desired.
|
||||
func (r *router) AttachGroup(relativePath string, handlers ...gin.HandlerFunc) *gin.RouterGroup {
|
||||
return r.engine.Group(relativePath, handlers...)
|
||||
}
|
||||
|
||||
func (r *router) AttachHandler(method string, path string, handler gin.HandlerFunc) {
|
||||
r.engine.Handle(method, path, handler)
|
||||
}
|
||||
|
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
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 router
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var corsConfig = cors.Config{
|
||||
// TODO: make this customizable so instance admins can specify an origin for CORS requests
|
||||
AllowAllOrigins: true,
|
||||
|
||||
// adds the following:
|
||||
// "chrome-extension://"
|
||||
// "safari-extension://"
|
||||
// "moz-extension://"
|
||||
// "ms-browser-extension://"
|
||||
AllowBrowserExtensions: true,
|
||||
AllowMethods: []string{
|
||||
"POST",
|
||||
"PUT",
|
||||
"DELETE",
|
||||
"GET",
|
||||
"PATCH",
|
||||
"OPTIONS",
|
||||
},
|
||||
AllowHeaders: []string{
|
||||
// basic cors stuff
|
||||
"Origin",
|
||||
"Content-Length",
|
||||
"Content-Type",
|
||||
|
||||
// needed to pass oauth bearer tokens
|
||||
"Authorization",
|
||||
|
||||
// needed for websocket upgrade requests
|
||||
"Upgrade",
|
||||
"Sec-WebSocket-Extensions",
|
||||
"Sec-WebSocket-Key",
|
||||
"Sec-WebSocket-Protocol",
|
||||
"Sec-WebSocket-Version",
|
||||
"Connection",
|
||||
},
|
||||
AllowWebSockets: true,
|
||||
ExposeHeaders: []string{
|
||||
// needed for accessing next/prev links when making GET timeline requests
|
||||
"Link",
|
||||
|
||||
// needed so clients can handle rate limits
|
||||
"X-RateLimit-Reset",
|
||||
"X-RateLimit-Limit",
|
||||
"X-RateLimit-Remaining",
|
||||
"X-Request-Id",
|
||||
|
||||
// websocket stuff
|
||||
"Connection",
|
||||
"Sec-WebSocket-Accept",
|
||||
"Upgrade",
|
||||
},
|
||||
MaxAge: 2 * time.Minute,
|
||||
}
|
||||
|
||||
// useCors attaches the corsConfig above to the given gin engine
|
||||
func useCors(engine *gin.Engine) error {
|
||||
c := cors.New(corsConfig)
|
||||
engine.Use(c)
|
||||
return nil
|
||||
}
|
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
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 router
|
||||
|
||||
import (
|
||||
ginGzip "github.com/gin-contrib/gzip"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func useGzip(engine *gin.Engine) error {
|
||||
gzipMiddleware := ginGzip.Gzip(ginGzip.DefaultCompression)
|
||||
engine.Use(gzipMiddleware)
|
||||
return nil
|
||||
}
|
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
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 router
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"codeberg.org/gruf/go-bytesize"
|
||||
"codeberg.org/gruf/go-errors/v2"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-logger/v2/level"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||
)
|
||||
|
||||
// loggingMiddleware provides a request logging and panic recovery gin handler.
|
||||
func loggingMiddleware(c *gin.Context) {
|
||||
// Initialize the logging fields
|
||||
fields := make(kv.Fields, 6, 7)
|
||||
|
||||
// Determine pre-handler time
|
||||
before := time.Now()
|
||||
|
||||
defer func() {
|
||||
code := c.Writer.Status()
|
||||
path := c.Request.URL.Path
|
||||
|
||||
if r := recover(); r != nil {
|
||||
if c.Writer.Status() == 0 {
|
||||
// No response was written, send a generic Internal Error
|
||||
c.Writer.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
// Append panic information to the request ctx
|
||||
err := fmt.Errorf("recovered panic: %v", r)
|
||||
_ = c.Error(err)
|
||||
|
||||
// Dump a stacktrace to error log
|
||||
callers := errors.GetCallers(3, 10)
|
||||
log.WithField("stacktrace", callers).Error(err)
|
||||
}
|
||||
|
||||
// NOTE:
|
||||
// It is very important here that we are ONLY logging
|
||||
// the request path, and none of the query parameters.
|
||||
// Query parameters can contain sensitive information
|
||||
// and could lead to storing plaintext API keys in logs
|
||||
|
||||
// Set request logging fields
|
||||
fields[0] = kv.Field{"latency", time.Since(before)}
|
||||
fields[1] = kv.Field{"clientIP", c.ClientIP()}
|
||||
fields[2] = kv.Field{"userAgent", c.Request.UserAgent()}
|
||||
fields[3] = kv.Field{"method", c.Request.Method}
|
||||
fields[4] = kv.Field{"statusCode", code}
|
||||
fields[5] = kv.Field{"path", path}
|
||||
|
||||
// Create log entry with fields
|
||||
l := log.WithFields(fields...)
|
||||
|
||||
// Default is info
|
||||
lvl := level.INFO
|
||||
|
||||
if code >= 500 {
|
||||
// This is a server error
|
||||
lvl = level.ERROR
|
||||
l = l.WithField("error", c.Errors)
|
||||
}
|
||||
|
||||
// Generate a nicer looking bytecount
|
||||
size := bytesize.Size(c.Writer.Size())
|
||||
|
||||
// Finally, write log entry with status text body size
|
||||
l.Logf(lvl, "%s: wrote %s", http.StatusText(code), size)
|
||||
}()
|
||||
|
||||
// Process request
|
||||
c.Next()
|
||||
}
|
@@ -25,34 +25,39 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"codeberg.org/gruf/go-bytesize"
|
||||
"codeberg.org/gruf/go-debug"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
const (
|
||||
readTimeout = 60 * time.Second
|
||||
writeTimeout = 30 * time.Second
|
||||
idleTimeout = 30 * time.Second
|
||||
readHeaderTimeout = 30 * time.Second
|
||||
shutdownTimeout = 30 * time.Second
|
||||
readTimeout = 60 * time.Second
|
||||
writeTimeout = 30 * time.Second
|
||||
idleTimeout = 30 * time.Second
|
||||
readHeaderTimeout = 30 * time.Second
|
||||
shutdownTimeout = 30 * time.Second
|
||||
maxMultipartMemory = int64(8 * bytesize.MiB)
|
||||
)
|
||||
|
||||
// Router provides the REST interface for gotosocial, using gin.
|
||||
type Router interface {
|
||||
// Attach a gin handler to the router with the given method and path
|
||||
AttachHandler(method string, path string, f gin.HandlerFunc)
|
||||
// Attach a gin middleware to the router that will be used globally
|
||||
AttachMiddleware(handler gin.HandlerFunc)
|
||||
// Attach global gin middlewares to this router.
|
||||
AttachGlobalMiddleware(handlers ...gin.HandlerFunc) gin.IRoutes
|
||||
// AttachGroup attaches the given handlers into a group with the given relativePath as
|
||||
// base path for that group. It then returns the *gin.RouterGroup so that the caller
|
||||
// can add any extra middlewares etc specific to that group, as desired.
|
||||
AttachGroup(relativePath string, handlers ...gin.HandlerFunc) *gin.RouterGroup
|
||||
// Attach a single gin handler to the router with the given method and path.
|
||||
// To make middleware management easier, AttachGroup should be preferred where possible.
|
||||
// However, this function can be used for attaching single handlers that only require
|
||||
// global middlewares.
|
||||
AttachHandler(method string, path string, handler gin.HandlerFunc)
|
||||
|
||||
// Attach 404 NoRoute handler
|
||||
AttachNoRouteHandler(handler gin.HandlerFunc)
|
||||
// Attach a router group, and receive that group back.
|
||||
// More middlewares and handlers can then be attached on
|
||||
// the group by the caller.
|
||||
AttachGroup(path string, handlers ...gin.HandlerFunc) *gin.RouterGroup
|
||||
// Start the router
|
||||
Start()
|
||||
// Stop the router
|
||||
@@ -142,19 +147,20 @@ func (r *router) Stop(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// New returns a new Router with the specified configuration.
|
||||
// New returns a new Router.
|
||||
//
|
||||
// The given DB is only used in the New function for parsing config values, and is not otherwise
|
||||
// pinned to the router.
|
||||
func New(ctx context.Context, db db.DB) (Router, error) {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
// The router's Attach functions should be used *before* the router is Started.
|
||||
//
|
||||
// When the router's work is finished, Stop should be called on it to close connections gracefully.
|
||||
//
|
||||
// The provided context will be used as the base context for all requests passing
|
||||
// through the underlying http.Server, so this should be a long-running context.
|
||||
func New(ctx context.Context) (Router, error) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// create the actual engine here -- this is the core request routing handler for gts
|
||||
engine := gin.New()
|
||||
engine.Use(loggingMiddleware)
|
||||
|
||||
// 8 MiB
|
||||
engine.MaxMultipartMemory = 8 << 20
|
||||
engine.MaxMultipartMemory = maxMultipartMemory
|
||||
|
||||
// set up IP forwarding via x-forward-* headers.
|
||||
trustedProxies := config.GetTrustedProxies()
|
||||
@@ -162,21 +168,6 @@ func New(ctx context.Context, db db.DB) (Router, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// enable cors on the engine
|
||||
if err := useCors(engine); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// enable gzip compression on the engine
|
||||
if err := useGzip(engine); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// enable session store middleware on the engine
|
||||
if err := useSession(ctx, db, engine); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// set template functions
|
||||
LoadTemplateFunctions(engine)
|
||||
|
||||
|
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
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 router
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-contrib/sessions/memstore"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
// SessionOptions returns the standard set of options to use for each session.
|
||||
func SessionOptions() sessions.Options {
|
||||
var samesite http.SameSite
|
||||
switch strings.TrimSpace(strings.ToLower(config.GetAdvancedCookiesSamesite())) {
|
||||
case "lax":
|
||||
samesite = http.SameSiteLaxMode
|
||||
case "strict":
|
||||
samesite = http.SameSiteStrictMode
|
||||
default:
|
||||
log.Warnf("%s set to %s which is not recognized, defaulting to 'lax'", config.AdvancedCookiesSamesiteFlag(), config.GetAdvancedCookiesSamesite())
|
||||
samesite = http.SameSiteLaxMode
|
||||
}
|
||||
|
||||
return sessions.Options{
|
||||
Path: "/",
|
||||
Domain: config.GetHost(),
|
||||
// 2 minutes
|
||||
MaxAge: 120,
|
||||
// only set secure over https
|
||||
Secure: config.GetProtocol() == "https",
|
||||
// forbid javascript from inspecting cookie
|
||||
HttpOnly: true,
|
||||
// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-cookie-same-site-00#section-4.1.1
|
||||
SameSite: samesite,
|
||||
}
|
||||
}
|
||||
|
||||
// SessionName is a utility function that derives an appropriate session name from the hostname.
|
||||
func SessionName() (string, error) {
|
||||
// parse the protocol + host
|
||||
protocol := config.GetProtocol()
|
||||
host := config.GetHost()
|
||||
u, err := url.Parse(fmt.Sprintf("%s://%s", protocol, host))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// take the hostname without any port attached
|
||||
strippedHostname := u.Hostname()
|
||||
if strippedHostname == "" {
|
||||
return "", fmt.Errorf("could not derive hostname without port from %s://%s", protocol, host)
|
||||
}
|
||||
|
||||
// make sure IDNs are converted to punycode or the cookie library breaks:
|
||||
// see https://en.wikipedia.org/wiki/Punycode
|
||||
punyHostname, err := idna.New().ToASCII(strippedHostname)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not convert %s to punycode: %s", strippedHostname, err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("gotosocial-%s", punyHostname), nil
|
||||
}
|
||||
|
||||
func useSession(ctx context.Context, sessionDB db.Session, engine *gin.Engine) error {
|
||||
// check if we have a saved router session already
|
||||
rs, err := sessionDB.GetSession(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error using session: %s", err)
|
||||
}
|
||||
if rs == nil || rs.Auth == nil || rs.Crypt == nil {
|
||||
return errors.New("router session was nil")
|
||||
}
|
||||
|
||||
store := memstore.NewStore(rs.Auth, rs.Crypt)
|
||||
store.Options(SessionOptions())
|
||||
|
||||
sessionName, err := SessionName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
engine.Use(sessions.Sessions(sessionName, store))
|
||||
return nil
|
||||
}
|
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
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 router_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
type SessionTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func (suite *SessionTestSuite) SetupTest() {
|
||||
testrig.InitTestConfig()
|
||||
}
|
||||
|
||||
func (suite *SessionTestSuite) TestDeriveSessionNameLocalhostWithPort() {
|
||||
config.SetProtocol("http")
|
||||
config.SetHost("localhost:8080")
|
||||
|
||||
sessionName, err := router.SessionName()
|
||||
suite.NoError(err)
|
||||
suite.Equal("gotosocial-localhost", sessionName)
|
||||
}
|
||||
|
||||
func (suite *SessionTestSuite) TestDeriveSessionNameLocalhost() {
|
||||
config.SetProtocol("http")
|
||||
config.SetHost("localhost")
|
||||
|
||||
sessionName, err := router.SessionName()
|
||||
suite.NoError(err)
|
||||
suite.Equal("gotosocial-localhost", sessionName)
|
||||
}
|
||||
|
||||
func (suite *SessionTestSuite) TestDeriveSessionNoProtocol() {
|
||||
config.SetProtocol("")
|
||||
config.SetHost("localhost")
|
||||
|
||||
sessionName, err := router.SessionName()
|
||||
suite.EqualError(err, "parse \"://localhost\": missing protocol scheme")
|
||||
suite.Equal("", sessionName)
|
||||
}
|
||||
|
||||
func (suite *SessionTestSuite) TestDeriveSessionNoHost() {
|
||||
config.SetProtocol("https")
|
||||
config.SetHost("")
|
||||
config.SetPort(0)
|
||||
|
||||
sessionName, err := router.SessionName()
|
||||
suite.EqualError(err, "could not derive hostname without port from https://")
|
||||
suite.Equal("", sessionName)
|
||||
}
|
||||
|
||||
func (suite *SessionTestSuite) TestDeriveSessionOK() {
|
||||
config.SetProtocol("https")
|
||||
config.SetHost("example.org")
|
||||
|
||||
sessionName, err := router.SessionName()
|
||||
suite.NoError(err)
|
||||
suite.Equal("gotosocial-example.org", sessionName)
|
||||
}
|
||||
|
||||
func (suite *SessionTestSuite) TestDeriveSessionIDNOK() {
|
||||
config.SetProtocol("https")
|
||||
config.SetHost("fóid.org")
|
||||
|
||||
sessionName, err := router.SessionName()
|
||||
suite.NoError(err)
|
||||
suite.Equal("gotosocial-xn--fid-gna.org", sessionName)
|
||||
}
|
||||
|
||||
func TestSessionTestSuite(t *testing.T) {
|
||||
suite.Run(t, &SessionTestSuite{})
|
||||
}
|
@@ -26,7 +26,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/text"
|
||||
@@ -130,19 +130,19 @@ type iconWithLabel struct {
|
||||
label string
|
||||
}
|
||||
|
||||
func visibilityIcon(visibility model.Visibility) template.HTML {
|
||||
func visibilityIcon(visibility apimodel.Visibility) template.HTML {
|
||||
var icon iconWithLabel
|
||||
|
||||
switch visibility {
|
||||
case model.VisibilityPublic:
|
||||
case apimodel.VisibilityPublic:
|
||||
icon = iconWithLabel{"globe", "public"}
|
||||
case model.VisibilityUnlisted:
|
||||
case apimodel.VisibilityUnlisted:
|
||||
icon = iconWithLabel{"unlock", "unlisted"}
|
||||
case model.VisibilityPrivate:
|
||||
case apimodel.VisibilityPrivate:
|
||||
icon = iconWithLabel{"lock", "private"}
|
||||
case model.VisibilityMutualsOnly:
|
||||
case apimodel.VisibilityMutualsOnly:
|
||||
icon = iconWithLabel{"handshake-o", "mutuals only"}
|
||||
case model.VisibilityDirect:
|
||||
case apimodel.VisibilityDirect:
|
||||
icon = iconWithLabel{"envelope", "direct"}
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ func visibilityIcon(visibility model.Visibility) template.HTML {
|
||||
}
|
||||
|
||||
// text is a template.HTML to affirm that the input of this function is already escaped
|
||||
func emojify(emojis []model.Emoji, inputText template.HTML) template.HTML {
|
||||
func emojify(emojis []apimodel.Emoji, inputText template.HTML) template.HTML {
|
||||
out := text.Emojify(emojis, string(inputText))
|
||||
|
||||
/* #nosec G203 */
|
||||
|
Reference in New Issue
Block a user