connected btx (no import)

This commit is contained in:
Ivan I. Ovchinnikov
2026-03-18 22:05:02 +03:00
parent f0fdba0ff6
commit d1570b30c4
17 changed files with 1914 additions and 602 deletions
+219 -25
View File
@@ -11,13 +11,20 @@
background: #f5f5f5;
padding: 20px;
}
.container { max-width: 600px; margin: 0 auto; }
.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 {
@@ -56,11 +63,11 @@
font-weight: 500;
}
.btn:hover { background: #0056b3; }
.btn-secondary {
background: #6c757d;
margin-left: 10px;
}
.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;
@@ -90,6 +97,52 @@
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>
@@ -98,16 +151,28 @@
<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">
{% if error.is_some() %}
<div class="error">{{ error.as_ref().unwrap() }}</div>
{% endif %}
<h2>
🔴 Redmine
{% if !redmine_url.is_empty() %}
<span class="saved-indicator">✓ Сохранено</span>
{% endif %}
</h2>
{% if success.is_some() %}
<div class="success">{{ success.as_ref().unwrap() }}</div>
{% endif %}
<form method="POST" action="/settings">
<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
@@ -115,10 +180,9 @@
id="redmine_url"
name="redmine_url"
value="{{ redmine_url }}"
placeholder="https://redmine.example.com"
required
placeholder="https://redmine.company.com"
>
<div class="help-text">Полный URL вашего Redmine, например: https://redmine.company.com</div>
<div class="help-text">Полный URL вашего Redmine</div>
</div>
<div class="form-group">
@@ -128,10 +192,12 @@
id="redmine_api_key"
name="redmine_api_key"
value=""
placeholder="Ваш API ключ из настроек Redmine"
required
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">
@@ -142,20 +208,148 @@
name="redmine_user_id"
value="{{ redmine_user_id }}"
placeholder="7"
required
>
<div class="help-text">Ваш ID пользователя в Redmine (видно в профиле или URL)</div>
</div>
<button type="submit" class="btn">💾 Сохранить настройки</button>
<a href="/" class="btn btn-secondary">Отмена</a>
<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>
<div class="security-note">
🔒 <strong>Безопасность:</strong> Ваши учётные данные хранятся только в сессии браузера и не сохраняются на сервере.
При закрытии браузера сессия истекает и данные удаляются.
</div>
<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>