// Глобальные переменные let currentRequestId = null; let requestsData = []; // Инициализация при загрузке страницы document.addEventListener('DOMContentLoaded', function () { loadRequests(); updateStatusIndicators(); addQueryParam(); addPayloadParam(); }); // Обновление индикаторов статуса function updateStatusIndicators() { const urlCheck = document.getElementById('urlCheck'); const apiKeyCheck = document.getElementById('apiKeyCheck'); const saveServerUrlButton = document.getElementById('saveServerUrlButton'); const uploadApiKeyButton = document.getElementById('uploadApiKeyButton'); // URL индикатор if (pageData && pageData.url) { urlCheck.innerHTML = 'Настроен'; saveServerUrlButton.innerHTML = 'Обновить URL'; saveServerUrlButton.classList.remove('btn-success'); saveServerUrlButton.classList.add('btn-outline-success'); } else { urlCheck.innerHTML = 'Не настроен'; saveServerUrlButton.innerHTML = 'Сохранить URL'; saveServerUrlButton.classList.remove('btn-outline-success'); saveServerUrlButton.classList.add('btn-success'); } // API Key индикатор if (pageData && pageData.apiKey) { apiKeyCheck.innerHTML = 'Загружен'; uploadApiKeyButton.innerHTML = 'Обновить ключ'; uploadApiKeyButton.classList.remove('btn-primary'); uploadApiKeyButton.classList.add('btn-outline-primary'); } else { apiKeyCheck.innerHTML = 'Не загружен'; uploadApiKeyButton.innerHTML = 'Загрузить API ключ'; uploadApiKeyButton.classList.remove('btn-outline-primary'); uploadApiKeyButton.classList.add('btn-primary'); } } // Сохранение URL сервера async function saveServerUrl() { const serverUrl = document.getElementById('server_url').value.trim(); if (!serverUrl) { showAlert('warning', 'Введите URL сервера'); return; } try { const response = await fetch('/api/medods', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: serverUrl }) }); if (response.ok) { window.pageData = window.pageData || {}; window.pageData.url = serverUrl; showAlert('success', 'URL сервера сохранен!'); updateStatusIndicators(); } else { const error = await response.text(); showAlert('danger', 'Ошибка: ' + error); } } catch (error) { console.error('Ошибка:', error); showAlert('danger', 'Ошибка сохранения!'); } } // Загрузка API ключа async function uploadApiKey() { const fileInput = document.getElementById('api_key_file'); const file = fileInput.files[0]; if (!file) { showAlert('warning', 'Выберите CSV файл'); return; } try { const text = await file.text(); const lines = text.trim().split('\n'); if (lines.length < 2) { showAlert('warning', 'Файл должен содержать минимум 2 строки'); return; } const headers = lines[0].split(';').map(h => h.trim()); if (!headers.includes('identity') || !headers.includes('secretKey')) { showAlert('warning', 'Файл должен содержать колонки: identity и secretKey'); return; } const keyInfo = lines[1].split(';').map(h => h.trim()); const apiKey = {}; for (let i = 0; i < headers.length; i++) { apiKey[headers[i]] = keyInfo[i]; } const response = await fetch('/api/medods', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ apiKey }) }); if (response.ok) { window.pageData = window.pageData || {}; window.pageData.apiKey = apiKey; showAlert('success', 'API ключ загружен!'); fileInput.value = ''; updateStatusIndicators(); } else { const error = await response.text(); showAlert('danger', 'Ошибка: ' + error); } } catch (error) { console.error('Ошибка:', error); showAlert('danger', 'Ошибка загрузки файла!'); } } // Загрузка списка запросов async function loadRequests() { try { const response = await fetch('/api/requests'); const data = await response.json(); requestsData = data.requests ? data.requests : []; renderRequestsList(); } catch (error) { console.error('Ошибка загрузки запросов:', error); showAlert('danger', 'Ошибка загрузки запросов'); } } // Отображение списка запросов function renderRequestsList() { const container = document.getElementById('requestsList'); if (requestsData.length === 0) { container.innerHTML = `
Нет сохраненных запросов
Нажмите "Новый запрос" для создания
`; return; } let html = '
'; requestsData.forEach(request => { const methodClass = getMethodClass(request.method); const isActive = currentRequestId === request.id; html += `
${request.method} ${request.title}
${request.url_path}
`; }); html += '
'; container.innerHTML = html; } // Получение класса для метода function getMethodClass(method) { const classes = { 'GET': 'bg-primary', 'POST': 'bg-success', 'PUT': 'bg-warning text-dark', 'DELETE': 'bg-danger', 'PATCH': 'bg-info' }; return classes[method] || 'bg-secondary'; } // Создание нового запроса function newRequest() { resetForm(); document.getElementById('editorTitle').textContent = 'Создание нового запроса'; document.getElementById('saveRequestButton').innerHTML = 'Сохранить запрос'; document.getElementById('executeButton').disabled = true; currentRequestId = null; updateActiveItem(); // Скролл к редактору document.querySelector('.col-md-8').scrollIntoView({ behavior: 'smooth', block: 'start' }); } // Редактирование запроса function editRequest(id) { const request = requestsData.find(r => r.id === id); if (!request) return; currentRequestId = id; document.getElementById('editorTitle').textContent = `Редактирование: ${request.title}`; document.getElementById('saveRequestButton').innerHTML = 'Обновить запрос'; document.getElementById('executeButton').disabled = false; // Заполняем поля формы document.getElementById('requestId').value = request.id; document.getElementById('title').value = request.title; document.getElementById('method').value = request.method; document.getElementById('url_path').value = request.url_path; // Очищаем и заполняем query параметры document.getElementById('queryParamsContainer').innerHTML = ''; if (request.query_params && typeof request.query_params === 'object' && Object.keys(request.query_params).length > 0) { Object.entries(request.query_params).forEach(([key, value]) => { addQueryParam(key, value); }); } else { addQueryParam(); } // Очищаем и заполняем payload параметры document.getElementById('payloadParamsContainer').innerHTML = ''; if (request.payload && typeof request.payload === 'object' && Object.keys(request.payload).length > 0) { Object.entries(request.payload).forEach(([key, value]) => { addPayloadParam(key, typeof value === 'object' ? JSON.stringify(value) : value); }); } else { addPayloadParam(); } // Даты создания и обновления document.getElementById('timestampDiv').classList.remove('d-none'); document.getElementById('createdAt').textContent = request.created_at; document.getElementById('updatedAt').textContent = request.updated_at; updateActiveItem(); // Скролл к редактору document.querySelector('.col-md-8').scrollIntoView({ behavior: 'smooth', block: 'start' }); } // Обновление активного элемента в списке function updateActiveItem() { document.querySelectorAll('.request-item').forEach(item => { item.classList.remove('active'); }); if (currentRequestId) { const activeItem = document.querySelector(`[onclick="editRequest(${currentRequestId})"]`); if (activeItem) { activeItem.classList.add('active'); } } } // Добавление параметра Query function addQueryParam(key = '', value = '') { const container = document.getElementById('queryParamsContainer'); const paramId = Date.now() + Math.random(); const html = `
`; container.insertAdjacentHTML('beforeend', html); } // Добавление параметра Payload function addPayloadParam(key = '', value = '') { const container = document.getElementById('payloadParamsContainer'); const paramId = Date.now() + Math.random(); const html = `
`; container.insertAdjacentHTML('beforeend', html); } // Сброс формы function resetForm() { document.getElementById('requestForm').reset(); document.getElementById('requestId').value = ''; document.getElementById('queryParamsContainer').innerHTML = ''; document.getElementById('payloadParamsContainer').innerHTML = ''; addQueryParam(); addPayloadParam(); currentRequestId = null; document.getElementById('editorTitle').textContent = 'Создание нового запроса'; document.getElementById('saveRequestButton').innerHTML = 'Сохранить запрос'; document.getElementById('executeButton').disabled = true; updateActiveItem(); } // Сохранение запроса async function saveRequest() { const id = document.getElementById('requestId').value; const title = document.getElementById('title').value.trim(); const method = document.getElementById('method').value; const url_path = document.getElementById('url_path').value.trim(); if (!title || !method || !url_path) { showAlert('warning', 'Заполните все обязательные поля'); return; } // Собираем query параметры const query_params = {}; document.querySelectorAll('#queryParamsContainer .param-row').forEach(row => { const key = row.querySelector('.param-key').value.trim(); const value = row.querySelector('.param-value').value.trim(); if (key) query_params[key] = value; }); // Собираем payload параметры const payload = {}; document.querySelectorAll('#payloadParamsContainer .param-row').forEach(row => { const key = row.querySelector('.param-key').value.trim(); const value = row.querySelector('.param-value').value.trim(); if (key) { try { payload[key] = JSON.parse(value); } catch { payload[key] = value; } } }); const requestData = { title, method, url_path, query_params, payload }; if (id) requestData.id = parseInt(id); try { const response = await fetch('/api/requests', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(requestData) }); if (response.ok) { showAlert('success', 'Запрос успешно сохранен!'); await loadRequests(); if (id) editRequest(parseInt(id)); } else { const error = await response.text(); showAlert('danger', 'Ошибка сохранения: ' + error); } } catch (error) { console.error('Ошибка:', error); showAlert('danger', 'Ошибка сохранения запроса!'); } } // Удаление запроса async function deleteRequest(id) { if (!confirm('Вы уверены, что хотите удалить этот запрос?')) return; try { const response = await fetch(`/api/requests/${id}`, { method: 'DELETE' }); if (response.ok) { showAlert('success', 'Запрос удален!'); if (currentRequestId === id) resetForm(); await loadRequests(); } else { const error = await response.text(); showAlert('danger', 'Ошибка удаления: ' + error); } } catch (error) { console.error('Ошибка:', error); showAlert('danger', 'Ошибка удаления запроса!'); } } // Выполнение текущего запроса async function executeCurrentRequest() { if (!currentRequestId) { showAlert('warning', 'Сначала выберите или создайте запрос'); return; } await executeRequest(currentRequestId); } // Выполнение запроса по ID async function executeRequest(id) { try { showLoader(true); const response = await fetch('/api/requests', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: parseInt(id) }) }); const data = await response.json(); displayResponse(data); showResponseSection(); } catch (error) { console.error('Ошибка:', error); displayResponse({ error: error.message }); showResponseSection(); } finally { showLoader(false); } } // Отображение ответа function displayResponse(data) { const container = document.getElementById('responseContainer'); container.innerHTML = ''; // Создаем пре для лучшего отображения const pre = document.createElement('pre'); pre.className = 'response-pre'; pre.appendChild(formatJson(data, 0)); container.appendChild(pre); window.lastResponse = data; } // Форматирование JSON function formatJson(data, indent) { const fragment = document.createDocumentFragment(); function format(value, depth) { const indentStr = ' '.repeat(depth); if (value === null) { const span = document.createElement('span'); span.className = 'json-null'; span.textContent = 'null'; return span; } else if (typeof value === 'boolean') { const span = document.createElement('span'); span.className = 'json-boolean'; span.textContent = value.toString(); return span; } else if (typeof value === 'number') { const span = document.createElement('span'); span.className = 'json-number'; span.textContent = value; return span; } else if (typeof value === 'string') { const span = document.createElement('span'); span.className = 'json-string'; span.textContent = JSON.stringify(value); return span; } else if (Array.isArray(value)) { if (value.length === 0) { return document.createTextNode('[]'); } const div = document.createElement('div'); div.appendChild(document.createTextNode('[')); value.forEach((item, index) => { const itemDiv = document.createElement('div'); itemDiv.style.marginLeft = '20px'; itemDiv.appendChild(format(item, depth + 1)); if (index < value.length - 1) { itemDiv.appendChild(document.createTextNode(',')); } div.appendChild(itemDiv); }); div.appendChild(document.createTextNode(']')); return div; } else if (typeof value === 'object') { const entries = Object.entries(value); if (entries.length === 0) { return document.createTextNode('{}'); } const div = document.createElement('div'); div.appendChild(document.createTextNode('{')); entries.forEach(([key, val], index) => { const itemDiv = document.createElement('div'); itemDiv.style.marginLeft = '20px'; const keySpan = document.createElement('span'); keySpan.className = 'json-key'; keySpan.textContent = JSON.stringify(key) + ': '; itemDiv.appendChild(keySpan); itemDiv.appendChild(format(val, depth + 1)); if (index < entries.length - 1) { itemDiv.appendChild(document.createTextNode(',')); } div.appendChild(itemDiv); }); div.appendChild(document.createTextNode('}')); return div; } return document.createTextNode(String(value)); } fragment.appendChild(format(data, 0)); return fragment; } // Показать раздел с ответом function showResponseSection() { const responseCard = document.getElementById('responseCard'); responseCard.style.display = 'block'; // Анимация появления responseCard.classList.add('fade-in'); // Скролл к результату setTimeout(() => { responseCard.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }, 100); } // Переключение видимости ответа function toggleResponse() { const responseBody = document.getElementById('responseBody'); const toggleBtn = document.querySelector('#responseCard .bi-chevron-up'); if (responseBody.style.display === 'none') { responseBody.style.display = 'block'; toggleBtn.classList.remove('bi-chevron-down'); toggleBtn.classList.add('bi-chevron-up'); } else { responseBody.style.display = 'none'; toggleBtn.classList.remove('bi-chevron-up'); toggleBtn.classList.add('bi-chevron-down'); } } // Скачивание ответа function downloadResponse() { if (!window.lastResponse) { showAlert('warning', 'Нет данных для скачивания'); return; } const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const filename = `medods_response_${timestamp}.json`; const jsonStr = JSON.stringify(window.lastResponse, null, 2); const blob = new Blob([jsonStr], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } // Вспомогательные функции function showAlert(type, message) { const alertContainer = document.getElementById('alertContainer'); const alert = document.createElement('div'); alert.className = `alert alert-${type} alert-dismissible fade show shadow`; alert.style.minWidth = '300px'; alert.style.maxWidth = '400px'; alert.innerHTML = `
${message}
`; alertContainer.appendChild(alert); // Автоматическое удаление через 4 секунды setTimeout(() => { if (alert.parentNode) { alert.classList.remove('show'); setTimeout(() => alert.remove(), 150); } }, 4000); } function getAlertIcon(type) { const icons = { 'success': 'bi-check-circle-fill', 'warning': 'bi-exclamation-triangle-fill', 'danger': 'bi-x-circle-fill', 'info': 'bi-info-circle-fill' }; return icons[type] || 'bi-info-circle-fill'; } function showLoader(show) { const executeButton = document.getElementById('executeButton'); if (show) { executeButton.innerHTML = 'Выполняется...'; executeButton.disabled = true; } else { executeButton.innerHTML = 'Запустить'; executeButton.disabled = !currentRequestId; } }