package response import ( "context" "net/http" "net/url" "strconv" "google.golang.org/grpc" "google.golang.org/grpc/metadata" ) type ctxKey string const ( CtxUserId ctxKey = "X-User-Id" CtxUserName ctxKey = "X-User-Name" ) type UserInfo struct { ID int64 Name string RawUID string Valid bool } func GetUserInfo(ctx context.Context) UserInfo { rawUID, ok := ctx.Value(CtxUserId).(string) if !ok || rawUID == "" { return UserInfo{Valid: false} } name, _ := ctx.Value(CtxUserName).(string) uid, err := strconv.ParseInt(rawUID, 10, 64) if err != nil { return UserInfo{RawUID: rawUID, Name: name, Valid: false} } return UserInfo{ID: uid, Name: name, RawUID: rawUID, Valid: true} } func UserReadMiddleware(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { uid := r.Header.Get("X-User-Id") uname := r.Header.Get("X-User-Name") newCtx := r.Context() newCtx = context.WithValue(newCtx, CtxUserId, uid) newCtx = context.WithValue(newCtx, CtxUserName, uname) next(w, r.WithContext(newCtx)) } } func UserClientInterceptor(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { user := GetUserInfo(ctx) if user.Valid { md := metadata.New(map[string]string{ "x-user-id": user.RawUID, "x-user-name": url.QueryEscape(user.Name), }) ctx = metadata.NewOutgoingContext(ctx, md) } return invoker(ctx, method, req, reply, cc, opts...) }