Loading CHANGELOG.md +2 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,8 @@ The format is based on [keep a changelog](http://keepachangelog.com) and this pr - New runtime function to get a single match by ID. - New runtime functions for link operations. - New Lua runtime function to print a log message at debug level. - Lua runtime accounts get operations now return Facebook Instant Game IDs. - Runtime account get operations now return account disable time if available. ### Changed - Replace metrics implementation. Loading apigrpc/apigrpc.swagger.json +5 −0 Original line number Diff line number Diff line Loading @@ -2541,6 +2541,11 @@ "type": "string", "format": "date-time", "description": "The UNIX time when the user's email was verified." }, "disable_time": { "type": "string", "format": "date-time", "description": "The UNIX time when the user's account was disabled/banned." } }, "description": "A user with additional account details. Always the current user." Loading server/api_account.go +6 −3 Original line number Diff line number Diff line Loading @@ -46,7 +46,7 @@ func (s *ApiServer) GetAccount(ctx context.Context, in *empty.Empty) (*api.Accou } } user, _, err := GetAccount(ctx, s.logger, s.db, s.tracker, userID) account, err := GetAccount(ctx, s.logger, s.db, s.tracker, userID) if err != nil { if err == ErrAccountNotFound { return nil, status.Error(codes.NotFound, "Account not found.") Loading @@ -54,17 +54,20 @@ func (s *ApiServer) GetAccount(ctx context.Context, in *empty.Empty) (*api.Accou return nil, status.Error(codes.Internal, "Error retrieving user account.") } // User-facing account retrieval does not expose disable time for now. account.DisableTime = nil // After hook. if fn := s.runtime.AfterGetAccount(); fn != nil { afterFn := func(clientIP, clientPort string) error { return fn(ctx, s.logger, userID.String(), ctx.Value(ctxUsernameKey{}).(string), ctx.Value(ctxVarsKey{}).(map[string]string), ctx.Value(ctxExpiryKey{}).(int64), clientIP, clientPort, user) return fn(ctx, s.logger, userID.String(), ctx.Value(ctxUsernameKey{}).(string), ctx.Value(ctxVarsKey{}).(map[string]string), ctx.Value(ctxExpiryKey{}).(int64), clientIP, clientPort, account) } // Execute the after function lambda wrapped in a trace for stats measurement. traceApiAfter(ctx, s.logger, s.metrics, ctx.Value(ctxFullMethodKey{}).(string), afterFn) } return user, nil return account, nil } func (s *ApiServer) UpdateAccount(ctx context.Context, in *api.UpdateAccountRequest) (*empty.Empty, error) { Loading server/console_account.go +5 −7 Original line number Diff line number Diff line Loading @@ -127,7 +127,7 @@ func (s *ConsoleServer) GetAccount(ctx context.Context, in *console.AccountId) ( return nil, status.Error(codes.InvalidArgument, "Requires a valid user ID.") } account, disableTime, err := GetAccount(ctx, s.logger, s.db, s.tracker, userID) account, err := GetAccount(ctx, s.logger, s.db, s.tracker, userID) if err != nil { // Error already logged in function above. if err == ErrAccountNotFound { Loading @@ -136,12 +136,10 @@ func (s *ConsoleServer) GetAccount(ctx context.Context, in *console.AccountId) ( return nil, status.Error(codes.Internal, "An error occurred while trying to retrieve user account.") } acc := &console.Account{Account: account} if disableTime.Unix() != 0 { acc.DisableTime = ×tamp.Timestamp{Seconds: disableTime.Unix()} } return acc, nil return &console.Account{ Account: account, DisableTime: account.DisableTime, }, nil } func (s *ConsoleServer) GetFriends(ctx context.Context, in *console.AccountId) (*api.FriendList, error) { Loading server/core_account.go +33 −24 Original line number Diff line number Diff line Loading @@ -18,26 +18,24 @@ import ( "context" "database/sql" "encoding/json" "github.com/heroiclabs/nakama/v2/console" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "strconv" "strings" "time" "github.com/gofrs/uuid" "github.com/golang/protobuf/ptypes/timestamp" "github.com/golang/protobuf/ptypes/wrappers" "github.com/heroiclabs/nakama-common/api" "github.com/heroiclabs/nakama/v2/console" "github.com/jackc/pgx" "github.com/jackc/pgx/pgtype" "github.com/pkg/errors" "go.uber.org/zap" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "strconv" "strings" ) var ErrAccountNotFound = errors.New("account not found") func GetAccount(ctx context.Context, logger *zap.Logger, db *sql.DB, tracker Tracker, userID uuid.UUID) (*api.Account, time.Time, error) { func GetAccount(ctx context.Context, logger *zap.Logger, db *sql.DB, tracker Tracker, userID uuid.UUID) (*api.Account, error) { var displayName sql.NullString var username sql.NullString var avatarURL sql.NullString Loading Loading @@ -69,10 +67,10 @@ WHERE u.id = $1` if err := db.QueryRowContext(ctx, query, userID).Scan(&username, &displayName, &avatarURL, &langTag, &location, &timezone, &metadata, &wallet, &email, &facebook, &facebookInstantGame, &google, &gamecenter, &steam, &customID, &edgeCount, &createTime, &updateTime, &verifyTime, &disableTime, &deviceIDs); err != nil { if err == sql.ErrNoRows { return nil, time.Time{}, ErrAccountNotFound return nil, ErrAccountNotFound } logger.Error("Error retrieving user account.", zap.Error(err)) return nil, time.Time{}, err return nil, err } devices := make([]*api.AccountDevice, 0, len(deviceIDs.Elements)) Loading @@ -84,6 +82,10 @@ WHERE u.id = $1` if verifyTime.Status == pgtype.Present && verifyTime.Time.Unix() != 0 { verifyTimestamp = ×tamp.Timestamp{Seconds: verifyTime.Time.Unix()} } var disableTimestamp *timestamp.Timestamp if disableTime.Status == pgtype.Present && disableTime.Time.Unix() != 0 { disableTimestamp = ×tamp.Timestamp{Seconds: disableTime.Time.Unix()} } online := false if tracker != nil { Loading Loading @@ -115,7 +117,8 @@ WHERE u.id = $1` Devices: devices, CustomId: customID.String, VerifyTime: verifyTimestamp, }, disableTime.Time, nil DisableTime: disableTimestamp, }, nil } func GetAccounts(ctx context.Context, logger *zap.Logger, db *sql.DB, tracker Tracker, userIDs []string) ([]*api.Account, error) { Loading @@ -129,7 +132,7 @@ func GetAccounts(ctx context.Context, logger *zap.Logger, db *sql.DB, tracker Tr query := ` SELECT u.id, u.username, u.display_name, u.avatar_url, u.lang_tag, u.location, u.timezone, u.metadata, u.wallet, u.email, u.facebook_id, u.facebook_instant_game_id, u.google_id, u.gamecenter_id, u.steam_id, u.custom_id, u.edge_count, u.create_time, u.update_time, u.verify_time, array(select ud.id from user_device ud where u.id = ud.user_id) u.create_time, u.update_time, u.verify_time, u.disable_time, array(select ud.id from user_device ud where u.id = ud.user_id) FROM users u WHERE u.id IN (` + strings.Join(statements, ",") + `)` rows, err := db.QueryContext(ctx, query, parameters...) Loading Loading @@ -161,9 +164,10 @@ WHERE u.id IN (` + strings.Join(statements, ",") + `)` var createTime pgtype.Timestamptz var updateTime pgtype.Timestamptz var verifyTime pgtype.Timestamptz var disableTime pgtype.Timestamptz var deviceIDs pgtype.VarcharArray err = rows.Scan(&userID, &username, &displayName, &avatarURL, &langTag, &location, &timezone, &metadata, &wallet, &email, &facebook, &facebookInstantGame, &google, &gamecenter, &steam, &customID, &edgeCount, &createTime, &updateTime, &verifyTime, &deviceIDs) err = rows.Scan(&userID, &username, &displayName, &avatarURL, &langTag, &location, &timezone, &metadata, &wallet, &email, &facebook, &facebookInstantGame, &google, &gamecenter, &steam, &customID, &edgeCount, &createTime, &updateTime, &verifyTime, &disableTime, &deviceIDs) if err != nil { logger.Error("Error retrieving user accounts.", zap.Error(err)) return nil, err Loading @@ -178,6 +182,10 @@ WHERE u.id IN (` + strings.Join(statements, ",") + `)` if verifyTime.Status == pgtype.Present && verifyTime.Time.Unix() != 0 { verifyTimestamp = ×tamp.Timestamp{Seconds: verifyTime.Time.Unix()} } var disableTimestamp *timestamp.Timestamp if disableTime.Status == pgtype.Present && disableTime.Time.Unix() != 0 { disableTimestamp = ×tamp.Timestamp{Seconds: disableTime.Time.Unix()} } online := false if tracker != nil { Loading Loading @@ -209,6 +217,7 @@ WHERE u.id IN (` + strings.Join(statements, ",") + `)` Devices: devices, CustomId: customID.String, VerifyTime: verifyTimestamp, DisableTime: disableTimestamp, }) } Loading Loading @@ -320,7 +329,7 @@ func UpdateAccount(ctx context.Context, logger *zap.Logger, db *sql.DB, userID u func ExportAccount(ctx context.Context, logger *zap.Logger, db *sql.DB, userID uuid.UUID) (*console.AccountExport, error) { // Core user account. account, _, err := GetAccount(ctx, logger, db, nil, userID) account, err := GetAccount(ctx, logger, db, nil, userID) if err != nil { if err == ErrAccountNotFound { return nil, status.Error(codes.NotFound, "Account not found.") Loading Loading
CHANGELOG.md +2 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,8 @@ The format is based on [keep a changelog](http://keepachangelog.com) and this pr - New runtime function to get a single match by ID. - New runtime functions for link operations. - New Lua runtime function to print a log message at debug level. - Lua runtime accounts get operations now return Facebook Instant Game IDs. - Runtime account get operations now return account disable time if available. ### Changed - Replace metrics implementation. Loading
apigrpc/apigrpc.swagger.json +5 −0 Original line number Diff line number Diff line Loading @@ -2541,6 +2541,11 @@ "type": "string", "format": "date-time", "description": "The UNIX time when the user's email was verified." }, "disable_time": { "type": "string", "format": "date-time", "description": "The UNIX time when the user's account was disabled/banned." } }, "description": "A user with additional account details. Always the current user." Loading
server/api_account.go +6 −3 Original line number Diff line number Diff line Loading @@ -46,7 +46,7 @@ func (s *ApiServer) GetAccount(ctx context.Context, in *empty.Empty) (*api.Accou } } user, _, err := GetAccount(ctx, s.logger, s.db, s.tracker, userID) account, err := GetAccount(ctx, s.logger, s.db, s.tracker, userID) if err != nil { if err == ErrAccountNotFound { return nil, status.Error(codes.NotFound, "Account not found.") Loading @@ -54,17 +54,20 @@ func (s *ApiServer) GetAccount(ctx context.Context, in *empty.Empty) (*api.Accou return nil, status.Error(codes.Internal, "Error retrieving user account.") } // User-facing account retrieval does not expose disable time for now. account.DisableTime = nil // After hook. if fn := s.runtime.AfterGetAccount(); fn != nil { afterFn := func(clientIP, clientPort string) error { return fn(ctx, s.logger, userID.String(), ctx.Value(ctxUsernameKey{}).(string), ctx.Value(ctxVarsKey{}).(map[string]string), ctx.Value(ctxExpiryKey{}).(int64), clientIP, clientPort, user) return fn(ctx, s.logger, userID.String(), ctx.Value(ctxUsernameKey{}).(string), ctx.Value(ctxVarsKey{}).(map[string]string), ctx.Value(ctxExpiryKey{}).(int64), clientIP, clientPort, account) } // Execute the after function lambda wrapped in a trace for stats measurement. traceApiAfter(ctx, s.logger, s.metrics, ctx.Value(ctxFullMethodKey{}).(string), afterFn) } return user, nil return account, nil } func (s *ApiServer) UpdateAccount(ctx context.Context, in *api.UpdateAccountRequest) (*empty.Empty, error) { Loading
server/console_account.go +5 −7 Original line number Diff line number Diff line Loading @@ -127,7 +127,7 @@ func (s *ConsoleServer) GetAccount(ctx context.Context, in *console.AccountId) ( return nil, status.Error(codes.InvalidArgument, "Requires a valid user ID.") } account, disableTime, err := GetAccount(ctx, s.logger, s.db, s.tracker, userID) account, err := GetAccount(ctx, s.logger, s.db, s.tracker, userID) if err != nil { // Error already logged in function above. if err == ErrAccountNotFound { Loading @@ -136,12 +136,10 @@ func (s *ConsoleServer) GetAccount(ctx context.Context, in *console.AccountId) ( return nil, status.Error(codes.Internal, "An error occurred while trying to retrieve user account.") } acc := &console.Account{Account: account} if disableTime.Unix() != 0 { acc.DisableTime = ×tamp.Timestamp{Seconds: disableTime.Unix()} } return acc, nil return &console.Account{ Account: account, DisableTime: account.DisableTime, }, nil } func (s *ConsoleServer) GetFriends(ctx context.Context, in *console.AccountId) (*api.FriendList, error) { Loading
server/core_account.go +33 −24 Original line number Diff line number Diff line Loading @@ -18,26 +18,24 @@ import ( "context" "database/sql" "encoding/json" "github.com/heroiclabs/nakama/v2/console" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "strconv" "strings" "time" "github.com/gofrs/uuid" "github.com/golang/protobuf/ptypes/timestamp" "github.com/golang/protobuf/ptypes/wrappers" "github.com/heroiclabs/nakama-common/api" "github.com/heroiclabs/nakama/v2/console" "github.com/jackc/pgx" "github.com/jackc/pgx/pgtype" "github.com/pkg/errors" "go.uber.org/zap" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "strconv" "strings" ) var ErrAccountNotFound = errors.New("account not found") func GetAccount(ctx context.Context, logger *zap.Logger, db *sql.DB, tracker Tracker, userID uuid.UUID) (*api.Account, time.Time, error) { func GetAccount(ctx context.Context, logger *zap.Logger, db *sql.DB, tracker Tracker, userID uuid.UUID) (*api.Account, error) { var displayName sql.NullString var username sql.NullString var avatarURL sql.NullString Loading Loading @@ -69,10 +67,10 @@ WHERE u.id = $1` if err := db.QueryRowContext(ctx, query, userID).Scan(&username, &displayName, &avatarURL, &langTag, &location, &timezone, &metadata, &wallet, &email, &facebook, &facebookInstantGame, &google, &gamecenter, &steam, &customID, &edgeCount, &createTime, &updateTime, &verifyTime, &disableTime, &deviceIDs); err != nil { if err == sql.ErrNoRows { return nil, time.Time{}, ErrAccountNotFound return nil, ErrAccountNotFound } logger.Error("Error retrieving user account.", zap.Error(err)) return nil, time.Time{}, err return nil, err } devices := make([]*api.AccountDevice, 0, len(deviceIDs.Elements)) Loading @@ -84,6 +82,10 @@ WHERE u.id = $1` if verifyTime.Status == pgtype.Present && verifyTime.Time.Unix() != 0 { verifyTimestamp = ×tamp.Timestamp{Seconds: verifyTime.Time.Unix()} } var disableTimestamp *timestamp.Timestamp if disableTime.Status == pgtype.Present && disableTime.Time.Unix() != 0 { disableTimestamp = ×tamp.Timestamp{Seconds: disableTime.Time.Unix()} } online := false if tracker != nil { Loading Loading @@ -115,7 +117,8 @@ WHERE u.id = $1` Devices: devices, CustomId: customID.String, VerifyTime: verifyTimestamp, }, disableTime.Time, nil DisableTime: disableTimestamp, }, nil } func GetAccounts(ctx context.Context, logger *zap.Logger, db *sql.DB, tracker Tracker, userIDs []string) ([]*api.Account, error) { Loading @@ -129,7 +132,7 @@ func GetAccounts(ctx context.Context, logger *zap.Logger, db *sql.DB, tracker Tr query := ` SELECT u.id, u.username, u.display_name, u.avatar_url, u.lang_tag, u.location, u.timezone, u.metadata, u.wallet, u.email, u.facebook_id, u.facebook_instant_game_id, u.google_id, u.gamecenter_id, u.steam_id, u.custom_id, u.edge_count, u.create_time, u.update_time, u.verify_time, array(select ud.id from user_device ud where u.id = ud.user_id) u.create_time, u.update_time, u.verify_time, u.disable_time, array(select ud.id from user_device ud where u.id = ud.user_id) FROM users u WHERE u.id IN (` + strings.Join(statements, ",") + `)` rows, err := db.QueryContext(ctx, query, parameters...) Loading Loading @@ -161,9 +164,10 @@ WHERE u.id IN (` + strings.Join(statements, ",") + `)` var createTime pgtype.Timestamptz var updateTime pgtype.Timestamptz var verifyTime pgtype.Timestamptz var disableTime pgtype.Timestamptz var deviceIDs pgtype.VarcharArray err = rows.Scan(&userID, &username, &displayName, &avatarURL, &langTag, &location, &timezone, &metadata, &wallet, &email, &facebook, &facebookInstantGame, &google, &gamecenter, &steam, &customID, &edgeCount, &createTime, &updateTime, &verifyTime, &deviceIDs) err = rows.Scan(&userID, &username, &displayName, &avatarURL, &langTag, &location, &timezone, &metadata, &wallet, &email, &facebook, &facebookInstantGame, &google, &gamecenter, &steam, &customID, &edgeCount, &createTime, &updateTime, &verifyTime, &disableTime, &deviceIDs) if err != nil { logger.Error("Error retrieving user accounts.", zap.Error(err)) return nil, err Loading @@ -178,6 +182,10 @@ WHERE u.id IN (` + strings.Join(statements, ",") + `)` if verifyTime.Status == pgtype.Present && verifyTime.Time.Unix() != 0 { verifyTimestamp = ×tamp.Timestamp{Seconds: verifyTime.Time.Unix()} } var disableTimestamp *timestamp.Timestamp if disableTime.Status == pgtype.Present && disableTime.Time.Unix() != 0 { disableTimestamp = ×tamp.Timestamp{Seconds: disableTime.Time.Unix()} } online := false if tracker != nil { Loading Loading @@ -209,6 +217,7 @@ WHERE u.id IN (` + strings.Join(statements, ",") + `)` Devices: devices, CustomId: customID.String, VerifyTime: verifyTimestamp, DisableTime: disableTimestamp, }) } Loading Loading @@ -320,7 +329,7 @@ func UpdateAccount(ctx context.Context, logger *zap.Logger, db *sql.DB, userID u func ExportAccount(ctx context.Context, logger *zap.Logger, db *sql.DB, userID uuid.UUID) (*console.AccountExport, error) { // Core user account. account, _, err := GetAccount(ctx, logger, db, nil, userID) account, err := GetAccount(ctx, logger, db, nil, userID) if err != nil { if err == ErrAccountNotFound { return nil, status.Error(codes.NotFound, "Account not found.") Loading