mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-07 13:00:59 +08:00
Security: - Remove /api/crypto/decrypt from public routes. The endpoint allowed anyone to decrypt ciphertext without authentication. Internal callers (exchange/model handlers) use the service directly and are behind auth. Reliability: - Add safe.Go / safe.GoNamed panic recovery wrapper (safe/go.go). Previously 31 goroutines had zero recover() calls — a single panic in any trader goroutine would crash the entire process. - Apply safe.GoNamed to all trader launch paths: - StartAll, RestoreRunning, LoadSingleTrader auto-start - API handler start/restart endpoints - Panics are now logged with full stack traces instead of crashing.
60 lines
1.6 KiB
Go
60 lines
1.6 KiB
Go
// Package safe provides panic-recovery wrappers for goroutines.
|
|
// A panic in any bare goroutine tears down the entire process.
|
|
// Use safe.Go instead of `go func()` in long-running or critical paths.
|
|
package safe
|
|
|
|
import (
|
|
"fmt"
|
|
"nofx/logger"
|
|
"runtime/debug"
|
|
)
|
|
|
|
// Go launches fn in a new goroutine with automatic panic recovery.
|
|
// If fn panics, the panic is logged (with stack trace) but the process
|
|
// continues running. An optional onPanic callback receives the recovered value.
|
|
func Go(fn func(), onPanic ...func(recovered interface{})) {
|
|
go func() {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
stack := string(debug.Stack())
|
|
logger.Errorf("🔥 goroutine panic recovered: %v\n%s", r, stack)
|
|
|
|
for _, cb := range onPanic {
|
|
func() {
|
|
defer func() {
|
|
if r2 := recover(); r2 != nil {
|
|
logger.Errorf("🔥 onPanic callback itself panicked: %v", r2)
|
|
}
|
|
}()
|
|
cb(r)
|
|
}()
|
|
}
|
|
}
|
|
}()
|
|
fn()
|
|
}()
|
|
}
|
|
|
|
// GoNamed is like Go but tags the log line with a human-readable name.
|
|
func GoNamed(name string, fn func(), onPanic ...func(recovered interface{})) {
|
|
Go(func() {
|
|
fn()
|
|
}, append([]func(interface{}){
|
|
func(r interface{}) {
|
|
logger.Errorf("🔥 [%s] goroutine panicked: %v", name, r)
|
|
},
|
|
}, onPanic...)...)
|
|
}
|
|
|
|
// Must converts a panic into an error. Useful inside goroutines where you
|
|
// want to handle panics as errors in the caller's recovery flow.
|
|
func Must(fn func()) (err error) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
err = fmt.Errorf("panic: %v\n%s", r, debug.Stack())
|
|
}
|
|
}()
|
|
fn()
|
|
return nil
|
|
}
|