@@ -12,6 +12,7 @@ import (
1212 "net/url"
1313 "os"
1414 "path/filepath"
15+ "runtime/debug"
1516 "strconv"
1617 "strings"
1718 "time"
@@ -43,6 +44,25 @@ type MyClient struct {
4344 s * server
4445}
4546
47+ // safeGo runs fn in a new goroutine with a defer recover so a panic inside
48+ // fire-and-forget side-effects (webhook delivery, MQ push) cannot crash
49+ // the whole process. Losing one delivery is preferable to taking wuzapi
50+ // down for every connected user.
51+ func safeGo (name string , fn func ()) {
52+ go func () {
53+ defer func () {
54+ if r := recover (); r != nil {
55+ log .Error ().
56+ Str ("goroutine" , name ).
57+ Interface ("panic" , r ).
58+ Str ("stack" , string (debug .Stack ())).
59+ Msg ("panic recovered in goroutine" )
60+ }
61+ }()
62+ fn ()
63+ }()
64+ }
65+
4666// ensureS3ClientForUser loads S3 config from DB and initializes client if not already present (lazy init for reconnect-after-restart)
4767func ensureS3ClientForUser (userID string ) {
4868 GetS3Manager ().EnsureClientFromDB (userID )
@@ -92,17 +112,9 @@ func sendToUserWebHookWithHmac(webhookurl string, path string, jsonData []byte,
92112 log .Info ().Str ("url" , webhookurl ).Msg ("Calling user webhook" )
93113
94114 if path == "" {
95- go callHookWithHmac (webhookurl , data , userID , encryptedHmacKey )
115+ safeGo ( "callHookWithHmac" , func () { callHookWithHmac (webhookurl , data , userID , encryptedHmacKey ) } )
96116 } else {
97- // Create a channel to capture the error from the goroutine
98- errChan := make (chan error , 1 )
99- go func () {
100- err := callHookFileWithHmac (webhookurl , data , userID , path , encryptedHmacKey )
101- errChan <- err
102- }()
103-
104- // Optionally handle the error from the channel (if needed)
105- if err := <- errChan ; err != nil {
117+ if err := callHookFileWithHmac (webhookurl , data , userID , path , encryptedHmacKey ); err != nil {
106118 log .Error ().Err (err ).Msg ("Error calling hook file" )
107119 }
108120 }
@@ -213,9 +225,9 @@ func sendEventWithWebHook(mycli *MyClient, postmap map[string]interface{}, path
213225 sendToUserWebHookWithHmac (webhookurl , path , jsonData , mycli .userID , mycli .token , encryptedHmacKey )
214226
215227 // Get global webhook if configured
216- go sendToGlobalWebHook (jsonData , mycli .token , mycli .userID )
228+ safeGo ( "sendToGlobalWebHook" , func () { sendToGlobalWebHook (jsonData , mycli .token , mycli .userID ) } )
217229
218- go sendToGlobalRabbit (jsonData , mycli .token , mycli .userID )
230+ safeGo ( "sendToGlobalRabbit" , func () { sendToGlobalRabbit (jsonData , mycli .token , mycli .userID ) } )
219231}
220232
221233func checkIfSubscribedToEvent (subscribedEvents []string , eventType string , userId string ) bool {
0 commit comments