Отключаемый модуль (instance_settings.consent_policy): галочка + ссылка на публичную страницу регламента на форме регистрации, редактируемый в админке текст с типовым шаблоном по умолчанию (плейсхолдеры под организацию, не проходил юридическую проверку), версия текста растёт при каждой правке. Факт согласия хранится в users (consent_version, consent_given_at) — второй эшелон проверки на сервере, как и для отключаемых модулей ранее. Дефолт (выключено) сохраняет поведение существующих инсталляций, у уже зарегистрированных пользователей согласие не запрашивается.
90 lines
4.0 KiB
TypeScript
90 lines
4.0 KiB
TypeScript
/**
|
||
* API-функции аутентификации.
|
||
*/
|
||
import { apiRequest } from '@/api/client'
|
||
|
||
export interface RegisterPayload {
|
||
email: string
|
||
name_user: string
|
||
password: string
|
||
/** Выбранная команда — только если выбор команды включён в настройках инстанса. */
|
||
team_id?: string | null
|
||
/** Согласие на обработку персональных данных — обязано быть `true`, если `consent_required`. */
|
||
consent_accepted?: boolean
|
||
}
|
||
|
||
/** Команда, доступная для выбора на экране регистрации. */
|
||
export interface RegistrationTeamOption {
|
||
id: string
|
||
name: string
|
||
}
|
||
|
||
/** Параметры экрана регистрации — зависят от настройки инстанса `registration_team_choice`. */
|
||
export interface RegistrationOptions {
|
||
team_choice_enabled: boolean
|
||
teams: RegistrationTeamOption[]
|
||
/** Эталонные домены почты при включённой верификации (email подходит под любой), иначе пуст. */
|
||
email_domains: string[]
|
||
/** Обязательна ли галочка согласия на обработку персональных данных на форме регистрации. */
|
||
consent_required: boolean
|
||
/** Текст регламента — отдаётся всегда, независимо от `consent_required` (нужен и странице регламента). */
|
||
consent_text: string
|
||
/** Номер редакции текста, с которой согласится пользователь при регистрации. */
|
||
consent_version: number
|
||
}
|
||
|
||
export interface CurrentUser {
|
||
id: string
|
||
email: string
|
||
name_user: string
|
||
role: string
|
||
/** URL загруженного аватара, `null` — показывается заглушка с инициалами (см. `Avatar`). */
|
||
avatar_url: string | null
|
||
team_id: string | null
|
||
team_name: string | null
|
||
}
|
||
|
||
export interface TokenResponse {
|
||
access_token: string
|
||
token_type: string
|
||
}
|
||
|
||
/** Регистрация нового пользователя. 409 — если email уже занят. */
|
||
export async function register(payload: RegisterPayload): Promise<void> {
|
||
await apiRequest('/auth/register', { method: 'POST', body: payload, skipAuthRefresh: true })
|
||
}
|
||
|
||
/**
|
||
* Параметры экрана регистрации — публичный эндпоинт, доступен без авторизации.
|
||
* Если выбор команды отключён в настройках инстанса, `teams` пуст.
|
||
*/
|
||
export async function getRegistrationOptions(): Promise<RegistrationOptions> {
|
||
return apiRequest<RegistrationOptions>('/auth/registration-options', { skipAuthRefresh: true })
|
||
}
|
||
|
||
/** Подтверждение почты по токену из письма. 400 — если ссылка просрочена/недействительна. */
|
||
export async function verifyEmail(token: string): Promise<void> {
|
||
await apiRequest('/auth/verify-email', { method: 'POST', body: { token }, skipAuthRefresh: true })
|
||
}
|
||
|
||
/**
|
||
* Вход по логину/паролю. Backend ждёт OAuth2-form-data (username=email).
|
||
* Refresh-токен приходит httpOnly cookie — тело ответа содержит только access.
|
||
*/
|
||
export async function login(email: string, password: string): Promise<TokenResponse> {
|
||
const form = new URLSearchParams()
|
||
form.set('username', email)
|
||
form.set('password', password)
|
||
return apiRequest<TokenResponse>('/auth/token', { method: 'POST', body: form, skipAuthRefresh: true })
|
||
}
|
||
|
||
/** Явный выход — инвалидирует refresh-сессию на backend. */
|
||
export async function logout(): Promise<void> {
|
||
await apiRequest('/auth/logout', { method: 'POST' })
|
||
}
|
||
|
||
/** Текущий пользователь по access-токену. */
|
||
export async function getMe(): Promise<CurrentUser> {
|
||
return apiRequest<CurrentUser>('/users/me')
|
||
}
|