init from gitlab

This commit is contained in:
texm
2023-04-25 14:33:14 +08:00
parent 6a85a41ff0
commit c8202a5c82
281 changed files with 19861 additions and 1 deletions

View File

@@ -0,0 +1,75 @@
package middleware
import (
"fmt"
"github.com/labstack/echo/v4"
echoMiddleware "github.com/labstack/echo/v4/middleware"
"gitlab.com/texm/shokku/internal/env"
"gitlab.com/texm/shokku/internal/server/auth"
"strings"
)
const (
tokenContextKey = "user-token"
usedCookieAuthContextKey = "cookie-auth"
)
func skipDuringSetup(e *env.Env, c echo.Context) bool {
reqPath := c.Request().URL.Path
return !e.SetupCompleted && strings.HasPrefix(reqPath, "/api/setup")
}
func ProvideUserContext(e *env.Env) echo.MiddlewareFunc {
logger := middlewareLogger("userContext")
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
if skipDuringSetup(e, c) {
return next(c)
}
if err := e.Auth.SetUserContext(c, tokenContextKey); err != nil {
logger.Error().Err(err).Msg("failed to set context")
return echo.ErrInternalServerError
}
return next(c)
}
}
}
func tokenAuthSkipper(e *env.Env) echoMiddleware.Skipper {
return func(c echo.Context) bool {
return skipDuringSetup(e, c)
}
}
func TokenAuth(e *env.Env) echo.MiddlewareFunc {
config := echoMiddleware.JWTConfig{
Claims: &auth.User{},
SigningKey: e.Auth.GetSigningKey(),
TokenLookupFuncs: []echoMiddleware.ValuesExtractor{SplitTokenLookup},
TokenLookup: "header:Authorization",
ContextKey: tokenContextKey,
Skipper: tokenAuthSkipper(e),
}
return echoMiddleware.JWTWithConfig(config)
}
func SplitTokenLookup(c echo.Context) ([]string, error) {
dataCookie, err := c.Request().Cookie(auth.DataCookieName)
if err != nil {
return nil, fmt.Errorf("no data cookie: %w", err)
}
signatureCookie, err := c.Request().Cookie(auth.SignatureCookieName)
if err != nil {
return nil, fmt.Errorf("no signature cookie: %w", err)
}
c.Set(usedCookieAuthContextKey, true)
authToken := dataCookie.Value + "." + signatureCookie.Value
return []string{authToken}, nil
}
func CheckCookieAuthUsed(c echo.Context) bool {
v, ok := c.Get(usedCookieAuthContextKey).(bool)
return ok && v
}

View File

@@ -0,0 +1,10 @@
package middleware
import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func middlewareLogger(ware string) zerolog.Logger {
return log.With().Str("middleware", ware).Logger()
}

View File

@@ -0,0 +1,31 @@
package middleware
import (
"github.com/labstack/echo/v4"
echoMiddleware "github.com/labstack/echo/v4/middleware"
"github.com/rs/zerolog"
"strings"
)
func logErrorFunc(logger zerolog.Logger, debug bool) echoMiddleware.LogErrorFunc {
return func(c echo.Context, err error, stack []byte) error {
event := logger.Error().Err(err)
if debug {
stacklines := strings.Split(string(stack), "\n")
funcName := strings.TrimRight(strings.SplitAfter(stacklines[5], "(0x")[0], "(0x")
callSite := strings.Trim(strings.SplitAfter(stacklines[6], " ")[0], "\t ")
event.Str("func", funcName)
event.Str("callsite", callSite)
}
event.Msg("Recovered from panic")
return nil
}
}
func Recover(debug bool) echo.MiddlewareFunc {
logger := middlewareLogger("recover")
cfg := echoMiddleware.RecoverConfig{
LogErrorFunc: logErrorFunc(logger, debug),
}
return echoMiddleware.RecoverWithConfig(cfg)
}

View File

