Фронт: вход по паролю, окно установки пароля, профиль и админка
Страница входа начинается с карточки «Вход» (ник и пароль), в ней же переключатель на регистрацию с повтором пароля. Telegram стоит ниже вторым способом. Под ним подсказка: аккаунт, созданный по паролю, сначала привязывают к Telegram в профиле, иначе вход через Telegram заведёт новый аккаунт. Поле логина без автозаглавной буквы и автоисправления, у полей правильный autocomplete для менеджеров паролей. SetPasswordDialog показывается в AppShell всем, у кого has_password=false: аккаунтам из Telegram и созданным до паролей. Закрыть окно нельзя, только задать пароль или выйти. Успешный ответ кладёт в кэш профиль с has_password=true, и окно исчезает само. Кнопка «Позже (dev)» есть лишь под import.meta.env.DEV: в прод-бандле её строки нет, это проверено по dist. В профиле появилась карточка «Вход в аккаунт»: смена пароля (текущий, если он задан, новый и повтор) и привязка Telegram тем же виджетом, что при входе. У смены ника подсказка, что ник — это и логин. В админке у игрока появилась кнопка «Задать пароль» с вводом в той же строке, как у переименования, а в строке игрока — пометка «без пароля». schema.d.ts перегенерирован с живого бэкенда. #24 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6
This commit is contained in:
@@ -203,6 +203,21 @@ export function useAdminUpdateUser() {
|
||||
});
|
||||
}
|
||||
|
||||
// Новый пароль игроку — когда он забыл свой (почту приложение не хранит).
|
||||
export function useAdminSetPassword() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (args: { userId: number; password: string }) =>
|
||||
unwrap(
|
||||
await api.PUT("/api/admin/users/{user_id}/password", {
|
||||
params: { path: { user_id: args.userId } },
|
||||
body: { new_password: args.password },
|
||||
}),
|
||||
),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: qk.adminUsers }),
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Ачивки (редактор; выдача игрокам — на будущее) ──────────────────────────
|
||||
|
||||
interface AchievementInput {
|
||||
|
||||
@@ -45,6 +45,52 @@ export function useStubLogin() {
|
||||
});
|
||||
}
|
||||
|
||||
// Вход по логину (нику) и паролю. Перебор сервер ограничивает: 429 TOO_MANY_ATTEMPTS.
|
||||
export function usePasswordLogin() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (body: { nickname: string; password: string }) =>
|
||||
unwrap(await api.POST("/api/auth/login", { body })),
|
||||
onSuccess: (me) => {
|
||||
qc.setQueryData(qk.me, me);
|
||||
qc.invalidateQueries();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Регистрация по логину (нику) и паролю; Telegram привязывается позже в профиле.
|
||||
export function useRegister() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (body: { nickname: string; password: string }) =>
|
||||
unwrap(await api.POST("/api/auth/register", { body })),
|
||||
onSuccess: (me) => {
|
||||
qc.setQueryData(qk.me, me);
|
||||
qc.invalidateQueries();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Задать пароль (первый раз — без текущего) или сменить его (нужен текущий).
|
||||
export function useSetPassword() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (body: { new_password: string; current_password?: string }): Promise<Me> =>
|
||||
unwrap(await api.PUT("/api/users/me/password", { body })),
|
||||
onSuccess: (me) => qc.setQueryData(qk.me, me),
|
||||
});
|
||||
}
|
||||
|
||||
// Привязка Telegram к аккаунту: тот же payload виджета, что при входе; ник не меняется.
|
||||
export function useLinkTelegram() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (payload: Record<string, unknown>): Promise<Me> =>
|
||||
unwrap(await api.POST("/api/users/me/telegram", { body: payload as never })),
|
||||
onSuccess: (me) => qc.setQueryData(qk.me, me),
|
||||
});
|
||||
}
|
||||
|
||||
// Вход через Telegram Login Widget: payload виджета проверяется на сервере по HMAC.
|
||||
// При первом входе, если тег занят, бэкенд отдаёт 409 TELEGRAM_NICKNAME_REQUIRED —
|
||||
// тогда вызывается useTelegramRegister с выбранным ником (тот же payload виджета).
|
||||
|
||||
Reference in New Issue
Block a user