v2.StaticPageCacheControlMiddleware   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
package v2
2
3
import (
4
	"crypto/rand"
5
	"encoding/base64"
6
	"fmt"
7
	"net/http"
8
	"sync"
9
10
	"github.com/labstack/echo/v4"
11
	"github.com/labstack/echo/v4/middleware"
12
	"github.com/memnix/memnix-rest/cmd/v2/config"
13
	"github.com/memnix/memnix-rest/domain"
14
)
15
16
var (
17
	instance *InstanceSingleton //nolint:gochecknoglobals //Singleton
18
	once     sync.Once          //nolint:gochecknoglobals //Singleton
19
)
20
21
type InstanceSingleton struct {
22
	echoInstance *echo.Echo
23
	config       config.ServerConfig
24
}
25
26
// New returns a new Echo instance.
27
func GetEchoInstance() *echo.Echo {
28
	return instance.echoInstance
29
}
30
31
func GetEchoSingleton() *InstanceSingleton {
32
	once.Do(func() {
33
		instance = &InstanceSingleton{}
34
		instance.echoInstance = echo.New()
35
		instance.registerMiddlewares(instance.echoInstance)
36
37
		instance.registerStaticRoutes(instance.echoInstance)
38
39
		instance.registerRoutes(instance.echoInstance)
40
	})
41
	return instance
42
}
43
44
func CreateEchoInstance(config config.ServerConfig) *InstanceSingleton {
45
	return GetEchoSingleton().WithConfig(config)
46
}
47
48
func (i *InstanceSingleton) Start() error {
49
	if err := i.echoInstance.Start(":" + i.config.Port); err != nil {
50
		return err
51
	}
52
53
	return nil
54
}
55
56
func (i *InstanceSingleton) WithConfig(config config.ServerConfig) *InstanceSingleton {
57
	i.config = config
58
	return i
59
}
60
61
func (i *InstanceSingleton) registerMiddlewares(e *echo.Echo) {
62
	e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
63
		AllowOrigins: []string{"http://localhost", i.config.FrontendURL, i.config.Host},
64
		AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept},
65
	}))
66
67
	e.Use(CSPMiddleware)
68
69
	// if debug
70
	if config.IsDevelopment() {
71
		e.Use(middleware.Logger())
72
	}
73
74
	e.Use(middleware.Recover())
75
76
	e.Use(middleware.Secure())
77
78
	csrfConfig := middleware.CSRFWithConfig(middleware.CSRFConfig{
79
		TokenLookup:    "cookie:_csrf",
80
		CookiePath:     "/",
81
		CookieDomain:   i.config.Host,
82
		CookieSecure:   true,
83
		CookieHTTPOnly: true,
84
		CookieSameSite: http.SameSiteStrictMode,
85
	})
86
87
	e.Use(csrfConfig)
88
}
89
90
func generateSecureNonce() (string, error) {
91
	nonce := make([]byte, config.NonceLength)
92
	_, err := rand.Read(nonce)
93
	if err != nil {
94
		return "", err
95
	}
96
	return base64.URLEncoding.EncodeToString(nonce), err
97
}
98
99
func CSPMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
100
	return func(c echo.Context) error {
101
		htmxNonce, _ := generateSecureNonce()
102
		hyperscriptNonce, _ := generateSecureNonce()
103
		picoCSSNonce, _ := generateSecureNonce()
104
		cssScopeInlineNonce, _ := generateSecureNonce()
105
		preloadNonce, _ := generateSecureNonce()
106
		umamiNonce, _ := generateSecureNonce()
107
108
		_ = "sha256-pgn1TCGZX6O77zDvy0oTODMOxemn0oj0LeCnQTRj7Kg="
109
110
		cspHeader := fmt.Sprintf(
111
			"default-src 'self'; connect-src 'self' https://umami.memnix.app ; script-src 'nonce-%s' 'nonce-%s' 'nonce-%s' 'nonce-%s' 'nonce-%s'; style-src 'self' 'unsafe-inline' https://fonts.bunny.net; font-src https://fonts.bunny.net 'self'",
112
			htmxNonce, hyperscriptNonce, preloadNonce, cssScopeInlineNonce, umamiNonce)
113
114
		c.Response().Header().Set("Content-Security-Policy", cspHeader)
115
116
		c.Set("nonce", domain.Nonce{
117
			HtmxNonce:           htmxNonce,
118
			HyperscriptNonce:    hyperscriptNonce,
119
			PreloadNonce:        preloadNonce,
120
			UmamiNonce:          umamiNonce,
121
			PicoCSSNonce:        picoCSSNonce,
122
			CSSScopeInlineNonce: cssScopeInlineNonce,
123
		})
124
125
		return next(c)
126
	}
127
}
128
129
func StaticAssetsCacheControlMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
130
	return func(c echo.Context) error {
131
		c.Response().Header().Set("Cache-Control", "public, max-age=31536000")
132
		return next(c)
133
	}
134
}
135
136
func StaticPageCacheControlMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
137
	return func(c echo.Context) error {
138
		// Set Cache-Control header
139
		c.Response().Header().Set("Cache-Control", "private, max-age=60")
140
141
		return next(c)
142
	}
143
}
144