@@ -0,0 +1,87 @@
package middleware
import (
"fmt"
"gitlab.com/texm/shokku/internal/server/dto"
"strings"
"time"
"github.com/labstack/echo/v4"
"github.com/rs/zerolog/log"
)
func parseDokkuError(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
err := next(c)
if err == nil {
return nil
}
if dokkuHttpErr := dto.MaybeConvertDokkuError(err); dokkuHttpErr != nil {
log.Debug().Err(err).Msgf("converted dokku error to %s", dokkuHttpErr.Error())
return dokkuHttpErr
}
log.Error().Err(err).Str("path", c.Path()).Msg("got error")
return echo.ErrInternalServerError
}
}
func RequestLogger(debug bool) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
start := time.Now()
chainErr := next(c)
if chainErr != nil {
c.Error(chainErr)
}
req := c.Request()
res := c.Response()
if isStaticFile, ok := c.Get("static").(bool); ok && isStaticFile {
contentType := res.Header().Get("Content-Type")
isHTML := strings.HasPrefix(contentType, echo.MIMETextHTML)
if !isHTML {
return nil
}
}
n := res.Status
l := log.Info()
msg := "Success"
switch {
case n >= 500:
l = log.Error().Err(chainErr)
// logger.With(zap.Error(chainErr)).Error("Server error", fields...)
msg = "Server error"
case n >= 400:
msg = "Client error"
case n >= 300:
msg = "Redirect"
}
if debug {
l.Str("request", fmt.Sprintf("%s %s", req.Method, req.RequestURI))
l.Int("status", res.Status)
} else {
l.Str("remote_ip", c.RealIP())
l.Str("latency", time.Since(start).String())
l.Str("host", req.Host)
l.Str("request", fmt.Sprintf("%s %s", req.Method, req.RequestURI))
l.Int("status", res.Status)
l.Int64("size", res.Size)
}
id := req.Header.Get(echo.HeaderXRequestID)
if id != "" {
l.Str("request_id", id)
}
l.Msg(msg)
return nil
}
}
}

View File

@@ -0,0 +1,30 @@
package middleware
import (
"github.com/labstack/echo/v4"
echoMiddleware "github.com/labstack/echo/v4/middleware"
"net/http"
)
func Secure() echo.MiddlewareFunc {
// logger := e.Logger.Desugar()
// debug := e.DebugMode
// cfg := echomiddleware.SecureConfig{}
cfg := echoMiddleware.DefaultSecureConfig
return echoMiddleware.SecureWithConfig(cfg)
}
func CSRF() echo.MiddlewareFunc {
// we skip requests where cookie authentication was not used,
// as these are api requests - not from the browser
cfg := echoMiddleware.CSRFConfig{
CookieName: "_csrf",
CookiePath: "/",
CookieSameSite: http.SameSiteStrictMode,
Skipper: func(c echo.Context) bool {
return !CheckCookieAuthUsed(c)
},
}
return echoMiddleware.CSRFWithConfig(cfg)
}

View File

@@ -0,0 +1,59 @@
package middleware
import (
"github.com/labstack/echo/v4"
"gitlab.com/texm/shokku/internal/env"
"net/http"
"strings"
)
const (
notSetupErr = "server not setup"
invalidKeyErr = "setup key invalid"
setupKeyHeader = "X-Setup-Key"
)
func ServerSetupBlocker(e *env.Env, setupKey string) echo.MiddlewareFunc {
logger := middlewareLogger("setup")
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
reqPath := c.Request().URL.Path
if !strings.HasPrefix(reqPath, "/api") {
return next(c)
}
if reqPath == "/api/github/events" {
return next(c)
}
if reqPath == "/api/setup/status" {
return next(c)
}
isSetupRoute := strings.HasPrefix(reqPath, "/api/setup")
if e.SetupCompleted {
if isSetupRoute {
return echo.NewHTTPError(http.StatusBadRequest, "already set up")
}
return next(c)
}
providedKey := c.Request().Header.Get(setupKeyHeader)
if providedKey != setupKey {
logger.Debug().
Str("path", reqPath).
Str("provided", providedKey).
Msg("invalid setup key")
return echo.NewHTTPError(http.StatusForbidden, invalidKeyErr)
}
if isSetupRoute {
return next(c)
}
logger.Debug().Str("path", reqPath).Msg("not setup path")
return echo.NewHTTPError(http.StatusForbidden, notSetupErr)
}
}
}

View File

@@ -0,0 +1,29 @@
package middleware
import (
"github.com/labstack/echo/v4"
echomiddleware "github.com/labstack/echo/v4/middleware"
"io/fs"
"net/http"
"strings"
)
func staticFileSkipperFunc(c echo.Context) bool {
if strings.HasPrefix(c.Request().URL.Path, "/api") {
c.Set("static", false)
return true
}
c.Set("static", true)
return false
}
func StaticFiles(staticFS fs.FS) echo.MiddlewareFunc {
cfg := echomiddleware.StaticConfig{
Root: "dist",
Index: "app.html",
HTML5: true,
Filesystem: http.FS(staticFS),
Skipper: staticFileSkipperFunc,
}
return echomiddleware.StaticWithConfig(cfg)
}