241 lines
6.2 KiB
Go
241 lines
6.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"text/tabwriter"
|
|
"time"
|
|
|
|
"git.warky.dev/wdevs/whatshooked/pkg/models"
|
|
"git.warky.dev/wdevs/whatshooked/pkg/storage"
|
|
resolvespec_common "github.com/bitechdev/ResolveSpec/pkg/spectypes"
|
|
"github.com/google/uuid"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var hookUser string
|
|
|
|
var hooksCmd = &cobra.Command{
|
|
Use: "hooks",
|
|
Short: "Manage webhooks",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
client := NewClient(cliConfig)
|
|
listHooks(client)
|
|
},
|
|
}
|
|
|
|
var hooksListCmd = &cobra.Command{
|
|
Use: "list",
|
|
Short: "List all hooks",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
client := NewClient(cliConfig)
|
|
listHooks(client)
|
|
},
|
|
}
|
|
|
|
var hooksAddCmd = &cobra.Command{
|
|
Use: "add",
|
|
Short: "Add a new hook",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
client := NewClient(cliConfig)
|
|
addHook(client)
|
|
},
|
|
}
|
|
|
|
var hooksRemoveCmd = &cobra.Command{
|
|
Use: "remove <hook_id>",
|
|
Short: "Remove a hook by ID",
|
|
Args: cobra.ExactArgs(1),
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
client := NewClient(cliConfig)
|
|
removeHook(client, args[0])
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
hooksAddCmd.Flags().StringVarP(&hookUser, "user", "u", "", "Owner username for DB mode (default: first admin)")
|
|
hooksCmd.AddCommand(hooksListCmd)
|
|
hooksCmd.AddCommand(hooksAddCmd)
|
|
hooksCmd.AddCommand(hooksRemoveCmd)
|
|
}
|
|
|
|
func listHooks(client *Client) {
|
|
if serverAvailable(client) {
|
|
listHooksHTTP(client)
|
|
} else {
|
|
fmt.Println("[server unavailable, reading from database]")
|
|
if !tryInitDB() {
|
|
fmt.Println("Error: server unreachable and no database config found. Use --server-config to specify config path.")
|
|
return
|
|
}
|
|
listHooksDB()
|
|
}
|
|
}
|
|
|
|
func listHooksHTTP(client *Client) {
|
|
resp, err := client.Get("/api/hooks")
|
|
checkError(err)
|
|
defer resp.Body.Close()
|
|
|
|
var hooks []map[string]interface{}
|
|
checkError(decodeJSON(resp, &hooks))
|
|
|
|
if len(hooks) == 0 {
|
|
fmt.Println("No hooks configured")
|
|
return
|
|
}
|
|
|
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
|
fmt.Fprintln(w, "ID\tNAME\tURL\tMETHOD\tACTIVE")
|
|
for _, h := range hooks {
|
|
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\n",
|
|
h["id"], h["name"], h["url"], h["method"], h["active"])
|
|
}
|
|
w.Flush()
|
|
}
|
|
|
|
func listHooksDB() {
|
|
var hooks []models.ModelPublicHook
|
|
err := storage.DB.NewSelect().Model(&hooks).OrderExpr("created_at ASC").Scan(context.Background())
|
|
checkError(err)
|
|
|
|
if len(hooks) == 0 {
|
|
fmt.Println("No hooks configured")
|
|
return
|
|
}
|
|
|
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
|
fmt.Fprintln(w, "ID\tNAME\tURL\tMETHOD\tACTIVE\tEVENTS")
|
|
for _, h := range hooks {
|
|
active := "yes"
|
|
if !h.Active {
|
|
active = "no"
|
|
}
|
|
events := parseEventsJSON(h.Events.String())
|
|
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n",
|
|
h.ID.String(),
|
|
h.Name.String(),
|
|
h.URL.String(),
|
|
h.Method.String(),
|
|
active,
|
|
events,
|
|
)
|
|
}
|
|
w.Flush()
|
|
}
|
|
|
|
func addHook(client *Client) {
|
|
name := promptRequired("Hook Name")
|
|
url := promptRequired("Webhook URL")
|
|
method := promptLine("HTTP Method", "POST")
|
|
|
|
fmt.Println("\nAvailable events:")
|
|
fmt.Println(" whatsapp.connected, whatsapp.disconnected, whatsapp.qr.code")
|
|
fmt.Println(" message.received, message.sent, message.delivered, message.read")
|
|
fmt.Println(" hook.triggered, hook.success, hook.failed")
|
|
eventsRaw := promptLine("\nEvents (comma-separated, or Enter for all)", "")
|
|
description := promptLine("Description (optional)", "")
|
|
|
|
var events []string
|
|
if eventsRaw != "" {
|
|
for _, e := range strings.Split(eventsRaw, ",") {
|
|
if t := strings.TrimSpace(e); t != "" {
|
|
events = append(events, t)
|
|
}
|
|
}
|
|
}
|
|
|
|
if serverAvailable(client) {
|
|
addHookHTTP(client, name, url, method, description, events)
|
|
} else {
|
|
fmt.Println("[server unavailable, writing to database]")
|
|
if !tryInitDB() {
|
|
fmt.Println("Error: server unreachable and no database config found. Use --server-config to specify config path.")
|
|
return
|
|
}
|
|
addHookDB(name, url, method, description, events)
|
|
}
|
|
}
|
|
|
|
func addHookHTTP(client *Client, name, url, method, description string, events []string) {
|
|
payload := map[string]interface{}{
|
|
"name": name,
|
|
"url": url,
|
|
"method": method,
|
|
"description": description,
|
|
"events": events,
|
|
"active": true,
|
|
}
|
|
resp, err := client.Post("/api/hooks/add", payload)
|
|
checkError(err)
|
|
defer resp.Body.Close()
|
|
fmt.Println("Hook added successfully")
|
|
}
|
|
|
|
func addHookDB(name, url, method, description string, events []string) {
|
|
userID := dbOwnerUserID(hookUser)
|
|
if userID == "" {
|
|
fmt.Println("Error: no users found in database. Create a user first with: users add")
|
|
return
|
|
}
|
|
|
|
eventsJSON := "[]"
|
|
if len(events) > 0 {
|
|
b, _ := json.Marshal(events)
|
|
eventsJSON = string(b)
|
|
}
|
|
|
|
id := uuid.New().String()
|
|
now := time.Now()
|
|
hook := &models.ModelPublicHook{
|
|
ID: resolvespec_common.NewSqlString(id),
|
|
Name: resolvespec_common.NewSqlString(name),
|
|
URL: resolvespec_common.NewSqlString(url),
|
|
Method: resolvespec_common.NewSqlString(method),
|
|
Description: resolvespec_common.NewSqlString(description),
|
|
Events: resolvespec_common.NewSqlString(eventsJSON),
|
|
UserID: resolvespec_common.NewSqlString(userID),
|
|
Active: true,
|
|
CreatedAt: resolvespec_common.NewSqlTimeStamp(now),
|
|
UpdatedAt: resolvespec_common.NewSqlTimeStamp(now),
|
|
}
|
|
|
|
repo := storage.NewHookRepository(storage.DB)
|
|
checkError(repo.Create(context.Background(), hook))
|
|
|
|
fmt.Printf("Hook '%s' added (ID: %s)\n", name, id)
|
|
}
|
|
|
|
func removeHook(client *Client, id string) {
|
|
if serverAvailable(client) {
|
|
resp, err := client.Post("/api/hooks/remove", map[string]string{"id": id})
|
|
checkError(err)
|
|
defer resp.Body.Close()
|
|
fmt.Println("Hook removed successfully")
|
|
} else {
|
|
fmt.Println("[server unavailable, removing from database]")
|
|
if !tryInitDB() {
|
|
fmt.Println("Error: server unreachable and no database config found. Use --server-config to specify config path.")
|
|
return
|
|
}
|
|
repo := storage.NewHookRepository(storage.DB)
|
|
checkError(repo.Delete(context.Background(), id))
|
|
fmt.Printf("Hook '%s' removed\n", id)
|
|
}
|
|
}
|
|
|
|
// parseEventsJSON parses a JSON events string into a comma-separated display string.
|
|
func parseEventsJSON(raw string) string {
|
|
if raw == "" || raw == "[]" {
|
|
return "all"
|
|
}
|
|
var events []string
|
|
if err := json.Unmarshal([]byte(raw), &events); err != nil {
|
|
return raw
|
|
}
|
|
return strings.Join(events, ", ")
|
|
}
|