You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
218 lines
8.2 KiB
218 lines
8.2 KiB
package redpacket
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"math/big"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
|
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
|
pbredpacket "github.com/openimsdk/protocol/redpacket"
|
|
"github.com/openimsdk/tools/errs"
|
|
"github.com/openimsdk/tools/log"
|
|
"github.com/openimsdk/tools/mcontext"
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
)
|
|
|
|
// checkAdminPermission is a convenience wrapper used by every admin handler.
|
|
func (s *redPacketServer) checkAdminPermission(ctx context.Context) error {
|
|
return authverify.CheckAdmin(ctx, s.config.Share.IMAdminUserID)
|
|
}
|
|
|
|
// recordAudit persists an admin audit entry asynchronously; errors are only
|
|
// logged so they never block the primary operation.
|
|
func (s *redPacketServer) recordAudit(ctx context.Context, action string, req interface{}, opErr error) {
|
|
params := ""
|
|
if b, err := json.Marshal(req); err == nil {
|
|
params = string(b)
|
|
}
|
|
result := "success"
|
|
errMsg := ""
|
|
if opErr != nil {
|
|
result = "failed"
|
|
errMsg = opErr.Error()
|
|
}
|
|
entry := &model.AdminAuditLog{
|
|
ID: primitive.NewObjectID(),
|
|
OperatorID: mcontext.GetOpUserID(ctx),
|
|
Action: action,
|
|
Params: params,
|
|
Result: result,
|
|
ErrMsg: errMsg,
|
|
CreatedAt: time.Now().UTC(),
|
|
}
|
|
if err := s.db.CreateAdminAuditLog(ctx, entry); err != nil {
|
|
log.ZWarn(ctx, "redpacket admin audit log write failed", err, "action", action)
|
|
}
|
|
}
|
|
|
|
func (s *redPacketServer) SetSigner(ctx context.Context, req *pbredpacket.SetSignerReq) (resp *pbredpacket.SetSignerResp, retErr error) {
|
|
defer func() { s.recordAudit(ctx, "SetSigner", req, retErr) }()
|
|
if err := s.checkAdminPermission(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
if req.SignerAddress == "" {
|
|
return nil, errs.ErrArgs.WrapMsg("signer_address is required")
|
|
}
|
|
if s.chainClient != nil {
|
|
log.ZInfo(ctx, "redpacket admin setSigner (eth mock)", "signerAddress", req.SignerAddress)
|
|
return &pbredpacket.SetSignerResp{Message: "signer address updated successfully"}, nil
|
|
}
|
|
if s.tronClient != nil {
|
|
if _, err := s.tronClient.SendAdminTransaction(ctx, "setSigner", req.SignerAddress); err != nil {
|
|
return nil, errs.ErrInternalServer.WrapMsg("setSigner failed: " + err.Error())
|
|
}
|
|
return &pbredpacket.SetSignerResp{Message: "signer address updated successfully"}, nil
|
|
}
|
|
return nil, errs.ErrInternalServer.WrapMsg("no blockchain client configured")
|
|
}
|
|
|
|
func (s *redPacketServer) SetToken(ctx context.Context, req *pbredpacket.SetTokenReq) (resp *pbredpacket.SetTokenResp, retErr error) {
|
|
defer func() { s.recordAudit(ctx, "SetToken", req, retErr) }()
|
|
if err := s.checkAdminPermission(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
if req.TokenAddress == "" {
|
|
return nil, errs.ErrArgs.WrapMsg("token_address is required")
|
|
}
|
|
|
|
minAmountBig := new(big.Int)
|
|
if req.MinAmount != "" {
|
|
if _, ok := minAmountBig.SetString(req.MinAmount, 10); !ok {
|
|
return nil, errs.ErrArgs.WrapMsg("invalid min_amount", "minAmount", req.MinAmount)
|
|
}
|
|
}
|
|
|
|
if s.chainClient != nil {
|
|
log.ZInfo(ctx, "redpacket admin setToken (eth mock)",
|
|
"tokenAddress", req.TokenAddress,
|
|
"allowed", req.Allowed,
|
|
"minAmount", req.MinAmount,
|
|
)
|
|
return &pbredpacket.SetTokenResp{Message: "token configuration updated"}, nil
|
|
}
|
|
if s.tronClient != nil {
|
|
if _, err := s.tronClient.SendAdminTransaction(ctx, "setAllowedToken", req.TokenAddress, req.Allowed, minAmountBig); err != nil {
|
|
return nil, errs.ErrInternalServer.WrapMsg("setAllowedToken failed: " + err.Error())
|
|
}
|
|
return &pbredpacket.SetTokenResp{Message: "token configuration updated"}, nil
|
|
}
|
|
return nil, errs.ErrInternalServer.WrapMsg("no blockchain client configured")
|
|
}
|
|
|
|
func (s *redPacketServer) SetExpiry(ctx context.Context, req *pbredpacket.SetExpiryReq) (resp *pbredpacket.SetExpiryResp, retErr error) {
|
|
defer func() { s.recordAudit(ctx, "SetExpiry", req, retErr) }()
|
|
if err := s.checkAdminPermission(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
if req.ExpirySeconds <= 0 {
|
|
return nil, errs.ErrArgs.WrapMsg("expiry_seconds must be positive")
|
|
}
|
|
if s.chainClient != nil {
|
|
log.ZInfo(ctx, "redpacket admin setExpiry (eth mock)", "expirySeconds", req.ExpirySeconds)
|
|
return &pbredpacket.SetExpiryResp{Message: "expiry duration updated"}, nil
|
|
}
|
|
if s.tronClient != nil {
|
|
if _, err := s.tronClient.SendAdminTransaction(ctx, "setDefaultExpiryDuration", req.ExpirySeconds); err != nil {
|
|
return nil, errs.ErrInternalServer.WrapMsg("setDefaultExpiryDuration failed: " + err.Error())
|
|
}
|
|
return &pbredpacket.SetExpiryResp{Message: "expiry duration updated"}, nil
|
|
}
|
|
return nil, errs.ErrInternalServer.WrapMsg("no blockchain client configured")
|
|
}
|
|
|
|
func (s *redPacketServer) SetAllowAllTokens(ctx context.Context, req *pbredpacket.SetAllowAllTokensReq) (resp *pbredpacket.SetAllowAllTokensResp, retErr error) {
|
|
defer func() { s.recordAudit(ctx, "SetAllowAllTokens", req, retErr) }()
|
|
if err := s.checkAdminPermission(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
if s.chainClient != nil {
|
|
log.ZInfo(ctx, "redpacket admin setAllowAllTokens (eth mock)", "allowAll", req.AllowAll)
|
|
return &pbredpacket.SetAllowAllTokensResp{Message: "allow all tokens setting updated"}, nil
|
|
}
|
|
if s.tronClient != nil {
|
|
if _, err := s.tronClient.SendAdminTransaction(ctx, "setAllowAllTokens", req.AllowAll); err != nil {
|
|
return nil, errs.ErrInternalServer.WrapMsg("setAllowAllTokens failed: " + err.Error())
|
|
}
|
|
return &pbredpacket.SetAllowAllTokensResp{Message: "allow all tokens setting updated"}, nil
|
|
}
|
|
return nil, errs.ErrInternalServer.WrapMsg("no blockchain client configured")
|
|
}
|
|
|
|
func (s *redPacketServer) SetNativeTokenEnabled(ctx context.Context, req *pbredpacket.SetNativeTokenEnabledReq) (resp *pbredpacket.SetNativeTokenEnabledResp, retErr error) {
|
|
defer func() { s.recordAudit(ctx, "SetNativeTokenEnabled", req, retErr) }()
|
|
if err := s.checkAdminPermission(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
if s.chainClient != nil {
|
|
log.ZInfo(ctx, "redpacket admin setNativeTokenEnabled (eth mock)", "enabled", req.Enabled)
|
|
return &pbredpacket.SetNativeTokenEnabledResp{Message: "native token setting updated"}, nil
|
|
}
|
|
if s.tronClient != nil {
|
|
if _, err := s.tronClient.SendAdminTransaction(ctx, "setNativeTokenEnabled", req.Enabled); err != nil {
|
|
return nil, errs.ErrInternalServer.WrapMsg("setNativeTokenEnabled failed: " + err.Error())
|
|
}
|
|
return &pbredpacket.SetNativeTokenEnabledResp{Message: "native token setting updated"}, nil
|
|
}
|
|
return nil, errs.ErrInternalServer.WrapMsg("no blockchain client configured")
|
|
}
|
|
|
|
func (s *redPacketServer) ParseTxEvents(ctx context.Context, req *pbredpacket.ParseTxEventsReq) (resp *pbredpacket.ParseTxEventsResp, retErr error) {
|
|
defer func() { s.recordAudit(ctx, "ParseTxEvents", req, retErr) }()
|
|
if err := s.checkAdminPermission(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
if req.TxHash == "" {
|
|
return nil, errs.ErrArgs.WrapMsg("tx_hash is required")
|
|
}
|
|
|
|
if req.Chain == "tron" {
|
|
if s.tronClient == nil {
|
|
return nil, errs.ErrInternalServer.WrapMsg("TRON client not configured")
|
|
}
|
|
events, err := s.tronClient.ParseTransactionReceipt(ctx, req.TxHash)
|
|
if err != nil {
|
|
return nil, errs.ErrInternalServer.WrapMsg("parse TRON tx receipt failed: " + err.Error())
|
|
}
|
|
out := make([]*pbredpacket.ParsedEvent, 0, len(events))
|
|
for _, e := range events {
|
|
data := make(map[string]string, len(e.Data))
|
|
for k, v := range e.Data {
|
|
data[k] = fmt.Sprintf("%v", v)
|
|
}
|
|
out = append(out, &pbredpacket.ParsedEvent{Name: e.Name, Data: data})
|
|
}
|
|
return &pbredpacket.ParseTxEventsResp{Chain: "tron", TxHash: req.TxHash, Events: out}, nil
|
|
}
|
|
|
|
if s.chainClient != nil {
|
|
txHashBytes := common.HexToHash(req.TxHash)
|
|
events, err := s.chainClient.ParseTransactionReceipt(ctx, txHashBytes)
|
|
if err != nil {
|
|
return nil, errs.ErrInternalServer.WrapMsg("parse tx receipt failed: " + err.Error())
|
|
}
|
|
|
|
out := make([]*pbredpacket.ParsedEvent, 0, len(events))
|
|
for _, e := range events {
|
|
data := make(map[string]string, len(e.Data))
|
|
for k, v := range e.Data {
|
|
data[k] = fmt.Sprintf("%v", v)
|
|
}
|
|
out = append(out, &pbredpacket.ParsedEvent{
|
|
Name: e.Name,
|
|
Data: data,
|
|
})
|
|
}
|
|
return &pbredpacket.ParseTxEventsResp{
|
|
Chain: "eth",
|
|
TxHash: req.TxHash,
|
|
Events: out,
|
|
}, nil
|
|
}
|
|
|
|
return nil, errs.ErrInternalServer.WrapMsg("no client available for chain: " + req.Chain)
|
|
}
|