SimpleCloudNotifier/scnserver/api/handler/website.go

240 lines
6.5 KiB
Go
Raw Normal View History

2022-11-18 21:25:40 +01:00
package handler
import (
2022-12-20 13:55:09 +01:00
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
2022-11-18 21:25:40 +01:00
"blackforestbytes.com/simplecloudnotifier/logic"
"blackforestbytes.com/simplecloudnotifier/website"
2022-11-21 22:52:44 +01:00
"errors"
2022-11-18 21:25:40 +01:00
"github.com/gin-gonic/gin"
2022-11-26 16:30:30 +01:00
"github.com/rs/zerolog/log"
"gogs.mikescher.com/BlackForestBytes/goext/ginext"
2023-01-15 06:30:30 +01:00
"gogs.mikescher.com/BlackForestBytes/goext/rext"
2022-11-18 21:25:40 +01:00
"net/http"
2022-11-21 22:52:44 +01:00
"regexp"
2022-11-18 21:25:40 +01:00
"strings"
)
type WebsiteHandler struct {
2022-11-21 22:52:44 +01:00
app *logic.Application
2023-01-15 06:30:30 +01:00
rexTemplate rext.Regex
rexConfig rext.Regex
2022-11-18 21:25:40 +01:00
}
func NewWebsiteHandler(app *logic.Application) WebsiteHandler {
return WebsiteHandler{
2022-11-21 22:52:44 +01:00
app: app,
2023-01-15 06:30:30 +01:00
rexTemplate: rext.W(regexp.MustCompile("{{template\\|[A-Za-z0-9_\\-\\[\\].]+}}")),
rexConfig: rext.W(regexp.MustCompile("{{config\\|[A-Za-z0-9_\\-.]+}}")),
2022-11-18 21:25:40 +01:00
}
}
func (h WebsiteHandler) Index(pctx ginext.PreContext) ginext.HTTPResponse {
ctx, g, errResp := pctx.Start()
if errResp != nil {
return *errResp
}
defer ctx.Cancel()
return h.app.DoRequest(ctx, g, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
return h.serveAsset(g, "index.html", true)
})
2022-11-18 21:25:40 +01:00
}
func (h WebsiteHandler) APIDocs(pctx ginext.PreContext) ginext.HTTPResponse {
ctx, g, errResp := pctx.Start()
if errResp != nil {
return *errResp
}
defer ctx.Cancel()
return h.app.DoRequest(ctx, g, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
return h.serveAsset(g, "api.html", true)
})
2022-11-18 21:25:40 +01:00
}
func (h WebsiteHandler) APIDocsMore(pctx ginext.PreContext) ginext.HTTPResponse {
ctx, g, errResp := pctx.Start()
if errResp != nil {
return *errResp
}
defer ctx.Cancel()
return h.app.DoRequest(ctx, g, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
return h.serveAsset(g, "api_more.html", true)
})
2022-11-18 21:25:40 +01:00
}
func (h WebsiteHandler) MessageSent(pctx ginext.PreContext) ginext.HTTPResponse {
ctx, g, errResp := pctx.Start()
if errResp != nil {
return *errResp
}
defer ctx.Cancel()
return h.app.DoRequest(ctx, g, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
return h.serveAsset(g, "message_sent.html", true)
})
2022-11-18 21:25:40 +01:00
}
func (h WebsiteHandler) FaviconIco(pctx ginext.PreContext) ginext.HTTPResponse {
ctx, g, errResp := pctx.Start()
if errResp != nil {
return *errResp
}
defer ctx.Cancel()
return h.app.DoRequest(ctx, g, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
return h.serveAsset(g, "favicon.ico", false)
})
2022-11-18 21:25:40 +01:00
}
func (h WebsiteHandler) FaviconPNG(pctx ginext.PreContext) ginext.HTTPResponse {
ctx, g, errResp := pctx.Start()
if errResp != nil {
return *errResp
}
defer ctx.Cancel()
return h.app.DoRequest(ctx, g, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
return h.serveAsset(g, "favicon.png", false)
})
2022-11-18 21:25:40 +01:00
}
func (h WebsiteHandler) Javascript(pctx ginext.PreContext) ginext.HTTPResponse {
ctx, g, errResp := pctx.Start()
if errResp != nil {
return *errResp
}
defer ctx.Cancel()
return h.app.DoRequest(ctx, g, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
2022-11-18 21:25:40 +01:00
type uri struct {
Filename string `uri:"fn"`
}
var u uri
if err := g.ShouldBindUri(&u); err != nil {
return ginext.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
2022-11-18 21:25:40 +01:00
return h.serveAsset(g, "js/"+u.Filename, false)
})
2022-11-18 21:25:40 +01:00
}
func (h WebsiteHandler) CSS(pctx ginext.PreContext) ginext.HTTPResponse {
2022-11-18 21:25:40 +01:00
type uri struct {
Filename string `uri:"fn"`
}
var u uri
ctx, g, errResp := pctx.URI(&u).Start()
if errResp != nil {
return *errResp
2022-11-18 21:25:40 +01:00
}
defer ctx.Cancel()
2022-11-18 21:25:40 +01:00
return h.app.DoRequest(ctx, g, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
return h.serveAsset(g, "css/"+u.Filename, false)
})
2022-11-18 21:25:40 +01:00
}
func (h WebsiteHandler) serveAsset(g *gin.Context, fn string, repl bool) ginext.HTTPResponse {
2023-01-15 06:30:30 +01:00
_data, err := website.Assets.ReadFile(fn)
2022-11-18 21:25:40 +01:00
if err != nil {
return ginext.Status(http.StatusNotFound)
2022-11-18 21:25:40 +01:00
}
2023-01-15 06:30:30 +01:00
data := string(_data)
2022-11-21 22:52:44 +01:00
if repl {
failed := false
2023-01-15 06:30:30 +01:00
data = h.rexTemplate.ReplaceAllFunc(data, func(match string) string {
2022-11-21 22:52:44 +01:00
prefix := len("{{template|")
suffix := len("}}")
2023-01-15 06:30:30 +01:00
fnSub := match[prefix : len(match)-suffix]
2022-11-26 16:30:30 +01:00
fnSub = strings.ReplaceAll(fnSub, "[theme]", h.getTheme(g))
subdata, err := website.Assets.ReadFile(fnSub)
2022-11-21 22:52:44 +01:00
if err != nil {
2022-11-26 16:30:30 +01:00
log.Error().Str("templ", string(match)).Str("fnSub", fnSub).Str("source", fn).Msg("Failed to replace template")
2022-11-21 22:52:44 +01:00
failed = true
}
2023-01-15 06:30:30 +01:00
return string(subdata)
2022-11-21 22:52:44 +01:00
})
if failed {
return ginresp.InternalError(errors.New("template replacement failed"))
}
2023-01-15 06:30:30 +01:00
data = h.rexConfig.ReplaceAllFunc(data, func(match string) string {
prefix := len("{{config|")
suffix := len("}}")
cfgKey := match[prefix : len(match)-suffix]
2023-01-15 06:30:30 +01:00
cval, ok := h.getReplConfig(cfgKey)
if !ok {
2023-01-15 06:30:30 +01:00
log.Error().Str("templ", match).Str("source", fn).Msg("Failed to replace config")
failed = true
}
2023-01-15 06:30:30 +01:00
return cval
})
if failed {
return ginresp.InternalError(errors.New("config replacement failed"))
}
2022-11-21 22:52:44 +01:00
}
2022-11-18 21:25:40 +01:00
mime := "text/plain"
lowerFN := strings.ToLower(fn)
if strings.HasSuffix(lowerFN, ".html") || strings.HasSuffix(lowerFN, ".htm") {
mime = "text/html"
} else if strings.HasSuffix(lowerFN, ".css") {
mime = "text/css"
} else if strings.HasSuffix(lowerFN, ".js") {
mime = "text/javascript"
} else if strings.HasSuffix(lowerFN, ".json") {
mime = "application/json"
} else if strings.HasSuffix(lowerFN, ".jpeg") || strings.HasSuffix(lowerFN, ".jpg") {
mime = "image/jpeg"
} else if strings.HasSuffix(lowerFN, ".png") {
mime = "image/png"
} else if strings.HasSuffix(lowerFN, ".svg") {
mime = "image/svg+xml"
}
return ginext.Data(http.StatusOK, mime, []byte(data))
2022-11-18 21:25:40 +01:00
}
func (h WebsiteHandler) getReplConfig(key string) (string, bool) {
key = strings.TrimSpace(strings.ToLower(key))
if key == "baseurl" {
return h.app.Config.BaseURL, true
}
if key == "ip" {
return h.app.Config.ServerIP, true
}
if key == "port" {
return h.app.Config.ServerPort, true
}
if key == "namespace" {
return h.app.Config.Namespace, true
}
return "", false
}
2022-11-26 16:30:30 +01:00
func (h WebsiteHandler) getTheme(g *gin.Context) string {
if c, err := g.Cookie("theme"); err != nil {
return "light"
} else if c == "light" {
return "light"
} else if c == "dark" {
return "dark"
} else {
return "light"
}
}