Files
portal/app/resources/js/api/account.ts
T
Дмитрий 2225a8487e
Accessibility (Pa11y live) / a11y (push) Has been cancelled
SAST — Semgrep / Semgrep SAST scan (push) Has been cancelled
feat(security): рабочая «Завершить сессию» — реальный отзыв активных сессий
UI-аудит: вкладка Безопасность показывала фейк-сессии с мёртвой кнопкой.
Теперь — реальные активные сессии + рабочий отзыв.

- UserSessionTracker (новый): запись сессии при входе (login + 2FA verify +
  recovery-use) в существующую таблицу user_sessions; отзыв = удаление строки
  + удаление сессии из Redis по session_id (реальный выход с устройства);
  logout снимает текущую сессию из списка. Best-effort (не ломает вход/выход).
- AccountController: GET /api/account/security отдаёт реальные сессии;
  DELETE /api/account/sessions/{id} — отзыв (только свои; чужая → 404).
- Фронт SessionsTable: список + кнопка «Завершить» (кроме текущей).
- phpstan-baseline обновлён (Pest-$this нового теста).

Pest: 10/10. Верификация: Playwright (2 сессии → «Завершить» → исчезла).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 17:15:26 +03:00

41 lines
1.3 KiB
TypeScript

import { apiClient } from './client';
export interface ActiveSession {
id: number;
device: string;
ip: string | null;
at: string | null;
current: boolean;
}
export interface AccountSecurity {
last_password_change_at: string | null;
sessions: ActiveSession[];
}
export interface ChangePasswordPayload {
current_password: string;
password: string;
password_confirmation: string;
}
/** GET /api/account/security — дата последней смены пароля + недавние входы. */
export async function getAccountSecurity(): Promise<AccountSecurity> {
const { data } = await apiClient.get<AccountSecurity>('/api/account/security');
return data;
}
/** POST /api/account/change-password — смена пароля. Возвращает ISO-дату смены. */
export async function changePassword(payload: ChangePasswordPayload): Promise<string> {
const { data } = await apiClient.post<{ last_password_change_at: string }>(
'/api/account/change-password',
payload,
);
return data.last_password_change_at;
}
/** DELETE /api/account/sessions/{id} — завершить (отозвать) сессию. */
export async function revokeSession(id: number): Promise<void> {
await apiClient.delete(`/api/account/sessions/${id}`);
}