mirror of
https://github.com/Walter-Sparrow/lunar-tear.git
synced 2026-07-02 13:53:41 +03:00
131 lines
2.9 KiB
Go
131 lines
2.9 KiB
Go
package interceptor
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"reflect"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
|
|
pb "lunar-tear/server/gen/proto"
|
|
"lunar-tear/server/internal/service"
|
|
"lunar-tear/server/internal/store"
|
|
"lunar-tear/server/internal/userdata"
|
|
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/metadata"
|
|
)
|
|
|
|
type diffUserDataGetter interface {
|
|
GetDiffUserData() map[string]*pb.DiffData
|
|
}
|
|
|
|
var (
|
|
diffFieldCache sync.Map // map[reflect.Type]bool
|
|
diffDataMapTyp = reflect.TypeFor[map[string]*pb.DiffData]()
|
|
)
|
|
|
|
func hasDiffField(resp any) bool {
|
|
t := reflect.TypeOf(resp)
|
|
if cached, ok := diffFieldCache.Load(t); ok {
|
|
return cached.(bool)
|
|
}
|
|
elem := t
|
|
if elem.Kind() == reflect.Pointer {
|
|
elem = elem.Elem()
|
|
}
|
|
f, ok := elem.FieldByName("DiffUserData")
|
|
result := ok && f.Type == diffDataMapTyp
|
|
diffFieldCache.Store(t, result)
|
|
return result
|
|
}
|
|
|
|
func NewDiffInterceptor(
|
|
users store.UserRepository,
|
|
sessions store.SessionRepository,
|
|
) grpc.UnaryServerInterceptor {
|
|
return func(
|
|
ctx context.Context,
|
|
req any,
|
|
info *grpc.UnaryServerInfo,
|
|
handler grpc.UnaryHandler,
|
|
) (any, error) {
|
|
if skipDiffForMethod(info.FullMethod) {
|
|
return handler(ctx, req)
|
|
}
|
|
|
|
userId := service.CurrentUserId(ctx, users, sessions)
|
|
if userId == 0 {
|
|
return handler(ctx, req)
|
|
}
|
|
|
|
before, err := users.LoadUser(userId)
|
|
if err != nil {
|
|
return handler(ctx, req)
|
|
}
|
|
|
|
resp, handlerErr := handler(ctx, req)
|
|
if handlerErr != nil || resp == nil {
|
|
return resp, handlerErr
|
|
}
|
|
|
|
if !hasDiffField(resp) {
|
|
return resp, nil
|
|
}
|
|
|
|
if getter, ok := resp.(diffUserDataGetter); ok {
|
|
if existing := getter.GetDiffUserData(); len(existing) > 0 {
|
|
setUpdateNamesTrailer(ctx, existing)
|
|
return resp, nil
|
|
}
|
|
}
|
|
|
|
after, err := users.LoadUser(userId)
|
|
if err != nil {
|
|
return resp, nil
|
|
}
|
|
|
|
changed := userdata.ChangedTables(&before, &after)
|
|
if len(changed) == 0 {
|
|
return resp, nil
|
|
}
|
|
|
|
diff := userdata.ComputeDelta(&before, &after, changed)
|
|
reflect.ValueOf(resp).Elem().FieldByName("DiffUserData").Set(reflect.ValueOf(diff))
|
|
setUpdateNamesTrailer(ctx, diff)
|
|
|
|
return resp, nil
|
|
}
|
|
}
|
|
|
|
func skipDiffForMethod(method string) bool {
|
|
switch method {
|
|
case "/apb.api.user.UserService/Auth",
|
|
"/apb.api.user.UserService/RegisterUser",
|
|
"/apb.api.user.UserService/TransferUser",
|
|
"/apb.api.user.UserService/TransferUserByFacebook",
|
|
"/apb.api.config.ConfigService/GetConfig",
|
|
"/apb.api.data.DataService/GetLatestMasterDataVersion",
|
|
"/apb.api.data.DataService/GetUserDataNameV2",
|
|
"/apb.api.data.DataService/GetUserData":
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func setUpdateNamesTrailer(ctx context.Context, diff map[string]*pb.DiffData) {
|
|
if len(diff) == 0 {
|
|
return
|
|
}
|
|
keys := make([]string, 0, len(diff))
|
|
for key := range diff {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
value := strings.Join(keys, ",")
|
|
if err := grpc.SetTrailer(ctx, metadata.Pairs("x-apb-update-user-data-names", value)); err != nil {
|
|
log.Printf("[DiffInterceptor] failed to set trailer: %v", err)
|
|
}
|
|
}
|