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"
|
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
|
|
|
|
rexTemplate *regexp.Regexp
|
2022-11-25 22:42:21 +01:00
|
|
|
rexConfig *regexp.Regexp
|
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,
|
2022-11-26 16:30:30 +01:00
|
|
|
rexTemplate: regexp.MustCompile("{{template\\|[A-Za-z0-9_\\-\\[\\].]+}}"),
|
2022-11-25 22:42:21 +01:00
|
|
|
rexConfig: regexp.MustCompile("{{config\\|[A-Za-z0-9_\\-.]+}}"),
|
2022-11-18 21:25:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h WebsiteHandler) Index(g *gin.Context) ginresp.HTTPResponse {
|
2022-11-21 22:52:44 +01:00
|
|
|
return h.serveAsset(g, "index.html", true)
|
2022-11-18 21:25:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (h WebsiteHandler) APIDocs(g *gin.Context) ginresp.HTTPResponse {
|
2022-11-21 22:52:44 +01:00
|
|
|
return h.serveAsset(g, "api.html", true)
|
2022-11-18 21:25:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (h WebsiteHandler) APIDocsMore(g *gin.Context) ginresp.HTTPResponse {
|
2022-11-21 22:52:44 +01:00
|
|
|
return h.serveAsset(g, "api_more.html", true)
|
2022-11-18 21:25:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (h WebsiteHandler) MessageSent(g *gin.Context) ginresp.HTTPResponse {
|
2022-11-21 22:52:44 +01:00
|
|
|
return h.serveAsset(g, "message_sent.html", true)
|
2022-11-18 21:25:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (h WebsiteHandler) FaviconIco(g *gin.Context) ginresp.HTTPResponse {
|
2022-11-21 22:52:44 +01:00
|
|
|
return h.serveAsset(g, "favicon.ico", false)
|
2022-11-18 21:25:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (h WebsiteHandler) FaviconPNG(g *gin.Context) ginresp.HTTPResponse {
|
2022-11-21 22:52:44 +01:00
|
|
|
return h.serveAsset(g, "favicon.png", false)
|
2022-11-18 21:25:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (h WebsiteHandler) Javascript(g *gin.Context) ginresp.HTTPResponse {
|
|
|
|
type uri struct {
|
|
|
|
Filename string `uri:"fn"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var u uri
|
|
|
|
if err := g.ShouldBindUri(&u); err != nil {
|
|
|
|
return ginresp.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
|
|
}
|
|
|
|
|
2022-11-21 22:52:44 +01:00
|
|
|
return h.serveAsset(g, "js/"+u.Filename, false)
|
2022-11-18 21:25:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (h WebsiteHandler) CSS(g *gin.Context) ginresp.HTTPResponse {
|
|
|
|
type uri struct {
|
|
|
|
Filename string `uri:"fn"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var u uri
|
|
|
|
if err := g.ShouldBindUri(&u); err != nil {
|
|
|
|
return ginresp.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
|
|
}
|
|
|
|
|
2022-11-21 22:52:44 +01:00
|
|
|
return h.serveAsset(g, "css/"+u.Filename, false)
|
2022-11-18 21:25:40 +01:00
|
|
|
}
|
|
|
|
|
2022-11-21 22:52:44 +01:00
|
|
|
func (h WebsiteHandler) serveAsset(g *gin.Context, fn string, repl bool) ginresp.HTTPResponse {
|
2022-11-18 21:25:40 +01:00
|
|
|
data, err := website.Assets.ReadFile(fn)
|
|
|
|
if err != nil {
|
|
|
|
return ginresp.Status(http.StatusNotFound)
|
|
|
|
}
|
|
|
|
|
2022-11-21 22:52:44 +01:00
|
|
|
if repl {
|
|
|
|
failed := false
|
|
|
|
data = h.rexTemplate.ReplaceAllFunc(data, func(match []byte) []byte {
|
|
|
|
prefix := len("{{template|")
|
|
|
|
suffix := len("}}")
|
2022-11-26 16:30:30 +01:00
|
|
|
fnSub := string(match[prefix : len(match)-suffix])
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
return subdata
|
|
|
|
})
|
|
|
|
if failed {
|
|
|
|
return ginresp.InternalError(errors.New("template replacement failed"))
|
|
|
|
}
|
2022-11-25 22:42:21 +01:00
|
|
|
|
|
|
|
data = h.rexConfig.ReplaceAllFunc(data, func(match []byte) []byte {
|
|
|
|
prefix := len("{{config|")
|
|
|
|
suffix := len("}}")
|
|
|
|
cfgKey := match[prefix : len(match)-suffix]
|
|
|
|
|
|
|
|
cval, ok := h.getReplConfig(string(cfgKey))
|
|
|
|
if !ok {
|
2022-11-26 16:30:30 +01:00
|
|
|
log.Error().Str("templ", string(match)).Str("source", fn).Msg("Failed to replace config")
|
2022-11-25 22:42:21 +01:00
|
|
|
failed = true
|
|
|
|
}
|
|
|
|
return []byte(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 ginresp.Data(http.StatusOK, mime, data)
|
|
|
|
}
|
2022-11-25 22:42:21 +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"
|
|
|
|
}
|
|
|
|
}
|