/me віддає ім'я з бази: Principal.Username ніколи не заповнювався
Перша спроба віддавала p.Username — прогін на стенді показав порожнє. Поле оголошене в структурі й не присвоюється ніде, а в токені лежить лише пошта. Нова UserByID читає ім'я з core.users; класти його в токен означало б, що після перейменування людина чверть години бачить старе. Спіймано лише тому, що перевірка йшла наживо, а не на збірці. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
ae07bd2a79
commit
9f901f8b8e
3 changed files with 49 additions and 1 deletions
|
|
@ -7637,7 +7637,13 @@ NULL і валить запис. Правило без власних канал
|
|||
|
||||
**Виправлено чотири речі, кожна закриває свій бік:**
|
||||
|
||||
* `/me` віддає `username`. Одне поле, без якого решта марна.
|
||||
* `/me` віддає `username` — і читає його З БАЗИ. Перша спроба віддавала
|
||||
`Principal.Username` — і прогін на стенді показав порожнє: це поле
|
||||
оголошене в структурі й НЕ ЗАПОВНЮЄТЬСЯ НІДЕ, а в токені лежить лише
|
||||
пошта. Ще одна обіцянка без виконання — вловлена лише тому, що я
|
||||
перевірив виправлення наживо, а не на збірці. Новий `UserByID`
|
||||
читає ім'я з `core.users`: покласти його в токен означало б, що після
|
||||
перейменування людина чверть години бачить у шапці старе ім'я.
|
||||
* Вхід у профіль малюється ЗАВЖДИ для людини (не для машинного токена),
|
||||
з іконкою й підписом «Мій профіль» у підказці; без імені й пошти пише
|
||||
просто «Профіль» замість того, щоб зникнути.
|
||||
|
|
|
|||
|
|
@ -250,7 +250,24 @@ func (s *Server) handleMe(w http.ResponseWriter, r *http.Request, p *Principal)
|
|||
// з шапки. Разом із нею — прив'язка Telegram, без якої кнопки
|
||||
// під сповіщеннями відповідають «ваш Telegram не прив'язано» і
|
||||
// не дають виходу.
|
||||
// Ім'я читається з бази, а не з токена: Principal.Username
|
||||
// оголошене, але ніде не заповнюється, а в самому токені лежить
|
||||
// лише пошта. Класти туди ще й ім'я означало б, що після
|
||||
// перейменування людина чверть години бачить у шапці старе
|
||||
// ім'я й не має способу це виправити.
|
||||
//
|
||||
// Збій запиту не валить /me: без імені інтерфейс покаже
|
||||
// «Профіль», а без відповіді взагалі — викине людину на вхід.
|
||||
resp["username"] = p.Username
|
||||
if u, err := s.store.UserByID(r.Context(), p.UserID); err == nil {
|
||||
resp["username"] = u.Username
|
||||
if u.Email != "" {
|
||||
resp["email"] = u.Email
|
||||
}
|
||||
resp["full_name"] = u.FullName
|
||||
} else {
|
||||
s.log.Warn("читання облікового запису для /me", "err", err)
|
||||
}
|
||||
} else {
|
||||
resp["token_id"] = p.TokenID
|
||||
resp["token_name"] = p.TokenName
|
||||
|
|
|
|||
|
|
@ -709,3 +709,28 @@ func (s *Store) UpdateUserProfile(ctx context.Context, tenantID, userID string,
|
|||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// UserByID читає обліковий запис для сторінки профілю та /me.
|
||||
//
|
||||
// Окремий запит, а не поле в токені доступу, і це свідомо. Токен живе
|
||||
// 15 хвилин і підписаний: усе, що в нього покладено, після перейменування
|
||||
// користувача лишається старим до кінця цього строку — тобто людина
|
||||
// бачила б у шапці чуже, вже неіснуюче ім'я й не мала б способу це
|
||||
// виправити. Ціна — один короткий SELECT на виклик, який робиться раз на
|
||||
// завантаження сторінки.
|
||||
func (s *Store) UserByID(ctx context.Context, userID string) (*User, error) {
|
||||
var u User
|
||||
err := s.bg.QueryRow(ctx, `
|
||||
SELECT id::text, username, COALESCE(email,''), COALESCE(full_name,''),
|
||||
COALESCE(locale,'uk'), COALESCE(timezone,'UTC'), mfa_enabled
|
||||
FROM core.users WHERE id = $1
|
||||
`, userID).Scan(&u.ID, &u.Username, &u.Email, &u.FullName,
|
||||
&u.Locale, &u.Timezone, &u.MFA)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &u, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue