208
This commit is contained in:
@@ -6,8 +6,10 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
model "github.com/perfect-panel/server/internal/model/user"
|
||||
"github.com/perfect-panel/server/pkg/constant"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -31,6 +33,21 @@ func (w bodyLogWriter) Write(b []byte) (int, error) {
|
||||
return w.ResponseWriter.Write(b)
|
||||
}
|
||||
|
||||
// inviteCodeRegex matches invite code patterns in URLs like:
|
||||
// /v1/common/client/download/file/Hi快VPN-mac-1.0.0-ic-uuSo11uy.dmg
|
||||
// Matches: ic-XXXXX or ic_XXXXX before file extension
|
||||
var inviteCodeRegex = regexp.MustCompile(`[-_]ic[-_]([a-zA-Z0-9]+)\.[a-zA-Z0-9]+$`)
|
||||
|
||||
// extractInviteCode extracts invite code from URL path
|
||||
// Returns empty string if no invite code found
|
||||
func extractInviteCode(path string) string {
|
||||
matches := inviteCodeRegex.FindStringSubmatch(path)
|
||||
if len(matches) >= 2 {
|
||||
return matches[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// statusByWriter returns a span status code and message for an HTTP status code
|
||||
// value returned by a server. Status codes in the 400-499 range are not
|
||||
// returned as errors.
|
||||
@@ -48,7 +65,7 @@ func requestAttributes(req *http.Request) []attribute.KeyValue {
|
||||
protoN := strings.SplitN(req.Proto, "/", 2)
|
||||
remoteAddrN := strings.SplitN(req.RemoteAddr, ":", 2)
|
||||
|
||||
return []attribute.KeyValue{
|
||||
attrs := []attribute.KeyValue{
|
||||
semconv.HTTPRequestMethodKey.String(req.Method),
|
||||
semconv.HTTPUserAgentKey.String(req.UserAgent()),
|
||||
semconv.HTTPRequestContentLengthKey.Int64(req.ContentLength),
|
||||
@@ -65,6 +82,66 @@ func requestAttributes(req *http.Request) []attribute.KeyValue {
|
||||
semconv.ClientAddressKey.String(remoteAddrN[0]),
|
||||
semconv.ClientPortKey.String(remoteAddrN[1]),
|
||||
}
|
||||
|
||||
// Extract invite code from URL path (e.g., /v1/common/client/download/file/Hi快VPN-mac-1.0.0-ic-uuSo11uy.dmg)
|
||||
if inviteCode := extractInviteCode(req.URL.Path); inviteCode != "" {
|
||||
attrs = append(attrs, attribute.String("affiliate.invite_code", inviteCode))
|
||||
attrs = append(attrs, attribute.String("affiliate.source", "download_link"))
|
||||
}
|
||||
|
||||
// Also check query parameter for invite code (e.g., ?ic=uuSo11uy)
|
||||
if ic := req.URL.Query().Get("ic"); ic != "" {
|
||||
attrs = append(attrs, attribute.String("affiliate.invite_code", ic))
|
||||
attrs = append(attrs, attribute.String("affiliate.source", "query_param"))
|
||||
}
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
// userAttributes extracts user information from context and returns span attributes
|
||||
func userAttributes(ctx context.Context) []attribute.KeyValue {
|
||||
var attrs []attribute.KeyValue
|
||||
|
||||
// Get user info from context (set by authMiddleware)
|
||||
if userInfo := ctx.Value(constant.CtxKeyUser); userInfo != nil {
|
||||
if user, ok := userInfo.(*model.User); ok {
|
||||
var email string
|
||||
for _, method := range user.AuthMethods {
|
||||
if method.AuthType == "email" {
|
||||
email = method.AuthIdentifier
|
||||
break
|
||||
}
|
||||
}
|
||||
attrs = append(attrs,
|
||||
attribute.Int64("user.id", user.Id),
|
||||
attribute.String("user.email", email),
|
||||
attribute.Bool("user.is_admin", *user.IsAdmin),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Get session ID from context
|
||||
if sessionID := ctx.Value(constant.CtxKeySessionID); sessionID != nil {
|
||||
if sid, ok := sessionID.(string); ok {
|
||||
attrs = append(attrs, attribute.String("user.session_id", sid))
|
||||
}
|
||||
}
|
||||
|
||||
// Get device ID from context
|
||||
if deviceID := ctx.Value(constant.CtxKeyDeviceID); deviceID != nil {
|
||||
if did, ok := deviceID.(int64); ok {
|
||||
attrs = append(attrs, attribute.Int64("user.device_id", did))
|
||||
}
|
||||
}
|
||||
|
||||
// Get login type from context
|
||||
if loginType := ctx.Value(constant.LoginType); loginType != nil {
|
||||
if lt, ok := loginType.(string); ok {
|
||||
attrs = append(attrs, attribute.String("user.login_type", lt))
|
||||
}
|
||||
}
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
func TraceMiddleware(_ *svc.ServiceContext) func(ctx *gin.Context) {
|
||||
@@ -99,6 +176,9 @@ func TraceMiddleware(_ *svc.ServiceContext) func(ctx *gin.Context) {
|
||||
semconv.HTTPRouteKey.String(c.FullPath()),
|
||||
)
|
||||
|
||||
// Add user attributes from context (set by authMiddleware)
|
||||
span.SetAttributes(userAttributes(ctx)...)
|
||||
|
||||
// Record Request Body (limit to 1MB)
|
||||
if len(reqBody) > 0 {
|
||||
limit := 1048576
|
||||
|
||||
Reference in New Issue
Block a user