```
### 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
```
### 3.6 Добавить кнопку Logout
**Добавить в App Bar Actions:**
```html
```
---
## Шаг 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 стили