mirror of https://github.com/velour/catbase.git
Compare commits
7 Commits
f602c2ec8d
...
def86a3c7e
Author | SHA1 | Date |
---|---|---|
Chris Sexton | def86a3c7e | |
Chris Sexton | 35499ee213 | |
Chris Sexton | 674e30125b | |
Chris Sexton | 28e9fc2480 | |
Chris Sexton | b73e64ad72 | |
Chris Sexton | a6d224c87b | |
Chris Sexton | 81fb3dd068 |
|
@ -63,6 +63,12 @@ type File struct {
|
||||||
mime *mimetype.MIME
|
mime *mimetype.MIME
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MessageReference struct {
|
||||||
|
MessageID string `json:"message_id"`
|
||||||
|
ChannelID string `json:"channel_id,omitempty"`
|
||||||
|
GuildID string `json:"guild_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
func (f File) Mime() *mimetype.MIME {
|
func (f File) Mime() *mimetype.MIME {
|
||||||
if f.mime == nil {
|
if f.mime == nil {
|
||||||
f.mime = mimetype.Detect(f.Data)
|
f.mime = mimetype.Detect(f.Data)
|
||||||
|
|
|
@ -11,6 +11,8 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
httpin_integration "github.com/ggicci/httpin/integration"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Web struct {
|
type Web struct {
|
||||||
|
@ -77,6 +79,8 @@ func (ws *Web) setupHTTP() {
|
||||||
ws.router.Use(middleware.Recoverer)
|
ws.router.Use(middleware.Recoverer)
|
||||||
ws.router.Use(middleware.StripSlashes)
|
ws.router.Use(middleware.StripSlashes)
|
||||||
|
|
||||||
|
httpin_integration.UseGochiURLParam("path", chi.URLParam)
|
||||||
|
|
||||||
ws.router.HandleFunc("/", ws.serveRoot)
|
ws.router.HandleFunc("/", ws.serveRoot)
|
||||||
ws.router.HandleFunc("/nav", ws.serveNav)
|
ws.router.HandleFunc("/nav", ws.serveNav)
|
||||||
ws.router.HandleFunc("/navHTML", ws.serveNavHTML)
|
ws.router.HandleFunc("/navHTML", ws.serveNavHTML)
|
||||||
|
|
|
@ -116,6 +116,7 @@ func (d *Discord) sendMessage(channel, message string, meMessage bool, args ...a
|
||||||
|
|
||||||
embeds := []*discordgo.MessageEmbed{}
|
embeds := []*discordgo.MessageEmbed{}
|
||||||
files := []*discordgo.File{}
|
files := []*discordgo.File{}
|
||||||
|
var ref *discordgo.MessageReference
|
||||||
|
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
switch a := arg.(type) {
|
switch a := arg.(type) {
|
||||||
|
@ -141,13 +142,20 @@ func (d *Discord) sendMessage(channel, message string, meMessage bool, args ...a
|
||||||
ContentType: a.ContentType(),
|
ContentType: a.ContentType(),
|
||||||
Reader: bytes.NewBuffer(a.Data),
|
Reader: bytes.NewBuffer(a.Data),
|
||||||
})
|
})
|
||||||
|
case bot.MessageReference:
|
||||||
|
ref = &discordgo.MessageReference{
|
||||||
|
MessageID: a.MessageID,
|
||||||
|
ChannelID: a.ChannelID,
|
||||||
|
GuildID: a.GuildID,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data := &discordgo.MessageSend{
|
data := &discordgo.MessageSend{
|
||||||
Content: message,
|
Content: message,
|
||||||
Embeds: embeds,
|
Embeds: embeds,
|
||||||
Files: files,
|
Files: files,
|
||||||
|
Reference: ref,
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug().
|
log.Debug().
|
||||||
|
|
6
go.mod
6
go.mod
|
@ -17,7 +17,8 @@ require (
|
||||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90
|
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90
|
||||||
github.com/forPelevin/gomoji v1.1.6
|
github.com/forPelevin/gomoji v1.1.6
|
||||||
github.com/gabriel-vasile/mimetype v1.4.1
|
github.com/gabriel-vasile/mimetype v1.4.1
|
||||||
github.com/go-chi/chi/v5 v5.0.7
|
github.com/ggicci/httpin v0.16.0
|
||||||
|
github.com/go-chi/chi/v5 v5.0.11
|
||||||
github.com/go-chi/httprate v0.7.0
|
github.com/go-chi/httprate v0.7.0
|
||||||
github.com/gocolly/colly v1.2.0
|
github.com/gocolly/colly v1.2.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
|
@ -32,7 +33,7 @@ require (
|
||||||
github.com/olebedev/when v0.0.0-20190311101825-c3b538a97254
|
github.com/olebedev/when v0.0.0-20190311101825-c3b538a97254
|
||||||
github.com/rs/zerolog v1.28.0
|
github.com/rs/zerolog v1.28.0
|
||||||
github.com/slack-go/slack v0.11.3
|
github.com/slack-go/slack v0.11.3
|
||||||
github.com/stretchr/testify v1.8.2
|
github.com/stretchr/testify v1.8.4
|
||||||
github.com/trubitsyn/go-zero-width v1.0.1
|
github.com/trubitsyn/go-zero-width v1.0.1
|
||||||
github.com/velour/velour v0.0.0-20160303155839-8e090e68d158
|
github.com/velour/velour v0.0.0-20160303155839-8e090e68d158
|
||||||
golang.org/x/crypto v0.14.0
|
golang.org/x/crypto v0.14.0
|
||||||
|
@ -57,6 +58,7 @@ require (
|
||||||
github.com/dustin/go-jsonpointer v0.0.0-20160814072949-ba0abeacc3dc // indirect
|
github.com/dustin/go-jsonpointer v0.0.0-20160814072949-ba0abeacc3dc // indirect
|
||||||
github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad // indirect
|
github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad // indirect
|
||||||
github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17 // indirect
|
github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17 // indirect
|
||||||
|
github.com/ggicci/owl v0.7.0 // indirect
|
||||||
github.com/go-stack/stack v1.8.0 // indirect
|
github.com/go-stack/stack v1.8.0 // indirect
|
||||||
github.com/gobwas/glob v0.2.3 // indirect
|
github.com/gobwas/glob v0.2.3 // indirect
|
||||||
github.com/golang-jwt/jwt v3.2.1+incompatible // indirect
|
github.com/golang-jwt/jwt v3.2.1+incompatible // indirect
|
||||||
|
|
14
go.sum
14
go.sum
|
@ -120,9 +120,13 @@ github.com/gabriel-vasile/mimetype v1.4.1 h1:TRWk7se+TOjCYgRth7+1/OYLNiRNIotknkF
|
||||||
github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M=
|
github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M=
|
||||||
github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17 h1:GOfMz6cRgTJ9jWV0qAezv642OhPnKEG7gtUjJSdStHE=
|
github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17 h1:GOfMz6cRgTJ9jWV0qAezv642OhPnKEG7gtUjJSdStHE=
|
||||||
github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17/go.mod h1:HfkOCN6fkKKaPSAeNq/er3xObxTW4VLeY6UUK895gLQ=
|
github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17/go.mod h1:HfkOCN6fkKKaPSAeNq/er3xObxTW4VLeY6UUK895gLQ=
|
||||||
|
github.com/ggicci/httpin v0.16.0 h1:ZR6RXH1xNWg39xqM33V7iz7PP/GuR7vc3aHa2g5mWo4=
|
||||||
|
github.com/ggicci/httpin v0.16.0/go.mod h1:whE/5nx1jCp//UQ6rgNpq2WNxOr9FV0OpxMnQQC0Xvs=
|
||||||
|
github.com/ggicci/owl v0.7.0 h1:+AMlCR0AY7j72q7hjtN4pm8VJiikwpROtMgvPnXtuik=
|
||||||
|
github.com/ggicci/owl v0.7.0/go.mod h1:TRPWshRwYej6uES//YW5aNgLB370URwyta1Ytfs7KXs=
|
||||||
github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
|
github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
|
||||||
github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
|
github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA=
|
||||||
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/go-chi/httprate v0.7.0 h1:8W0dF7Xa2Duz2p8ncGaehIphrxQGNlOtoGY0+NRRfjQ=
|
github.com/go-chi/httprate v0.7.0 h1:8W0dF7Xa2Duz2p8ncGaehIphrxQGNlOtoGY0+NRRfjQ=
|
||||||
github.com/go-chi/httprate v0.7.0/go.mod h1:6GOYBSwnpra4CQfAKXu8sQZg+nZ0M1g9QnyFvxrAB8A=
|
github.com/go-chi/httprate v0.7.0/go.mod h1:6GOYBSwnpra4CQfAKXu8sQZg+nZ0M1g9QnyFvxrAB8A=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
|
@ -215,6 +219,7 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
|
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
@ -252,6 +257,7 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||||
|
github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo=
|
||||||
github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o=
|
github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o=
|
||||||
github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak=
|
github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak=
|
||||||
github.com/kevinburke/go-types v0.0.0-20200309064045-f2d4aea18a7a h1:Z7+SSApKiwPjNic+NF9+j7h657Uyvdp/jA3iTKhpj4E=
|
github.com/kevinburke/go-types v0.0.0-20200309064045-f2d4aea18a7a h1:Z7+SSApKiwPjNic+NF9+j7h657Uyvdp/jA3iTKhpj4E=
|
||||||
|
@ -372,8 +378,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/temoto/robotstxt v1.1.1 h1:Gh8RCs8ouX3hRSxxK7B1mO5RFByQ4CmJZDwgom++JaA=
|
github.com/temoto/robotstxt v1.1.1 h1:Gh8RCs8ouX3hRSxxK7B1mO5RFByQ4CmJZDwgom++JaA=
|
||||||
github.com/temoto/robotstxt v1.1.1/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo=
|
github.com/temoto/robotstxt v1.1.1/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo=
|
||||||
github.com/trubitsyn/go-zero-width v1.0.1 h1:AAZhtyGXW79T5BouAF0R9FtDhGcp7IGbLZo2Id3N+m8=
|
github.com/trubitsyn/go-zero-width v1.0.1 h1:AAZhtyGXW79T5BouAF0R9FtDhGcp7IGbLZo2Id3N+m8=
|
||||||
|
|
|
@ -24,17 +24,17 @@ templ (a *AdminPlugin) page() {
|
||||||
<button hx-put="/apppass/api" hx-target="#data" class="submit success button">New</button>
|
<button hx-put="/apppass/api" hx-target="#data" class="submit success button">New</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
|
|
||||||
<div class="grid-x">
|
<div class="grid-x">
|
||||||
<div class="cell" id="data"></div>
|
<div class="cell" id="data"></div>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
templ (a *AdminPlugin) showPassword(entry PassEntry) {
|
templ (a *AdminPlugin) showPassword(entry PassEntry) {
|
||||||
<div><span>ID</span><span>{ fmt.Sprintf("%d", entry.ID) }</span></div>
|
<div><span style="margin-right: 2em">ID</span><span>{ fmt.Sprintf(" %d", entry.ID) }</span></div>
|
||||||
<div><span>Password</span><span>{ entry.Secret }:{ entry.Pass }</span></div>
|
<div><span style="margin-right: 2em">Password</span><span> { entry.Secret }:{ entry.Pass }</span></div>
|
||||||
}
|
}
|
||||||
|
|
||||||
templ (a *AdminPlugin) entries(items []PassEntry) {
|
templ (a *AdminPlugin) entries(items []PassEntry) {
|
||||||
|
@ -51,7 +51,6 @@ templ (a *AdminPlugin) entries(items []PassEntry) {
|
||||||
hx-delete="/apppass/api"
|
hx-delete="/apppass/api"
|
||||||
hx-confirm={ fmt.Sprintf("Are you sure you want to delete %d?", entry.ID) }
|
hx-confirm={ fmt.Sprintf("Are you sure you want to delete %d?", entry.ID) }
|
||||||
hx-target="#data"
|
hx-target="#data"
|
||||||
hx-include="this,[name='password'],[name='secret']"
|
|
||||||
name="id" value={ fmt.Sprintf("%d", entry.ID) }>X</button>
|
name="id" value={ fmt.Sprintf("%d", entry.ID) }>X</button>
|
||||||
{ fmt.Sprintf("%d", entry.ID) }
|
{ fmt.Sprintf("%d", entry.ID) }
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -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("<div class=\"grid-container\"><form><div class=\"grid-x grid-margin-x align-bottom\"><h2>App Pass</h2></div><div class=\"grid-x grid-margin-x align-bottom\"><div class=\"cell auto\"><label for=\"password\">Password: <input type=\"text\" name=\"password\"></label></div><div class=\"cell auto\"><label for=\"secret\">Secret: <input type=\"text\" name=\"secret\"></label></div><div class=\"cell auto\"><button hx-post=\"/apppass/api\" hx-target=\"#data\" class=\"button\">List</button> <button hx-put=\"/apppass/api\" hx-target=\"#data\" class=\"submit success button\">New</button></div></div></form><div class=\"grid-x\"><div class=\"cell\" id=\"data\"></div></div></div>")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"grid-container\"><form><div class=\"grid-x grid-margin-x align-bottom\"><h2>App Pass</h2></div><div class=\"grid-x grid-margin-x align-bottom\"><div class=\"cell auto\"><label for=\"password\">Password: <input type=\"text\" name=\"password\"></label></div><div class=\"cell auto\"><label for=\"secret\">Secret: <input type=\"text\" name=\"secret\"></label></div><div class=\"cell auto\"><button hx-post=\"/apppass/api\" hx-target=\"#data\" class=\"button\">List</button> <button hx-put=\"/apppass/api\" hx-target=\"#data\" class=\"submit success button\">New</button></div></div><div class=\"grid-x\"><div class=\"cell\" id=\"data\"></div></div></form></div>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
@ -49,27 +49,27 @@ func (a *AdminPlugin) showPassword(entry PassEntry) templ.Component {
|
||||||
templ_7745c5c3_Var2 = templ.NopComponent
|
templ_7745c5c3_Var2 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div><span>ID</span><span>")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div><span style=\"margin-right: 2em\">ID</span><span>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
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/admin.templ`, Line: 35, Col: 59}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 35, Col: 86}
|
||||||
}
|
}
|
||||||
_, 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 {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span></div><div><span>Password</span><span>")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span></div><div><span style=\"margin-right: 2em\">Password</span><span>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
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/admin.templ`, Line: 36, Col: 50}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 36, Col: 77}
|
||||||
}
|
}
|
||||||
_, 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/admin.templ`, Line: 36, Col: 65}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 36, Col: 92}
|
||||||
}
|
}
|
||||||
_, 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 {
|
||||||
|
@ -135,7 +135,7 @@ func (a *AdminPlugin) entries(items []PassEntry) templ.Component {
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-target=\"#data\" hx-include=\"this,[name='password'],[name='secret']\" name=\"id\" value=\"")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-target=\"#data\" name=\"id\" value=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
@ -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/admin.templ`, Line: 55, Col: 57}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 54, 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/admin.templ`, Line: 63, Col: 22}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 62, 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 {
|
||||||
|
@ -234,7 +234,7 @@ func vars(items []configEntry) templ.Component {
|
||||||
var templ_7745c5c3_Var11 string
|
var templ_7745c5c3_Var11 string
|
||||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(item.Key)
|
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(item.Key)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 79, Col: 38}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 78, Col: 38}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
|
@ -247,7 +247,7 @@ func vars(items []configEntry) templ.Component {
|
||||||
var templ_7745c5c3_Var12 string
|
var templ_7745c5c3_Var12 string
|
||||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(item.Value)
|
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(item.Value)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 79, Col: 61}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 78, Col: 61}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
|
|
|
@ -4,13 +4,12 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ggicci/httpin"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -21,8 +20,15 @@ func (p *AdminPlugin) registerWeb() {
|
||||||
r.HandleFunc("/", p.handleVars)
|
r.HandleFunc("/", p.handleVars)
|
||||||
p.bot.GetWeb().RegisterWebName(r, "/vars", "Variables")
|
p.bot.GetWeb().RegisterWebName(r, "/vars", "Variables")
|
||||||
r = chi.NewRouter()
|
r = chi.NewRouter()
|
||||||
r.HandleFunc("/verify", p.handleAppPassCheck)
|
r.With(httpin.NewInput(AppPassCheckReq{})).
|
||||||
r.HandleFunc("/api", p.handleAppPassAPI)
|
HandleFunc("/verify", p.handleAppPassCheck)
|
||||||
|
//r.HandleFunc("/api", p.handleAppPassAPI)
|
||||||
|
r.With(httpin.NewInput(AppPassAPIReq{})).
|
||||||
|
Put("/api", p.handleAppPassAPIPut)
|
||||||
|
r.With(httpin.NewInput(AppPassAPIReq{})).
|
||||||
|
Delete("/api", p.handleAppPassAPIDelete)
|
||||||
|
r.With(httpin.NewInput(AppPassAPIReq{})).
|
||||||
|
Post("/api", p.handleAppPassAPIPost)
|
||||||
r.HandleFunc("/", p.handleAppPass)
|
r.HandleFunc("/", p.handleAppPass)
|
||||||
p.bot.GetWeb().RegisterWebName(r, "/apppass", "App Pass")
|
p.bot.GetWeb().RegisterWebName(r, "/apppass", "App Pass")
|
||||||
}
|
}
|
||||||
|
@ -32,8 +38,8 @@ func (p *AdminPlugin) handleAppPass(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PassEntry struct {
|
type PassEntry struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id" in:"form=id;query=password"`
|
||||||
Secret string `json:"secret"`
|
Secret string `json:"secret" in:"form=secret;query=password"`
|
||||||
|
|
||||||
// Should be null unless inserting a new entry
|
// Should be null unless inserting a new entry
|
||||||
Pass string `json:"pass"`
|
Pass string `json:"pass"`
|
||||||
|
@ -57,107 +63,117 @@ func (p *PassEntry) Compare(pass string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AppPassCheckReq struct {
|
||||||
|
Secret string `json:"secret"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
func (p *AdminPlugin) handleAppPassCheck(w http.ResponseWriter, r *http.Request) {
|
func (p *AdminPlugin) handleAppPassCheck(w http.ResponseWriter, r *http.Request) {
|
||||||
req := struct {
|
input := r.Context().Value(httpin.Input).(*AppPassCheckReq)
|
||||||
Secret string `json:"secret"`
|
if p.bot.CheckPassword(input.Secret, input.Password) {
|
||||||
Password string `json:"password"`
|
|
||||||
}{}
|
|
||||||
body, _ := ioutil.ReadAll(r.Body)
|
|
||||||
_ = json.Unmarshal(body, &req)
|
|
||||||
if p.bot.CheckPassword(req.Secret, req.Password) {
|
|
||||||
w.WriteHeader(204)
|
w.WriteHeader(204)
|
||||||
} else {
|
} else {
|
||||||
w.WriteHeader(403)
|
w.WriteHeader(403)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *AdminPlugin) handleAppPassAPI(w http.ResponseWriter, r *http.Request) {
|
type AppPassAPIReq struct {
|
||||||
r.ParseForm()
|
Password string `in:"form=password;query=password"`
|
||||||
b, _ := io.ReadAll(r.Body)
|
PassEntry PassEntry
|
||||||
query, _ := url.ParseQuery(string(b))
|
}
|
||||||
secret := r.FormValue("secret")
|
|
||||||
password := r.FormValue("password")
|
func (p *AdminPlugin) checkAPIInput(w http.ResponseWriter, r *http.Request) *AppPassAPIReq {
|
||||||
id, _ := strconv.ParseInt(r.FormValue("id"), 10, 64)
|
input := r.Context().Value(httpin.Input).(*AppPassAPIReq)
|
||||||
if !r.Form.Has("secret") && query.Has("secret") {
|
|
||||||
secret = query.Get("secret")
|
if input.Password == "" && input.PassEntry.Secret == "" {
|
||||||
|
b, _ := io.ReadAll(r.Body)
|
||||||
|
query, _ := url.ParseQuery(string(b))
|
||||||
|
input.Password = query.Get("password")
|
||||||
|
input.PassEntry.ID, _ = strconv.ParseInt(query.Get("id"), 10, 64)
|
||||||
|
input.PassEntry.Secret = query.Get("secret")
|
||||||
}
|
}
|
||||||
if !r.Form.Has("password") && query.Has("password") {
|
|
||||||
password = query.Get("password")
|
log.Printf("checkAPIInput: %#v", input)
|
||||||
}
|
|
||||||
if !r.Form.Has("id") && query.Has("id") {
|
if input.PassEntry.Secret == "" {
|
||||||
id, _ = strconv.ParseInt(query.Get("id"), 10, 64)
|
|
||||||
}
|
|
||||||
req := struct {
|
|
||||||
Password string `json:"password"`
|
|
||||||
PassEntry PassEntry `json:"passEntry"`
|
|
||||||
}{
|
|
||||||
password,
|
|
||||||
PassEntry{
|
|
||||||
ID: id,
|
|
||||||
Secret: secret,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if req.PassEntry.Secret == "" {
|
|
||||||
writeErr(r.Context(), w, fmt.Errorf("missing secret"))
|
writeErr(r.Context(), w, fmt.Errorf("missing secret"))
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
if req.Password == "" || !p.bot.CheckPassword(req.PassEntry.Secret, req.Password) {
|
if input.Password == "" || !p.bot.CheckPassword(input.PassEntry.Secret, input.Password) {
|
||||||
writeErr(r.Context(), w, fmt.Errorf("missing or incorrect password"))
|
writeErr(r.Context(), w, fmt.Errorf("missing or incorrect password"))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AdminPlugin) handleAppPassAPIPut(w http.ResponseWriter, r *http.Request) {
|
||||||
|
input := p.checkAPIInput(w, r)
|
||||||
|
if input == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch r.Method {
|
if string(input.PassEntry.Pass) == "" {
|
||||||
case http.MethodPut:
|
c := 10
|
||||||
if string(req.PassEntry.Pass) == "" {
|
b := make([]byte, c)
|
||||||
c := 10
|
_, err := rand.Read(b)
|
||||||
b := make([]byte, c)
|
|
||||||
_, err := rand.Read(b)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("error:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.PassEntry.Pass = fmt.Sprintf("%x", md5.Sum(b))
|
|
||||||
}
|
|
||||||
q := `insert into apppass (secret, encoded_pass, cost) values (?, ?, ?)`
|
|
||||||
req.PassEntry.EncodePass()
|
|
||||||
|
|
||||||
check := bcrypt.CompareHashAndPassword(req.PassEntry.encodedPass, []byte(req.PassEntry.Pass))
|
|
||||||
|
|
||||||
log.Debug().
|
|
||||||
Str("secret", req.PassEntry.Secret).
|
|
||||||
Str("encoded", string(req.PassEntry.encodedPass)).
|
|
||||||
Str("password", string(req.PassEntry.Pass)).
|
|
||||||
Interface("check", check).
|
|
||||||
Msg("debug pass creation")
|
|
||||||
|
|
||||||
res, err := p.db.Exec(q, req.PassEntry.Secret, req.PassEntry.encodedPass, req.PassEntry.Cost)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErr(r.Context(), w, err)
|
fmt.Println("error:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
id, err := res.LastInsertId()
|
input.PassEntry.Pass = fmt.Sprintf("%x", md5.Sum(b))
|
||||||
if err != nil {
|
}
|
||||||
writeErr(r.Context(), w, err)
|
q := `insert into apppass (secret, encoded_pass, cost) values (?, ?, ?)`
|
||||||
return
|
input.PassEntry.EncodePass()
|
||||||
}
|
|
||||||
req.PassEntry.ID = id
|
check := bcrypt.CompareHashAndPassword(input.PassEntry.encodedPass, []byte(input.PassEntry.Pass))
|
||||||
p.showPassword(req.PassEntry).Render(r.Context(), w)
|
|
||||||
|
log.Debug().
|
||||||
|
Str("secret", input.PassEntry.Secret).
|
||||||
|
Str("encoded", string(input.PassEntry.encodedPass)).
|
||||||
|
Str("password", string(input.PassEntry.Pass)).
|
||||||
|
Interface("check", check).
|
||||||
|
Msg("debug pass creation")
|
||||||
|
|
||||||
|
res, err := p.db.Exec(q, input.PassEntry.Secret, input.PassEntry.encodedPass, input.PassEntry.Cost)
|
||||||
|
if err != nil {
|
||||||
|
writeErr(r.Context(), w, err)
|
||||||
return
|
return
|
||||||
case http.MethodDelete:
|
}
|
||||||
|
id, err := res.LastInsertId()
|
||||||
|
if err != nil {
|
||||||
|
writeErr(r.Context(), w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
input.PassEntry.ID = id
|
||||||
|
p.showPassword(input.PassEntry).Render(r.Context(), w)
|
||||||
|
}
|
||||||
|
|
||||||
if req.PassEntry.ID <= 0 {
|
func (p *AdminPlugin) handleAppPassAPIDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
writeErr(r.Context(), w, fmt.Errorf("missing ID"))
|
input := p.checkAPIInput(w, r)
|
||||||
return
|
if input == nil {
|
||||||
}
|
return
|
||||||
q := `delete from apppass where id = ?`
|
}
|
||||||
_, err := p.db.Exec(q, req.PassEntry.ID)
|
if input.PassEntry.ID <= 0 {
|
||||||
if err != nil {
|
writeErr(r.Context(), w, fmt.Errorf("missing ID"))
|
||||||
writeErr(r.Context(), w, err)
|
return
|
||||||
return
|
}
|
||||||
}
|
q := `delete from apppass where id = ?`
|
||||||
|
_, err := p.db.Exec(q, input.PassEntry.ID)
|
||||||
|
if err != nil {
|
||||||
|
writeErr(r.Context(), w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.handleAppPassAPIPost(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AdminPlugin) handleAppPassAPIPost(w http.ResponseWriter, r *http.Request) {
|
||||||
|
input := p.checkAPIInput(w, r)
|
||||||
|
if input == nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
q := `select id,secret from apppass where secret = ?`
|
q := `select id,secret from apppass where secret = ?`
|
||||||
passEntries := []PassEntry{}
|
passEntries := []PassEntry{}
|
||||||
err := p.db.Select(&passEntries, q, req.PassEntry.Secret)
|
|
||||||
|
err := p.db.Select(&passEntries, q, input.PassEntry.Secret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErr(r.Context(), w, err)
|
writeErr(r.Context(), w, err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -3,10 +3,8 @@ package counter
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"github.com/ggicci/httpin"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
|
@ -23,34 +21,48 @@ func (p *CounterPlugin) registerWeb() {
|
||||||
dur := time.Duration(seconds) * time.Second
|
dur := time.Duration(seconds) * time.Second
|
||||||
subrouter := chi.NewRouter()
|
subrouter := chi.NewRouter()
|
||||||
subrouter.Use(httprate.LimitByIP(requests, dur))
|
subrouter.Use(httprate.LimitByIP(requests, dur))
|
||||||
subrouter.HandleFunc("/api/users/{user}/items/{item}/increment/{delta}", p.mkIncrementByNAPI(1))
|
subrouter.With(httpin.NewInput(CounterChangeReq{})).
|
||||||
subrouter.HandleFunc("/api/users/{user}/items/{item}/decrement/{delta}", p.mkIncrementByNAPI(-1))
|
HandleFunc("/api/users/{user}/items/{item}/increment/{delta}", p.mkIncrementByNAPI(1))
|
||||||
subrouter.HandleFunc("/api/users/{user}/items/{item}/increment", p.mkIncrementByNAPI(1))
|
subrouter.With(httpin.NewInput(CounterChangeReq{})).
|
||||||
subrouter.HandleFunc("/api/users/{user}/items/{item}/decrement", p.mkIncrementByNAPI(-1))
|
HandleFunc("/api/users/{user}/items/{item}/decrement/{delta}", p.mkIncrementByNAPI(-1))
|
||||||
|
subrouter.With(httpin.NewInput(CounterChangeReq{})).
|
||||||
|
HandleFunc("/api/users/{user}/items/{item}/increment", p.mkIncrementByNAPI(1))
|
||||||
|
subrouter.With(httpin.NewInput(CounterChangeReq{})).
|
||||||
|
HandleFunc("/api/users/{user}/items/{item}/decrement", p.mkIncrementByNAPI(-1))
|
||||||
r.Mount("/", subrouter)
|
r.Mount("/", subrouter)
|
||||||
r.HandleFunc("/users/{user}/items/{item}/increment", p.incHandler(1))
|
r.With(httpin.NewInput(CounterChangeReq{})).
|
||||||
r.HandleFunc("/users/{user}/items/{item}/decrement", p.incHandler(-1))
|
HandleFunc("/users/{user}/items/{item}/increment", p.incHandler(1))
|
||||||
|
r.With(httpin.NewInput(CounterChangeReq{})).
|
||||||
|
HandleFunc("/users/{user}/items/{item}/decrement", p.incHandler(-1))
|
||||||
r.HandleFunc("/", p.handleCounter)
|
r.HandleFunc("/", p.handleCounter)
|
||||||
p.b.GetWeb().RegisterWebName(r, "/counter", "Counter")
|
p.b.GetWeb().RegisterWebName(r, "/counter", "Counter")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CounterChangeReq struct {
|
||||||
|
UserName string `in:"path=user"`
|
||||||
|
Item string `in:"path=item"`
|
||||||
|
Password string `in:"form=password"`
|
||||||
|
Delta int `in:"path=delta"`
|
||||||
|
Body struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
} `in:"body=json"`
|
||||||
|
}
|
||||||
|
|
||||||
func (p *CounterPlugin) incHandler(delta int) http.HandlerFunc {
|
func (p *CounterPlugin) incHandler(delta int) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
userName, _ := url.QueryUnescape(chi.URLParam(r, "user"))
|
input := r.Context().Value(httpin.Input).(*CounterChangeReq)
|
||||||
itemName, _ := url.QueryUnescape(chi.URLParam(r, "item"))
|
if !p.b.CheckPassword("", input.Password) {
|
||||||
pass := r.FormValue("password")
|
|
||||||
if !p.b.CheckPassword("", pass) {
|
|
||||||
w.WriteHeader(401)
|
w.WriteHeader(401)
|
||||||
fmt.Fprintf(w, "error")
|
fmt.Fprintf(w, "error")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item, err := p.delta(userName, itemName, "", delta)
|
item, err := p.delta(input.UserName, input.Item, "", delta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
fmt.Fprintf(w, "error")
|
fmt.Fprintf(w, "error")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.renderItem(userName, item).Render(r.Context(), w)
|
p.renderItem(input.UserName, item).Render(r.Context(), w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,13 +115,11 @@ func (p *CounterPlugin) delta(userName, itemName, personalMessage string, delta
|
||||||
|
|
||||||
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) {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
userName, _ := url.QueryUnescape(chi.URLParam(r, "user"))
|
input := r.Context().Value(httpin.Input).(*CounterChangeReq)
|
||||||
itemName, _ := url.QueryUnescape(chi.URLParam(r, "item"))
|
if input.Delta == 0 {
|
||||||
delta, err := strconv.Atoi(chi.URLParam(r, "delta"))
|
input.Delta = direction
|
||||||
if err != nil || delta == 0 {
|
|
||||||
delta = direction
|
|
||||||
} else {
|
} else {
|
||||||
delta = delta * direction
|
input.Delta = input.Delta * direction
|
||||||
}
|
}
|
||||||
|
|
||||||
secret, pass, ok := r.BasicAuth()
|
secret, pass, ok := r.BasicAuth()
|
||||||
|
@ -127,15 +137,12 @@ func (p *CounterPlugin) mkIncrementByNAPI(direction int) func(w http.ResponseWri
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, _ := io.ReadAll(r.Body)
|
|
||||||
postData := map[string]string{}
|
|
||||||
err = json.Unmarshal(body, &postData)
|
|
||||||
personalMsg := ""
|
personalMsg := ""
|
||||||
if inputMsg, ok := postData["message"]; ok {
|
if input.Body.Message != "" {
|
||||||
personalMsg = fmt.Sprintf("\nMessage: %s", inputMsg)
|
personalMsg = fmt.Sprintf("\nMessage: %s", input.Body.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := p.delta(userName, itemName, personalMsg, delta*direction); err != nil {
|
if _, err := p.delta(input.UserName, input.Item, personalMsg, input.Delta*direction); err != nil {
|
||||||
log.Error().Err(err).Msg("error finding item")
|
log.Error().Err(err).Msg("error finding item")
|
||||||
w.WriteHeader(400)
|
w.WriteHeader(400)
|
||||||
j, _ := json.Marshal(struct {
|
j, _ := json.Marshal(struct {
|
||||||
|
|
|
@ -1,104 +0,0 @@
|
||||||
<html>
|
|
||||||
<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/axios/dist/axios.min.js"></script>
|
|
||||||
<title>Counters</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div id="app">
|
|
||||||
<b-navbar>
|
|
||||||
<b-navbar-brand>Counters</b-navbar-brand>
|
|
||||||
<b-navbar-nav>
|
|
||||||
<b-nav-item v-for="item in nav" :href="item.url" :active="item.name === 'Counter'">{{ item.name }}</b-nav-item>
|
|
||||||
</b-navbar-nav>
|
|
||||||
</b-navbar>
|
|
||||||
<b-alert
|
|
||||||
dismissable
|
|
||||||
:show="err"
|
|
||||||
variant="error">
|
|
||||||
{{ err }}
|
|
||||||
</b-alert>
|
|
||||||
<b-container>
|
|
||||||
<b-row>
|
|
||||||
<b-col cols="5">Password:</b-col>
|
|
||||||
<b-col><b-input v-model="answer"></b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row v-for="(counter, user) in counters">
|
|
||||||
{{ user }}:
|
|
||||||
<b-container>
|
|
||||||
<b-row v-for="(count, thing) in counter">
|
|
||||||
<b-col offset="1">
|
|
||||||
{{ thing }}:
|
|
||||||
</b-col>
|
|
||||||
<b-col>
|
|
||||||
{{ count }}
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="2">
|
|
||||||
<button @click="subtract(user,thing,count)">-</button>
|
|
||||||
<button @click="add(user,thing,count)">+</button>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-container>
|
|
||||||
</b-row>
|
|
||||||
</b-container>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function convertData(data) {
|
|
||||||
var newData = {};
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
|
||||||
let entry = data[i]
|
|
||||||
if (newData[entry.Nick] === undefined) {
|
|
||||||
newData[entry.Nick] = {}
|
|
||||||
}
|
|
||||||
newData[entry.Nick][entry.Item] = entry.Count;
|
|
||||||
}
|
|
||||||
return newData;
|
|
||||||
}
|
|
||||||
var app = new Vue({
|
|
||||||
el: '#app',
|
|
||||||
data: {
|
|
||||||
err: '',
|
|
||||||
nav: [],
|
|
||||||
answer: '',
|
|
||||||
correct: 0,
|
|
||||||
counters: {}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
axios.get('/nav')
|
|
||||||
.then(resp => {
|
|
||||||
this.nav = resp.data;
|
|
||||||
})
|
|
||||||
.catch(err => console.log(err))
|
|
||||||
axios.get('/counter/api')
|
|
||||||
.then(resp => (this.counters = convertData(resp.data)))
|
|
||||||
.catch(err => (this.err = err));
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
add(user, thing, count) {
|
|
||||||
axios.post('/counter/api',
|
|
||||||
{user: user, thing: thing, action: '++', password: this.answer})
|
|
||||||
.then(resp => {this.counters = convertData(resp.data); this.err = '';})
|
|
||||||
.catch(err => this.err = err);
|
|
||||||
},
|
|
||||||
subtract(user, thing, count) {
|
|
||||||
axios.post('/counter/api',
|
|
||||||
{user: user, thing: thing, action: '--', password: this.answer})
|
|
||||||
.then(resp => {this.counters = convertData(resp.data); this.err = '';})
|
|
||||||
.catch(err => this.err = err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -12,6 +12,7 @@ templ (p *EmojyPlugin) uploadIndex() {
|
||||||
@p.emojyNav()
|
@p.emojyNav()
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<form hx-post="/emojy/upload" enctype="multipart/form-data">
|
||||||
<div class="grid-x">
|
<div class="grid-x">
|
||||||
<div class="cell">
|
<div class="cell">
|
||||||
<label>Passphrase</label>
|
<label>Passphrase</label>
|
||||||
|
@ -19,12 +20,13 @@ templ (p *EmojyPlugin) uploadIndex() {
|
||||||
</div>
|
</div>
|
||||||
<div class="cell">
|
<div class="cell">
|
||||||
<label>File
|
<label>File
|
||||||
<input type="file" />
|
<input type="file" name="attachment" />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="cell">
|
<div class="cell">
|
||||||
<button class="button" hx-post="/emojy/upload">Submit</button>
|
<button class="button" type="submit">Submit</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ func (p *EmojyPlugin) uploadIndex() templ.Component {
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div><div class=\"grid-x\"><div class=\"cell\"><label>Passphrase</label> <input type=\"text\" name=\"password\" placeholder=\"Password...\"></div><div class=\"cell\"><label>File <input type=\"file\"></label></div><div class=\"cell\"><button class=\"button\" hx-post=\"/emojy/upload\">Submit</button></div></div></div>")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div><form hx-post=\"/emojy/upload\" enctype=\"multipart/form-data\"><div class=\"grid-x\"><div class=\"cell\"><label>Passphrase</label> <input type=\"text\" name=\"password\" placeholder=\"Password...\"></div><div class=\"cell\"><label>File <input type=\"file\" name=\"attachment\"></label></div><div class=\"cell\"><button class=\"button\" type=\"submit\">Submit</button></div></div></form></div>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"embed"
|
"embed"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ggicci/httpin"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -23,8 +24,10 @@ func (p *EmojyPlugin) registerWeb() {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.HandleFunc("/all", p.handleAll)
|
r.HandleFunc("/all", p.handleAll)
|
||||||
r.HandleFunc("/allFiles", p.handleAllFiles)
|
r.HandleFunc("/allFiles", p.handleAllFiles)
|
||||||
r.HandleFunc("/upload", p.handleUpload)
|
r.With(httpin.NewInput(UploadReq{})).
|
||||||
r.HandleFunc("/file/{name}", p.handleEmojy)
|
Post("/upload", p.handleUpload)
|
||||||
|
r.With(httpin.NewInput(EmojyReq{})).
|
||||||
|
HandleFunc("/file/{name}", p.handleEmojy)
|
||||||
r.HandleFunc("/stats", p.handleStats)
|
r.HandleFunc("/stats", p.handleStats)
|
||||||
r.HandleFunc("/list", p.handleList)
|
r.HandleFunc("/list", p.handleList)
|
||||||
r.HandleFunc("/new", p.handleUploadForm)
|
r.HandleFunc("/new", p.handleUploadForm)
|
||||||
|
@ -98,8 +101,15 @@ func (p *EmojyPlugin) handleAllFiles(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UploadReq struct {
|
||||||
|
Password string `in:"form=password"`
|
||||||
|
Attachment *httpin.File `in:"form=attachment"`
|
||||||
|
}
|
||||||
|
|
||||||
func (p *EmojyPlugin) handleUpload(w http.ResponseWriter, r *http.Request) {
|
func (p *EmojyPlugin) handleUpload(w http.ResponseWriter, r *http.Request) {
|
||||||
newFilePath, err := p.FileSave(r)
|
input := r.Context().Value(httpin.Input).(*UploadReq)
|
||||||
|
log.Printf("handleUpload: %#v", input)
|
||||||
|
newFilePath, err := p.FileSave(input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("could not upload file")
|
log.Error().Err(err).Msgf("could not upload file")
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
|
@ -111,60 +121,46 @@ func (p *EmojyPlugin) handleUpload(w http.ResponseWriter, r *http.Request) {
|
||||||
fmt.Fprintf(w, "success")
|
fmt.Fprintf(w, "success")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *EmojyPlugin) FileSave(r *http.Request) (string, error) {
|
func (p *EmojyPlugin) FileSave(input *UploadReq) (string, error) {
|
||||||
if err := r.ParseMultipartForm(32 << 20); err != nil {
|
if !p.b.CheckPassword("", input.Password) {
|
||||||
if err != http.ErrNotMultipart {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if err := r.ParseForm(); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if r.MultipartForm == nil || len(r.MultipartForm.File) == 0 {
|
|
||||||
return "", fmt.Errorf("no files")
|
|
||||||
}
|
|
||||||
|
|
||||||
password := r.FormValue("password")
|
|
||||||
if password != p.b.GetPassword() {
|
|
||||||
return "", fmt.Errorf("incorrect password")
|
return "", fmt.Errorf("incorrect password")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, fileHeaders := range r.MultipartForm.File {
|
file := input.Attachment
|
||||||
for _, fileHeader := range fileHeaders {
|
emojyFileName := file.Filename()
|
||||||
body, err := fileHeader.Open()
|
emojyName := strings.TrimSuffix(emojyFileName, filepath.Ext(emojyFileName))
|
||||||
if err != nil {
|
if ok, _, _, _ := p.isKnownEmojy(emojyName); ok {
|
||||||
return "", fmt.Errorf("error opening part %q: %s", fileHeader.Filename, err)
|
return "", fmt.Errorf("emojy already exists")
|
||||||
}
|
|
||||||
emojyFileName := fileHeader.Filename
|
|
||||||
emojyName := strings.TrimSuffix(emojyFileName, filepath.Ext(emojyFileName))
|
|
||||||
if ok, _, _, _ := p.isKnownEmojy(emojyName); ok {
|
|
||||||
return "", fmt.Errorf("emojy already exists")
|
|
||||||
}
|
|
||||||
contentType := fileHeader.Header.Get("Content-Type")
|
|
||||||
if !strings.HasPrefix(contentType, "image") {
|
|
||||||
return "", fmt.Errorf("incorrect mime type - given: %s", contentType)
|
|
||||||
}
|
|
||||||
fullPath := filepath.Clean(filepath.Join(p.emojyPath, emojyFileName))
|
|
||||||
_ = os.MkdirAll(p.emojyPath, os.ModePerm)
|
|
||||||
log.Debug().Msgf("trying to create/open file: %s", fullPath)
|
|
||||||
file, err := os.OpenFile(fullPath, os.O_WRONLY|os.O_CREATE, os.ModePerm)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
_, err = io.Copy(file, body)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return emojyFileName, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("did not find file")
|
contentType := file.MIMEHeader().Get("Content-Type")
|
||||||
|
if !strings.HasPrefix(contentType, "image") {
|
||||||
|
return "", fmt.Errorf("incorrect mime type - given: %s", contentType)
|
||||||
|
}
|
||||||
|
fullPath := filepath.Clean(filepath.Join(p.emojyPath, emojyFileName))
|
||||||
|
_ = os.MkdirAll(p.emojyPath, os.ModePerm)
|
||||||
|
log.Debug().Msgf("trying to create/open file: %s", fullPath)
|
||||||
|
outFile, err := os.OpenFile(fullPath, os.O_WRONLY|os.O_CREATE, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer outFile.Close()
|
||||||
|
inFile, _ := file.ReadAll()
|
||||||
|
stream, _ := file.OpenReceiveStream()
|
||||||
|
_, err = outFile.Write(inFile)
|
||||||
|
_, err = io.Copy(outFile, stream)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return emojyFileName, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmojyReq struct {
|
||||||
|
Name string `in:"path=name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *EmojyPlugin) handleEmojy(w http.ResponseWriter, r *http.Request) {
|
func (p *EmojyPlugin) handleEmojy(w http.ResponseWriter, r *http.Request) {
|
||||||
fname := chi.URLParam(r, "name")
|
input := r.Context().Value(httpin.Input).(*EmojyReq)
|
||||||
contents, err := ioutil.ReadFile(path.Join(p.emojyPath, fname))
|
contents, err := ioutil.ReadFile(path.Join(p.emojyPath, input.Name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(404)
|
w.WriteHeader(404)
|
||||||
out, _ := json.Marshal(struct{ err error }{err})
|
out, _ := json.Marshal(struct{ err error }{err})
|
||||||
|
|
|
@ -3,10 +3,9 @@ package fact
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ggicci/httpin"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"html/template"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed *.html
|
//go:embed *.html
|
||||||
|
@ -15,26 +14,20 @@ var embeddedFS embed.FS
|
||||||
// Register any web URLs desired
|
// Register any web URLs desired
|
||||||
func (p *FactoidPlugin) registerWeb() {
|
func (p *FactoidPlugin) registerWeb() {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.Post("/search", p.handleSearch)
|
r.With(httpin.NewInput(SearchReq{})).
|
||||||
r.HandleFunc("/req", p.serveQuery)
|
Post("/search", p.handleSearch)
|
||||||
r.HandleFunc("/", p.serveQuery)
|
r.Get("/", p.serveQuery)
|
||||||
p.b.GetWeb().RegisterWebName(r, "/factoid", "Factoid")
|
p.b.GetWeb().RegisterWebName(r, "/factoid", "Factoid")
|
||||||
}
|
}
|
||||||
|
|
||||||
func linkify(text string) template.HTML {
|
type SearchReq struct {
|
||||||
parts := strings.Fields(text)
|
Query string `in:"query"`
|
||||||
for i, word := range parts {
|
|
||||||
if strings.HasPrefix(word, "http") {
|
|
||||||
parts[i] = fmt.Sprintf("<a href=\"%s\">%s</a>", word, word)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return template.HTML(strings.Join(parts, " "))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *FactoidPlugin) handleSearch(w http.ResponseWriter, r *http.Request) {
|
func (p *FactoidPlugin) handleSearch(w http.ResponseWriter, r *http.Request) {
|
||||||
query := r.FormValue("query")
|
input := r.Context().Value(httpin.Input).(*SearchReq)
|
||||||
|
|
||||||
entries, err := getFacts(p.db, query, "")
|
entries, err := getFacts(p.db, input.Query, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
fmt.Fprint(w, err)
|
fmt.Fprint(w, err)
|
||||||
|
|
|
@ -45,9 +45,10 @@ func (p *LastPlugin) migrate() error {
|
||||||
_, err = tx.Exec(`create table if not exists last (
|
_, err = tx.Exec(`create table if not exists last (
|
||||||
day integer,
|
day integer,
|
||||||
channel string not null,
|
channel string not null,
|
||||||
ts int not null,
|
time int not null,
|
||||||
who string not null,
|
nick string not null,
|
||||||
message string not null,
|
body string not null,
|
||||||
|
message_id string not null,
|
||||||
constraint last_key primary key (day, channel) on conflict replace
|
constraint last_key primary key (day, channel) on conflict replace
|
||||||
)`)
|
)`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -134,8 +135,8 @@ func (p *LastPlugin) recordLast(r bot.Request) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := p.db.Exec(
|
_, err := p.db.Exec(
|
||||||
`insert into last values (?, ?, ?, ?, ?)`,
|
`insert into last (day, channel, time, body, nick, message_id) values (?, ?, ?, ?, ?, ?)`,
|
||||||
day.Unix(), ch, time.Now().Unix(), who, r.Msg.Body)
|
day.Unix(), ch, time.Now().Unix(), r.Msg.Body, who, r.Msg.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("Could not record last.")
|
log.Error().Err(err).Msgf("Could not record last.")
|
||||||
}
|
}
|
||||||
|
@ -143,11 +144,13 @@ func (p *LastPlugin) recordLast(r bot.Request) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
type last struct {
|
type last struct {
|
||||||
Day int64
|
ID int64 `db:"id"`
|
||||||
TS int64
|
Day int64 `db:"day"`
|
||||||
Channel string
|
Time int64 `db:"time"`
|
||||||
Who string
|
Channel string `db:"channel"`
|
||||||
Message string
|
Nick string `db:"nick"`
|
||||||
|
Body string `db:"body"`
|
||||||
|
MessageID string `db:"message_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *LastPlugin) yesterdaysLast(ch string) (last, error) {
|
func (p *LastPlugin) yesterdaysLast(ch string) (last, error) {
|
||||||
|
@ -189,6 +192,11 @@ func (p *LastPlugin) sayLast(c bot.Connector, chFrom, chTo string, force bool) {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
msg := fmt.Sprintf(`%s killed the channel last night by saying "%s"`, l.Who, l.Message)
|
msg := fmt.Sprintf(`%s killed the channel last night by saying "%s"`, l.Nick, l.Body)
|
||||||
p.b.Send(c, bot.Message, chTo, msg)
|
guildID := p.c.Get("discord.guildid", "")
|
||||||
|
p.b.Send(c, bot.Message, chTo, msg, bot.MessageReference{
|
||||||
|
MessageID: l.MessageID,
|
||||||
|
ChannelID: l.Channel,
|
||||||
|
GuildID: guildID,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package meme
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ggicci/httpin"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -17,8 +18,10 @@ func (p *MemePlugin) registerWeb(c bot.Connector) {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.HandleFunc("/slash", p.slashMeme(c))
|
r.HandleFunc("/slash", p.slashMeme(c))
|
||||||
r.Get("/img", p.img)
|
r.Get("/img", p.img)
|
||||||
r.Put("/save/{name}", p.saveMeme)
|
r.With(httpin.NewInput(SaveReq{})).
|
||||||
r.Post("/add", p.saveMeme)
|
Put("/save/{name}", p.saveMeme)
|
||||||
|
r.With(httpin.NewInput(SaveReq{})).
|
||||||
|
Post("/add", p.saveMeme)
|
||||||
r.Delete("/rm/{name}", p.rmMeme)
|
r.Delete("/rm/{name}", p.rmMeme)
|
||||||
r.Get("/edit/{name}", p.editMeme)
|
r.Get("/edit/{name}", p.editMeme)
|
||||||
r.Get("/", p.webRoot)
|
r.Get("/", p.webRoot)
|
||||||
|
@ -85,31 +88,33 @@ func (p *MemePlugin) rmMeme(w http.ResponseWriter, r *http.Request) {
|
||||||
mkCheckError(w)(err)
|
mkCheckError(w)(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SaveReq struct {
|
||||||
|
Name string `in:"path=name"`
|
||||||
|
Config string `in:"form=config"`
|
||||||
|
URL string `in:"form=url"`
|
||||||
|
}
|
||||||
|
|
||||||
func (p *MemePlugin) saveMeme(w http.ResponseWriter, r *http.Request) {
|
func (p *MemePlugin) saveMeme(w http.ResponseWriter, r *http.Request) {
|
||||||
name := chi.URLParam(r, "name")
|
input := r.Context().Value(httpin.Input).(*SaveReq)
|
||||||
if name == "" {
|
|
||||||
name = r.FormValue("name")
|
|
||||||
}
|
|
||||||
checkError := mkCheckError(w)
|
checkError := mkCheckError(w)
|
||||||
|
|
||||||
formats := p.c.GetMap("meme.memes", defaultFormats)
|
formats := p.c.GetMap("meme.memes", defaultFormats)
|
||||||
formats[name] = r.FormValue("url")
|
formats[input.Name] = input.URL
|
||||||
err := p.c.SetMap("meme.memes", formats)
|
err := p.c.SetMap("meme.memes", formats)
|
||||||
checkError(err)
|
checkError(err)
|
||||||
|
|
||||||
config := r.FormValue("config")
|
if input.Config == "" {
|
||||||
if config == "" {
|
input.Config = p.defaultFormatConfigJSON()
|
||||||
config = p.defaultFormatConfigJSON()
|
|
||||||
}
|
}
|
||||||
configs := p.c.GetMap("meme.memeconfigs", map[string]string{})
|
configs := p.c.GetMap("meme.memeconfigs", map[string]string{})
|
||||||
configs[name] = config
|
configs[input.Name] = input.Config
|
||||||
err = p.c.SetMap("meme.memeconfigs", configs)
|
err = p.c.SetMap("meme.memeconfigs", configs)
|
||||||
checkError(err)
|
checkError(err)
|
||||||
|
|
||||||
meme := webResp{
|
meme := webResp{
|
||||||
Name: name,
|
Name: input.Name,
|
||||||
URL: formats[name],
|
URL: formats[input.Name],
|
||||||
Config: configs[name],
|
Config: configs[input.Name],
|
||||||
}
|
}
|
||||||
|
|
||||||
p.Show(meme).Render(r.Context(), w)
|
p.Show(meme).Render(r.Context(), w)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package secrets
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ggicci/httpin"
|
||||||
"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"
|
||||||
|
@ -31,9 +32,10 @@ func New(b bot.Bot) bot.Plugin {
|
||||||
|
|
||||||
func (p *SecretsPlugin) registerWeb() {
|
func (p *SecretsPlugin) registerWeb() {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.HandleFunc("/add", p.handleRegister)
|
r.With(httpin.NewInput(RegisterReq{})).
|
||||||
r.HandleFunc("/remove", p.handleRemove)
|
Post("/add", p.handleRegister)
|
||||||
r.HandleFunc("/", p.handleIndex)
|
r.Delete("/remove", p.handleRemove)
|
||||||
|
r.Get("/", p.handleIndex)
|
||||||
p.b.GetWeb().RegisterWebName(r, "/secrets", "Secrets")
|
p.b.GetWeb().RegisterWebName(r, "/secrets", "Secrets")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,14 +73,15 @@ func (p *SecretsPlugin) handleAll(w http.ResponseWriter, r *http.Request) {
|
||||||
p.sendKeys(w, r)
|
p.sendKeys(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RegisterReq struct {
|
||||||
|
Key string `in:"form=key"`
|
||||||
|
Value string `in:"form=value"`
|
||||||
|
}
|
||||||
|
|
||||||
func (p *SecretsPlugin) handleRegister(w http.ResponseWriter, r *http.Request) {
|
func (p *SecretsPlugin) handleRegister(w http.ResponseWriter, r *http.Request) {
|
||||||
if checkMethod(http.MethodPost, w, r) {
|
input := r.Context().Value(httpin.Input).(*RegisterReq)
|
||||||
log.Debug().Msgf("failed post %s", r.Method)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
checkError := mkCheckError(w)
|
checkError := mkCheckError(w)
|
||||||
key, value := r.FormValue("key"), r.FormValue("value")
|
err := p.c.RegisterSecret(input.Key, input.Value)
|
||||||
err := p.c.RegisterSecret(key, value)
|
|
||||||
if checkError(err) {
|
if checkError(err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -86,9 +89,6 @@ func (p *SecretsPlugin) handleRegister(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *SecretsPlugin) handleRemove(w http.ResponseWriter, r *http.Request) {
|
func (p *SecretsPlugin) handleRemove(w http.ResponseWriter, r *http.Request) {
|
||||||
if checkMethod(http.MethodDelete, w, r) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
checkError := mkCheckError(w)
|
checkError := mkCheckError(w)
|
||||||
b, err := io.ReadAll(r.Body)
|
b, err := io.ReadAll(r.Body)
|
||||||
if checkError(err) {
|
if checkError(err) {
|
||||||
|
|
Loading…
Reference in New Issue