diff --git a/api-client.js b/api-client.js index 525456d..bedec03 100644 --- a/api-client.js +++ b/api-client.js @@ -445,7 +445,7 @@ module.exports = function(app, db, upload) { }); /** - * POST /api/client/tasks/:taskId/sync - Синхронизировать задачу с целевым сервисом + * POST /api/client/tasks/:taskId/sync - Синхронизировать ЛОКАЛЬНУЮ задачу с целевым сервисом */ router.post('/api/client/tasks/:taskId/sync', requireAuth, async (req, res) => { const { taskId } = req.params; @@ -455,15 +455,9 @@ module.exports = function(app, db, upload) { target_api_key, sync_files = true } = req.body; - const { connection_id } = req.query; const userId = req.session.user.id; - if (!connection_id && !req.session.clientConnections) { - return res.status(400).json({ - error: 'Не указан источник (текущее подключение)' - }); - } - + // Определяем целевой сервис let targetUrl = target_api_url; let targetKey = target_api_key; @@ -479,40 +473,43 @@ module.exports = function(app, db, upload) { }); } + const baseUrl = targetUrl.replace(/\/$/, ''); + try { - const baseUrl = targetUrl.replace(/\/$/, ''); - const sourceConnection = req.session.clientConnections[connection_id]; + // 1. Получаем задачу из ЛОКАЛЬНОЙ базы данных + const localTask = await new Promise((resolve, reject) => { + db.get(` + SELECT t.*, u.name as creator_name, u.login as creator_login + FROM tasks t + LEFT JOIN users u ON t.created_by = u.id + WHERE t.id = ? + `, [taskId], (err, row) => { + if (err) reject(err); + else resolve(row); + }); + }); - if (!sourceConnection) { - return res.status(400).json({ error: 'Исходное подключение не найдено' }); + if (!localTask) { + return res.status(404).json({ error: 'Локальная задача не найдена' }); } - // 1. Получаем исходную задачу - const sourceResponse = await axios.get( - `${sourceConnection.url}/api/external/tasks/${taskId}`, - { - headers: { - 'X-API-Key': sourceConnection.api_key - }, - timeout: 10000 - } - ); + // Преобразуем в формат, аналогичный внешнему API + const sourceTask = { + id: localTask.id, + title: localTask.title, + description: localTask.description || '', + due_date: localTask.due_date, + task_type: localTask.task_type || 'regular', + files: await getLocalTaskFiles(taskId) + }; - if (!sourceResponse.data || !sourceResponse.data.success) { - return res.status(404).json({ error: 'Исходная задача не найдена' }); - } - - const sourceTask = sourceResponse.data.task; - // 2. Проверяем, существует ли задача в целевой системе (поиск по ID) let existingTask = null; try { const checkResponse = await axios.get( `${baseUrl}/api/external/tasks/${taskId}`, { - headers: { - 'X-API-Key': targetKey - }, + headers: { 'X-API-Key': targetKey }, timeout: 5000 } ); @@ -534,7 +531,7 @@ module.exports = function(app, db, upload) { title: sourceTask.title, description: sourceTask.description, due_date: sourceTask.due_date, - task_type: sourceTask.task_type || 'regular' + task_type: sourceTask.task_type }; const updateResponse = await axios.put( @@ -550,22 +547,17 @@ module.exports = function(app, db, upload) { ); if (!updateResponse.data || !updateResponse.data.success) { - return res.status(500).json({ - error: 'Не удалось обновить задачу в целевом сервисе' - }); + return res.status(500).json({ error: 'Не удалось обновить задачу в целевом сервисе' }); } - result = { - taskId: taskId, - action: 'updated' - }; + result = { taskId: taskId, action: 'updated' }; } else { - // 4. Создаем новую задачу (без префикса "Копия:") + // 4. Создаём новую задачу const taskData = { title: sourceTask.title, - description: sourceTask.description || '', + description: sourceTask.description, due_date: sourceTask.due_date, - task_type: sourceTask.task_type || 'regular' + task_type: sourceTask.task_type }; const createResponse = await axios.post( @@ -581,9 +573,7 @@ module.exports = function(app, db, upload) { ); if (!createResponse.data || !createResponse.data.success) { - return res.status(500).json({ - error: 'Не удалось создать задачу в целевом сервисе' - }); + return res.status(500).json({ error: 'Не удалось создать задачу в целевом сервисе' }); } result = { @@ -596,21 +586,17 @@ module.exports = function(app, db, upload) { if (sync_files && sourceTask.files && sourceTask.files.length > 0) { for (const file of sourceTask.files) { try { - // Скачиваем файл из источника - const fileResponse = await axios({ - method: 'GET', - url: `${sourceConnection.url}/api/external/tasks/${taskId}/files/${file.id}/download`, - headers: { - 'X-API-Key': sourceConnection.api_key - }, - responseType: 'arraybuffer', - timeout: 30000 - }); + // Читаем файл с диска + if (!fs.existsSync(file.file_path)) { + throw new Error(`Файл не найден на диске: ${file.file_path}`); + } + const fileBuffer = fs.readFileSync(file.file_path); + const fileName = file.original_name || path.basename(file.file_path); // Загружаем в целевую систему const formData = new FormData(); - formData.append('files', Buffer.from(fileResponse.data), { - filename: file.filename || file.original_name || 'file', + formData.append('files', fileBuffer, { + filename: fileName, contentType: file.file_type || 'application/octet-stream' }); @@ -629,25 +615,26 @@ module.exports = function(app, db, upload) { ); syncedFiles.push({ - original_name: file.filename || file.original_name, + original_name: fileName, success: uploadResponse.data && uploadResponse.data.success }); } catch (fileError) { console.error(`❌ Ошибка синхронизации файла:`, fileError.message); syncedFiles.push({ - original_name: file.filename || file.original_name, + original_name: file.original_name || 'unknown', success: false, error: fileError.message }); - warnings.push(`Не удалось синхронизировать файл: ${file.filename || file.original_name}`); + warnings.push(`Не удалось синхронизировать файл: ${file.original_name || file.filename}`); } } } + // Логируем действие const { logActivity } = require('./database'); if (logActivity) { logActivity(0, userId, 'API_CLIENT_SYNC_TASK', - `${existingTask ? 'Обновлена' : 'Создана'} задача ${taskId} из ${sourceConnection.url} в ${baseUrl}. ${existingTask ? 'Обновление' : 'Новый ID: ' + result.taskId}`); + `Локальная задача ${taskId} ${existingTask ? 'обновлена' : 'создана'} в ${baseUrl}. Новый ID: ${result.taskId}`); } res.json({ @@ -659,17 +646,17 @@ module.exports = function(app, db, upload) { target_task_id: result.taskId, target_service: baseUrl, synced_files: syncedFiles, - assignees: sourceTask.assignments || [], - warnings: warnings + assignees: [], // исполнители не передаём + warnings: warnings, + source: 'local' } }); } catch (error) { console.error('❌ Ошибка синхронизации задачи:', error.message); - let errorMessage = 'Ошибка синхронизации задачи'; let statusCode = 500; - + if (error.response) { if (error.response.status === 401) { errorMessage = 'Неверный API ключ для целевого сервиса'; @@ -678,7 +665,7 @@ module.exports = function(app, db, upload) { errorMessage = 'Нет прав для создания/обновления задачи в целевом сервисе'; statusCode = 403; } else if (error.response.status === 404) { - errorMessage = 'Исходная задача не найдена'; + errorMessage = 'Целевой эндпоинт не найден'; statusCode = 404; } else { errorMessage = error.response.data?.error || `Ошибка сервера: ${error.response.status}`; @@ -692,10 +679,7 @@ module.exports = function(app, db, upload) { statusCode = 504; } - res.status(statusCode).json({ - error: errorMessage, - details: error.message - }); + res.status(statusCode).json({ error: errorMessage, details: error.message }); } }); @@ -928,4 +912,20 @@ module.exports = function(app, db, upload) { app.use(router); console.log('✅ API клиент для внешних сервисов подключен'); -}; \ No newline at end of file +}; + +// Вспомогательная функция для получения файлов локальной задачи +async function getLocalTaskFiles(taskId) { + return new Promise((resolve, reject) => { + db.all(` + SELECT tf.*, u.name as user_name + FROM task_files tf + LEFT JOIN users u ON tf.user_id = u.id + WHERE tf.task_id = ? + ORDER BY tf.uploaded_at DESC + `, [taskId], (err, files) => { + if (err) reject(err); + else resolve(files || []); + }); + }); +} \ No newline at end of file diff --git a/public/client.html b/public/client.html index cdec17f..d199a2e 100644 --- a/public/client.html +++ b/public/client.html @@ -989,757 +989,6 @@
- +