This commit is contained in:
617
docs/DATA_SCHEMAS.md
Normal file
617
docs/DATA_SCHEMAS.md
Normal file
@@ -0,0 +1,617 @@
|
||||
# Полные схемы данных для мобильного приложения Women's Safety App
|
||||
|
||||
## 📋 Содержание
|
||||
1. [Схемы авторизации](#схемы-авторизации)
|
||||
2. [Схемы пользователей](#схемы-пользователей)
|
||||
3. [Схемы экстренных ситуаций](#схемы-экстренных-ситуаций)
|
||||
4. [Схемы местоположения](#схемы-местоположения)
|
||||
5. [Схемы уведомлений](#схемы-уведомлений)
|
||||
6. [Схемы календаря](#схемы-календаря)
|
||||
7. [TypeScript интерфейсы](#typescript-интерфейсы)
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Схемы авторизации
|
||||
|
||||
### UserRegister
|
||||
```json
|
||||
{
|
||||
"username": "string", // Имя пользователя (уникальное)
|
||||
"email": "string", // Email (уникальный)
|
||||
"password": "string", // Пароль (мин 8 символов)
|
||||
"full_name": "string?", // Полное имя
|
||||
"phone": "string?", // Номер телефона
|
||||
"date_of_birth": "date?", // Дата рождения (YYYY-MM-DD)
|
||||
"bio": "string?" // Биография (макс 500 символов)
|
||||
}
|
||||
```
|
||||
|
||||
### UserLogin
|
||||
```json
|
||||
{
|
||||
"username": "string?", // Имя пользователя ИЛИ
|
||||
"email": "string?", // Email (один из двух обязателен)
|
||||
"password": "string" // Пароль
|
||||
}
|
||||
```
|
||||
|
||||
### TokenResponse
|
||||
```json
|
||||
{
|
||||
"access_token": "string", // JWT токен
|
||||
"token_type": "bearer" // Тип токена
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 👤 Схемы пользователей
|
||||
|
||||
### UserProfile (полная информация)
|
||||
```json
|
||||
{
|
||||
"id": 123, // int, ID пользователя
|
||||
"uuid": "8ed4fb51-8a90-4b22...", // string, UUID пользователя
|
||||
"username": "testuser", // string, имя пользователя
|
||||
"email": "test@example.com", // string, email
|
||||
"phone": "+1234567890", // string?, телефон
|
||||
"first_name": "John", // string?, имя
|
||||
"last_name": "Doe", // string?, фамилия
|
||||
"date_of_birth": "1990-01-01", // date?, дата рождения
|
||||
"bio": "Краткая биография", // string?, биография
|
||||
"avatar_url": "https://...", // string?, URL аватара
|
||||
"location_sharing_enabled": true, // bool, разрешение на геолокацию
|
||||
"emergency_notifications_enabled": true, // bool, экстренные уведомления
|
||||
"push_notifications_enabled": true, // bool, push уведомления
|
||||
"email_verified": false, // bool, email подтвержден
|
||||
"phone_verified": true, // bool, телефон подтвержден
|
||||
"is_active": true, // bool, активен ли аккаунт
|
||||
"created_at": "2024-01-01T10:00:00Z", // datetime, дата регистрации
|
||||
"updated_at": "2024-01-15T10:00:00Z" // datetime, последнее обновление
|
||||
}
|
||||
```
|
||||
|
||||
### UserDashboard
|
||||
```json
|
||||
{
|
||||
"user_info": { // Краткая информация о пользователе
|
||||
"id": 123,
|
||||
"username": "testuser",
|
||||
"email": "test@example.com",
|
||||
"first_name": "John",
|
||||
"last_name": "Doe"
|
||||
},
|
||||
"recent_alerts": [ // Последние оповещения (макс 5)
|
||||
{
|
||||
"id": 456,
|
||||
"alert_type": "general",
|
||||
"message": "Нужна помощь",
|
||||
"created_at": "2024-01-15T09:30:00Z",
|
||||
"is_resolved": false,
|
||||
"responded_users_count": 2
|
||||
}
|
||||
],
|
||||
"emergency_contacts": [ // Экстренные контакты
|
||||
{
|
||||
"id": 789,
|
||||
"name": "Мама",
|
||||
"phone_number": "+1234567890",
|
||||
"relationship": "mother"
|
||||
}
|
||||
],
|
||||
"safety_stats": { // Статистика безопасности
|
||||
"total_alerts_created": 5,
|
||||
"total_responses_given": 12,
|
||||
"safety_checks_count": 45,
|
||||
"last_safety_check": "2024-01-15T08:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### EmergencyContact
|
||||
```json
|
||||
{
|
||||
"id": 789, // int, ID контакта
|
||||
"uuid": "8ed4fb51-8a90-4b22...", // string, UUID контакта
|
||||
"user_id": 123, // int, ID владельца
|
||||
"name": "Мама", // string, имя контакта
|
||||
"phone_number": "+1234567890", // string, телефон
|
||||
"relationship": "mother", // string?, отношение (mother, father, spouse, friend, etc.)
|
||||
"notes": "Основной контакт", // string?, заметки
|
||||
"is_active": true, // bool, активен ли контакт
|
||||
"created_at": "2024-01-01T10:00:00Z", // datetime, дата создания
|
||||
"updated_at": "2024-01-15T10:00:00Z" // datetime, последнее обновление
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🆘 Схемы экстренных ситуаций
|
||||
|
||||
### EmergencyAlert (полная схема)
|
||||
```json
|
||||
{
|
||||
"id": 123, // int, ID оповещения
|
||||
"uuid": "8ed4fb51-8a90-4b22...", // string, UUID оповещения
|
||||
"user_id": 26, // int, ID создателя
|
||||
"latitude": 55.7558, // float, широта (-90 до 90)
|
||||
"longitude": 37.6176, // float, долгота (-180 до 180)
|
||||
"address": "Красная площадь, Москва", // string?, адрес
|
||||
"alert_type": "general", // AlertType, тип оповещения
|
||||
"status": "active", // AlertStatus, текущий статус
|
||||
"message": "Нужна помощь", // string?, описание ситуации
|
||||
"is_resolved": false, // bool, решено ли
|
||||
"resolved_at": null, // datetime?, время решения
|
||||
"resolved_by": null, // int?, кем решено
|
||||
"resolved_notes": null, // string?, заметки о решении
|
||||
"contact_emergency_services": true, // bool, связаться со службами
|
||||
"notify_emergency_contacts": true, // bool, уведомить контакты
|
||||
"notified_users_count": 5, // int, количество уведомленных
|
||||
"responded_users_count": 2, // int, количество откликнувшихся
|
||||
"created_at": "2024-01-15T10:30:00Z", // datetime, время создания
|
||||
"updated_at": "2024-01-15T10:35:00Z" // datetime, последнее обновление
|
||||
}
|
||||
```
|
||||
|
||||
### EmergencyResponse (отклик на оповещение)
|
||||
```json
|
||||
{
|
||||
"id": 456, // int, ID отклика
|
||||
"uuid": "8ed4fb51-8a90-4b22...", // string, UUID отклика
|
||||
"alert_id": 123, // int, ID оповещения
|
||||
"user_id": 27, // int, ID откликнувшегося
|
||||
"response_type": "help_on_way", // ResponseType, тип отклика
|
||||
"message": "Еду к вам!", // string?, сообщение
|
||||
"eta_minutes": 15, // int?, время прибытия в минутах
|
||||
"location_sharing": true, // bool, делиться местоположением
|
||||
"created_at": "2024-01-15T10:32:00Z" // datetime, время создания
|
||||
}
|
||||
```
|
||||
|
||||
### EmergencyReport (отчет о происшествии)
|
||||
```json
|
||||
{
|
||||
"id": 789, // int, ID отчета
|
||||
"uuid": "8ed4fb51-8a90-4b22...", // string, UUID отчета
|
||||
"user_id": 26, // int?, ID автора (null для анонимных)
|
||||
"latitude": 55.7558, // float, широта
|
||||
"longitude": 37.6176, // float, долгота
|
||||
"address": "Тверская улица", // string?, адрес
|
||||
"report_type": "harassment", // string, тип происшествия
|
||||
"description": "Подробное описание...", // string, описание (10-1000 символов)
|
||||
"is_anonymous": false, // bool, анонимный отчет
|
||||
"severity": 4, // int, серьезность (1-5)
|
||||
"status": "pending", // string, статус (pending/investigating/resolved)
|
||||
"created_at": "2024-01-15T10:45:00Z" // datetime, время создания
|
||||
}
|
||||
```
|
||||
|
||||
### SafetyCheck (отметка безопасности)
|
||||
```json
|
||||
{
|
||||
"id": 101, // int, ID отметки
|
||||
"uuid": "8ed4fb51-8a90-4b22...", // string, UUID отметки
|
||||
"user_id": 26, // int, ID пользователя
|
||||
"message": "Добрался домой безопасно", // string?, сообщение
|
||||
"location_latitude": 55.7600, // float?, широта
|
||||
"location_longitude": 37.6100, // float?, долгота
|
||||
"created_at": "2024-01-15T22:00:00Z" // datetime, время создания
|
||||
}
|
||||
```
|
||||
|
||||
### EmergencyStatistics
|
||||
```json
|
||||
{
|
||||
"total_alerts": 150, // int, общее количество оповещений
|
||||
"active_alerts": 12, // int, активные оповещения
|
||||
"resolved_alerts": 138, // int, решенные оповещения
|
||||
"total_responders": 89, // int, всего откликнувшихся
|
||||
"avg_response_time_minutes": 8.5 // float, среднее время отклика
|
||||
}
|
||||
```
|
||||
|
||||
### NearbyAlert (ближайшие оповещения)
|
||||
```json
|
||||
{
|
||||
"id": 123, // int, ID оповещения
|
||||
"alert_type": "medical", // string, тип оповещения
|
||||
"latitude": 55.7558, // float, широта
|
||||
"longitude": 37.6176, // float, долгота
|
||||
"address": "Больница №1", // string?, адрес
|
||||
"distance_km": 2.5, // float, расстояние в километрах
|
||||
"created_at": "2024-01-15T09:15:00Z", // datetime, время создания
|
||||
"responded_users_count": 3 // int, количество откликов
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📍 Схемы местоположения
|
||||
|
||||
### LocationUpdate
|
||||
```json
|
||||
{
|
||||
"latitude": 55.7558, // float, широта (-90 до 90)
|
||||
"longitude": 37.6176, // float, долгота (-180 до 180)
|
||||
"accuracy": 10.0, // float?, точность в метрах
|
||||
"altitude": 150.0, // float?, высота в метрах
|
||||
"speed": 0.0, // float?, скорость м/с
|
||||
"heading": 90.0, // float?, направление (0-360 градусов)
|
||||
"timestamp": "2024-01-15T10:30:00Z" // datetime?, время получения координат
|
||||
}
|
||||
```
|
||||
|
||||
### NearbyUser
|
||||
```json
|
||||
{
|
||||
"user_id": 27, // int, ID пользователя
|
||||
"distance_meters": 500.0, // float, расстояние в метрах
|
||||
"latitude": 55.7568, // float, широта (может быть приблизительной)
|
||||
"longitude": 37.6186, // float, долгота (может быть приблизительной)
|
||||
"last_seen": "2024-01-15T10:25:00Z", // datetime, последнее обновление местоположения
|
||||
"is_available": true // bool, готов ли помочь
|
||||
}
|
||||
```
|
||||
|
||||
### GeocodeResult
|
||||
```json
|
||||
{
|
||||
"address": "Красная площадь, 1, Москва, Россия", // string, полный адрес
|
||||
"street": "Красная площадь", // string?, улица
|
||||
"house_number": "1", // string?, номер дома
|
||||
"city": "Москва", // string?, город
|
||||
"state": "Москва", // string?, регион/область
|
||||
"country": "Россия", // string?, страна
|
||||
"postal_code": "109012", // string?, почтовый индекс
|
||||
"latitude": 55.7558, // float, широта
|
||||
"longitude": 37.6176 // float, долгота
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔔 Схемы уведомлений
|
||||
|
||||
### NotificationCreate
|
||||
```json
|
||||
{
|
||||
"user_id": 123, // int, ID получателя
|
||||
"title": "Экстренное оповещение", // string, заголовок
|
||||
"message": "Новое оповещение рядом с вами", // string, текст сообщения
|
||||
"type": "emergency_alert", // string, тип уведомления
|
||||
"data": { // object?, дополнительные данные
|
||||
"alert_id": 456,
|
||||
"latitude": 55.7558,
|
||||
"longitude": 37.6176
|
||||
},
|
||||
"priority": "high", // string?, приоритет (low/normal/high/urgent)
|
||||
"schedule_at": null // datetime?, время отправки (null = сейчас)
|
||||
}
|
||||
```
|
||||
|
||||
### PushTokenRegistration
|
||||
```json
|
||||
{
|
||||
"token": "fcm_or_apns_token_here", // string, токен устройства
|
||||
"platform": "ios", // string, платформа (ios/android)
|
||||
"app_version": "1.0.0", // string?, версия приложения
|
||||
"device_info": { // object?, информация об устройстве
|
||||
"model": "iPhone 14",
|
||||
"os_version": "17.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### NotificationHistory
|
||||
```json
|
||||
{
|
||||
"id": 789, // int, ID уведомления
|
||||
"user_id": 123, // int, ID получателя
|
||||
"title": "Экстренное оповещение", // string, заголовок
|
||||
"message": "Текст уведомления", // string, сообщение
|
||||
"type": "emergency_alert", // string, тип
|
||||
"status": "delivered", // string, статус (sent/delivered/read/failed)
|
||||
"sent_at": "2024-01-15T10:30:00Z", // datetime, время отправки
|
||||
"delivered_at": "2024-01-15T10:30:05Z", // datetime?, время доставки
|
||||
"read_at": null // datetime?, время прочтения
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📅 Схемы календаря
|
||||
|
||||
### CalendarEntry
|
||||
```json
|
||||
{
|
||||
"id": 456, // int, ID записи
|
||||
"user_id": 123, // int, ID пользователя
|
||||
"date": "2024-01-15", // date, дата записи (YYYY-MM-DD)
|
||||
"entry_type": "period_start", // string, тип записи
|
||||
"notes": "Болезненные ощущения", // string?, заметки
|
||||
"mood_score": 3, // int?, настроение (1-5)
|
||||
"energy_level": 4, // int?, уровень энергии (1-5)
|
||||
"symptoms": [ // array?, симптомы
|
||||
"headache",
|
||||
"fatigue",
|
||||
"cramps"
|
||||
],
|
||||
"flow_intensity": "medium", // string?, интенсивность (light/medium/heavy)
|
||||
"temperature": 36.6, // float?, температура тела
|
||||
"weight": 65.5, // float?, вес
|
||||
"created_at": "2024-01-15T08:00:00Z", // datetime, время создания
|
||||
"updated_at": "2024-01-15T08:05:00Z" // datetime, последнее обновление
|
||||
}
|
||||
```
|
||||
|
||||
### CalendarAnalytics
|
||||
```json
|
||||
{
|
||||
"cycle_length": 28, // int?, средняя длина цикла
|
||||
"period_length": 5, // int?, средняя длина месячных
|
||||
"next_period_prediction": "2024-02-10", // date?, прогноз следующих месячных
|
||||
"fertility_window": { // object?, окно фертильности
|
||||
"start": "2024-01-20",
|
||||
"end": "2024-01-25"
|
||||
},
|
||||
"mood_trends": { // object?, тренды настроения
|
||||
"average_score": 3.5,
|
||||
"lowest_day": 2,
|
||||
"highest_day": 12
|
||||
},
|
||||
"symptoms_frequency": { // object?, частота симптомов
|
||||
"headache": 0.3,
|
||||
"cramps": 0.8,
|
||||
"fatigue": 0.6
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📱 TypeScript интерфейсы
|
||||
|
||||
```typescript
|
||||
// Перечисления
|
||||
export enum AlertType {
|
||||
GENERAL = 'general',
|
||||
MEDICAL = 'medical',
|
||||
VIOLENCE = 'violence',
|
||||
HARASSMENT = 'harassment',
|
||||
UNSAFE_AREA = 'unsafe_area',
|
||||
ACCIDENT = 'accident',
|
||||
FIRE = 'fire',
|
||||
NATURAL_DISASTER = 'natural_disaster'
|
||||
}
|
||||
|
||||
export enum AlertStatus {
|
||||
ACTIVE = 'active',
|
||||
RESOLVED = 'resolved',
|
||||
CANCELLED = 'cancelled',
|
||||
INVESTIGATING = 'investigating'
|
||||
}
|
||||
|
||||
export enum ResponseType {
|
||||
HELP_ON_WAY = 'help_on_way',
|
||||
CONTACTED_AUTHORITIES = 'contacted_authorities',
|
||||
SAFE_NOW = 'safe_now',
|
||||
FALSE_ALARM = 'false_alarm',
|
||||
INVESTIGATING = 'investigating',
|
||||
RESOLVED = 'resolved'
|
||||
}
|
||||
|
||||
// Интерфейсы авторизации
|
||||
export interface UserRegister {
|
||||
username: string;
|
||||
email: string;
|
||||
password: string;
|
||||
full_name?: string;
|
||||
phone?: string;
|
||||
date_of_birth?: string;
|
||||
bio?: string;
|
||||
}
|
||||
|
||||
export interface UserLogin {
|
||||
username?: string;
|
||||
email?: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface TokenResponse {
|
||||
access_token: string;
|
||||
token_type: string;
|
||||
}
|
||||
|
||||
// Интерфейсы пользователя
|
||||
export interface UserProfile {
|
||||
id: number;
|
||||
uuid: string;
|
||||
username: string;
|
||||
email: string;
|
||||
phone?: string;
|
||||
first_name?: string;
|
||||
last_name?: string;
|
||||
date_of_birth?: string;
|
||||
bio?: string;
|
||||
avatar_url?: string;
|
||||
location_sharing_enabled: boolean;
|
||||
emergency_notifications_enabled: boolean;
|
||||
push_notifications_enabled: boolean;
|
||||
email_verified: boolean;
|
||||
phone_verified: boolean;
|
||||
is_active: boolean;
|
||||
created_at: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
export interface EmergencyContact {
|
||||
id: number;
|
||||
uuid: string;
|
||||
user_id: number;
|
||||
name: string;
|
||||
phone_number: string;
|
||||
relationship?: string;
|
||||
notes?: string;
|
||||
is_active: boolean;
|
||||
created_at: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
// Интерфейсы экстренных ситуаций
|
||||
export interface EmergencyAlertCreate {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
alert_type: AlertType;
|
||||
message?: string;
|
||||
address?: string;
|
||||
contact_emergency_services?: boolean;
|
||||
notify_emergency_contacts?: boolean;
|
||||
}
|
||||
|
||||
export interface EmergencyAlert {
|
||||
id: number;
|
||||
uuid: string;
|
||||
user_id: number;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
address?: string;
|
||||
alert_type: AlertType;
|
||||
status: AlertStatus;
|
||||
message?: string;
|
||||
is_resolved: boolean;
|
||||
resolved_at?: string;
|
||||
resolved_by?: number;
|
||||
resolved_notes?: string;
|
||||
contact_emergency_services: boolean;
|
||||
notify_emergency_contacts: boolean;
|
||||
notified_users_count: number;
|
||||
responded_users_count: number;
|
||||
created_at: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
export interface EmergencyResponseCreate {
|
||||
response_type: ResponseType;
|
||||
message?: string;
|
||||
eta_minutes?: number;
|
||||
location_sharing?: boolean;
|
||||
}
|
||||
|
||||
export interface EmergencyResponse {
|
||||
id: number;
|
||||
uuid: string;
|
||||
alert_id: number;
|
||||
user_id: number;
|
||||
response_type: ResponseType;
|
||||
message?: string;
|
||||
eta_minutes?: number;
|
||||
location_sharing: boolean;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface EmergencyStatistics {
|
||||
total_alerts: number;
|
||||
active_alerts: number;
|
||||
resolved_alerts: number;
|
||||
total_responders: number;
|
||||
avg_response_time_minutes: number;
|
||||
}
|
||||
|
||||
export interface NearbyAlert {
|
||||
id: number;
|
||||
alert_type: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
address?: string;
|
||||
distance_km: number;
|
||||
created_at: string;
|
||||
responded_users_count: number;
|
||||
}
|
||||
|
||||
// Интерфейсы местоположения
|
||||
export interface LocationUpdate {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
accuracy?: number;
|
||||
altitude?: number;
|
||||
speed?: number;
|
||||
heading?: number;
|
||||
timestamp?: string;
|
||||
}
|
||||
|
||||
export interface NearbyUser {
|
||||
user_id: number;
|
||||
distance_meters: number;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
last_seen: string;
|
||||
is_available: boolean;
|
||||
}
|
||||
|
||||
// Интерфейсы уведомлений
|
||||
export interface PushTokenRegistration {
|
||||
token: string;
|
||||
platform: 'ios' | 'android';
|
||||
app_version?: string;
|
||||
device_info?: {
|
||||
model?: string;
|
||||
os_version?: string;
|
||||
};
|
||||
}
|
||||
|
||||
// API клиент
|
||||
export interface APIClient {
|
||||
setToken(token: string): void;
|
||||
login(username: string, password: string): Promise<TokenResponse>;
|
||||
register(userData: UserRegister): Promise<UserProfile>;
|
||||
getProfile(): Promise<UserProfile>;
|
||||
createAlert(alertData: EmergencyAlertCreate): Promise<EmergencyAlert>;
|
||||
getNearbyAlerts(lat: number, lng: number, radius?: number): Promise<NearbyAlert[]>;
|
||||
respondToAlert(alertId: number, response: EmergencyResponseCreate): Promise<EmergencyResponse>;
|
||||
updateLocation(location: LocationUpdate): Promise<void>;
|
||||
createSafetyCheck(data: { message?: string; location_latitude?: number; location_longitude?: number }): Promise<any>;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 Валидация данных
|
||||
|
||||
### Ограничения полей
|
||||
- **Координаты**: latitude (-90 до 90), longitude (-180 до 180)
|
||||
- **Пароль**: минимум 8 символов, максимум 70 (для совместимости с bcrypt)
|
||||
- **Email**: валидный формат email
|
||||
- **Телефон**: рекомендуется международный формат (+1234567890)
|
||||
- **Сообщения**: максимум 500 символов для alert message, 200 для safety check
|
||||
- **Описания**: 10-1000 символов для emergency reports
|
||||
|
||||
### Обязательные поля
|
||||
- При регистрации: username, email, password
|
||||
- При создании оповещения: latitude, longitude, alert_type
|
||||
- При отклике: response_type
|
||||
- При обновлении местоположения: latitude, longitude
|
||||
|
||||
### Рекомендации по обработке ошибок
|
||||
```typescript
|
||||
interface APIError {
|
||||
detail: string;
|
||||
status_code: number;
|
||||
field_errors?: {
|
||||
[field: string]: string[];
|
||||
};
|
||||
}
|
||||
|
||||
// Обработка ошибок валидации
|
||||
try {
|
||||
await api.createAlert(alertData);
|
||||
} catch (error) {
|
||||
if (error.status_code === 422) {
|
||||
// Показать ошибки валидации для каждого поля
|
||||
Object.entries(error.field_errors || {}).forEach(([field, errors]) => {
|
||||
console.error(`${field}: ${errors.join(', ')}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Эти схемы данных обеспечивают полную интеграцию мобильного приложения с Women's Safety App API.
|
||||
Reference in New Issue
Block a user