Initial commit: ERV Platform MVP - FastAPI + React
This commit is contained in:
37
.env.example
Normal file
37
.env.example
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# ERV Platform Configuration Example
|
||||||
|
APP_NAME=ERV Insurance Platform
|
||||||
|
APP_ENV=development
|
||||||
|
DEBUG=true
|
||||||
|
BACKEND_URL=http://localhost:8000
|
||||||
|
FRONTEND_URL=http://localhost:5173
|
||||||
|
|
||||||
|
# PostgreSQL
|
||||||
|
POSTGRES_HOST=147.45.189.234
|
||||||
|
POSTGRES_PORT=5432
|
||||||
|
POSTGRES_DB=default_db
|
||||||
|
POSTGRES_USER=gen_user
|
||||||
|
POSTGRES_PASSWORD=2~~9_^kVsU?2\S
|
||||||
|
|
||||||
|
# Redis
|
||||||
|
REDIS_HOST=localhost
|
||||||
|
REDIS_PORT=6379
|
||||||
|
REDIS_PASSWORD=CRM_Redis_Pass_2025_Secure!
|
||||||
|
|
||||||
|
# RabbitMQ
|
||||||
|
RABBITMQ_HOST=185.197.75.249
|
||||||
|
RABBITMQ_PORT=5672
|
||||||
|
RABBITMQ_USER=admin
|
||||||
|
RABBITMQ_PASSWORD=tyejvtej
|
||||||
|
|
||||||
|
# S3
|
||||||
|
S3_BUCKET=f9825c87-4e3558f6-f9b6-405c-ad3d-d1535c49b61c
|
||||||
|
|
||||||
|
# OCR
|
||||||
|
OCR_API_URL=http://147.45.146.17:8001
|
||||||
|
|
||||||
|
# OpenRouter
|
||||||
|
OPENROUTER_API_KEY=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
|
||||||
|
OPENROUTER_MODEL=google/gemini-2.0-flash-001
|
||||||
|
|
||||||
|
# FlightAware
|
||||||
|
FLIGHTAWARE_API_KEY=Puz0cdxAHzAEqMRZwtdeqBUSm9naJfwK
|
||||||
56
.gitignore
vendored
Normal file
56
.gitignore
vendored
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Environment
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
*.env
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
venv/
|
||||||
|
env/
|
||||||
|
ENV/
|
||||||
|
.venv
|
||||||
|
|
||||||
|
# Node
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
dist/
|
||||||
|
dist-ssr/
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# IDEs
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Storage
|
||||||
|
storage/uploads/*
|
||||||
|
!storage/uploads/.gitkeep
|
||||||
|
storage/cache/*
|
||||||
|
!storage/cache/.gitkeep
|
||||||
|
|
||||||
|
# Build
|
||||||
|
build/
|
||||||
|
*.egg-info/
|
||||||
|
.pytest_cache/
|
||||||
|
.coverage
|
||||||
|
htmlcov/
|
||||||
|
|
||||||
|
# Temp
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
|
||||||
586
PROJECT_ARCHITECTURE.md
Normal file
586
PROJECT_ARCHITECTURE.md
Normal file
@@ -0,0 +1,586 @@
|
|||||||
|
# 🏗️ ERV Insurance Platform - Архитектура проекта
|
||||||
|
|
||||||
|
**Дата создания**: 24.10.2025
|
||||||
|
**Технологии**: Python FastAPI + React TypeScript
|
||||||
|
**Статус**: 🚧 В разработке
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Обзор архитектуры
|
||||||
|
|
||||||
|
### **Технологический стек:**
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ Frontend Layer (React) │
|
||||||
|
│ ├─ React 18 + TypeScript │
|
||||||
|
│ ├─ Vite (сборка) │
|
||||||
|
│ ├─ Ant Design (UI компоненты) │
|
||||||
|
│ ├─ React Query (API кеш) │
|
||||||
|
│ ├─ Zustand (state) │
|
||||||
|
│ └─ WebSocket для real-time │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
│ REST API + WebSocket
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ Backend Layer (Python FastAPI) │
|
||||||
|
│ ├─ FastAPI (асинхронный web framework) │
|
||||||
|
│ ├─ Pydantic (валидация данных) │
|
||||||
|
│ ├─ SQLAlchemy (PostgreSQL ORM) │
|
||||||
|
│ ├─ Redis (кеш, rate limiting) │
|
||||||
|
│ ├─ RabbitMQ (очереди задач) │
|
||||||
|
│ ├─ Celery (воркеры) │
|
||||||
|
│ └─ pytest (тесты) │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
├─► PostgreSQL (147.45.189.234) - логи, данные
|
||||||
|
├─► Redis (localhost:6379) - кеш
|
||||||
|
├─► RabbitMQ (185.197.75.249) - очереди
|
||||||
|
├─► MySQL (localhost) - проверка полисов
|
||||||
|
├─► OCR Service (147.45.146.17:8001)
|
||||||
|
├─► OpenRouter AI (Gemini Vision)
|
||||||
|
├─► FlightAware API
|
||||||
|
└─► S3 Timeweb Cloud
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 Структура проекта
|
||||||
|
|
||||||
|
```
|
||||||
|
erv_platform/
|
||||||
|
│
|
||||||
|
├─ backend/ ← Python FastAPI
|
||||||
|
│ ├─ app/
|
||||||
|
│ │ ├─ main.py ← Точка входа FastAPI
|
||||||
|
│ │ ├─ config.py ← Настройки из .env
|
||||||
|
│ │ │
|
||||||
|
│ │ ├─ api/ ← API endpoints
|
||||||
|
│ │ │ ├─ __init__.py
|
||||||
|
│ │ │ ├─ deps.py ← Зависимости (auth, db)
|
||||||
|
│ │ │ └─ endpoints/
|
||||||
|
│ │ │ ├─ documents.py ← OCR, Vision
|
||||||
|
│ │ │ ├─ flights.py ← Проверка рейсов
|
||||||
|
│ │ │ ├─ claims.py ← Создание обращений
|
||||||
|
│ │ │ ├─ policies.py ← Проверка полисов
|
||||||
|
│ │ │ └─ analytics.py ← Статистика
|
||||||
|
│ │ │
|
||||||
|
│ │ ├─ services/ ← Бизнес-логика
|
||||||
|
│ │ │ ├─ ocr_service.py
|
||||||
|
│ │ │ ├─ ai_service.py ← OpenRouter Vision
|
||||||
|
│ │ │ ├─ flight_service.py ← FlightAware
|
||||||
|
│ │ │ ├─ s3_service.py ← Timeweb S3
|
||||||
|
│ │ │ ├─ crm_service.py ← Интеграция с Vtiger
|
||||||
|
│ │ │ ├─ cache_service.py ← Redis
|
||||||
|
│ │ │ └─ queue_service.py ← RabbitMQ
|
||||||
|
│ │ │
|
||||||
|
│ │ ├─ models/ ← Pydantic модели
|
||||||
|
│ │ │ ├─ claim.py ← Обращение
|
||||||
|
│ │ │ ├─ document.py ← Документ
|
||||||
|
│ │ │ ├─ passport.py ← Паспорт
|
||||||
|
│ │ │ ├─ ticket.py ← Билет
|
||||||
|
│ │ │ └─ flight.py ← Рейс
|
||||||
|
│ │ │
|
||||||
|
│ │ ├─ db/ ← База данных
|
||||||
|
│ │ │ ├─ postgres.py ← PostgreSQL session
|
||||||
|
│ │ │ ├─ mysql.py ← MySQL session (полисы)
|
||||||
|
│ │ │ └─ models.py ← SQLAlchemy модели
|
||||||
|
│ │ │
|
||||||
|
│ │ ├─ workers/ ← RabbitMQ воркеры
|
||||||
|
│ │ │ ├─ ocr_worker.py
|
||||||
|
│ │ │ ├─ flight_worker.py
|
||||||
|
│ │ │ └─ notification_worker.py
|
||||||
|
│ │ │
|
||||||
|
│ │ ├─ core/ ← Утилиты
|
||||||
|
│ │ │ ├─ security.py ← Rate limiting
|
||||||
|
│ │ │ ├─ logger.py ← Логирование
|
||||||
|
│ │ │ └─ exceptions.py
|
||||||
|
│ │ │
|
||||||
|
│ │ └─ tests/ ← Тесты
|
||||||
|
│ │ ├─ test_api.py
|
||||||
|
│ │ └─ test_services.py
|
||||||
|
│ │
|
||||||
|
│ ├─ migrations/ ← SQL миграции
|
||||||
|
│ │ ├─ 001_create_logs.sql
|
||||||
|
│ │ ├─ 002_create_documents.sql
|
||||||
|
│ │ └─ 003_create_claims.sql
|
||||||
|
│ │
|
||||||
|
│ ├─ requirements.txt ← Python зависимости
|
||||||
|
│ ├─ .env.example
|
||||||
|
│ ├─ Dockerfile
|
||||||
|
│ └─ pyproject.toml
|
||||||
|
│
|
||||||
|
├─ frontend/ ← React TypeScript
|
||||||
|
│ ├─ src/
|
||||||
|
│ │ ├─ App.tsx ← Главный компонент
|
||||||
|
│ │ ├─ main.tsx ← Точка входа
|
||||||
|
│ │ │
|
||||||
|
│ │ ├─ components/ ← UI компоненты
|
||||||
|
│ │ │ ├─ layout/
|
||||||
|
│ │ │ │ ├─ Header.tsx
|
||||||
|
│ │ │ │ └─ Footer.tsx
|
||||||
|
│ │ │ ├─ form/
|
||||||
|
│ │ │ │ ├─ FormWizard.tsx ← Многошаговая форма
|
||||||
|
│ │ │ │ ├─ StepPersonal.tsx ← Шаг 1
|
||||||
|
│ │ │ │ ├─ StepFlight.tsx ← Шаг 2
|
||||||
|
│ │ │ │ └─ StepDocuments.tsx ← Шаг 3
|
||||||
|
│ │ │ ├─ upload/
|
||||||
|
│ │ │ │ ├─ PassportUpload.tsx ← Загрузка паспорта + OCR
|
||||||
|
│ │ │ │ ├─ TicketUpload.tsx ← Загрузка билета + OCR
|
||||||
|
│ │ │ │ └─ DocumentUpload.tsx ← Общий компонент
|
||||||
|
│ │ │ ├─ flight/
|
||||||
|
│ │ │ │ ├─ FlightChecker.tsx ← Проверка рейса
|
||||||
|
│ │ │ │ └─ FlightStatus.tsx ← Показ статуса
|
||||||
|
│ │ │ └─ common/
|
||||||
|
│ │ │ ├─ Loading.tsx
|
||||||
|
│ │ │ ├─ ErrorBoundary.tsx
|
||||||
|
│ │ │ └─ Notification.tsx
|
||||||
|
│ │ │
|
||||||
|
│ │ ├─ pages/ ← Страницы
|
||||||
|
│ │ │ ├─ ClaimForm.tsx ← Главная форма
|
||||||
|
│ │ │ ├─ Success.tsx ← Успех
|
||||||
|
│ │ │ └─ Error.tsx ← Ошибка
|
||||||
|
│ │ │
|
||||||
|
│ │ ├─ api/ ← API клиент
|
||||||
|
│ │ │ ├─ client.ts ← Axios instance
|
||||||
|
│ │ │ ├─ documents.ts ← API документов
|
||||||
|
│ │ │ ├─ flights.ts ← API рейсов
|
||||||
|
│ │ │ └─ claims.ts ← API обращений
|
||||||
|
│ │ │
|
||||||
|
│ │ ├─ hooks/ ← React hooks
|
||||||
|
│ │ │ ├─ useOCR.ts
|
||||||
|
│ │ │ ├─ useFlightCheck.ts
|
||||||
|
│ │ │ └─ useWebSocket.ts
|
||||||
|
│ │ │
|
||||||
|
│ │ ├─ types/ ← TypeScript типы
|
||||||
|
│ │ │ ├─ claim.ts
|
||||||
|
│ │ │ ├─ document.ts
|
||||||
|
│ │ │ └─ flight.ts
|
||||||
|
│ │ │
|
||||||
|
│ │ └─ utils/ ← Утилиты
|
||||||
|
│ │ ├─ validators.ts
|
||||||
|
│ │ └─ formatters.ts
|
||||||
|
│ │
|
||||||
|
│ ├─ public/
|
||||||
|
│ │ ├─ index.html
|
||||||
|
│ │ └─ favicon.ico
|
||||||
|
│ │
|
||||||
|
│ ├─ package.json
|
||||||
|
│ ├─ tsconfig.json
|
||||||
|
│ ├─ vite.config.ts
|
||||||
|
│ └─ .env.example
|
||||||
|
│
|
||||||
|
├─ docker/
|
||||||
|
│ ├─ Dockerfile.backend
|
||||||
|
│ ├─ Dockerfile.frontend
|
||||||
|
│ ├─ docker-compose.dev.yml
|
||||||
|
│ ├─ docker-compose.prod.yml
|
||||||
|
│ └─ nginx.conf
|
||||||
|
│
|
||||||
|
├─ docs/
|
||||||
|
│ ├─ API.md ← API документация
|
||||||
|
│ ├─ DEPLOYMENT.md ← Деплой
|
||||||
|
│ └─ ARCHITECTURE.md ← Этот файл
|
||||||
|
│
|
||||||
|
├─ .gitignore
|
||||||
|
├─ README.md
|
||||||
|
└─ docker-compose.yml ← Главный compose
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Поток данных (детально):
|
||||||
|
|
||||||
|
### **1. Загрузка паспорта с OCR:**
|
||||||
|
|
||||||
|
```
|
||||||
|
[React] Пользователь загружает passport.jpg
|
||||||
|
↓
|
||||||
|
[React] PassportUpload.tsx
|
||||||
|
↓ FormData
|
||||||
|
[FastAPI] POST /api/v1/documents/scan
|
||||||
|
↓
|
||||||
|
[FastAPI] DocumentService
|
||||||
|
├─► S3Service.upload() → s3_url
|
||||||
|
├─► RabbitMQ.publish('ocr_queue', {s3_url, type: 'passport'})
|
||||||
|
└─► return {"job_id": "abc-123", "status": "processing"}
|
||||||
|
↓
|
||||||
|
[React] Показывает: "⏳ Распознаём паспорт..."
|
||||||
|
[React] WebSocket подключение: ws://api/ws/jobs/abc-123
|
||||||
|
↓
|
||||||
|
[Worker] OCR Worker получает из RabbitMQ
|
||||||
|
├─► OCRService.analyze(s3_url) → текст (3 сек)
|
||||||
|
├─► AIService.extract_passport(text) → JSON (2 сек)
|
||||||
|
├─► PostgreSQL.log(processing_result)
|
||||||
|
└─► WebSocket.send_to_client(session_id, result)
|
||||||
|
↓
|
||||||
|
[React] WebSocket получает результат
|
||||||
|
↓
|
||||||
|
[React] Автозаполняет поля:
|
||||||
|
form.setFieldsValue({
|
||||||
|
lastname: "Иванов",
|
||||||
|
firstname: "Иван",
|
||||||
|
...
|
||||||
|
})
|
||||||
|
↓
|
||||||
|
[React] Показывает: "✅ Паспорт распознан! Проверьте данные."
|
||||||
|
```
|
||||||
|
|
||||||
|
**Время**: ~5 секунд, но пользователь видит прогресс!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **2. Проверка рейса:**
|
||||||
|
|
||||||
|
```
|
||||||
|
[React] Пользователь ввёл "SU1234" и дату
|
||||||
|
↓
|
||||||
|
[React] FlightChecker.tsx
|
||||||
|
↓ debounce 500ms (не дёргаем API на каждый символ)
|
||||||
|
[FastAPI] GET /api/v1/flights/check?number=SU1234&date=2025-10-24
|
||||||
|
↓
|
||||||
|
[FastAPI] FlightService
|
||||||
|
├─► Redis.get('flight:SU1234:2025-10-24')
|
||||||
|
│ └─► Если есть → возврат из кеша (0.001 сек)
|
||||||
|
│
|
||||||
|
└─► Если нет:
|
||||||
|
├─► FlightAwareAPI.check() (1-2 сек)
|
||||||
|
├─► Redis.set('flight:...', result, ttl=3600) ← Кеш на час
|
||||||
|
└─► return result
|
||||||
|
↓
|
||||||
|
[React] Показывает красивую карточку:
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ ✈️ Рейс SU1234 │
|
||||||
|
│ Moscow (SVO) → Ufa (UFA) │
|
||||||
|
│ Статус: ⚠️ Задержка 45 минут │
|
||||||
|
│ Вылет: 09:25 (план: 08:40) │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **3. Отправка обращения:**
|
||||||
|
|
||||||
|
```
|
||||||
|
[React] Пользователь заполнил всё, жмёт "Подать обращение"
|
||||||
|
↓
|
||||||
|
[FastAPI] POST /api/v1/claims/submit
|
||||||
|
{
|
||||||
|
client: {lastname, firstname, ...},
|
||||||
|
flight: {number, date, type: "delay_flight"},
|
||||||
|
documents: [s3_urls],
|
||||||
|
sbp_phone: "+79991234567"
|
||||||
|
}
|
||||||
|
↓
|
||||||
|
[FastAPI] ClaimService
|
||||||
|
├─► Валидация (Pydantic)
|
||||||
|
├─► PostgreSQL: сохранение claim (для аналитики)
|
||||||
|
├─► Формирование payload для CRM
|
||||||
|
│ {
|
||||||
|
│ "client": {...},
|
||||||
|
│ "contractor": {...}, ← Из конфига
|
||||||
|
│ "project": {...},
|
||||||
|
│ "ticket": {...}
|
||||||
|
│ }
|
||||||
|
├─► POST → https://form.clientright.ru/server_webservice2.php
|
||||||
|
│ └─► PHP Bridge → Vtiger Webservices (PHP 7.3)
|
||||||
|
│ └─► CRM создаёт тикет
|
||||||
|
│
|
||||||
|
├─► RabbitMQ.publish('notifications', {...}) ← Email в фоне
|
||||||
|
└─► return {"success": true, "ticket_id": "TT12345"}
|
||||||
|
↓
|
||||||
|
[React] Редирект на Success.tsx
|
||||||
|
"✅ Ваше обращение #TT12345 принято!"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔌 Интеграции (детально):
|
||||||
|
|
||||||
|
### **OCR + Vision Pipeline:**
|
||||||
|
|
||||||
|
```python
|
||||||
|
# services/document_processor.py
|
||||||
|
|
||||||
|
async def process_passport(file_url: str) -> PassportData:
|
||||||
|
# 1. OCR (ваш сервис)
|
||||||
|
ocr_response = await ocr_service.analyze(
|
||||||
|
file_url=file_url,
|
||||||
|
file_type="auto" # Автоопределение
|
||||||
|
)
|
||||||
|
|
||||||
|
ocr_text = ocr_response['pages_data'][0]['ocr_text']
|
||||||
|
|
||||||
|
# 2. Vision AI (OpenRouter Gemini)
|
||||||
|
vision_prompt = """
|
||||||
|
Извлеки из текста паспорта РФ следующие данные в формате JSON:
|
||||||
|
{
|
||||||
|
"surname": "...",
|
||||||
|
"name": "...",
|
||||||
|
"patronymic": "...",
|
||||||
|
"birthdate": "DD.MM.YYYY",
|
||||||
|
"passport_series": "XXXX",
|
||||||
|
"passport_number": "XXXXXX"
|
||||||
|
}
|
||||||
|
|
||||||
|
Текст паспорта:
|
||||||
|
{ocr_text}
|
||||||
|
"""
|
||||||
|
|
||||||
|
vision_result = await ai_service.extract_structured_data(
|
||||||
|
prompt=vision_prompt.format(ocr_text=ocr_text)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 3. Валидация через Pydantic
|
||||||
|
passport = PassportData(**vision_result)
|
||||||
|
|
||||||
|
# 4. Логирование в PostgreSQL
|
||||||
|
await db.execute("""
|
||||||
|
INSERT INTO document_processing
|
||||||
|
(file_url, document_type, ocr_text, vision_result, processing_time_ms)
|
||||||
|
VALUES ($1, $2, $3, $4, $5)
|
||||||
|
""", file_url, 'passport', ocr_text, vision_result, elapsed_ms)
|
||||||
|
|
||||||
|
return passport
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📡 API Спецификация:
|
||||||
|
|
||||||
|
### **Базовый URL:**
|
||||||
|
```
|
||||||
|
DEV: http://localhost:8000/api/v1
|
||||||
|
PROD: https://api.erv-claims.clientright.ru/api/v1
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Endpoints:**
|
||||||
|
|
||||||
|
| Method | Endpoint | Описание | Время |
|
||||||
|
|--------|----------|----------|-------|
|
||||||
|
| POST | `/documents/upload` | Загрузка в S3 | ~500ms |
|
||||||
|
| POST | `/documents/scan` | OCR + Vision | ~5s (async) |
|
||||||
|
| GET | `/flights/check` | Проверка рейса | ~200ms (cache) |
|
||||||
|
| GET | `/policies/verify` | Проверка полиса | ~5ms (cache) |
|
||||||
|
| POST | `/claims/submit` | Создание обращения | ~1s |
|
||||||
|
| WS | `/ws/jobs/{job_id}` | Real-time статусы | - |
|
||||||
|
|
||||||
|
### **Пример: Scan Passport**
|
||||||
|
|
||||||
|
**Request:**
|
||||||
|
```http
|
||||||
|
POST /api/v1/documents/scan
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"file_url": "https://s3.twcstorage.ru/.../passport.jpg",
|
||||||
|
"document_type": "passport"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response (сразу):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"job_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"status": "processing",
|
||||||
|
"estimated_time_seconds": 5
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**WebSocket update (через 5 сек):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"job_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"status": "completed",
|
||||||
|
"data": {
|
||||||
|
"surname": "Иванов",
|
||||||
|
"name": "Иван",
|
||||||
|
"patronymic": "Иванович",
|
||||||
|
"birthdate": "01.01.1990",
|
||||||
|
"passport_series": "4510",
|
||||||
|
"passport_number": "123456"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗄️ Структура баз данных:
|
||||||
|
|
||||||
|
### **PostgreSQL (новые данные):**
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Обращения (дубликат для аналитики)
|
||||||
|
CREATE TABLE claims (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
session_id VARCHAR(100),
|
||||||
|
insurance_type VARCHAR(50), -- erv_flight, erv_train, etc.
|
||||||
|
|
||||||
|
-- Данные клиента (JSONB)
|
||||||
|
client_data JSONB,
|
||||||
|
|
||||||
|
-- Данные события (JSONB)
|
||||||
|
event_data JSONB,
|
||||||
|
|
||||||
|
-- Документы
|
||||||
|
documents JSONB, -- [{type, s3_url, ocr_result, vision_result}]
|
||||||
|
|
||||||
|
-- Статус
|
||||||
|
status VARCHAR(20), -- processing, submitted, completed, error
|
||||||
|
crm_ticket_id VARCHAR(50),
|
||||||
|
|
||||||
|
-- Метаданные
|
||||||
|
ip_address INET,
|
||||||
|
user_agent TEXT,
|
||||||
|
created_at TIMESTAMP DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMP DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_claims_status ON claims(status);
|
||||||
|
CREATE INDEX idx_claims_created ON claims(created_at);
|
||||||
|
CREATE INDEX idx_claims_crm_ticket ON claims(crm_ticket_id);
|
||||||
|
|
||||||
|
-- Обработка документов
|
||||||
|
CREATE TABLE document_processing (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
claim_id UUID REFERENCES claims(id),
|
||||||
|
|
||||||
|
file_url TEXT,
|
||||||
|
s3_url TEXT,
|
||||||
|
document_type VARCHAR(50), -- passport, ticket, etc.
|
||||||
|
|
||||||
|
-- OCR результат
|
||||||
|
ocr_text TEXT,
|
||||||
|
ocr_confidence NUMERIC,
|
||||||
|
|
||||||
|
-- Vision результат
|
||||||
|
vision_result JSONB,
|
||||||
|
|
||||||
|
-- Метрики
|
||||||
|
processing_time_ms INTEGER,
|
||||||
|
ocr_time_ms INTEGER,
|
||||||
|
vision_time_ms INTEGER,
|
||||||
|
|
||||||
|
created_at TIMESTAMP DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Логи
|
||||||
|
CREATE TABLE logs (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
level VARCHAR(10), -- INFO, WARNING, ERROR
|
||||||
|
logger VARCHAR(50), -- ocr_service, flight_service, etc.
|
||||||
|
message TEXT,
|
||||||
|
context JSONB,
|
||||||
|
|
||||||
|
session_id VARCHAR(100),
|
||||||
|
ip_address INET,
|
||||||
|
|
||||||
|
created_at TIMESTAMP DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_logs_level ON logs(level);
|
||||||
|
CREATE INDEX idx_logs_created ON logs(created_at DESC);
|
||||||
|
CREATE INDEX idx_logs_session ON logs(session_id);
|
||||||
|
|
||||||
|
-- Кеш API (fallback)
|
||||||
|
CREATE TABLE api_cache (
|
||||||
|
cache_key VARCHAR(255) PRIMARY KEY,
|
||||||
|
cache_value JSONB,
|
||||||
|
expires_at TIMESTAMP,
|
||||||
|
created_at TIMESTAMP DEFAULT NOW()
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### **MySQL (существующая CRM база):**
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- НЕ трогаем! Только читаем
|
||||||
|
ci20465_erv.lexrpiority
|
||||||
|
├─ voucher
|
||||||
|
├─ insured_from
|
||||||
|
└─ insured_to
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Deployment стратегия:
|
||||||
|
|
||||||
|
### **Development (сейчас):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Backend
|
||||||
|
cd backend
|
||||||
|
python -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
uvicorn app.main:app --reload --port 8000
|
||||||
|
|
||||||
|
# 2. Frontend
|
||||||
|
cd frontend
|
||||||
|
npm install
|
||||||
|
npm run dev # Vite dev server на :5173
|
||||||
|
|
||||||
|
# 3. Доступ:
|
||||||
|
Frontend: http://localhost:5173
|
||||||
|
Backend: http://localhost:8000
|
||||||
|
API Docs: http://localhost:8000/docs ← Swagger UI!
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Production (потом):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Вариант 1: Тот же сервер, другой домен
|
||||||
|
cd /var/www/erv-claims.clientright.ru/
|
||||||
|
git clone http://147.45.146.17:3002/fedya/erv-platform.git .
|
||||||
|
docker-compose -f docker/docker-compose.prod.yml up -d
|
||||||
|
|
||||||
|
# Nginx конфиг:
|
||||||
|
server {
|
||||||
|
server_name erv-claims.clientright.ru;
|
||||||
|
|
||||||
|
# Frontend (статика)
|
||||||
|
location / {
|
||||||
|
root /var/www/erv-claims/frontend/dist;
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Backend API
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://localhost:8000;
|
||||||
|
}
|
||||||
|
|
||||||
|
# WebSocket
|
||||||
|
location /ws/ {
|
||||||
|
proxy_pass http://localhost:8000;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Что делаю СЕЙЧАС (следующие 2-3 часа):
|
||||||
|
|
||||||
|
### **Создаю базу:**
|
||||||
|
|
||||||
|
1. ✅ Структура проекта (сделано)
|
||||||
|
2. ✅ requirements.txt (Python зависимости)
|
||||||
|
3. ✅ package.json (React зависимости)
|
||||||
|
4. ✅ .env.example
|
||||||
|
5. ✅ Базовый FastAPI app
|
||||||
|
6. ✅ Базовый React app
|
||||||
|
7. ✅ SQL миграции для PostgreSQL
|
||||||
|
8. ✅ Docker compose для dev
|
||||||
|
9. ✅ README с инструкциями
|
||||||
|
|
||||||
|
**Время**: ~1 час на базовую настройку
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💪 Готов?
|
||||||
|
|
||||||
|
Сейчас создам всю базовую структуру и запущу оба приложения (FastAPI + React).
|
||||||
|
|
||||||
|
**Начинаю прямо сейчас!** 🚀
|
||||||
183
QUICK_START.md
Normal file
183
QUICK_START.md
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
# 🚀 Быстрый запуск ERV Platform (MVP)
|
||||||
|
|
||||||
|
## ✅ Что уже готово:
|
||||||
|
|
||||||
|
- ✅ Структура проекта создана
|
||||||
|
- ✅ FastAPI backend (базовый)
|
||||||
|
- ✅ React frontend (базовый)
|
||||||
|
- ✅ Git репозиторий настроен
|
||||||
|
- ✅ .env конфигурация
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📍 Адреса после запуска:
|
||||||
|
|
||||||
|
```
|
||||||
|
Frontend (React): http://147.45.146.17:5173/
|
||||||
|
Backend API: http://147.45.146.17:8100/
|
||||||
|
API Docs (Swagger): http://147.45.146.17:8100/docs
|
||||||
|
Health Check: http://147.45.146.17:8100/health
|
||||||
|
Gitea: http://147.45.146.17:3002/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Запуск Backend (FastAPI)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Терминал 1: Backend
|
||||||
|
|
||||||
|
cd /var/www/fastuser/data/www/crm.clientright.ru/erv_platform/backend
|
||||||
|
|
||||||
|
# Активировать виртуальное окружение
|
||||||
|
source venv/bin/activate
|
||||||
|
|
||||||
|
# Если зависимости ещё не установлены:
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Запустить FastAPI сервер
|
||||||
|
uvicorn app.main:app --reload --host 0.0.0.0 --port 8100
|
||||||
|
|
||||||
|
# Увидишь:
|
||||||
|
# INFO: Uvicorn running on http://0.0.0.0:8100
|
||||||
|
# 🚀 ERV Insurance Platform запускается...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎨 Запуск Frontend (React)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Терминал 2: Frontend (НОВЫЙ терминал!)
|
||||||
|
|
||||||
|
cd /var/www/fastuser/data/www/crm.clientright.ru/erv_platform/frontend
|
||||||
|
|
||||||
|
# Установить зависимости (первый раз):
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Запустить React dev server
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Увидишь:
|
||||||
|
# VITE v5.x.x ready in XXX ms
|
||||||
|
# ➜ Local: http://localhost:5173/
|
||||||
|
# ➜ Network: http://147.45.146.17:5173/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 Проверка работоспособности
|
||||||
|
|
||||||
|
### 1. **Проверь Backend:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8100/
|
||||||
|
|
||||||
|
# Ожидается:
|
||||||
|
# {"message":"🚀 ERV Insurance Platform API","version":"1.0.0","status":"running"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. **Проверь Health:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8100/health
|
||||||
|
|
||||||
|
# Увидишь статус всех сервисов:
|
||||||
|
# {"status":"healthy","services":{"api":"ok","redis":"ok","postgres":"ok"}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. **Открой в браузере:**
|
||||||
|
|
||||||
|
```
|
||||||
|
http://147.45.146.17:5173/
|
||||||
|
|
||||||
|
Увидишь:
|
||||||
|
- Информацию о платформе
|
||||||
|
- Статус сервисов (Redis, PostgreSQL, OCR)
|
||||||
|
- Список возможностей
|
||||||
|
- Технологический стек
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 Если что-то не работает:
|
||||||
|
|
||||||
|
### **Backend не запускается:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверь логи
|
||||||
|
tail -f ../logs/backend.log
|
||||||
|
|
||||||
|
# Проверь занят ли порт
|
||||||
|
netstat -tuln | grep 8100
|
||||||
|
|
||||||
|
# Если занят - измени порт в команде запуска
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Frontend не запускается:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверь Node.js версию (нужна >= 18)
|
||||||
|
node --version
|
||||||
|
|
||||||
|
# Если старая - обнови:
|
||||||
|
# curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
||||||
|
# sudo apt-get install -y nodejs
|
||||||
|
```
|
||||||
|
|
||||||
|
### **API не отвечает:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверь что FastAPI запущен
|
||||||
|
ps aux | grep uvicorn
|
||||||
|
|
||||||
|
# Проверь firewall
|
||||||
|
sudo ufw status
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Что показывает MVP:
|
||||||
|
|
||||||
|
1. ✅ **Работающий FastAPI** с автодокументацией
|
||||||
|
2. ✅ **Работающий React** интерфейс
|
||||||
|
3. ✅ **Подключение к сервисам** (Redis, PostgreSQL, OCR)
|
||||||
|
4. ✅ **Health Check** всех компонентов
|
||||||
|
5. ✅ **API endpoints** (базовые)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Следующие шаги (после запуска MVP):
|
||||||
|
|
||||||
|
После того как убедишься что всё работает:
|
||||||
|
|
||||||
|
1. ✅ Создам полные API endpoints (документы, рейсы, обращения)
|
||||||
|
2. ✅ Создам React компоненты (форма, загрузка файлов, OCR)
|
||||||
|
3. ✅ Подключу WebSocket для real-time
|
||||||
|
4. ✅ Интегрирую все сервисы (S3, RabbitMQ, и т.д.)
|
||||||
|
5. ✅ Создам Docker окружение
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Git репозиторий
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Репозиторий создан
|
||||||
|
http://147.45.146.17:3002/negodiy/erv-platform
|
||||||
|
|
||||||
|
# Логин: negodiy
|
||||||
|
# Пароль: yft,fkjdj90
|
||||||
|
|
||||||
|
# Настройка remote:
|
||||||
|
git remote add origin http://negodiy:yft,fkjdj90@147.45.146.17:3002/negodiy/erv-platform.git
|
||||||
|
git push -u origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ❓ Вопросы?
|
||||||
|
|
||||||
|
Если что-то не работает - смотри логи или пиши мне!
|
||||||
|
|
||||||
|
**Удачи!** 🚀
|
||||||
|
|
||||||
172
README.md
Normal file
172
README.md
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
# 🚀 ERV Insurance Platform
|
||||||
|
|
||||||
|
**Современная платформа для страховых обращений**
|
||||||
|
|
||||||
|
- **Backend**: Python FastAPI (async)
|
||||||
|
- **Frontend**: React 18 + TypeScript
|
||||||
|
- **Database**: PostgreSQL + MySQL + Redis
|
||||||
|
- **Queue**: RabbitMQ
|
||||||
|
- **Storage**: S3 Timeweb Cloud
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Быстрый старт
|
||||||
|
|
||||||
|
### 📍 **Визуальный доступ:**
|
||||||
|
|
||||||
|
После запуска доступны по адресам:
|
||||||
|
|
||||||
|
```
|
||||||
|
Frontend (форма):
|
||||||
|
http://147.45.146.17:5173/
|
||||||
|
|
||||||
|
Backend API:
|
||||||
|
http://147.45.146.17:8100/
|
||||||
|
|
||||||
|
API Документация (Swagger UI):
|
||||||
|
http://147.45.146.17:8100/docs ← Интерактивная!
|
||||||
|
|
||||||
|
Gitea (Git репозиторий):
|
||||||
|
http://147.45.146.17:3002/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Установка и запуск
|
||||||
|
|
||||||
|
### **Backend (FastAPI):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
|
||||||
|
# Создаём виртуальное окружение
|
||||||
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
|
|
||||||
|
# Устанавливаем зависимости
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Запускаем сервер
|
||||||
|
uvicorn app.main:app --reload --host 0.0.0.0 --port 8100
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Frontend (React):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
|
||||||
|
# Устанавливаем зависимости
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Запускаем dev сервер
|
||||||
|
npm run dev -- --host 0.0.0.0 --port 5173
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Архитектура
|
||||||
|
|
||||||
|
### **Поток данных:**
|
||||||
|
|
||||||
|
```
|
||||||
|
React (5173) → FastAPI (8100) → [Redis, RabbitMQ, PostgreSQL]
|
||||||
|
↓
|
||||||
|
OCR Service (8001)
|
||||||
|
OpenRouter AI
|
||||||
|
FlightAware API
|
||||||
|
↓
|
||||||
|
PHP Bridge → Vtiger CRM
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Что НЕ трогаем:**
|
||||||
|
|
||||||
|
✅ CRM Vtiger (работает как работала)
|
||||||
|
✅ MySQL полисы (только READ)
|
||||||
|
✅ Существующий PHP код
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗄️ Базы данных
|
||||||
|
|
||||||
|
| База | Назначение | Хост |
|
||||||
|
|------|------------|------|
|
||||||
|
| PostgreSQL | Логи, метрики, новые данные | 147.45.189.234:5432 |
|
||||||
|
| MySQL | Проверка полисов (READ) | localhost:3306 |
|
||||||
|
| Redis | Кеш, Rate Limiting | localhost:6379 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 Структура проекта
|
||||||
|
|
||||||
|
```
|
||||||
|
erv_platform/
|
||||||
|
├─ backend/ ← Python FastAPI
|
||||||
|
│ ├─ app/
|
||||||
|
│ │ ├─ main.py
|
||||||
|
│ │ ├─ api/
|
||||||
|
│ │ ├─ services/
|
||||||
|
│ │ └─ models/
|
||||||
|
│ └─ requirements.txt
|
||||||
|
│
|
||||||
|
├─ frontend/ ← React TypeScript
|
||||||
|
│ ├─ src/
|
||||||
|
│ │ ├─ components/
|
||||||
|
│ │ ├─ pages/
|
||||||
|
│ │ └─ api/
|
||||||
|
│ └─ package.json
|
||||||
|
│
|
||||||
|
└─ .env ← Конфигурация
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔌 API Endpoints
|
||||||
|
|
||||||
|
### **Документы:**
|
||||||
|
- `POST /api/v1/documents/upload` - Загрузка в S3
|
||||||
|
- `POST /api/v1/documents/scan` - OCR + Vision
|
||||||
|
|
||||||
|
### **Рейсы:**
|
||||||
|
- `GET /api/v1/flights/check` - Проверка статуса
|
||||||
|
|
||||||
|
### **Обращения:**
|
||||||
|
- `POST /api/v1/claims/submit` - Создание обращения
|
||||||
|
|
||||||
|
### **Полисы:**
|
||||||
|
- `GET /api/v1/policies/verify` - Проверка полиса
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 Отладка
|
||||||
|
|
||||||
|
### **Логи:**
|
||||||
|
```bash
|
||||||
|
# FastAPI
|
||||||
|
tail -f backend/logs/app.log
|
||||||
|
|
||||||
|
# PostgreSQL логи
|
||||||
|
SELECT * FROM logs ORDER BY created_at DESC LIMIT 50;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Git
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Репозиторий
|
||||||
|
http://147.45.146.17:3002/negodiy/erv-platform
|
||||||
|
|
||||||
|
# Клонирование
|
||||||
|
git clone http://147.45.146.17:3002/negodiy/erv-platform.git
|
||||||
|
|
||||||
|
# Push изменений
|
||||||
|
git add .
|
||||||
|
git commit -m "Your message"
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Автор**: AI Assistant + Фёдор
|
||||||
|
**Дата**: 24.10.2025
|
||||||
|
|
||||||
1
backend.pid
Normal file
1
backend.pid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
1654
|
||||||
0
backend/app/__init__.py
Normal file
0
backend/app/__init__.py
Normal file
0
backend/app/api/__init__.py
Normal file
0
backend/app/api/__init__.py
Normal file
68
backend/app/config.py
Normal file
68
backend/app/config.py
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
"""
|
||||||
|
Конфигурация приложения
|
||||||
|
"""
|
||||||
|
from pydantic_settings import BaseSettings
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
|
||||||
|
class Settings(BaseSettings):
|
||||||
|
# App
|
||||||
|
app_name: str = "ERV Insurance Platform"
|
||||||
|
app_env: str = "development"
|
||||||
|
debug: bool = True
|
||||||
|
|
||||||
|
# API
|
||||||
|
api_v1_prefix: str = "/api/v1"
|
||||||
|
backend_url: str = "http://localhost:8100"
|
||||||
|
frontend_url: str = "http://localhost:5173"
|
||||||
|
|
||||||
|
# PostgreSQL
|
||||||
|
postgres_host: str = "147.45.189.234"
|
||||||
|
postgres_port: int = 5432
|
||||||
|
postgres_db: str = "default_db"
|
||||||
|
postgres_user: str = "gen_user"
|
||||||
|
postgres_password: str = "2~~9_^kVsU?2\\S"
|
||||||
|
|
||||||
|
# Redis
|
||||||
|
redis_host: str = "localhost"
|
||||||
|
redis_port: int = 6379
|
||||||
|
redis_password: str = "CRM_Redis_Pass_2025_Secure!"
|
||||||
|
redis_db: int = 0
|
||||||
|
redis_prefix: str = "erv:"
|
||||||
|
|
||||||
|
# RabbitMQ
|
||||||
|
rabbitmq_host: str = "185.197.75.249"
|
||||||
|
rabbitmq_port: int = 5672
|
||||||
|
rabbitmq_user: str = "admin"
|
||||||
|
rabbitmq_password: str = "tyejvtej"
|
||||||
|
rabbitmq_vhost: str = "/"
|
||||||
|
|
||||||
|
# OCR Service
|
||||||
|
ocr_api_url: str = "http://147.45.146.17:8001"
|
||||||
|
|
||||||
|
# OpenRouter AI
|
||||||
|
openrouter_api_key: str = "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"
|
||||||
|
openrouter_model: str = "google/gemini-2.0-flash-001"
|
||||||
|
|
||||||
|
# FlightAware
|
||||||
|
flightaware_api_key: str = "Puz0cdxAHzAEqMRZwtdeqBUSm9naJfwK"
|
||||||
|
|
||||||
|
# CORS
|
||||||
|
cors_origins: list = [
|
||||||
|
"http://localhost:5173",
|
||||||
|
"http://147.45.146.17:5173",
|
||||||
|
"https://erv-claims.clientright.ru"
|
||||||
|
]
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
env_file = "../.env"
|
||||||
|
case_sensitive = False
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache()
|
||||||
|
def get_settings() -> Settings:
|
||||||
|
return Settings()
|
||||||
|
|
||||||
|
|
||||||
|
settings = get_settings()
|
||||||
|
|
||||||
176
backend/app/main.py
Normal file
176
backend/app/main.py
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
"""
|
||||||
|
ERV Insurance Platform - FastAPI Backend
|
||||||
|
"""
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
from app.config import settings
|
||||||
|
import redis
|
||||||
|
import asyncpg
|
||||||
|
|
||||||
|
# Создаём FastAPI приложение
|
||||||
|
app = FastAPI(
|
||||||
|
title="ERV Insurance Platform API",
|
||||||
|
description="API для обработки страховых обращений с OCR, AI и интеграциями",
|
||||||
|
version="1.0.0",
|
||||||
|
docs_url="/docs",
|
||||||
|
redoc_url="/redoc"
|
||||||
|
)
|
||||||
|
|
||||||
|
# CORS middleware
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=settings.cors_origins,
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# HEALTH CHECKS
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
async def root():
|
||||||
|
"""Главная страница API"""
|
||||||
|
return {
|
||||||
|
"message": "🚀 ERV Insurance Platform API",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"docs": f"{settings.backend_url}/docs",
|
||||||
|
"status": "running"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/health")
|
||||||
|
async def health_check():
|
||||||
|
"""Проверка здоровья сервисов"""
|
||||||
|
health_status = {
|
||||||
|
"api": "ok",
|
||||||
|
"redis": "checking",
|
||||||
|
"postgres": "checking",
|
||||||
|
"ocr": "checking"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Проверка Redis
|
||||||
|
try:
|
||||||
|
r = redis.Redis(
|
||||||
|
host=settings.redis_host,
|
||||||
|
port=settings.redis_port,
|
||||||
|
password=settings.redis_password,
|
||||||
|
decode_responses=True
|
||||||
|
)
|
||||||
|
r.ping()
|
||||||
|
health_status["redis"] = "ok"
|
||||||
|
except Exception as e:
|
||||||
|
health_status["redis"] = f"error: {str(e)}"
|
||||||
|
|
||||||
|
# Проверка PostgreSQL
|
||||||
|
try:
|
||||||
|
conn = await asyncpg.connect(
|
||||||
|
host=settings.postgres_host,
|
||||||
|
port=settings.postgres_port,
|
||||||
|
database=settings.postgres_db,
|
||||||
|
user=settings.postgres_user,
|
||||||
|
password=settings.postgres_password
|
||||||
|
)
|
||||||
|
await conn.execute("SELECT 1")
|
||||||
|
await conn.close()
|
||||||
|
health_status["postgres"] = "ok"
|
||||||
|
except Exception as e:
|
||||||
|
health_status["postgres"] = f"error: {str(e)}"
|
||||||
|
|
||||||
|
# Проверка OCR
|
||||||
|
import httpx
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
response = await client.get(f"{settings.ocr_api_url}/", timeout=5.0)
|
||||||
|
health_status["ocr"] = "ok" if response.status_code in [200, 404] else "unreachable"
|
||||||
|
except Exception as e:
|
||||||
|
health_status["ocr"] = f"error: {str(e)}"
|
||||||
|
|
||||||
|
all_ok = all(v == "ok" for v in health_status.values())
|
||||||
|
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=200 if all_ok else 503,
|
||||||
|
content={
|
||||||
|
"status": "healthy" if all_ok else "degraded",
|
||||||
|
"services": health_status
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# API V1 ENDPOINTS
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
@app.get("/api/v1/test")
|
||||||
|
async def test_endpoint():
|
||||||
|
"""Тестовый endpoint"""
|
||||||
|
return {
|
||||||
|
"message": "✅ API работает!",
|
||||||
|
"env": settings.app_env,
|
||||||
|
"debug": settings.debug,
|
||||||
|
"services": {
|
||||||
|
"redis": f"{settings.redis_host}:{settings.redis_port}",
|
||||||
|
"postgres": f"{settings.postgres_host}:{settings.postgres_port}",
|
||||||
|
"rabbitmq": f"{settings.rabbitmq_host}:{settings.rabbitmq_port}",
|
||||||
|
"ocr": settings.ocr_api_url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/api/v1/info")
|
||||||
|
async def get_info():
|
||||||
|
"""Информация о платформе"""
|
||||||
|
return {
|
||||||
|
"platform": "ERV Insurance Claims",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"features": [
|
||||||
|
"OCR документов (паспорт, билеты)",
|
||||||
|
"AI автозаполнение (Gemini Vision)",
|
||||||
|
"Проверка рейсов (FlightAware)",
|
||||||
|
"СБП выплаты",
|
||||||
|
"Интеграция с CRM"
|
||||||
|
],
|
||||||
|
"tech_stack": {
|
||||||
|
"backend": "Python FastAPI",
|
||||||
|
"frontend": "React TypeScript",
|
||||||
|
"database": "PostgreSQL + MySQL",
|
||||||
|
"cache": "Redis",
|
||||||
|
"queue": "RabbitMQ",
|
||||||
|
"storage": "S3 Timeweb",
|
||||||
|
"ocr": "Internal Service",
|
||||||
|
"ai": "OpenRouter Gemini 2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# STARTUP/SHUTDOWN
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
@app.on_event("startup")
|
||||||
|
async def startup_event():
|
||||||
|
"""При старте приложения"""
|
||||||
|
print("🚀 ERV Insurance Platform запускается...")
|
||||||
|
print(f"📍 Backend URL: {settings.backend_url}")
|
||||||
|
print(f"📍 API Docs: {settings.backend_url}/docs")
|
||||||
|
print(f"🔗 Frontend URL: {settings.frontend_url}")
|
||||||
|
|
||||||
|
|
||||||
|
@app.on_event("shutdown")
|
||||||
|
async def shutdown_event():
|
||||||
|
"""При остановке приложения"""
|
||||||
|
print("👋 ERV Insurance Platform остановлен")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import uvicorn
|
||||||
|
uvicorn.run(
|
||||||
|
"main:app",
|
||||||
|
host="0.0.0.0",
|
||||||
|
port=8100,
|
||||||
|
reload=True
|
||||||
|
)
|
||||||
|
|
||||||
46
backend/requirements.txt
Normal file
46
backend/requirements.txt
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# Core FastAPI
|
||||||
|
fastapi==0.115.0
|
||||||
|
uvicorn[standard]==0.32.0
|
||||||
|
python-multipart==0.0.12
|
||||||
|
websockets==14.1
|
||||||
|
|
||||||
|
# Database
|
||||||
|
sqlalchemy[asyncio]==2.0.35
|
||||||
|
asyncpg==0.30.0
|
||||||
|
aiomysql==0.2.0
|
||||||
|
alembic==1.14.0
|
||||||
|
|
||||||
|
# Redis & RabbitMQ
|
||||||
|
redis==5.2.0
|
||||||
|
hiredis==3.0.0
|
||||||
|
aio-pika==9.4.3
|
||||||
|
|
||||||
|
# HTTP & File operations
|
||||||
|
httpx==0.27.2
|
||||||
|
aiofiles==24.1.0
|
||||||
|
|
||||||
|
# S3
|
||||||
|
boto3==1.35.56
|
||||||
|
aioboto3==13.2.0
|
||||||
|
|
||||||
|
# Validation
|
||||||
|
pydantic==2.10.0
|
||||||
|
pydantic-settings==2.6.0
|
||||||
|
email-validator==2.2.0
|
||||||
|
|
||||||
|
# Security
|
||||||
|
python-jose[cryptography]==3.3.0
|
||||||
|
passlib[bcrypt]==1.7.4
|
||||||
|
|
||||||
|
# Utils
|
||||||
|
python-dotenv==1.0.1
|
||||||
|
python-slugify==8.0.4
|
||||||
|
pytz==2024.2
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
structlog==24.4.0
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
pytest==8.3.3
|
||||||
|
pytest-asyncio==0.24.0
|
||||||
|
pytest-cov==6.0.0
|
||||||
41
frontend/package.json
Normal file
41
frontend/package.json
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "erv-insurance-platform-frontend",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "ERV Insurance Claims Platform - Frontend",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
|
"type-check": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react": "^18.3.1",
|
||||||
|
"react-dom": "^18.3.1",
|
||||||
|
"react-router-dom": "^6.26.2",
|
||||||
|
"antd": "^5.21.6",
|
||||||
|
"@ant-design/icons": "^5.5.1",
|
||||||
|
"axios": "^1.7.7",
|
||||||
|
"@tanstack/react-query": "^5.59.16",
|
||||||
|
"zustand": "^5.0.1",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
|
"imask": "^7.6.1",
|
||||||
|
"react-dropzone": "^14.3.5",
|
||||||
|
"socket.io-client": "^4.8.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.3.11",
|
||||||
|
"@types/react-dom": "^18.3.1",
|
||||||
|
"@vitejs/plugin-react": "^4.3.3",
|
||||||
|
"typescript": "^5.6.3",
|
||||||
|
"vite": "^5.4.10",
|
||||||
|
"eslint": "^9.13.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8.11.0",
|
||||||
|
"@typescript-eslint/parser": "^8.11.0",
|
||||||
|
"eslint-plugin-react-hooks": "^5.0.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.13"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
14
frontend/public/index.html
Normal file
14
frontend/public/index.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>ERV Insurance Platform</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
129
frontend/src/App.css
Normal file
129
frontend/src/App.css
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
.app {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-header {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-header h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-header p {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-main {
|
||||||
|
flex: 1;
|
||||||
|
max-width: 1200px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 2rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card h2 {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: #333;
|
||||||
|
border-bottom: 2px solid #667eea;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card h3 {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card ul {
|
||||||
|
list-style: none;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card ul li {
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card ul li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.services li {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-ok {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-error {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card pre {
|
||||||
|
background: #f5f5f5;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow-x: auto;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card a {
|
||||||
|
color: #667eea;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
text-align: center;
|
||||||
|
padding: 3rem;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success {
|
||||||
|
color: #52c41a;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
color: #faad14;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: #f5222d;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-footer {
|
||||||
|
background: #333;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
124
frontend/src/App.tsx
Normal file
124
frontend/src/App.tsx
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import './App.css'
|
||||||
|
|
||||||
|
interface APIInfo {
|
||||||
|
platform?: string;
|
||||||
|
version?: string;
|
||||||
|
features?: string[];
|
||||||
|
tech_stack?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [apiInfo, setApiInfo] = useState<APIInfo | null>(null)
|
||||||
|
const [health, setHealth] = useState<any>(null)
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Проверяем подключение к API
|
||||||
|
Promise.all([
|
||||||
|
fetch('http://147.45.146.17:8100/api/v1/info').then(r => r.json()),
|
||||||
|
fetch('http://147.45.146.17:8100/health').then(r => r.json())
|
||||||
|
])
|
||||||
|
.then(([info, healthData]) => {
|
||||||
|
setApiInfo(info)
|
||||||
|
setHealth(healthData)
|
||||||
|
setLoading(false)
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('API Error:', err)
|
||||||
|
setLoading(false)
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="app">
|
||||||
|
<header className="app-header">
|
||||||
|
<h1>🚀 ERV Insurance Platform</h1>
|
||||||
|
<p>Python FastAPI + React TypeScript</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main className="app-main">
|
||||||
|
{loading ? (
|
||||||
|
<div className="loading">⏳ Подключение к API...</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className="card">
|
||||||
|
<h2>📊 Информация о платформе</h2>
|
||||||
|
{apiInfo ? (
|
||||||
|
<>
|
||||||
|
<p><strong>Платформа:</strong> {apiInfo.platform}</p>
|
||||||
|
<p><strong>Версия:</strong> {apiInfo.version}</p>
|
||||||
|
|
||||||
|
<h3>✨ Возможности:</h3>
|
||||||
|
<ul>
|
||||||
|
{apiInfo.features?.map((f, i) => (
|
||||||
|
<li key={i}>{f}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>🛠️ Технологический стек:</h3>
|
||||||
|
<pre>{JSON.stringify(apiInfo.tech_stack, null, 2)}</pre>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<p className="error">❌ Не удалось получить данные</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="card">
|
||||||
|
<h2>🏥 Здоровье сервисов</h2>
|
||||||
|
{health ? (
|
||||||
|
<>
|
||||||
|
<p className={health.status === 'healthy' ? 'success' : 'warning'}>
|
||||||
|
Статус: <strong>{health.status}</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Сервисы:</h3>
|
||||||
|
<ul className="services">
|
||||||
|
{Object.entries(health.services || {}).map(([name, status]) => (
|
||||||
|
<li key={name}>
|
||||||
|
<span className={status === 'ok' ? 'status-ok' : 'status-error'}>
|
||||||
|
{status === 'ok' ? '✅' : '❌'}
|
||||||
|
</span>
|
||||||
|
<strong>{name}:</strong> {String(status)}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<p className="error">❌ Health check недоступен</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="card">
|
||||||
|
<h2>🔗 Полезные ссылки</h2>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="http://147.45.146.17:8100/docs" target="_blank">
|
||||||
|
📚 API Документация (Swagger UI)
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="http://147.45.146.17:8100/health" target="_blank">
|
||||||
|
🏥 Health Check
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="http://147.45.146.17:3002" target="_blank">
|
||||||
|
🐙 Gitea (Git репозиторий)
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer className="app-footer">
|
||||||
|
<p>© 2025 ERV Insurance Platform | Powered by FastAPI + React</p>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
||||||
|
|
||||||
17
frontend/src/index.css
Normal file
17
frontend/src/index.css
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
11
frontend/src/main.tsx
Normal file
11
frontend/src/main.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import ReactDOM from 'react-dom/client'
|
||||||
|
import App from './App.tsx'
|
||||||
|
import './index.css'
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>,
|
||||||
|
)
|
||||||
|
|
||||||
22
frontend/tsconfig.json
Normal file
22
frontend/tsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
|
}
|
||||||
|
|
||||||
17
frontend/vite.config.ts
Normal file
17
frontend/vite.config.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
server: {
|
||||||
|
host: '0.0.0.0',
|
||||||
|
port: 5173,
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'http://localhost:8100',
|
||||||
|
changeOrigin: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
Reference in New Issue
Block a user