fixes for loading issues
This commit is contained in:
parent
7ddbcc9022
commit
d7fd75e487
4 changed files with 60 additions and 48 deletions
|
|
@ -424,6 +424,11 @@ type PrivacyService interface {
|
||||||
SetRules(ctx context.Context, ownerUserID int64, key domain.PrivacyKey, rules []domain.PrivacyRule) (domain.PrivacyRules, error)
|
SetRules(ctx context.Context, ownerUserID int64, key domain.PrivacyKey, rules []domain.PrivacyRule) (domain.PrivacyRules, error)
|
||||||
AddAllowUser(ctx context.Context, ownerUserID int64, key domain.PrivacyKey, targetUserID int64) (domain.PrivacyRules, bool, error)
|
AddAllowUser(ctx context.Context, ownerUserID int64, key domain.PrivacyKey, targetUserID int64) (domain.PrivacyRules, bool, error)
|
||||||
CanSee(ctx context.Context, ownerUserID, viewerUserID int64, key domain.PrivacyKey) (bool, error)
|
CanSee(ctx context.Context, ownerUserID, viewerUserID int64, key domain.PrivacyKey) (bool, error)
|
||||||
|
// CanSeeBatch evaluates multiple keys for one (owner, viewer) pair with a
|
||||||
|
// single ListPrivacyRules + GetReverseContacts round trip, equivalent to
|
||||||
|
// one CanSee call per key. Used where a single projection (e.g.
|
||||||
|
// getFullUser) otherwise needs several keys checked at once.
|
||||||
|
CanSeeBatch(ctx context.Context, ownerUserIDs []int64, viewerUserID int64, keys []domain.PrivacyKey) (map[int64]map[domain.PrivacyKey]bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HelpService 抽象启动配置与国家区号目录。
|
// HelpService 抽象启动配置与国家区号目录。
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,18 @@ func (p stubPrivacy) CanSee(_ context.Context, _, _ int64, key domain.PrivacyKey
|
||||||
return !p.deny[key], nil
|
return !p.deny[key], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p stubPrivacy) CanSeeBatch(_ context.Context, ownerUserIDs []int64, _ int64, keys []domain.PrivacyKey) (map[int64]map[domain.PrivacyKey]bool, error) {
|
||||||
|
out := make(map[int64]map[domain.PrivacyKey]bool, len(ownerUserIDs))
|
||||||
|
for _, owner := range ownerUserIDs {
|
||||||
|
m := make(map[domain.PrivacyKey]bool, len(keys))
|
||||||
|
for _, k := range keys {
|
||||||
|
m[k] = !p.deny[k]
|
||||||
|
}
|
||||||
|
out[owner] = m
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
type phoneFixture struct {
|
type phoneFixture struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
|
|
||||||
|
|
@ -237,15 +237,40 @@ func applyContactNoteToUserFull(user domain.User, full *tg.UserFull) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) buildUserFullProjection(ctx context.Context, currentUserID int64, u domain.User) (tg.UserFull, error) {
|
func (r *Router) buildUserFullProjection(ctx context.Context, currentUserID int64, u domain.User) (tg.UserFull, error) {
|
||||||
about := u.About
|
// One CanSeeBatch call replaces what used to be up to six sequential
|
||||||
if r.deps.Privacy != nil && u.ID != currentUserID {
|
// CanSee calls (about/phone_call/phone_p2p/profile_photo/saved_music/
|
||||||
allowed, err := r.deps.Privacy.CanSee(ctx, u.ID, currentUserID, domain.PrivacyKeyAbout)
|
// birthday) below and in fillUserFullPhotos -- each CanSee cost two
|
||||||
|
// queries (rules + contacts), so this alone was ~12 of the ~15 db
|
||||||
|
// queries getFullUser used to make.
|
||||||
|
var privacyVisibility map[domain.PrivacyKey]bool
|
||||||
|
if r.deps.Privacy != nil && currentUserID != 0 && u.ID != 0 && u.ID != currentUserID {
|
||||||
|
batch, err := r.deps.Privacy.CanSeeBatch(ctx, []int64{u.ID}, currentUserID, []domain.PrivacyKey{
|
||||||
|
domain.PrivacyKeyAbout,
|
||||||
|
domain.PrivacyKeyPhoneCall,
|
||||||
|
domain.PrivacyKeyPhoneP2P,
|
||||||
|
domain.PrivacyKeyProfilePhoto,
|
||||||
|
domain.PrivacyKeySavedMusic,
|
||||||
|
domain.PrivacyKeyBirthday,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return tg.UserFull{}, internalErr()
|
return tg.UserFull{}, internalErr()
|
||||||
}
|
}
|
||||||
if !allowed {
|
privacyVisibility = batch[u.ID]
|
||||||
about = ""
|
}
|
||||||
|
// canSee mirrors CanSee's own fail-open semantics: self is always
|
||||||
|
// visible, and an unset map means the batch call above was skipped
|
||||||
|
// (privacy service absent, or self) -- both cases default to visible,
|
||||||
|
// exactly like every "if r.deps.Privacy != nil" guard did individually.
|
||||||
|
canSee := func(key domain.PrivacyKey) bool {
|
||||||
|
if u.ID == currentUserID || privacyVisibility == nil {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
return privacyVisibility[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
about := u.About
|
||||||
|
if u.ID != currentUserID && !canSee(domain.PrivacyKeyAbout) {
|
||||||
|
about = ""
|
||||||
}
|
}
|
||||||
// Surface the scam/fake warning to other viewers (never to the account
|
// Surface the scam/fake warning to other viewers (never to the account
|
||||||
// itself), non-destructively over the projected About.
|
// itself), non-destructively over the projected About.
|
||||||
|
|
@ -261,19 +286,8 @@ func (r *Router) buildUserFullProjection(ctx context.Context, currentUserID int6
|
||||||
// 通话入口:客户端不见 phone_calls_available=true 不显示通话按钮(P1 前置项)。
|
// 通话入口:客户端不见 phone_calls_available=true 不显示通话按钮(P1 前置项)。
|
||||||
// phone_calls_private 标记对端禁 P2P(p2p_allowed 真值在通话确认时另行计算)。
|
// phone_calls_private 标记对端禁 P2P(p2p_allowed 真值在通话确认时另行计算)。
|
||||||
if !u.Bot && u.ID != currentUserID {
|
if !u.Bot && u.ID != currentUserID {
|
||||||
callsAllowed, p2pAllowed := true, true
|
callsAllowed := canSee(domain.PrivacyKeyPhoneCall)
|
||||||
if r.deps.Privacy != nil {
|
p2pAllowed := canSee(domain.PrivacyKeyPhoneP2P)
|
||||||
allowed, err := r.deps.Privacy.CanSee(ctx, u.ID, currentUserID, domain.PrivacyKeyPhoneCall)
|
|
||||||
if err != nil {
|
|
||||||
return tg.UserFull{}, internalErr()
|
|
||||||
}
|
|
||||||
callsAllowed = allowed
|
|
||||||
allowed, err = r.deps.Privacy.CanSee(ctx, u.ID, currentUserID, domain.PrivacyKeyPhoneP2P)
|
|
||||||
if err != nil {
|
|
||||||
return tg.UserFull{}, internalErr()
|
|
||||||
}
|
|
||||||
p2pAllowed = allowed
|
|
||||||
}
|
|
||||||
full.PhoneCallsAvailable = callsAllowed
|
full.PhoneCallsAvailable = callsAllowed
|
||||||
full.VideoCallsAvailable = callsAllowed
|
full.VideoCallsAvailable = callsAllowed
|
||||||
full.PhoneCallsPrivate = !p2pAllowed
|
full.PhoneCallsPrivate = !p2pAllowed
|
||||||
|
|
@ -302,15 +316,11 @@ func (r *Router) buildUserFullProjection(ctx context.Context, currentUserID int6
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := r.fillUserFullPhotos(ctx, currentUserID, u.ID, &full); err != nil {
|
if err := r.fillUserFullPhotos(ctx, currentUserID, u.ID, &full, canSee(domain.PrivacyKeyProfilePhoto)); err != nil {
|
||||||
return tg.UserFull{}, err
|
return tg.UserFull{}, err
|
||||||
}
|
}
|
||||||
if r.deps.Account != nil {
|
if r.deps.Account != nil {
|
||||||
allowed, err := r.canSeeSavedMusic(ctx, currentUserID, u.ID)
|
if canSee(domain.PrivacyKeySavedMusic) {
|
||||||
if err != nil {
|
|
||||||
return tg.UserFull{}, err
|
|
||||||
}
|
|
||||||
if allowed {
|
|
||||||
music, err := r.deps.Account.ListSavedMusic(ctx, u.ID, 0, 1)
|
music, err := r.deps.Account.ListSavedMusic(ctx, u.ID, 0, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return tg.UserFull{}, internalErr()
|
return tg.UserFull{}, internalErr()
|
||||||
|
|
@ -382,15 +392,7 @@ func (r *Router) buildUserFullProjection(ctx context.Context, currentUserID int6
|
||||||
// 生日(account.updateBirthday):落 userFull.birthday,按 PrivacyKeyBirthday 对他人裁剪,
|
// 生日(account.updateBirthday):落 userFull.birthday,按 PrivacyKeyBirthday 对他人裁剪,
|
||||||
// 本人恒可见。
|
// 本人恒可见。
|
||||||
if u.Birthday.IsSet() {
|
if u.Birthday.IsSet() {
|
||||||
birthdayVisible := true
|
if canSee(domain.PrivacyKeyBirthday) {
|
||||||
if r.deps.Privacy != nil && u.ID != currentUserID {
|
|
||||||
allowed, err := r.deps.Privacy.CanSee(ctx, u.ID, currentUserID, domain.PrivacyKeyBirthday)
|
|
||||||
if err != nil {
|
|
||||||
return tg.UserFull{}, internalErr()
|
|
||||||
}
|
|
||||||
birthdayVisible = allowed
|
|
||||||
}
|
|
||||||
if birthdayVisible {
|
|
||||||
full.SetBirthday(tgBirthday(u.Birthday))
|
full.SetBirthday(tgBirthday(u.Birthday))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -570,7 +572,7 @@ func savedMusicDocumentIDs(docs []domain.Document) []int64 {
|
||||||
return ids
|
return ids
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) fillUserFullPhotos(ctx context.Context, viewerUserID, ownerUserID int64, full *tg.UserFull) error {
|
func (r *Router) fillUserFullPhotos(ctx context.Context, viewerUserID, ownerUserID int64, full *tg.UserFull, profileAllowed bool) error {
|
||||||
if r.deps.Files == nil || full == nil || ownerUserID == 0 {
|
if r.deps.Files == nil || full == nil || ownerUserID == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -602,14 +604,6 @@ func (r *Router) fillUserFullPhotos(ctx context.Context, viewerUserID, ownerUser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
profileAllowed := true
|
|
||||||
if r.deps.Privacy != nil {
|
|
||||||
var err error
|
|
||||||
profileAllowed, err = r.deps.Privacy.CanSee(ctx, ownerUserID, viewerUserID, domain.PrivacyKeyProfilePhoto)
|
|
||||||
if err != nil {
|
|
||||||
return internalErr()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if profileAllowed {
|
if profileAllowed {
|
||||||
if photo, found, err := r.deps.Files.CurrentProfilePhotoKind(ctx, domain.PeerTypeUser, ownerUserID, domain.ProfilePhotoKindProfile); err != nil {
|
if photo, found, err := r.deps.Files.CurrentProfilePhotoKind(ctx, domain.PeerTypeUser, ownerUserID, domain.ProfilePhotoKindProfile); err != nil {
|
||||||
return internalErr()
|
return internalErr()
|
||||||
|
|
|
||||||
|
|
@ -30,15 +30,16 @@ ON CONFLICT (user_id) DO NOTHING`, userID)
|
||||||
|
|
||||||
func reserveUserPts(ctx context.Context, db sqlcgen.DBTX, userID int64, count int) (int, error) {
|
func reserveUserPts(ctx context.Context, db sqlcgen.DBTX, userID int64, count int) (int, error) {
|
||||||
count = normalizePtsCount(count)
|
count = normalizePtsCount(count)
|
||||||
if err := ensureUserUpdateWatermark(ctx, db, userID); err != nil {
|
// Single upsert instead of ensure-insert-then-update: this runs on every
|
||||||
return 0, err
|
// message send/update event, and the ensure step was a no-op round trip
|
||||||
}
|
// for every user past their first-ever pts allocation.
|
||||||
var pts int
|
var pts int
|
||||||
if err := db.QueryRow(ctx, `
|
if err := db.QueryRow(ctx, `
|
||||||
UPDATE user_update_watermarks
|
INSERT INTO user_update_watermarks (user_id, contiguous_pts)
|
||||||
SET contiguous_pts = contiguous_pts + $2,
|
VALUES ($1, $2)
|
||||||
|
ON CONFLICT (user_id) DO UPDATE
|
||||||
|
SET contiguous_pts = user_update_watermarks.contiguous_pts + $2,
|
||||||
updated_at = now()
|
updated_at = now()
|
||||||
WHERE user_id = $1
|
|
||||||
RETURNING contiguous_pts`, userID, count).Scan(&pts); err != nil {
|
RETURNING contiguous_pts`, userID, count).Scan(&pts); err != nil {
|
||||||
return 0, fmt.Errorf("reserve user pts: %w", err)
|
return 0, fmt.Errorf("reserve user pts: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue