opengist/internal/web/server/server.go
Thomas Miceli d75840eba8 wip
2024-12-29 11:40:23 +01:00

178 lines
4.2 KiB
Go

package server
import (
"errors"
"github.com/thomiceli/opengist/internal/utils"
"github.com/thomiceli/opengist/internal/web/context"
"github.com/thomiceli/opengist/internal/web/handler"
"html/template"
"io"
"net/http"
"strings"
"github.com/gorilla/sessions"
"github.com/labstack/echo/v4"
"github.com/rs/zerolog/log"
"github.com/thomiceli/opengist/internal/auth"
"github.com/thomiceli/opengist/internal/config"
"github.com/thomiceli/opengist/internal/db"
"github.com/thomiceli/opengist/internal/i18n"
)
type Template struct {
templates *template.Template
}
func (t *Template) Render(w io.Writer, name string, data interface{}, _ echo.Context) error {
return t.templates.ExecuteTemplate(w, name, data)
}
type Server struct {
echo *echo.Echo
flashStore *sessions.CookieStore // session store for flash messages
UserStore *sessions.FilesystemStore // session store for user sessions
dev bool
sessionsPath string
ignoreCsrf bool
}
func NewServer(isDev bool, sessionsPath string, ignoreCsrf bool) *Server {
e := echo.New()
e.HideBanner = true
e.HidePort = true
s := &Server{echo: e, dev: isDev, sessionsPath: sessionsPath, ignoreCsrf: ignoreCsrf}
s.useCustomContext()
if err := i18n.Locales.LoadAll(); err != nil {
log.Fatal().Err(err).Msg("Failed to load locales")
}
s.RegisterMiddlewares()
s.setFuncMap()
s.echo.HTTPErrorHandler = s.errorHandler
e.Validator = utils.NewValidator()
if !s.dev {
parseManifestEntries()
}
s.setupRoutes()
return s
}
func (s *Server) Start() {
addr := config.C.HttpHost + ":" + config.C.HttpPort
log.Info().Msg("Starting HTTP server on http://" + addr)
if err := s.echo.Start(addr); err != nil && err != http.ErrServerClosed {
log.Fatal().Err(err).Msg("Failed to start HTTP server")
}
}
func (s *Server) Stop() {
if err := s.echo.Close(); err != nil {
log.Fatal().Err(err).Msg("Failed to stop HTTP server")
}
}
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.echo.ServeHTTP(w, r)
}
func writePermission(next Handler) Handler {
return func(ctx *context.OGContext) error {
gist := ctx.GetData("gist")
user := ctx.User
if !gist.(*db.Gist).CanWrite(user) {
return ctx.RedirectTo("/" + gist.(*db.Gist).User.Username + "/" + gist.(*db.Gist).Identifier())
}
return next(ctx)
}
}
func adminPermission(next Handler) Handler {
return func(ctx *context.OGContext) error {
user := ctx.User
if user == nil || !user.IsAdmin {
return ctx.NotFound("User not found")
}
return next(ctx)
}
}
func logged(next Handler) Handler {
return func(ctx *context.OGContext) error {
user := ctx.User
if user != nil {
return next(ctx)
}
return ctx.RedirectTo("/all")
}
}
func inMFASession(next Handler) Handler {
return func(ctx *context.OGContext) error {
sess := ctx.GetSession()
_, ok := sess.Values["mfaID"].(uint)
if !ok {
return ctx.ErrorRes(400, ctx.Tr("error.not-in-mfa-session"), nil)
}
return next(ctx)
}
}
func makeCheckRequireLogin(isSingleGistAccess bool) Middleware {
return func(next Handler) Handler {
return func(ctx *context.OGContext) error {
if user := ctx.User; user != nil {
return next(ctx)
}
allow, err := auth.ShouldAllowUnauthenticatedGistAccess(handler.ContextAuthInfo{Context: ctx}, isSingleGistAccess)
if err != nil {
log.Fatal().Err(err).Msg("Failed to check if unauthenticated access is allowed")
}
if !allow {
ctx.AddFlash(ctx.Tr("flash.auth.must-be-logged-in"), "error")
return ctx.RedirectTo("/login")
}
return next(ctx)
}
}
}
func checkRequireLogin(next Handler) Handler {
return makeCheckRequireLogin(false)(next)
}
func noRouteFound(ctx *context.OGContext) error {
return ctx.NotFound("Page not found")
}
func (s *Server) errorHandler(err error, ctx echo.Context) {
var httpErr *echo.HTTPError
if errors.As(err, &httpErr) {
acceptJson := strings.Contains(ctx.Request().Header.Get("Accept"), "application/json")
data := ctx.Request().Context().Value("data").(echo.Map)
data["error"] = err
if acceptJson {
if err := ctx.JSON(httpErr.Code, httpErr); err != nil {
log.Fatal().Err(err).Send()
}
return
}
if err := ctx.Render(httpErr.Code, "error", data); err != nil {
log.Fatal().Err(err).Send()
}
return
}
log.Fatal().Err(err).Send()
}