Compare commits

..

No commits in common. "3328ccbeab979ae7e732aa7e7ff56e588dfa570d" and "66091ede72de94c5ddc49758b822d81bf11a0acc" have entirely different histories.

7 changed files with 5 additions and 79 deletions

View file

@ -1007,12 +1007,10 @@ func run(logger *zap.Logger) error {
account.WithLoginEmailVerification(codeStore, loginEmailSender, cfg.AuthCodeTTL, cfg.AuthCodeMaxAttempts, cfg.LoginEmailCodeLength))
}
accountService := account.NewService(passwordStore, accountOptions...)
reservedUsernameStore := postgres.NewReservedUsernameStore(pool)
botsService := botsapp.NewService(userStore, botStore, messageStore,
botsapp.WithLogger(logger.Named("bots")),
botsapp.WithBlockChecker(contactStore),
botsapp.WithPublicChannelUsernameResolver(channelStore),
botsapp.WithReservedUsernames(reservedUsernameStore),
botsapp.WithUserCache(userCache),
botsapp.WithStickerSetCreator(filesService),
botsapp.WithGifCatalogSource(filesService),
@ -1205,6 +1203,7 @@ func run(logger *zap.Logger) error {
// Collectible (NFT) usernames are an optional read model projected at the
// protocol edge.
collectibleUsernameStore := postgres.NewCollectibleUsernameStore(pool)
reservedUsernameStore := postgres.NewReservedUsernameStore(pool)
usernamesService := usernamesapp.NewService(
usernamesapp.WithRegistryStore(collectibleUsernameStore),
usernamesapp.WithCollectibleStore(collectibleUsernameStore),

View file

@ -121,7 +121,6 @@ type Service struct {
messages store.MessageStore
blocker blockChecker
channels publicChannelUsernameResolver
reserved reservedUsernameChecker
stickers stickerSetCreator
installer userStickerSetInstaller
aiChat aiChatGenerator
@ -200,21 +199,6 @@ func WithBotAvatarStore(a botAvatarStore) Option {
// WithPublicChannelUsernameResolver 注入公开频道 username 查询能力,用于 bot
// username 预检,避免 bot 与 public channel 产生同名可见入口。
// reservedUsernameChecker reports whether a name is on the operator blocklist.
type reservedUsernameChecker interface {
IsReserved(ctx context.Context, usernameLower string) (bool, error)
}
// WithReservedUsernames wires the operator username blocklist so CheckUsername
// reports a reserved bot name as taken instead of available.
func WithReservedUsernames(c reservedUsernameChecker) Option {
return func(s *Service) {
if c != nil {
s.reserved = c
}
}
}
func WithPublicChannelUsernameResolver(c publicChannelUsernameResolver) Option {
return func(s *Service) {
if c != nil {
@ -549,13 +533,6 @@ func (s *Service) CheckUsername(ctx context.Context, ownerUserID int64, username
if !domain.ValidBotUsername(username) {
return false, domain.ErrBotUsernameInvalid
}
if s.reserved != nil {
if r, err := s.reserved.IsReserved(ctx, strings.ToLower(username)); err != nil {
return false, err
} else if r {
return false, nil
}
}
if _, found, err := s.users.ByUsername(ctx, username); err != nil {
return false, err
} else if found {

View file

@ -128,9 +128,6 @@ func (s *ChannelStore) CheckUsername(_ context.Context, userID, channelID int64,
return false, err
}
usernameLower := strings.ToLower(strings.TrimSpace(strings.TrimPrefix(username, "@")))
if s.usernameRegistry != nil && s.usernameRegistry.nameReserved(usernameLower) {
return false, nil
}
for id, channel := range s.channels {
if channel.Deleted || channel.Username == "" {
continue

View file

@ -67,10 +67,8 @@ func (s *CollectibleUsernameStore) WithReservedUsernames(reserved *ReservedUsern
return s
}
// nameReserved reports whether a name is on the operator blocklist. It touches
// only s.reserved (its own lock), so it is safe from any context.
func (s *CollectibleUsernameStore) nameReserved(usernameLower string) bool {
if s == nil || s.reserved == nil {
func (s *CollectibleUsernameStore) nameReservedLocked(usernameLower string) bool {
if s.reserved == nil {
return false
}
r, _ := s.reserved.IsReserved(context.Background(), usernameLower)
@ -121,7 +119,7 @@ func (s *CollectibleUsernameStore) SetEditableUsername(_ context.Context, peer d
return false, domain.ErrUsernameInvalid
}
key := strings.ToLower(username)
if s.nameReserved(key) {
if s.nameReservedLocked(key) {
return false, domain.ErrUsernameOccupied
}
if existing, ok := s.registry[key]; ok {
@ -336,7 +334,7 @@ func (s *CollectibleUsernameStore) MintCollectibleUsername(_ context.Context, re
if _, ok := s.registry[key]; ok {
return domain.CollectibleUsername{}, false, domain.ErrUsernameOccupied
}
if s.nameReserved(key) {
if s.nameReservedLocked(key) {
return domain.CollectibleUsername{}, false, domain.ErrUsernameOccupied
}
now := time.Now().UTC()

View file

@ -1,37 +0,0 @@
package memory
import (
"context"
"testing"
"telesrv/internal/domain"
)
func TestCheckUsernameReportsReservedAsTaken(t *testing.T) {
ctx := context.Background()
reserved := NewReservedUsernameStore()
if _, err := reserved.ReserveUsername(ctx, "support", "official", "ops"); err != nil {
t.Fatalf("seed reserve: %v", err)
}
registry := NewCollectibleUsernameStore().WithReservedUsernames(reserved)
users := NewUserStore()
users.AttachUsernameRegistry(registry)
u, _ := users.Create(ctx, domain.User{AccessHash: 1, Phone: "15550001000", FirstName: "A"})
if ok, err := users.CheckUsername(ctx, u.ID, "support"); err != nil || ok {
t.Fatalf("CheckUsername(reserved) = %v, %v; want false, nil", ok, err)
}
if ok, err := users.CheckUsername(ctx, u.ID, "freename"); err != nil || !ok {
t.Fatalf("CheckUsername(free) = %v, %v; want true, nil", ok, err)
}
channels := NewChannelStore()
channels.AttachUsernameRegistry(registry)
created, err := channels.CreateChannel(ctx, domain.CreateChannelRequest{CreatorUserID: u.ID, Title: "C", Megagroup: true, Date: 1})
if err != nil {
t.Fatalf("create channel: %v", err)
}
if ok, err := channels.CheckUsername(ctx, u.ID, created.Channel.ID, "support"); err != nil || ok {
t.Fatalf("channel CheckUsername(reserved) = %v, %v; want false, nil", ok, err)
}
}

View file

@ -158,9 +158,6 @@ func (s *UserStore) CheckUsername(_ context.Context, userID int64, username stri
if username == "" {
return true, nil
}
if s.usernameRegistry != nil && s.usernameRegistry.nameReserved(username) {
return false, nil
}
s.mu.RLock()
defer s.mu.RUnlock()
for id, u := range s.byID {

View file

@ -77,11 +77,6 @@ func usernameReservedTx(ctx context.Context, db sqlcgen.DBTX, usernameLower stri
}
func peerUsernameAvailable(ctx context.Context, db sqlcgen.DBTX, usernameLower, peerType string, peerID int64) (bool, error) {
if reserved, err := usernameReservedTx(ctx, db, usernameLower); err != nil {
return false, err
} else if reserved {
return false, nil
}
owner, found, err := getPeerUsernameOwner(ctx, db, usernameLower, false)
if err != nil || !found {
return !found, err