web: refactor and convert secrets

This commit is contained in:
Chris Sexton 2024-02-27 14:29:54 -05:00
parent 3e3cc3cf95
commit f83cc32788
33 changed files with 696 additions and 621 deletions

View File

@ -4,10 +4,8 @@ package bot
import ( import (
"fmt" "fmt"
"github.com/go-chi/chi/v5/middleware" "github.com/velour/catbase/bot/web"
"github.com/go-chi/httprate"
"math/rand" "math/rand"
"net/http"
"os" "os"
"os/signal" "os/signal"
"reflect" "reflect"
@ -15,7 +13,6 @@ import (
"strings" "strings"
"time" "time"
"github.com/go-chi/chi/v5"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/velour/catbase/bot/history" "github.com/velour/catbase/bot/history"
@ -53,8 +50,7 @@ type bot struct {
version string version string
// The entries to the bot's HTTP interface web *web.Web
httpEndPoints []EndPoint
// filters registered by plugins // filters registered by plugins
filters map[string]func(string) string filters map[string]func(string) string
@ -66,16 +62,9 @@ type bot struct {
quiet bool quiet bool
router *chi.Mux
history *history.History history *history.History
} }
type EndPoint struct {
Name string `json:"name"`
URL string `json:"url"`
}
// Variable represents a $var replacement // Variable represents a $var replacement
type Variable struct { type Variable struct {
Variable, Value string Variable, Value string
@ -107,10 +96,8 @@ func New(config *config.Config, connector Connector) Bot {
me: users[0], me: users[0],
logIn: logIn, logIn: logIn,
logOut: logOut, logOut: logOut,
httpEndPoints: make([]EndPoint, 0),
filters: make(map[string]func(string) string), filters: make(map[string]func(string) string),
callbacks: make(CallbackMap), callbacks: make(CallbackMap),
router: chi.NewRouter(),
history: history.New(historySz), history: history.New(historySz),
} }
@ -119,60 +106,25 @@ func New(config *config.Config, connector Connector) Bot {
bot.RefreshPluginBlacklist() bot.RefreshPluginBlacklist()
bot.RefreshPluginWhitelist() bot.RefreshPluginWhitelist()
log.Debug().Msgf("created web router") bot.web = web.New(bot.config)
bot.setupHTTP()
connector.RegisterEvent(bot.Receive) connector.RegisterEvent(bot.Receive)
return bot return bot
} }
func (b *bot) setupHTTP() {
// Make the http logger optional
// It has never served a purpose in production and with the emojy page, can make a rather noisy log
if b.Config().GetInt("bot.useLogger", 0) == 1 {
b.router.Use(middleware.Logger)
}
reqCount := b.Config().GetInt("bot.httprate.requests", 500)
reqTime := time.Duration(b.Config().GetInt("bot.httprate.seconds", 5))
if reqCount > 0 && reqTime > 0 {
b.router.Use(httprate.LimitByIP(reqCount, reqTime*time.Second))
}
b.router.Use(middleware.RequestID)
b.router.Use(middleware.Recoverer)
b.router.Use(middleware.StripSlashes)
b.router.HandleFunc("/", b.serveRoot)
b.router.HandleFunc("/nav", b.serveNav)
b.router.HandleFunc("/navHTML", b.serveNavHTML)
b.router.HandleFunc("/navHTML/{currentPage}", b.serveNavHTML)
}
func (b *bot) ListenAndServe() { func (b *bot) ListenAndServe() {
addr := b.config.Get("HttpAddr", "127.0.0.1:1337") addr := b.config.Get("HttpAddr", "127.0.0.1:1337")
stop := make(chan os.Signal, 1) stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt) signal.Notify(stop, os.Interrupt)
go func() { go func() {
log.Debug().Msgf("starting web service at %s", addr) b.web.ListenAndServe(addr)
log.Fatal().Err(http.ListenAndServe(addr, b.router)).Msg("bot killed")
}() }()
<-stop <-stop
b.DefaultConnector().Shutdown() b.DefaultConnector().Shutdown()
b.Receive(b.DefaultConnector(), Shutdown, msg.Message{}) b.Receive(b.DefaultConnector(), Shutdown, msg.Message{})
} }
func (b *bot) RegisterWeb(r http.Handler, root string) {
b.router.Mount(root, r)
}
func (b *bot) RegisterWebName(r http.Handler, root, name string) {
b.httpEndPoints = append(b.httpEndPoints, EndPoint{name, root})
b.router.Mount(root, r)
}
// DefaultConnector is the main connector used for the bot // DefaultConnector is the main connector used for the bot
// If more than one connector is on, some users may not see all messages if this is used. // If more than one connector is on, some users may not see all messages if this is used.
// Usage should be limited to out-of-band communications such as timed messages. // Usage should be limited to out-of-band communications such as timed messages.
@ -458,3 +410,7 @@ func (b *bot) CheckPassword(secret, password string) bool {
} }
return false return false
} }
func (b *bot) GetWeb() *web.Web {
return b.web
}

View File

@ -1,22 +0,0 @@
package bot
templ (b *bot) index() {
<!DOCTYPE html />
<html lang="en">
<head>
<!-- Load required Bootstrap and BootstrapVue CSS -->
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css"/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charset="UTF-8" />
<title>catbase</title>
</head>
<body>
<div id="app">
@b.Nav("", b.GetWebNavigation())
</div>
<script src="//unpkg.com/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
}

View File

@ -1,43 +0,0 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.543
package bot
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import "context"
import "io"
import "bytes"
func (b *bot) index() templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html /><html lang=\"en\"><head><!-- Load required Bootstrap and BootstrapVue CSS --><link type=\"text/css\" rel=\"stylesheet\" href=\"//unpkg.com/bootstrap/dist/css/bootstrap.min.css\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta charset=\"UTF-8\"><title>catbase</title></head><body><div id=\"app\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = b.Nav("", b.GetWebNavigation()).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><script src=\"//unpkg.com/bootstrap/dist/js/bootstrap.bundle.min.js\"></script></body></html>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}

View File

@ -4,6 +4,7 @@ package bot
import ( import (
"github.com/gabriel-vasile/mimetype" "github.com/gabriel-vasile/mimetype"
"github.com/velour/catbase/bot/web"
"net/http" "net/http"
"regexp" "regexp"
"strings" "strings"
@ -168,20 +169,14 @@ type Bot interface {
// RegisterFilter creates a filter function for message processing // RegisterFilter creates a filter function for message processing
RegisterFilter(string, func(string) string) RegisterFilter(string, func(string) string)
// RegisterWeb records a web endpoint for the UI
RegisterWebName(http.Handler, string, string)
// RegisterWeb records a web endpoint for the API
RegisterWeb(http.Handler, string)
// Start the HTTP service // Start the HTTP service
ListenAndServe() ListenAndServe()
// DefaultConnector returns the base connector, which may not be the only connector // DefaultConnector returns the base connector, which may not be the only connector
DefaultConnector() Connector DefaultConnector() Connector
// GetWebNavigation returns the current known web endpoints // GetWeb returns the bot's webserver structure
GetWebNavigation() []EndPoint GetWeb() *web.Web
// GetPassword generates a unique password for modification commands on the public website // GetPassword generates a unique password for modification commands on the public website
GetPassword() string GetPassword() string

View File

@ -4,6 +4,7 @@ package bot
import ( import (
"fmt" "fmt"
"github.com/velour/catbase/bot/web"
"net/http" "net/http"
"regexp" "regexp"
"strconv" "strconv"
@ -26,6 +27,8 @@ type MockBot struct {
Messages []string Messages []string
Actions []string Actions []string
Reactions []string Reactions []string
web *web.Web
} }
func (mb *MockBot) Config() *config.Config { return mb.Cfg } func (mb *MockBot) Config() *config.Config { return mb.Cfg }
@ -60,9 +63,7 @@ func (mb *MockBot) Register(p Plugin, kind Kind, cb Callback)
func (mb *MockBot) RegisterTable(p Plugin, hs HandlerTable) {} func (mb *MockBot) RegisterTable(p Plugin, hs HandlerTable) {}
func (mb *MockBot) RegisterRegex(p Plugin, kind Kind, r *regexp.Regexp, h ResponseHandler) {} func (mb *MockBot) RegisterRegex(p Plugin, kind Kind, r *regexp.Regexp, h ResponseHandler) {}
func (mb *MockBot) RegisterRegexCmd(p Plugin, kind Kind, r *regexp.Regexp, h ResponseHandler) {} func (mb *MockBot) RegisterRegexCmd(p Plugin, kind Kind, r *regexp.Regexp, h ResponseHandler) {}
func (mb *MockBot) RegisterWebName(_ http.Handler, _, _ string) {} func (mb *MockBot) GetWeb() *web.Web { return mb.web }
func (mb *MockBot) RegisterWeb(_ http.Handler, _ string) {}
func (mb *MockBot) GetWebNavigation() []EndPoint { return nil }
func (mb *MockBot) Receive(c Connector, kind Kind, msg msg.Message, args ...any) bool { func (mb *MockBot) Receive(c Connector, kind Kind, msg msg.Message, args ...any) bool {
return false return false
} }
@ -118,6 +119,7 @@ func NewMockBot() *MockBot {
Messages: make([]string, 0), Messages: make([]string, 0),
Actions: make([]string, 0), Actions: make([]string, 0),
} }
b.web = web.New(cfg)
// If any plugin registered a route, we need to reset those before any new test // If any plugin registered a route, we need to reset those before any new test
http.DefaultServeMux = new(http.ServeMux) http.DefaultServeMux = new(http.ServeMux)
return &b return &b

View File

@ -1,25 +0,0 @@
package bot
templ (b *bot) Nav(currentPage string, items []EndPoint) {
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<a class="navbar-brand" href="/">catbase</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
for _, item := range items {
<li class="nav-item">
if currentPage == item.Name {
<a class="nav-link active" aria-current="page" href={ templ.URL(item.URL) }>{ item.Name }</a>
} else {
<a class="nav-link" href={ templ.URL(item.URL) }>{ item.Name }</a>
}
</li>
}
</ul>
</div>
</div>
</nav>
}

View File

@ -1,104 +0,0 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.543
package bot
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import "context"
import "io"
import "bytes"
func (b *bot) Nav(currentPage string, items []EndPoint) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<nav class=\"navbar navbar-expand-lg bg-body-tertiary\"><div class=\"container-fluid\"><a class=\"navbar-brand\" href=\"/\">catbase</a> <button class=\"navbar-toggler\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#navbarNav\" aria-controls=\"navbarNav\" aria-expanded=\"false\" aria-label=\"Toggle navigation\"><span class=\"navbar-toggler-icon\"></span></button><div class=\"collapse navbar-collapse\" id=\"navbarNav\"><ul class=\"navbar-nav\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, item := range items {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li class=\"nav-item\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if currentPage == item.Name {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a class=\"nav-link active\" aria-current=\"page\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 templ.SafeURL = templ.URL(item.URL)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var2)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(item.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bot/nav.templ`, Line: 14, Col: 119}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a class=\"nav-link\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 templ.SafeURL = templ.URL(item.URL)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var4)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(item.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bot/nav.templ`, Line: 16, Col: 92}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</ul></div></div></nav>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}

View File

@ -1,43 +0,0 @@
package bot
import (
"encoding/json"
"github.com/go-chi/chi/v5"
"net/http"
"strings"
)
func (b *bot) serveRoot(w http.ResponseWriter, r *http.Request) {
b.index().Render(r.Context(), w)
}
func (b *bot) serveNavHTML(w http.ResponseWriter, r *http.Request) {
currentPage := chi.URLParam(r, "currentPage")
b.Nav(currentPage, b.GetWebNavigation()).Render(r.Context(), w)
}
func (b *bot) serveNav(w http.ResponseWriter, r *http.Request) {
enc := json.NewEncoder(w)
err := enc.Encode(b.GetWebNavigation())
if err != nil {
jsonErr, _ := json.Marshal(err)
w.WriteHeader(500)
w.Write(jsonErr)
}
}
// GetWebNavigation returns a list of bootstrap-vue <b-nav-item> links
// The parent <nav> is not included so each page may display it as
// best fits
func (b *bot) GetWebNavigation() []EndPoint {
endpoints := b.httpEndPoints
moreEndpoints := b.config.GetArray("bot.links", []string{})
for _, e := range moreEndpoints {
link := strings.SplitN(e, ":", 2)
if len(link) != 2 {
continue
}
endpoints = append(endpoints, EndPoint{link[0], link[1]})
}
return endpoints
}

61
bot/web/index.templ Normal file
View File

@ -0,0 +1,61 @@
package web
templ (w *Web) Header(title string) {
<head>
<!-- Load required Bootstrap and BootstrapVue CSS -->
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css"/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charset="UTF-8" />
if title != "" {
<title>catbase - { title }</title>
} else {
<title>catbase</title>
}
</head>
}
templ (w *Web) Footer() {
<script src="//unpkg.com/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="//unpkg.com/htmx.org@1.9.10" integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC" crossorigin="anonymous"></script>
}
templ (w *Web) Index(title string, contents templ.Component) {
<!DOCTYPE html />
<html lang="en">
@w.Header(title)
<body>
@w.Nav(title)
if contents != nil {
@contents
}
@w.Footer()
</body>
</html>
}
templ (w *Web) Nav(currentPage string) {
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<a class="navbar-brand" href="/">catbase</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
for _, item := range w.GetWebNavigation() {
<li class="nav-item">
if currentPage == item.Name {
<a class="nav-link active" aria-current="page" href={ templ.URL(item.URL) }>{ item.Name }</a>
} else {
<a class="nav-link" href={ templ.URL(item.URL) }>{ item.Name }</a>
}
</li>
}
</ul>
</div>
</div>
</nav>
}

230
bot/web/index_templ.go Normal file
View File

@ -0,0 +1,230 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.543
package web
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import "context"
import "io"
import "bytes"
func (w *Web) Header(title string) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<head><!-- Load required Bootstrap and BootstrapVue CSS --><link type=\"text/css\" rel=\"stylesheet\" href=\"//unpkg.com/bootstrap/dist/css/bootstrap.min.css\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta charset=\"UTF-8\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if title != "" {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<title>catbase - ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bot/web/index.templ`, Line: 9, Col: 36}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</title>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<title>catbase</title>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</head>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}
func (w *Web) Footer() templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
if templ_7745c5c3_Var3 == nil {
templ_7745c5c3_Var3 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<script src=\"//unpkg.com/bootstrap/dist/js/bootstrap.bundle.min.js\"></script><script src=\"//unpkg.com/htmx.org@1.9.10\" integrity=\"sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC\" crossorigin=\"anonymous\"></script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}
func (w *Web) Index(title string, contents templ.Component) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var4 := templ.GetChildren(ctx)
if templ_7745c5c3_Var4 == nil {
templ_7745c5c3_Var4 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html /><html lang=\"en\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = w.Header(title).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<body>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = w.Nav(title).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if contents != nil {
templ_7745c5c3_Err = contents.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = w.Footer().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</body></html>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}
func (w *Web) Nav(currentPage string) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var5 := templ.GetChildren(ctx)
if templ_7745c5c3_Var5 == nil {
templ_7745c5c3_Var5 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<nav class=\"navbar navbar-expand-lg bg-body-tertiary\"><div class=\"container-fluid\"><a class=\"navbar-brand\" href=\"/\">catbase</a> <button class=\"navbar-toggler\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#navbarNav\" aria-controls=\"navbarNav\" aria-expanded=\"false\" aria-label=\"Toggle navigation\"><span class=\"navbar-toggler-icon\"></span></button><div class=\"collapse navbar-collapse\" id=\"navbarNav\"><ul class=\"navbar-nav\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, item := range w.GetWebNavigation() {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li class=\"nav-item\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if currentPage == item.Name {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a class=\"nav-link active\" aria-current=\"page\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 templ.SafeURL = templ.URL(item.URL)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var6)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(item.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bot/web/index.templ`, Line: 50, Col: 119}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a class=\"nav-link\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 templ.SafeURL = templ.URL(item.URL)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var8)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(item.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bot/web/index.templ`, Line: 52, Col: 92}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</ul></div></div></nav>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}

105
bot/web/web.go Normal file
View File

@ -0,0 +1,105 @@
package web
import (
"encoding/json"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/httprate"
"github.com/rs/zerolog/log"
"github.com/velour/catbase/config"
"net/http"
"strings"
"time"
)
type Web struct {
config *config.Config
router *chi.Mux
httpEndPoints []EndPoint
}
type EndPoint struct {
Name string `json:"name"`
URL string `json:"url"`
}
// GetWebNavigation returns a list of bootstrap-vue <b-nav-item> links
// The parent <nav> is not included so each page may display it as
// best fits
func (ws *Web) GetWebNavigation() []EndPoint {
endpoints := ws.httpEndPoints
moreEndpoints := ws.config.GetArray("bot.links", []string{})
for _, e := range moreEndpoints {
link := strings.SplitN(e, ":", 2)
if len(link) != 2 {
continue
}
endpoints = append(endpoints, EndPoint{link[0], link[1]})
}
return endpoints
}
func (ws *Web) serveRoot(w http.ResponseWriter, r *http.Request) {
ws.Index("Home", nil).Render(r.Context(), w)
}
func (ws *Web) serveNavHTML(w http.ResponseWriter, r *http.Request) {
currentPage := chi.URLParam(r, "currentPage")
ws.Nav(currentPage).Render(r.Context(), w)
}
func (ws *Web) serveNav(w http.ResponseWriter, r *http.Request) {
enc := json.NewEncoder(w)
err := enc.Encode(ws.GetWebNavigation())
if err != nil {
jsonErr, _ := json.Marshal(err)
w.WriteHeader(500)
w.Write(jsonErr)
}
}
func (ws *Web) setupHTTP() {
// Make the http logger optional
// It has never served a purpose in production and with the emojy page, can make a rather noisy log
if ws.config.GetInt("bot.useLogger", 0) == 1 {
ws.router.Use(middleware.Logger)
}
reqCount := ws.config.GetInt("bot.httprate.requests", 500)
reqTime := time.Duration(ws.config.GetInt("bot.httprate.seconds", 5))
if reqCount > 0 && reqTime > 0 {
ws.router.Use(httprate.LimitByIP(reqCount, reqTime*time.Second))
}
ws.router.Use(middleware.RequestID)
ws.router.Use(middleware.Recoverer)
ws.router.Use(middleware.StripSlashes)
ws.router.HandleFunc("/", ws.serveRoot)
ws.router.HandleFunc("/nav", ws.serveNav)
ws.router.HandleFunc("/navHTML", ws.serveNavHTML)
ws.router.HandleFunc("/navHTML/{currentPage}", ws.serveNavHTML)
}
func (ws *Web) RegisterWeb(r http.Handler, root string) {
ws.router.Mount(root, r)
}
func (ws *Web) RegisterWebName(r http.Handler, root, name string) {
ws.httpEndPoints = append(ws.httpEndPoints, EndPoint{name, root})
ws.router.Mount(root, r)
}
func (ws *Web) ListenAndServe(addr string) {
log.Debug().Msgf("starting web service at %s", addr)
log.Fatal().Err(http.ListenAndServe(addr, ws.router)).Msg("bot killed")
}
func New(config *config.Config) *Web {
w := &Web{
config: config,
router: chi.NewRouter(),
}
w.setupHTTP()
return w
}

View File

@ -130,7 +130,7 @@ func main() {
b := bot.New(c, client) b := bot.New(c, client)
if r, path := client.GetRouter(); r != nil { if r, path := client.GetRouter(); r != nil {
b.RegisterWeb(r, path) b.GetWeb().RegisterWeb(r, path)
} }
b.AddPlugin(admin.New(b)) b.AddPlugin(admin.New(b))

View File

@ -3,18 +3,6 @@ package admin
import "fmt" import "fmt"
templ (a *AdminPlugin) page() { templ (a *AdminPlugin) page() {
<!DOCTYPE html />
<html lang="en">
<head>
<!-- Load required Bootstrap and BootstrapVue CSS -->
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css"/>
<meta charset="UTF-8" />
<title>Vars</title>
</head>
<body>
<div hx-get="/navHTML/Variables" hx-trigger="load" hx-swap="outerHTML"></div>
<div class="container"> <div class="container">
<form> <form>
<div class="row"> <div class="row">
@ -39,10 +27,6 @@ templ (a *AdminPlugin) page() {
<div id="data"></div> <div id="data"></div>
</div> </div>
</div> </div>
<script src="//unpkg.com/htmx.org@1.9.10" integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC" crossorigin="anonymous"></script>
</body>
</html>
} }
templ (a *AdminPlugin) showPassword(entry PassEntry) { templ (a *AdminPlugin) showPassword(entry PassEntry) {
@ -74,4 +58,29 @@ templ (a *AdminPlugin) entries(items []PassEntry) {
templ renderError(err error) { templ renderError(err error) {
<div>{ err.Error() }</div> <div>{ err.Error() }</div>
} }
templ vars(items []configEntry) {
<div class="container">
<table class="table-responsive table-striped">
<thead>
<tr>
<th>Key</th>
<th>Value</th>
</tr>
</thead>
<tbody>
for _, item := range items {
<tr>
<td>{ item.Key }</td><td>{ item.Value }</td>
</tr>
}
if len(items) == 0 {
<tr>
<td colspan="2">No data</td>
</tr>
}
</tbody>
</table>
</div>
}

View File

@ -25,7 +25,7 @@ func (a *AdminPlugin) page() templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent templ_7745c5c3_Var1 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html /><html lang=\"en\"><head><!-- Load required Bootstrap and BootstrapVue CSS --><link type=\"text/css\" rel=\"stylesheet\" href=\"//unpkg.com/bootstrap/dist/css/bootstrap.min.css\"><meta charset=\"UTF-8\"><title>Vars</title></head><body><div hx-get=\"/navHTML/Variables\" hx-trigger=\"load\" hx-swap=\"outerHTML\"></div><div class=\"container\"><form><div class=\"row\"><div class=\"col-auto\"><label for=\"password\">Password: <input type=\"text\" name=\"password\"></label></div><div class=\"col-auto\"><label for=\"secret\">Secret: <input type=\"text\" name=\"secret\"></label></div><div class=\"col-auto\"><button hx-post=\"/apppass/api\" hx-target=\"#data\" class=\"btn btn-primary\">List</button> <button hx-put=\"/apppass/api\" hx-target=\"#data\" class=\"btn btn-secondary\">New</button></div></div></form><div class=\"row\"><div id=\"data\"></div></div></div><script src=\"//unpkg.com/htmx.org@1.9.10\" integrity=\"sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC\" crossorigin=\"anonymous\"></script></body></html>") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"container\"><form><div class=\"row\"><div class=\"col\"><label for=\"password\">Password: <input type=\"text\" name=\"password\"></label></div><div class=\"col\"><label for=\"secret\">Secret: <input type=\"text\" name=\"secret\"></label></div><div class=\"col\"><button hx-post=\"/apppass/api\" hx-target=\"#data\" class=\"btn btn-primary\">List</button> <button hx-put=\"/apppass/api\" hx-target=\"#data\" class=\"btn btn-secondary\">New</button></div></div></form><div class=\"row\"><div id=\"data\"></div></div></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -56,7 +56,7 @@ func (a *AdminPlugin) showPassword(entry PassEntry) templ.Component {
var templ_7745c5c3_Var3 string var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", entry.ID)) templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", entry.ID))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/apppass.templ`, Line: 48, Col: 59} return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 32, Col: 59}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -69,7 +69,7 @@ func (a *AdminPlugin) showPassword(entry PassEntry) templ.Component {
var templ_7745c5c3_Var4 string var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(entry.Secret) templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(entry.Secret)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/apppass.templ`, Line: 49, Col: 50} return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 33, Col: 50}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -82,7 +82,7 @@ func (a *AdminPlugin) showPassword(entry PassEntry) templ.Component {
var templ_7745c5c3_Var5 string var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(entry.Pass) templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(entry.Pass)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/apppass.templ`, Line: 49, Col: 65} return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 33, Col: 65}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -150,7 +150,7 @@ func (a *AdminPlugin) entries(items []PassEntry) templ.Component {
var templ_7745c5c3_Var7 string var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", entry.ID)) templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", entry.ID))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/apppass.templ`, Line: 67, Col: 57} return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 51, Col: 57}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -192,7 +192,7 @@ func renderError(err error) templ.Component {
var templ_7745c5c3_Var9 string var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(err.Error()) templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(err.Error())
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/apppass.templ`, Line: 75, Col: 22} return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 59, Col: 22}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -208,3 +208,69 @@ func renderError(err error) templ.Component {
return templ_7745c5c3_Err return templ_7745c5c3_Err
}) })
} }
func vars(items []configEntry) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var10 := templ.GetChildren(ctx)
if templ_7745c5c3_Var10 == nil {
templ_7745c5c3_Var10 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"container\"><table class=\"table-responsive table-striped\"><thead><tr><th>Key</th><th>Value</th></tr></thead> <tbody>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, item := range items {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<tr><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(item.Key)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 74, Col: 38}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(item.Value)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 74, Col: 61}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td></tr>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if len(items) == 0 {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<tr><td colspan=\"2\">No data</td></tr>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</tbody></table></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}

View File

@ -1,42 +0,0 @@
package admin
templ vars(items []configEntry) {
<!doctype html />
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>vars</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9" crossorigin="anonymous" />
<script src="https://unpkg.com/htmx.org@1.9.4"></script>
</head>
<body>
<div hx-get="/navHTML/Variables" hx-trigger="load" hx-swap="outerHTML"></div>
<div class="container">
<table class="table-responsive table-striped">
<thead>
<tr>
<th>Key</th>
<th>Value</th>
</tr>
</thead>
<tbody>
for _, item := range items {
<tr>
<td>{ item.Key }</td><td>{ item.Value }</td>
</tr>
}
if len(items) == 0 {
<tr>
<td colspan="2">No data</td>
</tr>
}
</tbody>
</table>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm" crossorigin="anonymous"></script>
</body>
</html>
}

View File

@ -1,77 +0,0 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.543
package admin
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import "context"
import "io"
import "bytes"
func vars(items []configEntry) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html /><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><title>vars</title><link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9\" crossorigin=\"anonymous\"><script src=\"https://unpkg.com/htmx.org@1.9.4\"></script></head><body><div hx-get=\"/navHTML/Variables\" hx-trigger=\"load\" hx-swap=\"outerHTML\"></div><div class=\"container\"><table class=\"table-responsive table-striped\"><thead><tr><th>Key</th><th>Value</th></tr></thead> <tbody>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, item := range items {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<tr><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(item.Key)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/vars.templ`, Line: 27, Col: 38}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(item.Value)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/vars.templ`, Line: 27, Col: 61}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td></tr>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if len(items) == 0 {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<tr><td colspan=\"2\">No data</td></tr>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</tbody></table></div><script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js\" integrity=\"sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm\" crossorigin=\"anonymous\"></script></body></html>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}

View File

@ -19,16 +19,16 @@ import (
func (p *AdminPlugin) registerWeb() { func (p *AdminPlugin) registerWeb() {
r := chi.NewRouter() r := chi.NewRouter()
r.HandleFunc("/", p.handleVars) r.HandleFunc("/", p.handleVars)
p.bot.RegisterWebName(r, "/vars", "Variables") p.bot.GetWeb().RegisterWebName(r, "/vars", "Variables")
r = chi.NewRouter() r = chi.NewRouter()
r.HandleFunc("/verify", p.handleAppPassCheck) r.HandleFunc("/verify", p.handleAppPassCheck)
r.HandleFunc("/api", p.handleAppPassAPI) r.HandleFunc("/api", p.handleAppPassAPI)
r.HandleFunc("/", p.handleAppPass) r.HandleFunc("/", p.handleAppPass)
p.bot.RegisterWebName(r, "/apppass", "App Pass") p.bot.GetWeb().RegisterWebName(r, "/apppass", "App Pass")
} }
func (p *AdminPlugin) handleAppPass(w http.ResponseWriter, r *http.Request) { func (p *AdminPlugin) handleAppPass(w http.ResponseWriter, r *http.Request) {
p.page().Render(r.Context(), w) p.bot.GetWeb().Index("App Pass", p.page()).Render(r.Context(), w)
} }
type PassEntry struct { type PassEntry struct {
@ -188,5 +188,5 @@ func (p *AdminPlugin) handleVars(w http.ResponseWriter, r *http.Request) {
return return
} }
vars(configEntries).Render(r.Context(), w) p.bot.GetWeb().Index("Variables", vars(configEntries)).Render(r.Context(), w)
} }

View File

@ -598,7 +598,7 @@ func (p *BeersPlugin) untappdLoop(c bot.Connector, channel string) {
func (p *BeersPlugin) registerWeb() { func (p *BeersPlugin) registerWeb() {
r := chi.NewRouter() r := chi.NewRouter()
r.HandleFunc("/img/{id}", p.img) r.HandleFunc("/img/{id}", p.img)
p.b.RegisterWeb(r, "/beers") p.b.GetWeb().RegisterWeb(r, "/beers")
} }
func (p *BeersPlugin) img(w http.ResponseWriter, r *http.Request) { func (p *BeersPlugin) img(w http.ResponseWriter, r *http.Request) {

View File

@ -40,7 +40,7 @@ func (p *CliPlugin) registerWeb() {
r := chi.NewRouter() r := chi.NewRouter()
r.HandleFunc("/api", p.handleWebAPI) r.HandleFunc("/api", p.handleWebAPI)
r.HandleFunc("/", p.handleWeb) r.HandleFunc("/", p.handleWeb)
p.bot.RegisterWebName(r, "/cli", "CLI") p.bot.GetWeb().RegisterWebName(r, "/cli", "CLI")
} }
func (p *CliPlugin) Shutdown() {} func (p *CliPlugin) Shutdown() {}

View File

@ -35,7 +35,7 @@ func (p *CounterPlugin) registerWeb() {
r.Mount("/", subrouter) r.Mount("/", subrouter)
r.HandleFunc("/api", p.handleCounterAPI) r.HandleFunc("/api", p.handleCounterAPI)
r.HandleFunc("/", p.handleCounter) r.HandleFunc("/", p.handleCounter)
p.b.RegisterWebName(r, "/counter", "Counter") p.b.GetWeb().RegisterWebName(r, "/counter", "Counter")
} }
func (p *CounterPlugin) mkIncrementByNAPI(direction int) func(w http.ResponseWriter, r *http.Request) { func (p *CounterPlugin) mkIncrementByNAPI(direction int) func(w http.ResponseWriter, r *http.Request) {

View File

@ -10,7 +10,7 @@ import (
func (p *Cowboy) registerWeb() { func (p *Cowboy) registerWeb() {
r := chi.NewRouter() r := chi.NewRouter()
r.HandleFunc("/img/{overlay}/{what}", p.handleImage) r.HandleFunc("/img/{overlay}/{what}", p.handleImage)
p.b.RegisterWeb(r, "/cowboy") p.b.GetWeb().RegisterWeb(r, "/cowboy")
} }
func (p *Cowboy) handleImage(w http.ResponseWriter, r *http.Request) { func (p *Cowboy) handleImage(w http.ResponseWriter, r *http.Request) {

View File

@ -29,7 +29,7 @@ func (p *EmojyPlugin) registerWeb() {
r.HandleFunc("/list", p.handlePage("list.html")) r.HandleFunc("/list", p.handlePage("list.html"))
r.HandleFunc("/new", p.handlePage("upload.html")) r.HandleFunc("/new", p.handlePage("upload.html"))
r.HandleFunc("/", p.handleIndex) r.HandleFunc("/", p.handleIndex)
p.b.RegisterWebName(r, "/emojy", "Emojys") p.b.GetWeb().RegisterWebName(r, "/emojy", "Emojys")
} }
func (p *EmojyPlugin) handleIndex(w http.ResponseWriter, r *http.Request) { func (p *EmojyPlugin) handleIndex(w http.ResponseWriter, r *http.Request) {

View File

@ -19,7 +19,7 @@ func (p *FactoidPlugin) registerWeb() {
r.HandleFunc("/api", p.serveAPI) r.HandleFunc("/api", p.serveAPI)
r.HandleFunc("/req", p.serveQuery) r.HandleFunc("/req", p.serveQuery)
r.HandleFunc("/", p.serveQuery) r.HandleFunc("/", p.serveQuery)
p.b.RegisterWebName(r, "/factoid", "Factoid") p.b.GetWeb().RegisterWebName(r, "/factoid", "Factoid")
} }
func linkify(text string) template.HTML { func linkify(text string) template.HTML {

View File

@ -81,5 +81,5 @@ func (p *GitPlugin) registerWeb() {
r.HandleFunc("/gitea/event", p.giteaEvent) r.HandleFunc("/gitea/event", p.giteaEvent)
r.HandleFunc("/github/event", p.githubEvent) r.HandleFunc("/github/event", p.githubEvent)
r.HandleFunc("/gitlab/event", p.gitlabEvent) r.HandleFunc("/gitlab/event", p.gitlabEvent)
p.b.RegisterWeb(r, "/git") p.b.GetWeb().RegisterWeb(r, "/git")
} }

View File

@ -25,7 +25,7 @@ func (p *MemePlugin) registerWeb(c bot.Connector) {
r.HandleFunc("/add", p.addMeme) r.HandleFunc("/add", p.addMeme)
r.HandleFunc("/rm", p.rmMeme) r.HandleFunc("/rm", p.rmMeme)
r.HandleFunc("/", p.webRoot) r.HandleFunc("/", p.webRoot)
p.bot.RegisterWebName(r, "/meme", "Memes") p.bot.GetWeb().RegisterWebName(r, "/meme", "Memes")
} }
type webResp struct { type webResp struct {

View File

@ -1,120 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Load required Bootstrap and BootstrapVue CSS -->
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue@^2/dist/bootstrap-vue.min.css" />
<!-- Load polyfills to support older browsers -->
<script src="//polyfill.io/v3/polyfill.min.js?features=es2015%2CMutationObserver"></script>
<!-- Load Vue followed by BootstrapVue -->
<script src="//unpkg.com/vue@^2/dist/vue.min.js"></script>
<script src="//unpkg.com/bootstrap-vue@^2/dist/bootstrap-vue.min.js"></script>
<script src="https://unpkg.com/vue-router@^2"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<meta charset="UTF-8">
<title>Memes</title>
</head>
<body>
<div id="app">
<b-navbar>
<b-navbar-brand>Memes</b-navbar-brand>
<b-navbar-nav>
<b-nav-item v-for="item in nav" :href="item.url" :active="item.name === 'Meme'" :key="item.key">{{ item.name }}</b-nav-item>
</b-navbar-nav>
</b-navbar>
<b-alert
dismissable
variant="error"
:show="err != ''"
@dismissed="err = ''">
{{ err }}
</b-alert>
<b-form @submit="add">
<b-container>
<b-row>
<b-col cols="3">
<b-input placeholder="Key..." v-model="secret.key"></b-input>
</b-col>
<b-col cols="3">
<b-input placeholder="Value..." v-model="secret.value"></b-input>
</b-col>
<b-col cols="3">
<b-button type="submit">Add Secret</b-button>
</b-col>
</b-row>
<b-row style="padding-top: 2em;">
<b-col>
<ul>
<li v-for="key in results" key="key"><a @click="rm(key)" href="#">X</a> {{key}}</li>
</ul>
</b-col>
</b-row>
</b-container>
</b-form>
</div>
<script>
var router = new VueRouter({
mode: 'history',
routes: []
});
var app = new Vue({
el: '#app',
router,
data: {
err: '',
nav: [],
secret: {key: '', value: ''},
results: [],
fields: [
{key: 'key', sortable: true},
]
},
mounted() {
axios.get('/nav')
.then(resp => {
this.nav = resp.data;
})
.catch(err => console.log(err))
this.refresh();
},
methods: {
refresh: function () {
axios.get('/secrets/all')
.then(resp => {
this.results = resp.data
this.err = ''
})
.catch(err => (this.err = err))
},
add: function (evt) {
if (evt) {
evt.preventDefault();
evt.stopPropagation();
}
axios.post('/secrets/add', this.secret)
.then(resp => {
this.results = resp.data;
this.secret.key = '';
this.secret.value = '';
this.refresh();
})
.catch(err => this.err = err)
},
rm: function (key) {
if (confirm("Are you sure you want to delete this meme?")) {
axios.delete('/secrets/remove', {data: {key: key}})
.then(resp => {
this.refresh();
})
.catch(err => this.err = err)
}
}
}
})
</script>
</body>
</html>

View File

@ -1,21 +1,18 @@
package secrets package secrets
import ( import (
"embed"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/velour/catbase/bot" "github.com/velour/catbase/bot"
"github.com/velour/catbase/config" "github.com/velour/catbase/config"
"io"
"net/http"
"net/url"
) )
//go:embed *.html
var embeddedFS embed.FS
type SecretsPlugin struct { type SecretsPlugin struct {
b bot.Bot b bot.Bot
c *config.Config c *config.Config
@ -36,14 +33,8 @@ func (p *SecretsPlugin) registerWeb() {
r := chi.NewRouter() r := chi.NewRouter()
r.HandleFunc("/add", p.handleRegister) r.HandleFunc("/add", p.handleRegister)
r.HandleFunc("/remove", p.handleRemove) r.HandleFunc("/remove", p.handleRemove)
r.HandleFunc("/all", p.handleAll)
r.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
value := r.URL.Query().Get("test")
j, _ := json.Marshal(map[string]string{"value": value})
w.Write(j)
})
r.HandleFunc("/", p.handleIndex) r.HandleFunc("/", p.handleIndex)
p.b.RegisterWebName(r, "/secrets", "Secrets") p.b.GetWeb().RegisterWebName(r, "/secrets", "Secrets")
} }
func mkCheckError(w http.ResponseWriter) func(error) bool { func mkCheckError(w http.ResponseWriter) func(error) bool {
@ -68,20 +59,12 @@ func checkMethod(method string, w http.ResponseWriter, r *http.Request) bool {
return false return false
} }
func (p *SecretsPlugin) keys() []string {
return p.c.SecretKeys()
}
func (p *SecretsPlugin) sendKeys(w http.ResponseWriter, r *http.Request) { func (p *SecretsPlugin) sendKeys(w http.ResponseWriter, r *http.Request) {
checkError := mkCheckError(w) p.keysList().Render(r.Context(), w)
log.Debug().Msgf("Keys before refresh: %v", p.c.SecretKeys())
err := p.c.RefreshSecrets()
log.Debug().Msgf("Keys after refresh: %v", p.c.SecretKeys())
if checkError(err) {
return
}
keys, err := json.Marshal(p.c.SecretKeys())
if checkError(err) {
return
}
w.WriteHeader(200)
w.Write(keys)
} }
func (p *SecretsPlugin) handleAll(w http.ResponseWriter, r *http.Request) { func (p *SecretsPlugin) handleAll(w http.ResponseWriter, r *http.Request) {
@ -89,21 +72,13 @@ func (p *SecretsPlugin) handleAll(w http.ResponseWriter, r *http.Request) {
} }
func (p *SecretsPlugin) handleRegister(w http.ResponseWriter, r *http.Request) { func (p *SecretsPlugin) handleRegister(w http.ResponseWriter, r *http.Request) {
log.Debug().Msgf("handleRegister")
if checkMethod(http.MethodPost, w, r) { if checkMethod(http.MethodPost, w, r) {
log.Debug().Msgf("failed post %s", r.Method) log.Debug().Msgf("failed post %s", r.Method)
return return
} }
checkError := mkCheckError(w) checkError := mkCheckError(w)
decoder := json.NewDecoder(r.Body) key, value := r.FormValue("key"), r.FormValue("value")
secret := config.Secret{} err := p.c.RegisterSecret(key, value)
err := decoder.Decode(&secret)
log.Debug().Msgf("decoding: %s", err)
if checkError(err) {
return
}
log.Debug().Msgf("Secret: %s", secret)
err = p.c.RegisterSecret(secret.Key, secret.Value)
if checkError(err) { if checkError(err) {
return return
} }
@ -115,13 +90,16 @@ func (p *SecretsPlugin) handleRemove(w http.ResponseWriter, r *http.Request) {
return return
} }
checkError := mkCheckError(w) checkError := mkCheckError(w)
decoder := json.NewDecoder(r.Body) b, err := io.ReadAll(r.Body)
secret := config.Secret{}
err := decoder.Decode(&secret)
if checkError(err) { if checkError(err) {
return return
} }
err = p.c.RemoveSecret(secret.Key) q, err := url.ParseQuery(string(b))
if checkError(err) {
return
}
secret := q.Get("key")
err = p.c.RemoveSecret(secret)
if checkError(err) { if checkError(err) {
return return
} }
@ -129,6 +107,5 @@ func (p *SecretsPlugin) handleRemove(w http.ResponseWriter, r *http.Request) {
} }
func (p *SecretsPlugin) handleIndex(w http.ResponseWriter, r *http.Request) { func (p *SecretsPlugin) handleIndex(w http.ResponseWriter, r *http.Request) {
index, _ := embeddedFS.ReadFile("index.html") p.b.GetWeb().Index("Secrets", p.index()).Render(r.Context(), w)
w.Write(index)
} }

View File

@ -0,0 +1,42 @@
package secrets
import "fmt"
templ (s *SecretsPlugin) index() {
<div class="container">
<form hx-post="/secrets/add" hx-target="#data">
<div class="row">
<div class="col-3">
<input placeholder="Key..." name="key" />
</div>
<div class="col-3">
<input placeholder="Value..." name="value" />
</div>
<div class="col-3">
<button class="btn btn-primary" type="submit">Add Secret</button>
</div>
</div>
</form>
<div class="row" style="padding-top: 2em;">
<div id="data">
@s.keysList()
</div>
</div>
</div>
}
templ (s *SecretsPlugin) keysList() {
<ul>
for _, key := range s.keys() {
<li>
<button
class="btn btn-danger"
hx-delete="/secrets/remove"
hx-confirm={ fmt.Sprintf("Are you sure you want to delete %s?", key) }
hx-target="#data"
hx-include="this"
name="key" value={ key }>X</button>
{ key }</li>
}
</ul>
}

View File

@ -0,0 +1,108 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.543
package secrets
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import "context"
import "io"
import "bytes"
import "fmt"
func (s *SecretsPlugin) index() templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"container\"><form hx-post=\"/secrets/add\" hx-target=\"#data\"><div class=\"row\"><div class=\"col-3\"><input placeholder=\"Key...\" name=\"key\"></div><div class=\"col-3\"><input placeholder=\"Value...\" name=\"value\"></div><div class=\"col-3\"><button class=\"btn btn-primary\" type=\"submit\">Add Secret</button></div></div></form><div class=\"row\" style=\"padding-top: 2em;\"><div id=\"data\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = s.keysList().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}
func (s *SecretsPlugin) keysList() templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
if templ_7745c5c3_Var2 == nil {
templ_7745c5c3_Var2 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<ul>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, key := range s.keys() {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li><button class=\"btn btn-danger\" hx-delete=\"/secrets/remove\" hx-confirm=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(fmt.Sprintf("Are you sure you want to delete %s?", key)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-target=\"#data\" hx-include=\"this\" name=\"key\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(key))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">X</button> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(key)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/secrets/secrets.templ`, Line: 38, Col: 17}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</ul>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}

View File

@ -121,7 +121,7 @@ func (p *SMSPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, ar
func (p *SMSPlugin) registerWeb() { func (p *SMSPlugin) registerWeb() {
r := chi.NewRouter() r := chi.NewRouter()
r.HandleFunc("/new", p.receive) r.HandleFunc("/new", p.receive)
p.b.RegisterWeb(r, "/sms") p.b.GetWeb().RegisterWeb(r, "/sms")
} }
func (p *SMSPlugin) receive(w http.ResponseWriter, r *http.Request) { func (p *SMSPlugin) receive(w http.ResponseWriter, r *http.Request) {

View File

@ -186,5 +186,5 @@ func (p *TalkerPlugin) registerWeb(c bot.Connector) {
p.bot.Send(c, bot.Message, channel, msg) p.bot.Send(c, bot.Message, channel, msg)
w.WriteHeader(200) w.WriteHeader(200)
}) })
p.bot.RegisterWeb(r, "/cowsay") p.bot.GetWeb().RegisterWeb(r, "/cowsay")
} }

View File

@ -14,7 +14,7 @@ import (
func (p *Tappd) registerWeb() { func (p *Tappd) registerWeb() {
r := chi.NewRouter() r := chi.NewRouter()
r.HandleFunc("/", p.serveImage) r.HandleFunc("/", p.serveImage)
p.b.RegisterWeb(r, "/tappd/{id}") p.b.GetWeb().RegisterWeb(r, "/tappd/{id}")
} }
func (p *Tappd) getImg(id string) ([]byte, error) { func (p *Tappd) getImg(id string) ([]byte, error) {

View File

@ -102,7 +102,7 @@ func (p *Twitch) registerWeb() {
r := chi.NewRouter() r := chi.NewRouter()
r.HandleFunc("/online", p.onlineCB) r.HandleFunc("/online", p.onlineCB)
r.HandleFunc("/offline", p.offlineCB) r.HandleFunc("/offline", p.offlineCB)
p.b.RegisterWeb(r, "/twitch") p.b.GetWeb().RegisterWeb(r, "/twitch")
} }
func (p *Twitch) register() { func (p *Twitch) register() {