gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

web.go (5407B)


      1 // GoToSocial
      2 // Copyright (C) GoToSocial Authors admin@gotosocial.org
      3 // SPDX-License-Identifier: AGPL-3.0-or-later
      4 //
      5 // This program is free software: you can redistribute it and/or modify
      6 // it under the terms of the GNU Affero General Public License as published by
      7 // the Free Software Foundation, either version 3 of the License, or
      8 // (at your option) any later version.
      9 //
     10 // This program is distributed in the hope that it will be useful,
     11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 // GNU Affero General Public License for more details.
     14 //
     15 // You should have received a copy of the GNU Affero General Public License
     16 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17 
     18 package web
     19 
     20 import (
     21 	"context"
     22 	"net/http"
     23 	"net/url"
     24 	"path/filepath"
     25 
     26 	"codeberg.org/gruf/go-cache/v3"
     27 	"github.com/gin-gonic/gin"
     28 	"github.com/superseriousbusiness/gotosocial/internal/config"
     29 	"github.com/superseriousbusiness/gotosocial/internal/db"
     30 	"github.com/superseriousbusiness/gotosocial/internal/log"
     31 	"github.com/superseriousbusiness/gotosocial/internal/middleware"
     32 	"github.com/superseriousbusiness/gotosocial/internal/processing"
     33 	"github.com/superseriousbusiness/gotosocial/internal/router"
     34 	"github.com/superseriousbusiness/gotosocial/internal/uris"
     35 )
     36 
     37 const (
     38 	confirmEmailPath   = "/" + uris.ConfirmEmailPath
     39 	profileGroupPath   = "/@:" + usernameKey
     40 	statusPath         = "/statuses/:" + statusIDKey // leave out the '/@:username' prefix as this will be served within the profile group
     41 	customCSSPath      = profileGroupPath + "/custom.css"
     42 	rssFeedPath        = profileGroupPath + "/feed.rss"
     43 	assetsPathPrefix   = "/assets"
     44 	distPathPrefix     = assetsPathPrefix + "/dist"
     45 	settingsPathPrefix = "/settings"
     46 	settingsPanelGlob  = settingsPathPrefix + "/*panel"
     47 	userPanelPath      = settingsPathPrefix + "/user"
     48 	adminPanelPath     = settingsPathPrefix + "/admin"
     49 
     50 	tokenParam  = "token"
     51 	usernameKey = "username"
     52 	statusIDKey = "status"
     53 
     54 	cacheControlHeader    = "Cache-Control"     // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
     55 	cacheControlNoCache   = "no-cache"          // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#response_directives
     56 	ifModifiedSinceHeader = "If-Modified-Since" // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since
     57 	ifNoneMatchHeader     = "If-None-Match"     // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match
     58 	eTagHeader            = "ETag"              // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
     59 	lastModifiedHeader    = "Last-Modified"     // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified
     60 )
     61 
     62 type Module struct {
     63 	processor    *processing.Processor
     64 	eTagCache    cache.Cache[string, eTagCacheEntry]
     65 	isURIBlocked func(context.Context, *url.URL) (bool, db.Error)
     66 }
     67 
     68 func New(db db.DB, processor *processing.Processor) *Module {
     69 	return &Module{
     70 		processor:    processor,
     71 		eTagCache:    newETagCache(),
     72 		isURIBlocked: db.IsURIBlocked,
     73 	}
     74 }
     75 
     76 func (m *Module) Route(r router.Router, mi ...gin.HandlerFunc) {
     77 	// Group all static files from assets dir at /assets,
     78 	// so that they can use the same cache control middleware.
     79 	webAssetsAbsFilePath, err := filepath.Abs(config.GetWebAssetBaseDir())
     80 	if err != nil {
     81 		log.Panicf(nil, "error getting absolute path of assets dir: %s", err)
     82 	}
     83 	fs := fileSystem{http.Dir(webAssetsAbsFilePath)}
     84 	assetsGroup := r.AttachGroup(assetsPathPrefix)
     85 	assetsGroup.Use(m.assetsCacheControlMiddleware(fs))
     86 	assetsGroup.Use(mi...)
     87 	assetsGroup.StaticFS("/", fs)
     88 
     89 	// handlers that serve profiles and statuses should use the SignatureCheck
     90 	// middleware, so that requests with content-type application/activity+json
     91 	// can still be served
     92 	profileGroup := r.AttachGroup(profileGroupPath)
     93 	profileGroup.Use(mi...)
     94 	profileGroup.Use(middleware.SignatureCheck(m.isURIBlocked), middleware.CacheControl("no-store"))
     95 	profileGroup.Handle(http.MethodGet, "", m.profileGETHandler) // use empty path here since it's the base of the group
     96 	profileGroup.Handle(http.MethodGet, statusPath, m.threadGETHandler)
     97 
     98 	// Attach individual web handlers which require no specific middlewares
     99 	r.AttachHandler(http.MethodGet, "/", m.baseHandler) // front-page
    100 	r.AttachHandler(http.MethodGet, settingsPathPrefix, m.SettingsPanelHandler)
    101 	r.AttachHandler(http.MethodGet, settingsPanelGlob, m.SettingsPanelHandler)
    102 	r.AttachHandler(http.MethodGet, customCSSPath, m.customCSSGETHandler)
    103 	r.AttachHandler(http.MethodGet, rssFeedPath, m.rssFeedGETHandler)
    104 	r.AttachHandler(http.MethodGet, confirmEmailPath, m.confirmEmailGETHandler)
    105 	r.AttachHandler(http.MethodGet, robotsPath, m.robotsGETHandler)
    106 	r.AttachHandler(http.MethodGet, aboutPath, m.aboutGETHandler)
    107 	r.AttachHandler(http.MethodGet, domainBlockListPath, m.domainBlockListGETHandler)
    108 
    109 	// Attach redirects from old endpoints to current ones for backwards compatibility
    110 	r.AttachHandler(http.MethodGet, "/auth/edit", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, userPanelPath) })
    111 	r.AttachHandler(http.MethodGet, "/user", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, userPanelPath) })
    112 	r.AttachHandler(http.MethodGet, "/admin", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, adminPanelPath) })
    113 }