Add HTTP /v1 MVC, TCP send path, groups with server-side member-avatar collage, object-storage upload, and friends/favorites around the architecture-v3 layout. Co-authored-by: Cursor <cursoragent@cursor.com>
95 lines
2.1 KiB
Go
95 lines
2.1 KiB
Go
package reqctx
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"net"
|
|
"strings"
|
|
"time"
|
|
|
|
"spark-server/internal/pkg/i18n"
|
|
)
|
|
|
|
type ctxKey struct{}
|
|
|
|
// RequestContext is the per-request identity. Use RealIP for rate limit and audit, never 127.0.0.1 from gnet.RemoteAddr.
|
|
type RequestContext struct {
|
|
TraceID string
|
|
RequestID string
|
|
UserID int64
|
|
DeviceID uint64
|
|
Language string
|
|
AppVersion string
|
|
ConnectionID string
|
|
RemoteAddr string
|
|
RealIP string
|
|
JTI string
|
|
TokenExpires time.Time
|
|
}
|
|
|
|
func With(ctx context.Context, rc *RequestContext) context.Context {
|
|
return context.WithValue(ctx, ctxKey{}, rc)
|
|
}
|
|
|
|
func From(ctx context.Context) *RequestContext {
|
|
rc, _ := ctx.Value(ctxKey{}).(*RequestContext)
|
|
return rc
|
|
}
|
|
|
|
// RealIP returns the client IP from context (PROXY / X-Real-IP), not gnet RemoteAddr.
|
|
func RealIP(ctx context.Context) string {
|
|
if rc := From(ctx); rc != nil {
|
|
return rc.RealIP
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// Lang is the canonical UI language (zh-CN / en / zh-TW) for errors and notifications.
|
|
func Lang(ctx context.Context) i18n.Lang {
|
|
if rc := From(ctx); rc != nil {
|
|
return i18n.Parse(rc.Language)
|
|
}
|
|
return i18n.Default
|
|
}
|
|
|
|
// Language returns the canonical language tag stored on context.
|
|
func Language(ctx context.Context) string {
|
|
return Lang(ctx).String()
|
|
}
|
|
|
|
// ParseHTTPRealIP prefers X-Real-IP, then X-Forwarded-For, then RemoteAddr host.
|
|
func ParseHTTPRealIP(realIPHeader, forwardedFor, remoteAddr string) string {
|
|
if ip := strings.TrimSpace(realIPHeader); ip != "" {
|
|
return stripHost(ip)
|
|
}
|
|
if forwardedFor != "" {
|
|
first, _, _ := strings.Cut(forwardedFor, ",")
|
|
if ip := stripHost(strings.TrimSpace(first)); ip != "" {
|
|
return ip
|
|
}
|
|
}
|
|
return stripHost(remoteAddr)
|
|
}
|
|
|
|
func stripHost(v string) string {
|
|
v = strings.TrimSpace(v)
|
|
if v == "" {
|
|
return ""
|
|
}
|
|
host, _, err := net.SplitHostPort(v)
|
|
if err == nil {
|
|
return host
|
|
}
|
|
return v
|
|
}
|
|
|
|
// NewID returns a 16-byte hex id for trace_id / request_id.
|
|
func NewID() string {
|
|
var b [16]byte
|
|
if _, err := rand.Read(b[:]); err != nil {
|
|
return "00000000000000000000000000000000"
|
|
}
|
|
return hex.EncodeToString(b[:])
|
|
}
|