356 lines
14 KiB
HTML
356 lines
14 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Настройки — Bitmine</title>
|
||
<style>
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||
background: #f5f5f5;
|
||
padding: 20px;
|
||
}
|
||
.container { max-width: 800px; margin: 0 auto; }
|
||
h1 { color: #333; margin-bottom: 20px; }
|
||
.card {
|
||
background: white;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||
padding: 24px;
|
||
margin-bottom: 20px;
|
||
}
|
||
.card h2 {
|
||
color: #333;
|
||
margin-bottom: 16px;
|
||
padding-bottom: 12px;
|
||
border-bottom: 2px solid #eee;
|
||
}
|
||
.form-group { margin-bottom: 20px; }
|
||
label {
|
||
display: block;
|
||
margin-bottom: 8px;
|
||
font-weight: 500;
|
||
color: #333;
|
||
}
|
||
input[type="text"],
|
||
input[type="password"],
|
||
input[type="number"] {
|
||
width: 100%;
|
||
padding: 12px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
}
|
||
input:focus {
|
||
outline: none;
|
||
border-color: #007bff;
|
||
box-shadow: 0 0 0 3px rgba(0,123,255,0.1);
|
||
}
|
||
.help-text {
|
||
font-size: 12px;
|
||
color: #666;
|
||
margin-top: 4px;
|
||
}
|
||
.btn {
|
||
background: #007bff;
|
||
color: white;
|
||
border: none;
|
||
padding: 12px 24px;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
}
|
||
.btn:hover { background: #0056b3; }
|
||
.btn-secondary { background: #6c757d; margin-left: 10px; }
|
||
.btn-secondary:hover { background: #545b62; }
|
||
.btn-test { background: #28a745; margin-left: 10px; }
|
||
.btn-test:hover { background: #218838; }
|
||
.btn-test:disabled { background: #ccc; cursor: not-allowed; }
|
||
.error {
|
||
background: #ffebee;
|
||
color: #c62828;
|
||
padding: 16px;
|
||
border-radius: 8px;
|
||
margin-bottom: 20px;
|
||
}
|
||
.success {
|
||
background: #e8f5e9;
|
||
color: #388e3c;
|
||
padding: 16px;
|
||
border-radius: 8px;
|
||
margin-bottom: 20px;
|
||
}
|
||
.back-link {
|
||
display: inline-block;
|
||
margin-bottom: 20px;
|
||
color: #007bff;
|
||
text-decoration: none;
|
||
}
|
||
.back-link:hover { text-decoration: underline; }
|
||
.security-note {
|
||
background: #fff3e0;
|
||
border-left: 4px solid #f57c00;
|
||
padding: 12px;
|
||
margin-top: 20px;
|
||
font-size: 13px;
|
||
color: #e65100;
|
||
}
|
||
.test-result {
|
||
margin-top: 12px;
|
||
padding: 12px;
|
||
border-radius: 6px;
|
||
display: none;
|
||
}
|
||
.test-result.success {
|
||
background: #e8f5e9;
|
||
color: #388e3c;
|
||
display: block;
|
||
}
|
||
.test-result.error {
|
||
background: #ffebee;
|
||
color: #c62828;
|
||
display: block;
|
||
}
|
||
.webhook-instructions {
|
||
background: #f8f9fa;
|
||
border: 1px solid #dee2e6;
|
||
border-radius: 6px;
|
||
padding: 16px;
|
||
margin-top: 12px;
|
||
font-size: 13px;
|
||
}
|
||
.webhook-instructions ol {
|
||
margin-left: 20px;
|
||
margin-top: 8px;
|
||
}
|
||
.webhook-instructions li {
|
||
margin-bottom: 8px;
|
||
}
|
||
.webhook-instructions code {
|
||
background: #e9ecef;
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
font-family: monospace;
|
||
}
|
||
.saved-indicator {
|
||
display: inline-block;
|
||
margin-left: 8px;
|
||
padding: 4px 8px;
|
||
background: #e8f5e9;
|
||
color: #388e3c;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<a href="/" class="back-link">← Назад к списку задач</a>
|
||
|
||
<h1>⚙️ Настройки подключения</h1>
|
||
|
||
{% if error.is_some() %}
|
||
<div class="error">{{ error.as_ref().unwrap() }}</div>
|
||
{% endif %}
|
||
|
||
{% if success.is_some() %}
|
||
<div class="success">{{ success.as_ref().unwrap() }}</div>
|
||
{% endif %}
|
||
|
||
<!-- Redmine Settings -->
|
||
<div class="card">
|
||
<h2>
|
||
🔴 Redmine
|
||
{% if !redmine_url.is_empty() %}
|
||
<span class="saved-indicator">✓ Сохранено</span>
|
||
{% endif %}
|
||
</h2>
|
||
|
||
<form method="POST" action="/settings" id="redmine-form">
|
||
<!-- Скрытые поля для сохранения Bitrix настроек -->
|
||
<input type="hidden" name="bitrix_url" value="{{ bitrix_url }}">
|
||
<input type="hidden" name="bitrix_webhook" value="{{ bitrix_webhook }}">
|
||
|
||
<div class="form-group">
|
||
<label for="redmine_url">Redmine URL</label>
|
||
<input
|
||
type="text"
|
||
id="redmine_url"
|
||
name="redmine_url"
|
||
value="{{ redmine_url }}"
|
||
placeholder="https://redmine.company.com"
|
||
>
|
||
<div class="help-text">Полный URL вашего Redmine</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="redmine_api_key">API Key</label>
|
||
<input
|
||
type="password"
|
||
id="redmine_api_key"
|
||
name="redmine_api_key"
|
||
value=""
|
||
placeholder="Введите API ключ для сохранения"
|
||
>
|
||
<div class="help-text">Ключ можно получить в Настройки → Мой аккаунт → API access key</div>
|
||
{% if !redmine_url.is_empty() %}
|
||
<div class="help-text" style="color: #388e3c;">✓ API ключ уже сохранён в сессии</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="redmine_user_id">User ID</label>
|
||
<input
|
||
type="number"
|
||
id="redmine_user_id"
|
||
name="redmine_user_id"
|
||
value="{{ redmine_user_id }}"
|
||
placeholder="7"
|
||
>
|
||
<div class="help-text">Ваш ID пользователя в Redmine (видно в профиле или URL)</div>
|
||
</div>
|
||
|
||
<button type="submit" class="btn">💾 Сохранить Redmine</button>
|
||
<button type="button" class="btn btn-test" onclick="testRedmineConnection()">🔍 Тест подключения</button>
|
||
<div id="redmine-test-result" class="test-result"></div>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- Bitrix24 Settings -->
|
||
<div class="card">
|
||
<h2>
|
||
🔵 Bitrix24
|
||
{% if !bitrix_url.is_empty() %}
|
||
<span class="saved-indicator">✓ Сохранено</span>
|
||
{% endif %}
|
||
</h2>
|
||
|
||
<form method="POST" action="/settings" id="bitrix-form">
|
||
<!-- Скрытые поля для сохранения Redmine настроек -->
|
||
<input type="hidden" name="redmine_url" value="{{ redmine_url }}">
|
||
<input type="hidden" name="redmine_user_id" value="{{ redmine_user_id }}">
|
||
|
||
<div class="form-group">
|
||
<label for="bitrix_url">Bitrix24 URL</label>
|
||
<input
|
||
type="text"
|
||
id="bitrix_url"
|
||
name="bitrix_url"
|
||
value="{{ bitrix_url }}"
|
||
placeholder="https://corp.company.com"
|
||
>
|
||
<div class="help-text">URL вашего портала Bitrix24</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="bitrix_webhook">Входящий вебхук</label>
|
||
<input
|
||
type="text"
|
||
id="bitrix_webhook"
|
||
name="bitrix_webhook"
|
||
value="{{ bitrix_webhook }}"
|
||
placeholder="rest/105/abc123xyz/"
|
||
>
|
||
<div class="help-text">Код вебхука из раздела Разработчикам</div>
|
||
{% if !bitrix_url.is_empty() %}
|
||
<div class="help-text" style="color: #388e3c;">✓ Вебхук уже сохранён в сессии</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div class="webhook-instructions">
|
||
<strong>📋 Как получить входящий вебхук в Bitrix24:</strong>
|
||
<ol>
|
||
<li>Откройте ваш Bitrix24 портал</li>
|
||
<li>Перейдите в меню <code>Разработчикам</code> (внизу левого меню)</li>
|
||
<li>Выберите <code>Другое</code> → <code>Входящий вебхук</code></li>
|
||
<li>Выберите права доступа: <code>tasks</code>, <code>user</code>, <code>department</code></li>
|
||
<li>Нажмите <code>Создать вебхук</code></li>
|
||
<li>Скопируйте URL вида <code>https://your-portal.com/rest/105/CODE/</code></li>
|
||
<li>В поле выше вставьте только часть после домена: <code>rest/105/CODE/</code></li>
|
||
</ol>
|
||
</div>
|
||
|
||
<button type="submit" class="btn">💾 Сохранить Bitrix24</button>
|
||
<button type="button" class="btn btn-test" onclick="testBitrixConnection()">🔍 Тест подключения</button>
|
||
<div id="bitrix-test-result" class="test-result"></div>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="security-note">
|
||
🔒 <strong>Безопасность:</strong> Ваши учётные данные хранятся только в сессии браузера и не сохраняются на сервере.
|
||
При закрытии браузера сессия истекает и данные удаляются.
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
async function testRedmineConnection() {
|
||
const resultDiv = document.getElementById('redmine-test-result');
|
||
const btn = event.target;
|
||
|
||
btn.disabled = true;
|
||
btn.textContent = '⏳ Проверка...';
|
||
resultDiv.className = 'test-result';
|
||
resultDiv.textContent = '';
|
||
|
||
try {
|
||
const response = await fetch('/api/test/redmine', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({})
|
||
});
|
||
const data = await response.json();
|
||
|
||
if (data.success) {
|
||
resultDiv.className = 'test-result success';
|
||
resultDiv.textContent = '✅ ' + data.message;
|
||
} else {
|
||
resultDiv.className = 'test-result error';
|
||
resultDiv.textContent = '❌ ' + data.message;
|
||
}
|
||
} catch (e) {
|
||
resultDiv.className = 'test-result error';
|
||
resultDiv.textContent = '❌ Ошибка: ' + e.message;
|
||
} finally {
|
||
btn.disabled = false;
|
||
btn.textContent = '🔍 Тест подключения';
|
||
}
|
||
}
|
||
|
||
async function testBitrixConnection() {
|
||
const resultDiv = document.getElementById('bitrix-test-result');
|
||
const btn = event.target;
|
||
|
||
btn.disabled = true;
|
||
btn.textContent = '⏳ Проверка...';
|
||
resultDiv.className = 'test-result';
|
||
resultDiv.textContent = '';
|
||
|
||
try {
|
||
const response = await fetch('/api/test/bitrix', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({})
|
||
});
|
||
const data = await response.json();
|
||
|
||
if (data.success) {
|
||
resultDiv.className = 'test-result success';
|
||
resultDiv.textContent = '✅ ' + data.message;
|
||
} else {
|
||
resultDiv.className = 'test-result error';
|
||
resultDiv.textContent = '❌ ' + data.message;
|
||
}
|
||
} catch (e) {
|
||
resultDiv.className = 'test-result error';
|
||
resultDiv.textContent = '❌ Ошибка: ' + e.message;
|
||
} finally {
|
||
btn.disabled = false;
|
||
btn.textContent = '🔍 Тест подключения';
|
||
}
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|