2025-12-17 15:37:32 +01:00
|
|
|
|
# Руководство по миграции с rag-bench-old-version на FastAPI
|
|
|
|
|
|
|
|
|
|
|
|
Этот документ содержит детальные инструкции по переносу статического SPA на новую архитектуру с FastAPI backend.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Основные изменения в архитектуре
|
|
|
|
|
|
|
|
|
|
|
|
### Старая версия (rag-bench-old-version)
|
|
|
|
|
|
```
|
|
|
|
|
|
Browser (index.html + app.js)
|
|
|
|
|
|
↓ fetch (HTTPS + mTLS)
|
|
|
|
|
|
RAG Backend (IFT/PSI/PROD)
|
|
|
|
|
|
|
|
|
|
|
|
Настройки: localStorage
|
|
|
|
|
|
Авторизация: нет
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Новая версия (brief-bench-fastapi)
|
|
|
|
|
|
```
|
|
|
|
|
|
Browser (index.html + app.js + api-client.js)
|
|
|
|
|
|
↓ fetch (HTTP, JWT token)
|
|
|
|
|
|
FastAPI Backend
|
|
|
|
|
|
↓ httpx (HTTPS + mTLS)
|
|
|
|
|
|
RAG Backend (IFT/PSI/PROD)
|
|
|
|
|
|
|
|
|
|
|
|
Настройки: DB API (per-user)
|
|
|
|
|
|
Авторизация: JWT (8-значный логин)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Шаг 1: Копирование статических файлов
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# Из корня проекта brief-bench-fastapi
|
|
|
|
|
|
cp rag-bench-old-version/index.html static/
|
|
|
|
|
|
cp rag-bench-old-version/styles.css static/
|
|
|
|
|
|
cp rag-bench-old-version/app.js static/
|
|
|
|
|
|
cp rag-bench-old-version/settings.js static/
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Шаг 2: Создание api-client.js
|
|
|
|
|
|
|
2025-12-18 09:52:02 +01:00
|
|
|
|
См. полную реализацию в DEVELOPMENT_PLAN.md (раздел "Создать API client для frontend").
|
2025-12-17 15:37:32 +01:00
|
|
|
|
|
|
|
|
|
|
Создать файл `static/api-client.js` с классом `BriefBenchAPI`.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Шаг 3: Модификация index.html
|
|
|
|
|
|
|
|
|
|
|
|
### 3.1 Добавить подключение api-client.js
|
|
|
|
|
|
|
|
|
|
|
|
**Изменить:**
|
|
|
|
|
|
```html
|
|
|
|
|
|
<!-- Scripts -->
|
|
|
|
|
|
<script src="settings.js"></script>
|
|
|
|
|
|
<script src="app.js"></script>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**На:**
|
|
|
|
|
|
```html
|
|
|
|
|
|
<!-- Scripts -->
|
|
|
|
|
|
<script src="settings.js"></script>
|
|
|
|
|
|
<script src="api-client.js"></script>
|
|
|
|
|
|
<script src="app.js"></script>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3.2 Добавить Login Screen
|
|
|
|
|
|
|
|
|
|
|
|
**Добавить ПЕРЕД `<div id="app">`:**
|
|
|
|
|
|
```html
|
|
|
|
|
|
<!-- Login Screen -->
|
|
|
|
|
|
<div id="login-screen" class="login-container" style="display: none;">
|
|
|
|
|
|
<div class="login-card">
|
|
|
|
|
|
<h2 style="text-align: center; margin-bottom: var(--md-spacing-xl);">Brief Bench</h2>
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label class="form-label" for="login-input">8-значный логин</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
class="form-input"
|
|
|
|
|
|
id="login-input"
|
|
|
|
|
|
placeholder="12345678"
|
|
|
|
|
|
maxlength="8"
|
|
|
|
|
|
pattern="[0-9]{8}"
|
|
|
|
|
|
autocomplete="off"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="form-helper-text" id="login-error" style="color: var(--md-error); display: none;"></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<button class="btn btn-filled" id="login-submit-btn" style="width: 100%;">
|
|
|
|
|
|
Войти
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3.3 Добавить стили для Login Screen
|
|
|
|
|
|
|
|
|
|
|
|
**Добавить в конец `styles.css`:**
|
|
|
|
|
|
```css
|
|
|
|
|
|
/* Login Screen */
|
|
|
|
|
|
.login-container {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
z-index: 9999;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.login-card {
|
|
|
|
|
|
background: white;
|
|
|
|
|
|
padding: var(--md-spacing-xxl);
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
|
|
|
|
|
max-width: 400px;
|
|
|
|
|
|
width: 90%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.login-card h2 {
|
|
|
|
|
|
color: #667eea;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.login-card .form-input:focus {
|
|
|
|
|
|
border-color: #667eea;
|
|
|
|
|
|
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.login-card .btn-filled {
|
|
|
|
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
margin-top: var(--md-spacing-md);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.login-card .btn-filled:hover {
|
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3.4 Удалить настройки сертификатов из UI
|
|
|
|
|
|
|
|
|
|
|
|
Сертификаты теперь настраиваются в `.env` на сервере, поэтому пользователь не должен их редактировать.
|
|
|
|
|
|
|
|
|
|
|
|
**Удалить из Settings Dialog:**
|
|
|
|
|
|
```html
|
|
|
|
|
|
<h6 class="mt-lg mb-md">Сертификаты (для прокси)</h6>
|
|
|
|
|
|
<div class="form-helper-text mb-md">...</div>
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label class="form-label">CA Certificate Path</label>
|
|
|
|
|
|
...
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<!-- И все остальные поля сертификатов -->
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3.5 Убрать поля host/port/endpoint из Settings Dialog
|
|
|
|
|
|
|
|
|
|
|
|
Эти поля теперь тоже настраиваются на сервере. Пользователь редактирует только:
|
|
|
|
|
|
- API Mode (bench/backend)
|
|
|
|
|
|
- Bearer Token
|
|
|
|
|
|
- System Platform / System Platform User
|
|
|
|
|
|
- Platform User ID / Platform ID
|
|
|
|
|
|
- With Classify / Reset Session Mode
|
|
|
|
|
|
|
|
|
|
|
|
**Удалить:**
|
|
|
|
|
|
```html
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label class="form-label">Полный URL (переопределяет host/port/endpoint)</label>
|
|
|
|
|
|
<input type="text" class="form-input" id="setting-full-url" ...>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label class="form-label">Host</label>
|
|
|
|
|
|
<input type="text" class="form-input" id="setting-host" ...>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label class="form-label">Port</label>
|
|
|
|
|
|
<input type="text" class="form-input" id="setting-port" ...>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label class="form-label">Endpoint</label>
|
|
|
|
|
|
<input type="text" class="form-input" id="setting-endpoint" ...>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label class="form-label">System ID</label>
|
|
|
|
|
|
<input type="text" class="form-input" id="setting-system-id" ...>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label class="form-label">Request ID Template</label>
|
|
|
|
|
|
<input type="text" class="form-input" id="setting-request-id" ...>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3.6 Добавить кнопку Logout
|
|
|
|
|
|
|
|
|
|
|
|
**Добавить в App Bar Actions:**
|
|
|
|
|
|
```html
|
|
|
|
|
|
<button class="btn-icon" id="logout-btn" title="Выход">
|
|
|
|
|
|
<span class="material-icons">logout</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Шаг 4: Модификация app.js
|
|
|
|
|
|
|
|
|
|
|
|
### 4.1 Удалить старые API функции
|
|
|
|
|
|
|
|
|
|
|
|
**Удалить функции:**
|
|
|
|
|
|
- `buildApiUrl()`
|
|
|
|
|
|
- `sendQuery()`
|
|
|
|
|
|
- `buildBackendApiUrl()`
|
|
|
|
|
|
- `buildBackendHeaders()`
|
|
|
|
|
|
- `sendBackendAsk()`
|
|
|
|
|
|
- `sendBackendReset()`
|
|
|
|
|
|
- `sendBackendSequential()`
|
|
|
|
|
|
|
|
|
|
|
|
### 4.2 Добавить Login Flow
|
|
|
|
|
|
|
|
|
|
|
|
**Добавить в начало файла (после объявления AppState):**
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// ============================================
|
|
|
|
|
|
// Authentication
|
|
|
|
|
|
// ============================================
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Проверить авторизацию при загрузке страницы
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function checkAuth() {
|
|
|
|
|
|
if (!api.isAuthenticated()) {
|
|
|
|
|
|
showLoginScreen()
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Попробовать загрузить настройки (валидация токена)
|
|
|
|
|
|
try {
|
|
|
|
|
|
await loadSettingsFromServer()
|
|
|
|
|
|
return true
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Token validation failed:', error)
|
|
|
|
|
|
showLoginScreen()
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Показать экран авторизации
|
|
|
|
|
|
*/
|
|
|
|
|
|
function showLoginScreen() {
|
|
|
|
|
|
document.getElementById('login-screen').style.display = 'flex'
|
|
|
|
|
|
document.getElementById('app').style.display = 'none'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Скрыть экран авторизации и показать приложение
|
|
|
|
|
|
*/
|
|
|
|
|
|
function hideLoginScreen() {
|
|
|
|
|
|
document.getElementById('login-screen').style.display = 'none'
|
|
|
|
|
|
document.getElementById('app').style.display = 'block'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Обработка авторизации
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function handleLogin() {
|
|
|
|
|
|
const loginInput = document.getElementById('login-input')
|
|
|
|
|
|
const loginError = document.getElementById('login-error')
|
|
|
|
|
|
const loginBtn = document.getElementById('login-submit-btn')
|
|
|
|
|
|
|
|
|
|
|
|
const login = loginInput.value.trim()
|
|
|
|
|
|
|
|
|
|
|
|
// Валидация
|
|
|
|
|
|
if (!/^[0-9]{8}$/.test(login)) {
|
|
|
|
|
|
loginError.textContent = 'Логин должен состоять из 8 цифр'
|
|
|
|
|
|
loginError.style.display = 'block'
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
loginError.style.display = 'none'
|
|
|
|
|
|
loginBtn.disabled = true
|
|
|
|
|
|
loginBtn.textContent = 'Вход...'
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await api.login(login)
|
|
|
|
|
|
console.log('Login successful:', response.user)
|
|
|
|
|
|
|
|
|
|
|
|
// Загрузить настройки с сервера
|
|
|
|
|
|
await loadSettingsFromServer()
|
|
|
|
|
|
|
|
|
|
|
|
// Скрыть login screen, показать приложение
|
|
|
|
|
|
hideLoginScreen()
|
|
|
|
|
|
loginInput.value = ''
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Login failed:', error)
|
|
|
|
|
|
loginError.textContent = error.message || 'Ошибка авторизации'
|
|
|
|
|
|
loginError.style.display = 'block'
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
loginBtn.disabled = false
|
|
|
|
|
|
loginBtn.textContent = 'Войти'
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Выход из системы
|
|
|
|
|
|
*/
|
|
|
|
|
|
function handleLogout() {
|
|
|
|
|
|
if (confirm('Вы уверены, что хотите выйти?')) {
|
|
|
|
|
|
api.logout()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.3 Изменить управление настройками
|
|
|
|
|
|
|
|
|
|
|
|
**Заменить функции:**
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Загрузить настройки с сервера (DB API)
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function loadSettingsFromServer() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await api.getSettings()
|
|
|
|
|
|
|
|
|
|
|
|
// Преобразовать в формат AppState.settings
|
|
|
|
|
|
AppState.settings = {
|
|
|
|
|
|
activeEnvironment: AppState.currentEnvironment,
|
|
|
|
|
|
environments: {
|
|
|
|
|
|
ift: {
|
|
|
|
|
|
name: 'ИФТ',
|
|
|
|
|
|
...response.settings.ift
|
|
|
|
|
|
},
|
|
|
|
|
|
psi: {
|
|
|
|
|
|
name: 'ПСИ',
|
|
|
|
|
|
...response.settings.psi
|
|
|
|
|
|
},
|
|
|
|
|
|
prod: {
|
|
|
|
|
|
name: 'ПРОМ',
|
|
|
|
|
|
...response.settings.prod
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
requestTimeout: 1800000, // 30 минут (фиксировано)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log('Settings loaded from server:', AppState.settings)
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Failed to load settings from server:', error)
|
|
|
|
|
|
throw error
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Сохранить настройки на сервер (DB API)
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function saveSettingsToServer(settings) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// Извлечь только поля, которые сервер ожидает
|
|
|
|
|
|
const settingsToSave = {
|
|
|
|
|
|
ift: extractEnvironmentSettings(settings.environments.ift),
|
|
|
|
|
|
psi: extractEnvironmentSettings(settings.environments.psi),
|
|
|
|
|
|
prod: extractEnvironmentSettings(settings.environments.prod)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await api.updateSettings(settingsToSave)
|
|
|
|
|
|
AppState.settings = settings
|
|
|
|
|
|
|
|
|
|
|
|
console.log('Settings saved to server')
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Failed to save settings to server:', error)
|
|
|
|
|
|
throw error
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Извлечь только нужные поля для сервера
|
|
|
|
|
|
*/
|
|
|
|
|
|
function extractEnvironmentSettings(envSettings) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
apiMode: envSettings.apiMode,
|
|
|
|
|
|
bearerToken: envSettings.bearerToken || '',
|
|
|
|
|
|
systemPlatform: envSettings.systemPlatform || '',
|
|
|
|
|
|
systemPlatformUser: envSettings.systemPlatformUser || '',
|
|
|
|
|
|
platformUserId: envSettings.platformUserId || '',
|
|
|
|
|
|
platformId: envSettings.platformId || '',
|
|
|
|
|
|
withClassify: envSettings.withClassify || false,
|
|
|
|
|
|
resetSessionMode: envSettings.resetSessionMode !== false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// УДАЛИТЬ старые функции loadSettings() и saveSettings()
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.4 Заменить вызовы API для Query
|
|
|
|
|
|
|
|
|
|
|
|
**Было:**
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// Старый код
|
|
|
|
|
|
async function handleSendQuery() {
|
|
|
|
|
|
// ...
|
|
|
|
|
|
const response = await sendQuery(requestBody) // Прямой fetch к RAG
|
|
|
|
|
|
// ...
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Стало:**
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
async function handleSendQuery() {
|
|
|
|
|
|
// Получить текущие настройки окружения
|
|
|
|
|
|
const envSettings = getCurrentEnvSettings()
|
|
|
|
|
|
const env = AppState.currentEnvironment
|
|
|
|
|
|
|
|
|
|
|
|
// Проверить режим API
|
|
|
|
|
|
if (envSettings.apiMode === 'bench') {
|
|
|
|
|
|
// Batch mode
|
|
|
|
|
|
const response = await api.benchQuery(env, requestBody)
|
|
|
|
|
|
processResponse(response.response)
|
|
|
|
|
|
} else if (envSettings.apiMode === 'backend') {
|
|
|
|
|
|
// Sequential mode
|
|
|
|
|
|
const resetSession = envSettings.resetSessionMode !== false
|
|
|
|
|
|
const response = await api.backendQuery(env, requestBody, resetSession)
|
|
|
|
|
|
processResponse(response.response)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.5 Изменить Save Settings в Settings Dialog
|
|
|
|
|
|
|
|
|
|
|
|
**Заменить обработчик:**
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// Было
|
|
|
|
|
|
document.getElementById('save-settings-btn').addEventListener('click', () => {
|
|
|
|
|
|
const updatedSettings = readSettingsFromDialog()
|
|
|
|
|
|
saveSettings(updatedSettings) // localStorage
|
|
|
|
|
|
showToast('Настройки сохранены')
|
|
|
|
|
|
closeSettingsDialog()
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// Стало
|
|
|
|
|
|
document.getElementById('save-settings-btn').addEventListener('click', async () => {
|
|
|
|
|
|
const saveBtn = document.getElementById('save-settings-btn')
|
|
|
|
|
|
saveBtn.disabled = true
|
|
|
|
|
|
saveBtn.textContent = 'Сохранение...'
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const updatedSettings = readSettingsFromDialog()
|
|
|
|
|
|
await saveSettingsToServer(updatedSettings)
|
|
|
|
|
|
showToast('Настройки сохранены на сервере')
|
|
|
|
|
|
closeSettingsDialog()
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Failed to save settings:', error)
|
|
|
|
|
|
showToast(`Ошибка сохранения: ${error.message}`, 'error')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
saveBtn.disabled = false
|
|
|
|
|
|
saveBtn.textContent = 'Сохранить'
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.6 Обновить populateSettingsDialog()
|
|
|
|
|
|
|
|
|
|
|
|
**Удалить строки для полей, которые больше не редактируются:**
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
function populateSettingsDialog() {
|
|
|
|
|
|
const env = AppState.currentEnvironment
|
|
|
|
|
|
const envSettings = AppState.settings.environments[env]
|
|
|
|
|
|
|
|
|
|
|
|
// Set environment selector
|
|
|
|
|
|
document.getElementById('settings-env-selector').value = env
|
|
|
|
|
|
|
|
|
|
|
|
// API Mode
|
|
|
|
|
|
const apiMode = envSettings.apiMode || 'bench'
|
|
|
|
|
|
document.getElementById('setting-api-mode').value = apiMode
|
|
|
|
|
|
toggleBackendSettings(apiMode === 'backend')
|
|
|
|
|
|
|
|
|
|
|
|
// Populate environment-specific fields (только те что остались!)
|
|
|
|
|
|
document.getElementById('setting-bearer-token').value = envSettings.bearerToken || ''
|
|
|
|
|
|
document.getElementById('setting-system-platform').value = envSettings.systemPlatform || ''
|
|
|
|
|
|
document.getElementById('setting-system-platform-user').value = envSettings.systemPlatformUser || ''
|
|
|
|
|
|
|
|
|
|
|
|
// Backend mode fields
|
|
|
|
|
|
document.getElementById('setting-platform-user-id').value = envSettings.platformUserId || ''
|
|
|
|
|
|
document.getElementById('setting-platform-id').value = envSettings.platformId || ''
|
|
|
|
|
|
document.getElementById('setting-with-classify').checked = envSettings.withClassify || false
|
|
|
|
|
|
document.getElementById('setting-reset-session-mode').checked = envSettings.resetSessionMode !== false
|
|
|
|
|
|
|
|
|
|
|
|
// Убрать global timeout (теперь не редактируется)
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.7 Обновить readSettingsFromDialog()
|
|
|
|
|
|
|
|
|
|
|
|
**Изменить:**
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
function readSettingsFromDialog() {
|
|
|
|
|
|
const env = document.getElementById('settings-env-selector').value
|
|
|
|
|
|
|
|
|
|
|
|
// Update environment-specific settings
|
|
|
|
|
|
const updatedSettings = JSON.parse(JSON.stringify(AppState.settings)) // Deep copy
|
|
|
|
|
|
updatedSettings.environments[env] = {
|
|
|
|
|
|
name: updatedSettings.environments[env].name,
|
|
|
|
|
|
apiMode: document.getElementById('setting-api-mode').value,
|
|
|
|
|
|
bearerToken: document.getElementById('setting-bearer-token').value.trim(),
|
|
|
|
|
|
systemPlatform: document.getElementById('setting-system-platform').value.trim(),
|
|
|
|
|
|
systemPlatformUser: document.getElementById('setting-system-platform-user').value.trim(),
|
|
|
|
|
|
platformUserId: document.getElementById('setting-platform-user-id').value.trim(),
|
|
|
|
|
|
platformId: document.getElementById('setting-platform-id').value.trim(),
|
|
|
|
|
|
withClassify: document.getElementById('setting-with-classify').checked,
|
|
|
|
|
|
resetSessionMode: document.getElementById('setting-reset-session-mode').checked,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return updatedSettings
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.8 Добавить event listeners
|
|
|
|
|
|
|
|
|
|
|
|
**Добавить в секцию Event Listeners:**
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// Login
|
|
|
|
|
|
document.getElementById('login-submit-btn').addEventListener('click', handleLogin)
|
|
|
|
|
|
document.getElementById('login-input').addEventListener('keypress', (e) => {
|
|
|
|
|
|
if (e.key === 'Enter') {
|
|
|
|
|
|
handleLogin()
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// Logout
|
|
|
|
|
|
document.getElementById('logout-btn').addEventListener('click', handleLogout)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.9 Изменить инициализацию приложения
|
|
|
|
|
|
|
|
|
|
|
|
**Было:**
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// Initialize app on load
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
|
|
|
|
AppState.settings = loadSettings()
|
|
|
|
|
|
// ...
|
|
|
|
|
|
initApp()
|
|
|
|
|
|
})
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Стало:**
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// Initialize app on load
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
|
|
|
|
// Проверить авторизацию
|
|
|
|
|
|
const isAuthenticated = await checkAuth()
|
|
|
|
|
|
|
|
|
|
|
|
if (isAuthenticated) {
|
|
|
|
|
|
// Пользователь авторизован, инициализировать приложение
|
|
|
|
|
|
initApp()
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Шаг 5: Изменения в settings.js
|
|
|
|
|
|
|
|
|
|
|
|
**Упростить defaultSettings:**
|
|
|
|
|
|
|
|
|
|
|
|
Удалить поля, которые теперь настраиваются на сервере (host, port, endpoint, certPaths, systemId, requestIdTemplate).
|
|
|
|
|
|
|
|
|
|
|
|
Оставить только дефолты для тех полей, которые пользователь может редактировать:
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
const defaultSettings = {
|
|
|
|
|
|
activeEnvironment: 'ift',
|
|
|
|
|
|
|
|
|
|
|
|
environments: {
|
|
|
|
|
|
ift: {
|
|
|
|
|
|
name: 'ИФТ',
|
|
|
|
|
|
apiMode: 'bench',
|
|
|
|
|
|
bearerToken: '',
|
|
|
|
|
|
systemPlatform: '',
|
|
|
|
|
|
systemPlatformUser: '',
|
|
|
|
|
|
platformUserId: '',
|
|
|
|
|
|
platformId: '',
|
|
|
|
|
|
withClassify: false,
|
|
|
|
|
|
resetSessionMode: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
psi: {
|
|
|
|
|
|
name: 'ПСИ',
|
|
|
|
|
|
apiMode: 'bench',
|
|
|
|
|
|
bearerToken: '',
|
|
|
|
|
|
systemPlatform: '',
|
|
|
|
|
|
systemPlatformUser: '',
|
|
|
|
|
|
platformUserId: '',
|
|
|
|
|
|
platformId: '',
|
|
|
|
|
|
withClassify: false,
|
|
|
|
|
|
resetSessionMode: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
prod: {
|
|
|
|
|
|
name: 'ПРОМ',
|
|
|
|
|
|
apiMode: 'bench',
|
|
|
|
|
|
bearerToken: '',
|
|
|
|
|
|
systemPlatform: '',
|
|
|
|
|
|
systemPlatformUser: '',
|
|
|
|
|
|
platformUserId: '',
|
|
|
|
|
|
platformId: '',
|
|
|
|
|
|
withClassify: false,
|
|
|
|
|
|
resetSessionMode: true,
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
requestTimeout: 1800000, // 30 minutes (не редактируется)
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Шаг 6: Раскомментировать static files в main.py
|
|
|
|
|
|
|
|
|
|
|
|
**В `app/main.py`:**
|
|
|
|
|
|
```python
|
|
|
|
|
|
# Serve static files (frontend)
|
|
|
|
|
|
app.mount("/static", StaticFiles(directory="static"), name="static")
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Раскомментировать эту строку.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Шаг 7: Тестирование
|
|
|
|
|
|
|
|
|
|
|
|
### 7.1 Запустить FastAPI локально
|
|
|
|
|
|
```bash
|
|
|
|
|
|
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 7.2 Открыть в браузере
|
|
|
|
|
|
```
|
|
|
|
|
|
http://localhost:8000/static/index.html
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 7.3 Тестовые сценарии
|
|
|
|
|
|
1. Login screen должен показаться
|
|
|
|
|
|
2. Ввести 8-значный логин → успешный вход
|
|
|
|
|
|
3. Настройки должны загрузиться с сервера
|
|
|
|
|
|
4. Изменить настройки → сохранить → перезагрузить → настройки сохранились
|
|
|
|
|
|
5. Отправить bench query → получить ответ
|
|
|
|
|
|
6. Отправить backend query → получить ответ
|
|
|
|
|
|
7. Нажать Logout → вернуться на login screen
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Ключевые отличия для разработчика
|
|
|
|
|
|
|
|
|
|
|
|
| Аспект | Старая версия | Новая версия |
|
|
|
|
|
|
|--------|--------------|--------------|
|
|
|
|
|
|
| Авторизация | Нет | JWT (8 цифр) |
|
|
|
|
|
|
| Настройки | localStorage | DB API (per-user) |
|
|
|
|
|
|
| API вызовы | fetch → RAG напрямую | fetch → FastAPI → RAG |
|
|
|
|
|
|
| mTLS | В браузере (невозможно) | На FastAPI сервере |
|
|
|
|
|
|
| Endpoints | RAG endpoints | FastAPI endpoints |
|
|
|
|
|
|
| Пользователи | Один (все настройки общие) | Множество (настройки персональные) |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Что НЕ нужно менять
|
|
|
|
|
|
|
|
|
|
|
|
- Весь UI (остаётся как есть)
|
|
|
|
|
|
- Логика отображения ответов
|
|
|
|
|
|
- Аннотации
|
|
|
|
|
|
- Экспорт/импорт анализа (работает через сохранение сессий на сервер)
|
|
|
|
|
|
- Material Design стили
|