diff --git a/api-client.js b/api-client.js index bedec03..0748025 100644 --- a/api-client.js +++ b/api-client.js @@ -444,245 +444,6 @@ module.exports = function(app, db, upload) { } }); - /** - * POST /api/client/tasks/:taskId/sync - Синхронизировать ЛОКАЛЬНУЮ задачу с целевым сервисом - */ - router.post('/api/client/tasks/:taskId/sync', requireAuth, async (req, res) => { - const { taskId } = req.params; - const { - target_connection_id, - target_api_url, - target_api_key, - sync_files = true - } = req.body; - const userId = req.session.user.id; - - // Определяем целевой сервис - let targetUrl = target_api_url; - let targetKey = target_api_key; - - if (target_connection_id && req.session.clientConnections && req.session.clientConnections[target_connection_id]) { - const connection = req.session.clientConnections[target_connection_id]; - targetUrl = connection.url; - targetKey = connection.api_key; - } - - if (!targetUrl || !targetKey) { - return res.status(400).json({ - error: 'Не указан целевой сервис (URL или API ключ)' - }); - } - - const baseUrl = targetUrl.replace(/\/$/, ''); - - try { - // 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 (!localTask) { - return res.status(404).json({ error: 'Локальная задача не найдена' }); - } - - // Преобразуем в формат, аналогичный внешнему 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) - }; - - // 2. Проверяем, существует ли задача в целевой системе (поиск по ID) - let existingTask = null; - try { - const checkResponse = await axios.get( - `${baseUrl}/api/external/tasks/${taskId}`, - { - headers: { 'X-API-Key': targetKey }, - timeout: 5000 - } - ); - if (checkResponse.data && checkResponse.data.success) { - existingTask = checkResponse.data.task; - } - } catch (checkError) { - // Задача не найдена - это нормально, будем создавать новую - console.log('Задача не найдена в целевой системе, будет создана новая'); - } - - let result; - const syncedFiles = []; - const warnings = []; - - if (existingTask) { - // 3. Обновляем существующую задачу - const updateData = { - title: sourceTask.title, - description: sourceTask.description, - due_date: sourceTask.due_date, - task_type: sourceTask.task_type - }; - - const updateResponse = await axios.put( - `${baseUrl}/api/external/tasks/${taskId}`, - updateData, - { - headers: { - 'X-API-Key': targetKey, - 'Content-Type': 'application/json' - }, - timeout: 15000 - } - ); - - if (!updateResponse.data || !updateResponse.data.success) { - return res.status(500).json({ error: 'Не удалось обновить задачу в целевом сервисе' }); - } - - result = { taskId: taskId, action: 'updated' }; - } else { - // 4. Создаём новую задачу - const taskData = { - title: sourceTask.title, - description: sourceTask.description, - due_date: sourceTask.due_date, - task_type: sourceTask.task_type - }; - - const createResponse = await axios.post( - `${baseUrl}/api/external/tasks/create`, - taskData, - { - headers: { - 'X-API-Key': targetKey, - 'Content-Type': 'application/json' - }, - timeout: 15000 - } - ); - - if (!createResponse.data || !createResponse.data.success) { - return res.status(500).json({ error: 'Не удалось создать задачу в целевом сервисе' }); - } - - result = { - taskId: createResponse.data.taskId, - action: 'created' - }; - } - - // 5. Синхронизируем файлы, если нужно - if (sync_files && sourceTask.files && sourceTask.files.length > 0) { - for (const file of sourceTask.files) { - try { - // Читаем файл с диска - 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', fileBuffer, { - filename: fileName, - contentType: file.file_type || 'application/octet-stream' - }); - - const uploadResponse = await axios.post( - `${baseUrl}/api/external/tasks/${result.taskId}/files`, - formData, - { - headers: { - ...formData.getHeaders(), - 'X-API-Key': targetKey - }, - timeout: 60000, - maxContentLength: Infinity, - maxBodyLength: Infinity - } - ); - - syncedFiles.push({ - original_name: fileName, - success: uploadResponse.data && uploadResponse.data.success - }); - } catch (fileError) { - console.error(`❌ Ошибка синхронизации файла:`, fileError.message); - syncedFiles.push({ - original_name: file.original_name || 'unknown', - success: false, - error: fileError.message - }); - warnings.push(`Не удалось синхронизировать файл: ${file.original_name || file.filename}`); - } - } - } - - // Логируем действие - const { logActivity } = require('./database'); - if (logActivity) { - logActivity(0, userId, 'API_CLIENT_SYNC_TASK', - `Локальная задача ${taskId} ${existingTask ? 'обновлена' : 'создана'} в ${baseUrl}. Новый ID: ${result.taskId}`); - } - - res.json({ - success: true, - message: `Задача успешно ${existingTask ? 'обновлена' : 'создана'} в целевой системе`, - data: { - sync_type: result.action, - original_task_id: taskId, - target_task_id: result.taskId, - target_service: baseUrl, - synced_files: syncedFiles, - 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 ключ для целевого сервиса'; - statusCode = 401; - } else if (error.response.status === 403) { - errorMessage = 'Нет прав для создания/обновления задачи в целевом сервисе'; - statusCode = 403; - } else if (error.response.status === 404) { - errorMessage = 'Целевой эндпоинт не найден'; - statusCode = 404; - } else { - errorMessage = error.response.data?.error || `Ошибка сервера: ${error.response.status}`; - statusCode = error.response.status; - } - } else if (error.code === 'ECONNREFUSED') { - errorMessage = 'Целевой сервис недоступен'; - statusCode = 503; - } else if (error.code === 'ETIMEDOUT') { - errorMessage = 'Превышено время ожидания ответа'; - statusCode = 504; - } - - res.status(statusCode).json({ error: errorMessage, details: error.message }); - } - }); - /** * POST /api/client/tasks/:taskId/files - Загрузить файлы в задачу */ @@ -912,20 +673,4 @@ module.exports = function(app, db, upload) { app.use(router); console.log('✅ API клиент для внешних сервисов подключен'); -}; - -// Вспомогательная функция для получения файлов локальной задачи -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 +}; \ No newline at end of file diff --git a/auth.js b/auth.js index 4433746..7f41a58 100644 --- a/auth.js +++ b/auth.js @@ -22,29 +22,32 @@ class AuthService { try { // Создаем пользователей из .env - const users = [ - { - login: process.env.USER_1_LOGIN, - password: process.env.USER_1_PASSWORD, - name: process.env.USER_1_NAME, - email: process.env.USER_1_EMAIL, - auth_type: 'local' - }, - { - login: process.env.USER_2_LOGIN, - password: process.env.USER_2_PASSWORD, - name: process.env.USER_2_NAME, - email: process.env.USER_2_EMAIL, - auth_type: 'local' - }, - { - login: process.env.USER_3_LOGIN, - password: process.env.USER_3_PASSWORD, - name: process.env.USER_3_NAME, - email: process.env.USER_3_EMAIL, - auth_type: 'local' - } - ]; + const users = [ + { + login: process.env.USER_1_LOGIN, + password: process.env.USER_1_PASSWORD, + name: process.env.USER_1_NAME, + email: process.env.USER_1_EMAIL, + role: process.env.USER_1_ROLE || 'teacher', + auth_type: 'local' + }, + { + login: process.env.USER_2_LOGIN, + password: process.env.USER_2_PASSWORD, + name: process.env.USER_2_NAME, + email: process.env.USER_2_EMAIL, + role: process.env.USER_2_ROLE || 'teacher', + auth_type: 'local' + }, + { + login: process.env.USER_3_LOGIN, + password: process.env.USER_3_PASSWORD, + name: process.env.USER_3_NAME, + email: process.env.USER_3_EMAIL, + role: process.env.USER_3_ROLE || 'teacher', + auth_type: 'local' + } + ]; for (const userData of users) { if (userData.login && userData.password) { @@ -79,7 +82,7 @@ class AuthService { hashedPassword, userData.name, userData.email, - 'teacher', + userData.role, userData.auth_type || 'local' ], function(err) { diff --git a/public/client.html b/public/client.html index d199a2e..9e96d0b 100644 --- a/public/client.html +++ b/public/client.html @@ -988,7 +988,34 @@
- + +