mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 00:39:13 +00:00
feat(mockindexer): support feeds and webhooks (#1361)
This commit is contained in:
parent
f488c88f1b
commit
c377bc9157
6 changed files with 346 additions and 49 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -37,9 +37,12 @@ web/build
|
||||||
bin/
|
bin/
|
||||||
dist/
|
dist/
|
||||||
.run/
|
.run/
|
||||||
|
.dev/
|
||||||
tmp/
|
tmp/
|
||||||
.golangci.yml
|
.golangci.yml
|
||||||
web/eslint-sarif-report.sarif
|
web/eslint-sarif-report.sarif
|
||||||
|
test/mockindexer/feeds/*
|
||||||
|
test/mockindexer/files/*
|
||||||
|
|
||||||
# Preserve files
|
# Preserve files
|
||||||
!.gitkeep
|
!.gitkeep
|
||||||
|
|
|
@ -13,10 +13,10 @@ extra indexer definitions. Then start autobrr as usual.
|
||||||
|
|
||||||
* Add an instance of the MockIndexer in autobrr UI. Pick any nickname,
|
* Add an instance of the MockIndexer in autobrr UI. Pick any nickname,
|
||||||
_don't set any auth_.
|
_don't set any auth_.
|
||||||
* Set up an action - for example the watchdir action which will make autobrr
|
* Set up an action - for example the Watchdir action which will make autobrr
|
||||||
actually download the announced torrent file from the MockIndexer.
|
actually download the announced torrent file from the MockIndexer.
|
||||||
|
|
||||||
Posting announces.
|
## Post announce
|
||||||
|
|
||||||
* Open `http://localhost:3999` in your browser. A simple input will allow you to
|
* Open `http://localhost:3999` in your browser. A simple input will allow you to
|
||||||
post announces to the channel. For example, to announce the `1.torrent` file
|
post announces to the channel. For example, to announce the `1.torrent` file
|
||||||
|
@ -27,3 +27,20 @@ New Torrent Announcement: <PC :: Iso> Name:'debian live 10 6 0 amd64 standard i
|
||||||
```
|
```
|
||||||
|
|
||||||
It is the `1` at the end of the announce line that should match the file name.
|
It is the `1` at the end of the announce line that should match the file name.
|
||||||
|
|
||||||
|
## RSS Feed
|
||||||
|
|
||||||
|
You can use the mockindexer as an RSS feed as well. Place a complete XML feed in `./feeds` and name it something like `mock.xml`.
|
||||||
|
|
||||||
|
In autobrr to set up the feed you use the url like `http://localhost:3999/feeds/mock` where the last part is the name of the xml file without extension.
|
||||||
|
|
||||||
|
## Webhook
|
||||||
|
|
||||||
|
The mockindexer can also be used as a simple webhook endpoint. Use it with a method `POST` to `http://localhost:3999/webhook`.
|
||||||
|
|
||||||
|
You can trigger different behavior by appending the following URL parameters.
|
||||||
|
|
||||||
|
- `timeout=2` - wait for 2 seconds to respond
|
||||||
|
- `status=500` - respond with status 500
|
||||||
|
|
||||||
|
Use it like `http://localhost:3999/webhook?timeout=2&status=500`.
|
|
@ -49,7 +49,7 @@ func (c *Client) readerLoop() {
|
||||||
line := scanner.Text()
|
line := scanner.Text()
|
||||||
cmd := strings.Split(line, " ")
|
cmd := strings.Split(line, " ")
|
||||||
|
|
||||||
log.Printf("%s", scanner.Text())
|
log.Printf("--> %s", scanner.Text())
|
||||||
|
|
||||||
c.handler(c, cmd)
|
c.handler(c, cmd)
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,7 @@ func (c *Client) readerLoop() {
|
||||||
|
|
||||||
func (c *Client) writerLoop() {
|
func (c *Client) writerLoop() {
|
||||||
for cmd := range c.writer {
|
for cmd := range c.writer {
|
||||||
|
log.Printf("<-- %s", []byte(cmd+"\r\n"))
|
||||||
c.conn.Write([]byte(cmd + "\r\n"))
|
c.conn.Write([]byte(cmd + "\r\n"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegistrationHandler(c *Client, cmd []string) {
|
func RegistrationHandler(c *Client, cmd []string) {
|
||||||
|
@ -23,21 +24,35 @@ func RegistrationHandler(c *Client, cmd []string) {
|
||||||
|
|
||||||
c.handler = CommandHandler
|
c.handler = CommandHandler
|
||||||
|
|
||||||
c.writer <- fmt.Sprintf(
|
c.writer <- fmt.Sprintf(":localhost 001 %s :Welcome %s", c.nick, c.nick)
|
||||||
"001 %s :\r\n002 %s :\r\n003 %s :\r\n004 %s n n-d o o\r\n251 %s :\r\n422 %s :",
|
c.writer <- fmt.Sprintf(":localhost 002 %s :Your host is localhost, running mock-irc-0.0.1", c.nick)
|
||||||
c.nick, c.nick, c.nick, c.nick, c.nick, c.nick)
|
c.writer <- fmt.Sprintf(":localhost 003 %s :This server was created %s", c.nick, time.Now().String())
|
||||||
|
c.writer <- fmt.Sprintf(":localhost 004 %s localhost mock-irc-0.0.1 o o o", c.nick)
|
||||||
|
c.writer <- fmt.Sprintf(":localhost 251 %s :there are 1 users on 1 server", c.nick)
|
||||||
|
c.writer <- fmt.Sprintf(":localhost 422 %s :MOTD File is missing", c.nick)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func CommandHandler(c *Client, cmd []string) {
|
func CommandHandler(c *Client, cmd []string) {
|
||||||
|
log.Printf("cmd: %+v", cmd)
|
||||||
switch cmd[0] {
|
switch cmd[0] {
|
||||||
|
case "CAP":
|
||||||
|
log.Printf("caps: %+v", cmd)
|
||||||
case "JOIN":
|
case "JOIN":
|
||||||
c.writer <- fmt.Sprintf("331 %s %s :No topic", c.nick, c.channelName)
|
c.writer <- fmt.Sprintf(":localhost 221 %s +Zi", c.nick)
|
||||||
c.writer <- fmt.Sprintf("353 %s = %s :%s %s", c.nick, c.channelName, c.nick, c.botName)
|
c.writer <- fmt.Sprintf(":localhost 331 %s %s :No topic", c.nick, c.channelName)
|
||||||
c.writer <- fmt.Sprintf("366 %s %s :End", c.nick, c.channelName)
|
c.writer <- fmt.Sprintf(":localhost 353 %s = %s :%s %s", c.nick, c.channelName, c.nick, c.botName)
|
||||||
|
c.writer <- fmt.Sprintf(":localhost 366 %s %s :End of NAMES list", c.nick, c.channelName)
|
||||||
case "PING":
|
case "PING":
|
||||||
c.writer <- fmt.Sprintf("PONG n %s", strings.Join(cmd[1:], " "))
|
c.writer <- fmt.Sprintf(":localhost PONG localhost %s", strings.Join(cmd[1:], " "))
|
||||||
case "PRIVMSG":
|
case "PRIVMSG":
|
||||||
c.writer <- fmt.Sprintf("%s PRIVMSG %s %s", fmt.Sprintf(":%s", c.nick), cmd[1], fmt.Sprintf("%s", strings.Join(cmd[2:], " ")))
|
c.writer <- fmt.Sprintf(":localhost %s PRIVMSG %s %s", fmt.Sprintf(":%s", c.nick), cmd[1], fmt.Sprintf("%s", strings.Join(cmd[2:], " ")))
|
||||||
|
case "QUIT":
|
||||||
|
c.writer <- fmt.Sprintf(":%s!%s@localhost QUIT :Quit%s", c.nick, c.nick, strings.Join(cmd[1:], " "))
|
||||||
|
//c.writer <- fmt.Sprintf(":localhost %s!%s@localhost QUIT :Quit%s", c.nick, c.nick, strings.Join(cmd[1:], " "))
|
||||||
|
//c.writer <- fmt.Sprintf(":localhost :%s@localhost QUIT :Quit%s", c.nick, strings.Join(cmd[1:], " "))
|
||||||
|
c.writer <- fmt.Sprintf("ERROR :Quit%s", strings.Join(cmd[1:], " "))
|
||||||
|
case "ERROR":
|
||||||
|
c.writer <- fmt.Sprintf("ERROR :Quit%s", strings.Join(cmd[1:], " "))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,12 +21,13 @@ type ServerOptions struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(options *ServerOptions) (*Server, error) {
|
func NewServer(options *ServerOptions) (*Server, error) {
|
||||||
listener, err := net.Listen("tcp", "localhost:6697")
|
listener, err := net.Listen("tcp", ":6697")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("IRC server running on %q", listener.Addr())
|
||||||
|
|
||||||
return &Server{
|
return &Server{
|
||||||
listener: listener,
|
listener: listener,
|
||||||
options: options,
|
options: options,
|
||||||
|
@ -36,7 +37,6 @@ func NewServer(options *ServerOptions) (*Server, error) {
|
||||||
func (s *Server) Run() {
|
func (s *Server) Run() {
|
||||||
for {
|
for {
|
||||||
conn, err := s.listener.Accept()
|
conn, err := s.listener.Accept()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed accept: %v", err)
|
log.Printf("Failed accept: %v", err)
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -5,64 +5,325 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/autobrr/autobrr/test/mockindexer/irc"
|
"github.com/autobrr/autobrr/test/mockindexer/irc"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/chi/v5/middleware"
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
|
"github.com/go-chi/render"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
s, err := irc.NewServer(&irc.ServerOptions{
|
var (
|
||||||
BotName: "_AnnounceBot_",
|
port int
|
||||||
Channel: "#announces",
|
channel string
|
||||||
})
|
announcer string
|
||||||
|
)
|
||||||
|
|
||||||
|
pflag.IntVarP(&port, "port", "p", 3999, "http port. Default: 3999")
|
||||||
|
pflag.StringVar(&channel, "irc-channel", "#announces", "Announce channel. Default: #announces")
|
||||||
|
pflag.StringVar(&announcer, "irc-announcer", "_AnnounceBot_", "Announcer. Default: _AnnounceBot_")
|
||||||
|
|
||||||
|
pflag.Parse()
|
||||||
|
|
||||||
|
log.Print("MockIndexer starting..")
|
||||||
|
|
||||||
|
options := &irc.ServerOptions{
|
||||||
|
BotName: announcer,
|
||||||
|
Channel: channel,
|
||||||
|
}
|
||||||
|
|
||||||
|
ircServer, err := irc.NewServer(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Err: %v", err)
|
log.Fatalf("Err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
go s.Run()
|
go ircServer.Run()
|
||||||
|
|
||||||
log.Print("autobrr MockIndexer running")
|
api := NewAPI(ircServer)
|
||||||
|
|
||||||
|
sigCh := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
if err := api.ListenAndServe(fmt.Sprintf(":%d", port)); err != nil {
|
||||||
|
log.Fatalf("could not start mock indexer api server, %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for sig := range sigCh {
|
||||||
|
log.Printf("received signal: %v", sig)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Api struct {
|
||||||
|
router *chi.Mux
|
||||||
|
|
||||||
|
ircServer *irc.Server
|
||||||
|
|
||||||
|
announces []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAPI(ircServer *irc.Server) *Api {
|
||||||
|
a := &Api{
|
||||||
|
ircServer: ircServer,
|
||||||
|
announces: make([]string, 0),
|
||||||
|
}
|
||||||
|
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.Use(middleware.Logger)
|
r.Use(middleware.Logger)
|
||||||
|
|
||||||
r.Post("/webhook", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/", a.indexHandler)
|
||||||
time.Sleep(30 * time.Second)
|
r.Post("/send", a.formHandler)
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
})
|
|
||||||
|
|
||||||
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/file/{fileId}/{fileName}", a.fileDownloadHandler)
|
||||||
w.Write([]byte("<html><form method=\"POST\" action=\"/send\">Send an announce line to the channel<br><input style=\"width: 100%; margin-top: 5px; margin-bottom: 5px;\" name=\"line\" type=\"text\"><br><button type=\"submit\">Send to channel</button></form></html>"))
|
r.Get("/torrent/download/{torrentId}", a.torrentDownloadHandler)
|
||||||
})
|
r.Get("/feeds/{name}", a.feedHandler)
|
||||||
|
r.Post("/webhook", a.webhookHandler)
|
||||||
|
|
||||||
r.Get("/file/{fileId}/{fileName}", func(w http.ResponseWriter, r *http.Request) {
|
a.router = r
|
||||||
f, err := os.Open("files/" + chi.URLParam(r, "fileId") + ".torrent")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Err: %v", err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
w.Header().Set("Content-Disposition", "attachment; filename="+chi.URLParam(r, "fileName"))
|
return a
|
||||||
w.Header().Set("Content-Type", "application/x-bittorrent")
|
|
||||||
|
|
||||||
io.Copy(w, bufio.NewReader(f))
|
|
||||||
})
|
|
||||||
|
|
||||||
r.Post("/send", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
r.ParseForm()
|
|
||||||
|
|
||||||
line := r.Form.Get("line")
|
|
||||||
s.SendAll(line)
|
|
||||||
|
|
||||||
http.Redirect(w, r, "/", 302)
|
|
||||||
})
|
|
||||||
|
|
||||||
http.ListenAndServe("localhost:3999", r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Api) ListenAndServe(addr string) error {
|
||||||
|
go func() {
|
||||||
|
if err := http.ListenAndServe(addr, a.router); err != nil {
|
||||||
|
log.Printf("err: %q", err)
|
||||||
|
//return err
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
log.Printf("API running on %q", addr)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// formHandler takes in an HTTP response writer and request and handles the logic
|
||||||
|
// for processing form data. It parses the form data using r.ParseForm() and checks for any errors.
|
||||||
|
// If there is an error parsing the form data, it returns an internal server error response.
|
||||||
|
// It then retrieves the value of the "line" form field using r.Form.Get("line").
|
||||||
|
// If the "line" value is not empty, it appends it to the "announces" slice
|
||||||
|
// and sends the line to all clients using a.a.ircServer.SendAll(line).
|
||||||
|
// Finally, it redirects the user to the index page ("/") with a 302 status code.
|
||||||
|
func (a *Api) formHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if err := r.ParseForm(); err != nil {
|
||||||
|
render.Status(r, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
line := r.Form.Get("line")
|
||||||
|
if line != "" {
|
||||||
|
a.announces = append(a.announces, line)
|
||||||
|
|
||||||
|
a.ircServer.SendAll(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
http.Redirect(w, r, "/", 302)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Api) indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tmpl, err := template.New("body").Parse(htmlBody)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := struct {
|
||||||
|
Lines []string
|
||||||
|
}{
|
||||||
|
Lines: a.announces,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tmpl.Execute(w, data)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// feedHandler takes in an HTTP response writer and request, and handles the logic
|
||||||
|
// for retrieving and serving an RSS feed based on the provided 'name' URL parameter.
|
||||||
|
// If the 'name' parameter is missing, it returns a bad request response.
|
||||||
|
// If there is an error reading the feed file, it returns an internal server error response.
|
||||||
|
// It sets the 'Content-Type' header of the response to 'application/rss+xml'.
|
||||||
|
// It writes the feed payload to the response body.
|
||||||
|
func (a *Api) feedHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
name := chi.URLParam(r, "name")
|
||||||
|
if name == "" {
|
||||||
|
render.Status(r, http.StatusBadRequest)
|
||||||
|
render.PlainText(w, r, "bad request: missing param name")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open("feeds/" + name + ".xml")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Err: %v", err)
|
||||||
|
render.Status(r, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/rss+xml")
|
||||||
|
io.Copy(w, bufio.NewReader(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
// fileDownloadHandler takes in an HTTP response writer and request, and handles the logic
|
||||||
|
// for retrieving and serving a file based on the provided 'fileId' and 'fileName' URL parameters.
|
||||||
|
// If either parameter is missing, it returns a bad request response.
|
||||||
|
// It opens the file with the corresponding fileId and ".torrent" extension.
|
||||||
|
// If there is an error opening the file, it logs the error and terminates the program.
|
||||||
|
// It sets the 'Content-Disposition' header of the response to indicate the file should be downloaded with the provided fileName.
|
||||||
|
// It sets the 'Content-Type' header of the response to 'application/x-bittorrent'.
|
||||||
|
// It copies the file contents to the response body.
|
||||||
|
func (a *Api) fileDownloadHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fileIdParam := chi.URLParam(r, "fileId")
|
||||||
|
if fileIdParam == "" {
|
||||||
|
render.Status(r, http.StatusBadRequest)
|
||||||
|
render.PlainText(w, r, "bad request: missing param fileId")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fileNameParam := chi.URLParam(r, "fileName")
|
||||||
|
if fileNameParam == "" {
|
||||||
|
render.Status(r, http.StatusBadRequest)
|
||||||
|
render.PlainText(w, r, "bad request: missing param fileName")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open("files/" + fileIdParam + ".torrent")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Err: %v", err)
|
||||||
|
render.Status(r, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
w.Header().Set("Content-Disposition", "attachment; filename="+fileNameParam)
|
||||||
|
w.Header().Set("Content-Type", "application/x-bittorrent")
|
||||||
|
|
||||||
|
io.Copy(w, bufio.NewReader(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
// torrentDownloadHandler takes in an HTTP response writer and request, and handles the logic
|
||||||
|
// for retrieving and serving a file based on the provided 'torrentId' URL parameters.
|
||||||
|
// If either parameter is missing, it returns a bad request response.
|
||||||
|
// It opens the file with the corresponding fileId and ".torrent" extension.
|
||||||
|
// If there is an error opening the file, it logs the error and terminates the program.
|
||||||
|
// It sets the 'Content-Disposition' header of the response to indicate the file should be downloaded with the provided fileName.
|
||||||
|
// It sets the 'Content-Type' header of the response to 'application/x-bittorrent'.
|
||||||
|
// It copies the file contents to the response body.
|
||||||
|
func (a *Api) torrentDownloadHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fileIdParam := chi.URLParam(r, "torrentId")
|
||||||
|
if fileIdParam == "" {
|
||||||
|
render.Status(r, http.StatusBadRequest)
|
||||||
|
render.PlainText(w, r, "bad request: missing param fileId")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open("files/" + fileIdParam + ".torrent")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Err: %v", err)
|
||||||
|
render.Status(r, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
w.Header().Set("Content-Disposition", "attachment; filename="+fileIdParam+".torrent")
|
||||||
|
w.Header().Set("Content-Type", "application/x-bittorrent")
|
||||||
|
|
||||||
|
io.Copy(w, bufio.NewReader(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
// webhookHandler takes in an HTTP response writer and request, and handles the logic
|
||||||
|
// for processing webhook requests.
|
||||||
|
// If the 'timeout' query parameter is provided, it sleeps for the specified duration.
|
||||||
|
// If the 'status' query parameter is provided, it sets the response status to the provided value.
|
||||||
|
// If none of the query parameters are provided, it sets the response status to http.StatusOK by default.
|
||||||
|
func (a *Api) webhookHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
body, err := httputil.DumpRequest(r, true)
|
||||||
|
if err != nil {
|
||||||
|
render.Status(r, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println(string(body))
|
||||||
|
|
||||||
|
if timeout := r.URL.Query().Get("timeout"); timeout != "" {
|
||||||
|
t, err := strconv.Atoi(timeout)
|
||||||
|
if err != nil || t <= 0 || t > 60 { // Set a maximum limit for timeout
|
||||||
|
render.Status(r, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(time.Duration(t) * time.Second) // Changed t to time.Duration(t) to match type
|
||||||
|
}
|
||||||
|
|
||||||
|
if status := r.URL.Query().Get("status"); status != "" {
|
||||||
|
s, err := strconv.Atoi(status)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Err: %v", err)
|
||||||
|
render.Status(r, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Status(r, s)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Status(r, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
var htmlBody = `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="min-h-full">
|
||||||
|
<div class="py-10">
|
||||||
|
<main>
|
||||||
|
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
|
||||||
|
<!-- Your content -->
|
||||||
|
<form method="POST" action="/send">
|
||||||
|
Send an announce line to the channel<br>
|
||||||
|
<div class="flex">
|
||||||
|
<input style="width: 100%; margin-top: 5px; margin-bottom: 5px;" name="line" type="text"
|
||||||
|
class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" />
|
||||||
|
<button type="submit"
|
||||||
|
class="rounded bg-indigo-600 ml-2 px-2 py-1 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">
|
||||||
|
Send
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{{range .Lines}}
|
||||||
|
<div class="mb-2">
|
||||||
|
<form method="POST" action="/send" class="truncate">
|
||||||
|
<button type="submit"
|
||||||
|
class="rounded bg-white px-2 py-1 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50">
|
||||||
|
Re-send
|
||||||
|
</button>
|
||||||
|
<label for="line">{{.}}</label>
|
||||||
|
<input name="line" id="line" value="{{.}}" hidden />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue