216 lines
6.5 KiB
Go
216 lines
6.5 KiB
Go
package userstore
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"galaxy/user/internal/adapters/redisstate"
|
|
"galaxy/user/internal/domain/account"
|
|
"galaxy/user/internal/domain/common"
|
|
"galaxy/user/internal/domain/entitlement"
|
|
"galaxy/user/internal/domain/policy"
|
|
"galaxy/user/internal/ports"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
)
|
|
|
|
var knownSanctionCodes = []policy.SanctionCode{
|
|
policy.SanctionCodeLoginBlock,
|
|
policy.SanctionCodePrivateGameCreateBlock,
|
|
policy.SanctionCodePrivateGameManageBlock,
|
|
policy.SanctionCodeGameJoinBlock,
|
|
policy.SanctionCodeProfileUpdateBlock,
|
|
}
|
|
|
|
var knownLimitCodes = []policy.LimitCode{
|
|
policy.LimitCodeMaxOwnedPrivateGames,
|
|
policy.LimitCodeMaxPendingPublicApplications,
|
|
policy.LimitCodeMaxActiveGameMemberships,
|
|
}
|
|
|
|
var knownEligibilityMarkers = []policy.EligibilityMarker{
|
|
policy.EligibilityMarkerCanLogin,
|
|
policy.EligibilityMarkerCanCreatePrivateGame,
|
|
policy.EligibilityMarkerCanManagePrivateGame,
|
|
policy.EligibilityMarkerCanJoinGame,
|
|
policy.EligibilityMarkerCanUpdateProfile,
|
|
}
|
|
|
|
func (store *Store) addCreatedAtIndex(
|
|
pipe redis.Pipeliner,
|
|
ctx context.Context,
|
|
record account.UserAccount,
|
|
) {
|
|
pipe.ZAdd(ctx, store.keyspace.CreatedAtIndex(), redis.Z{
|
|
Score: redisstate.CreatedAtScore(record.CreatedAt),
|
|
Member: record.UserID.String(),
|
|
})
|
|
}
|
|
|
|
func (store *Store) syncDeclaredCountryIndex(
|
|
pipe redis.Pipeliner,
|
|
ctx context.Context,
|
|
previous account.UserAccount,
|
|
current account.UserAccount,
|
|
) {
|
|
if !previous.DeclaredCountry.IsZero() {
|
|
pipe.SRem(ctx, store.keyspace.DeclaredCountryIndex(previous.DeclaredCountry), current.UserID.String())
|
|
}
|
|
if !current.DeclaredCountry.IsZero() {
|
|
pipe.SAdd(ctx, store.keyspace.DeclaredCountryIndex(current.DeclaredCountry), current.UserID.String())
|
|
}
|
|
}
|
|
|
|
func (store *Store) syncEntitlementIndexes(
|
|
pipe redis.Pipeliner,
|
|
ctx context.Context,
|
|
snapshot entitlement.CurrentSnapshot,
|
|
) {
|
|
pipe.SRem(ctx, store.keyspace.PaidStateIndex(entitlement.PaidStateFree), snapshot.UserID.String())
|
|
pipe.SRem(ctx, store.keyspace.PaidStateIndex(entitlement.PaidStatePaid), snapshot.UserID.String())
|
|
pipe.SAdd(ctx, store.keyspace.PaidStateIndex(paidStateFromSnapshot(snapshot)), snapshot.UserID.String())
|
|
|
|
pipe.ZRem(ctx, store.keyspace.FinitePaidExpiryIndex(), snapshot.UserID.String())
|
|
if snapshot.HasFiniteExpiry() {
|
|
pipe.ZAdd(ctx, store.keyspace.FinitePaidExpiryIndex(), redis.Z{
|
|
Score: redisstate.ExpiryScore(*snapshot.EndsAt),
|
|
Member: snapshot.UserID.String(),
|
|
})
|
|
}
|
|
}
|
|
|
|
func (store *Store) syncActiveSanctionCodeIndexes(
|
|
pipe redis.Pipeliner,
|
|
ctx context.Context,
|
|
userID common.UserID,
|
|
activeCodes map[policy.SanctionCode]struct{},
|
|
) {
|
|
for _, code := range knownSanctionCodes {
|
|
pipe.SRem(ctx, store.keyspace.ActiveSanctionCodeIndex(code), userID.String())
|
|
if _, ok := activeCodes[code]; ok {
|
|
pipe.SAdd(ctx, store.keyspace.ActiveSanctionCodeIndex(code), userID.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
func (store *Store) syncActiveLimitCodeIndexes(
|
|
pipe redis.Pipeliner,
|
|
ctx context.Context,
|
|
userID common.UserID,
|
|
activeCodes map[policy.LimitCode]struct{},
|
|
) {
|
|
for _, code := range knownLimitCodes {
|
|
pipe.SRem(ctx, store.keyspace.ActiveLimitCodeIndex(code), userID.String())
|
|
if _, ok := activeCodes[code]; ok {
|
|
pipe.SAdd(ctx, store.keyspace.ActiveLimitCodeIndex(code), userID.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
func (store *Store) syncEligibilityMarkerIndexes(
|
|
pipe redis.Pipeliner,
|
|
ctx context.Context,
|
|
userID common.UserID,
|
|
isPaid bool,
|
|
activeSanctionCodes map[policy.SanctionCode]struct{},
|
|
) {
|
|
values := deriveEligibilityMarkerValues(isPaid, activeSanctionCodes)
|
|
|
|
for _, marker := range knownEligibilityMarkers {
|
|
pipe.SRem(ctx, store.keyspace.EligibilityMarkerIndex(marker, true), userID.String())
|
|
pipe.SRem(ctx, store.keyspace.EligibilityMarkerIndex(marker, false), userID.String())
|
|
pipe.SAdd(ctx, store.keyspace.EligibilityMarkerIndex(marker, values[marker]), userID.String())
|
|
}
|
|
}
|
|
|
|
func (store *Store) loadActiveSanctionCodeSet(
|
|
ctx context.Context,
|
|
getter bytesGetter,
|
|
userID common.UserID,
|
|
) (map[policy.SanctionCode]struct{}, error) {
|
|
activeCodes := make(map[policy.SanctionCode]struct{}, len(knownSanctionCodes))
|
|
|
|
for _, code := range knownSanctionCodes {
|
|
_, err := store.loadActiveSanctionRecordID(ctx, getter, store.keyspace.ActiveSanction(userID, code))
|
|
switch {
|
|
case err == nil:
|
|
activeCodes[code] = struct{}{}
|
|
case errors.Is(err, ports.ErrNotFound):
|
|
continue
|
|
default:
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return activeCodes, nil
|
|
}
|
|
|
|
func (store *Store) loadActiveLimitCodeSet(
|
|
ctx context.Context,
|
|
getter bytesGetter,
|
|
userID common.UserID,
|
|
) (map[policy.LimitCode]struct{}, error) {
|
|
activeCodes := make(map[policy.LimitCode]struct{}, len(knownLimitCodes))
|
|
|
|
for _, code := range knownLimitCodes {
|
|
_, err := store.loadActiveLimitRecordID(ctx, getter, store.keyspace.ActiveLimit(userID, code))
|
|
switch {
|
|
case err == nil:
|
|
activeCodes[code] = struct{}{}
|
|
case errors.Is(err, ports.ErrNotFound):
|
|
continue
|
|
default:
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return activeCodes, nil
|
|
}
|
|
|
|
func (store *Store) activeSanctionWatchKeys(userID common.UserID) []string {
|
|
keys := make([]string, 0, len(knownSanctionCodes))
|
|
for _, code := range knownSanctionCodes {
|
|
keys = append(keys, store.keyspace.ActiveSanction(userID, code))
|
|
}
|
|
|
|
return keys
|
|
}
|
|
|
|
func (store *Store) activeLimitWatchKeys(userID common.UserID) []string {
|
|
keys := make([]string, 0, len(knownLimitCodes))
|
|
for _, code := range knownLimitCodes {
|
|
keys = append(keys, store.keyspace.ActiveLimit(userID, code))
|
|
}
|
|
|
|
return keys
|
|
}
|
|
|
|
func deriveEligibilityMarkerValues(
|
|
isPaid bool,
|
|
activeSanctionCodes map[policy.SanctionCode]struct{},
|
|
) map[policy.EligibilityMarker]bool {
|
|
_, loginBlocked := activeSanctionCodes[policy.SanctionCodeLoginBlock]
|
|
_, createBlocked := activeSanctionCodes[policy.SanctionCodePrivateGameCreateBlock]
|
|
_, manageBlocked := activeSanctionCodes[policy.SanctionCodePrivateGameManageBlock]
|
|
_, joinBlocked := activeSanctionCodes[policy.SanctionCodeGameJoinBlock]
|
|
_, profileBlocked := activeSanctionCodes[policy.SanctionCodeProfileUpdateBlock]
|
|
|
|
canLogin := !loginBlocked
|
|
|
|
return map[policy.EligibilityMarker]bool{
|
|
policy.EligibilityMarkerCanLogin: canLogin,
|
|
policy.EligibilityMarkerCanCreatePrivateGame: canLogin && isPaid && !createBlocked,
|
|
policy.EligibilityMarkerCanManagePrivateGame: canLogin && isPaid && !manageBlocked,
|
|
policy.EligibilityMarkerCanJoinGame: canLogin && !joinBlocked,
|
|
policy.EligibilityMarkerCanUpdateProfile: canLogin && !profileBlocked,
|
|
}
|
|
}
|
|
|
|
func paidStateFromSnapshot(snapshot entitlement.CurrentSnapshot) entitlement.PaidState {
|
|
if snapshot.IsPaid {
|
|
return entitlement.PaidStatePaid
|
|
}
|
|
|
|
return entitlement.PaidStateFree
|
|
}
|