173 lines
5.6 KiB
JavaScript
173 lines
5.6 KiB
JavaScript
/**
|
||
* Copyright (c) 2024-2025 Тарабанов Александр Викторович
|
||
* All rights reserved.
|
||
*
|
||
* This software is proprietary and confidential.
|
||
* Unauthorized copying, modification, or distribution is prohibited.
|
||
*
|
||
* For licensing inquiries: info@hb3-accelerator.com
|
||
* Website: https://hb3-accelerator.com
|
||
* GitHub: https://github.com/HB3-ACCELERATOR
|
||
*/
|
||
|
||
const express = require('express');
|
||
const router = express.Router();
|
||
const { exec } = require('child_process');
|
||
const util = require('util');
|
||
const execAsync = util.promisify(exec);
|
||
const logger = require('../utils/logger');
|
||
const { requireAuth } = require('../middleware/auth');
|
||
|
||
// Проверка статуса подключения к Ollama
|
||
router.get('/status', requireAuth, async (req, res) => {
|
||
try {
|
||
// Проверяем, что контейнер Ollama запущен
|
||
const { stdout } = await execAsync('docker ps --filter "name=dapp-ollama" --format "{{.Names}}"');
|
||
const isContainerRunning = stdout.trim() === 'dapp-ollama';
|
||
|
||
if (!isContainerRunning) {
|
||
return res.json({ connected: false, error: 'Ollama container not running' });
|
||
}
|
||
|
||
// Проверяем API Ollama
|
||
try {
|
||
const { stdout: apiResponse } = await execAsync('docker exec dapp-ollama ollama list');
|
||
return res.json({ connected: true, message: 'Ollama is running' });
|
||
} catch (apiError) {
|
||
return res.json({ connected: false, error: 'Ollama API not responding' });
|
||
}
|
||
} catch (error) {
|
||
logger.error('Error checking Ollama status:', error);
|
||
res.status(500).json({ connected: false, error: 'Failed to check Ollama status' });
|
||
}
|
||
});
|
||
|
||
// Получение списка установленных моделей
|
||
router.get('/models', requireAuth, async (req, res) => {
|
||
try {
|
||
const { stdout } = await execAsync('docker exec dapp-ollama ollama list');
|
||
const lines = stdout.trim().split('\n').slice(1); // Пропускаем заголовок
|
||
|
||
const models = lines.map(line => {
|
||
const parts = line.trim().split(/\s+/);
|
||
if (parts.length >= 4) {
|
||
return {
|
||
name: parts[0],
|
||
id: parts[1],
|
||
size: parseInt(parts[2]) || 0,
|
||
modified: parts.slice(3).join(' ')
|
||
};
|
||
}
|
||
return null;
|
||
}).filter(model => model !== null);
|
||
|
||
res.json({ models });
|
||
} catch (error) {
|
||
logger.error('Error getting Ollama models:', error);
|
||
res.status(500).json({ error: 'Failed to get models' });
|
||
}
|
||
});
|
||
|
||
// Установка модели
|
||
router.post('/install', requireAuth, async (req, res) => {
|
||
const { model } = req.body;
|
||
|
||
if (!model) {
|
||
return res.status(400).json({ error: 'Model name is required' });
|
||
}
|
||
|
||
try {
|
||
logger.info(`Starting installation of model: ${model}`);
|
||
|
||
// Запускаем установку в фоне
|
||
const installProcess = exec(`docker exec dapp-ollama ollama pull ${model}`, (error, stdout, stderr) => {
|
||
if (error) {
|
||
logger.error(`Error installing model ${model}:`, error);
|
||
} else {
|
||
logger.info(`Successfully installed model: ${model}`);
|
||
}
|
||
});
|
||
|
||
// Возвращаем ответ сразу, не ждем завершения
|
||
res.json({
|
||
success: true,
|
||
message: `Installation of ${model} started`,
|
||
processId: installProcess.pid
|
||
});
|
||
} catch (error) {
|
||
logger.error('Error starting model installation:', error);
|
||
res.status(500).json({ error: 'Failed to start installation' });
|
||
}
|
||
});
|
||
|
||
// Удаление модели
|
||
router.delete('/models/:modelName', requireAuth, async (req, res) => {
|
||
const { modelName } = req.params;
|
||
|
||
if (!modelName) {
|
||
return res.status(400).json({ error: 'Model name is required' });
|
||
}
|
||
|
||
try {
|
||
logger.info(`Removing model: ${modelName}`);
|
||
|
||
const { stdout, stderr } = await execAsync(`docker exec dapp-ollama ollama rm ${modelName}`);
|
||
|
||
if (stderr && !stderr.includes('deleted')) {
|
||
throw new Error(stderr);
|
||
}
|
||
|
||
logger.info(`Successfully removed model: ${modelName}`);
|
||
res.json({ success: true, message: `Model ${modelName} removed successfully` });
|
||
} catch (error) {
|
||
logger.error(`Error removing model ${modelName}:`, error);
|
||
res.status(500).json({ error: `Failed to remove model: ${error.message}` });
|
||
}
|
||
});
|
||
|
||
// Получение информации о модели
|
||
router.get('/models/:modelName', requireAuth, async (req, res) => {
|
||
const { modelName } = req.params;
|
||
|
||
try {
|
||
const { stdout } = await execAsync(`docker exec dapp-ollama ollama show ${modelName}`);
|
||
res.json({ model: modelName, info: stdout });
|
||
} catch (error) {
|
||
logger.error(`Error getting model info for ${modelName}:`, error);
|
||
res.status(404).json({ error: 'Model not found' });
|
||
}
|
||
});
|
||
|
||
// Поиск моделей в реестре (если поддерживается)
|
||
router.get('/search', requireAuth, async (req, res) => {
|
||
const { query } = req.query;
|
||
|
||
if (!query) {
|
||
return res.status(400).json({ error: 'Search query is required' });
|
||
}
|
||
|
||
try {
|
||
// Пока просто возвращаем популярные модели
|
||
const popularModels = [
|
||
'qwen2.5:7b',
|
||
'llama2:7b',
|
||
'mistral:7b',
|
||
'codellama:7b',
|
||
'llama2:13b',
|
||
'qwen2.5:14b',
|
||
'gemma:7b',
|
||
'phi3:3.8b'
|
||
];
|
||
|
||
const filteredModels = popularModels.filter(model =>
|
||
model.toLowerCase().includes(query.toLowerCase())
|
||
);
|
||
|
||
res.json({ models: filteredModels });
|
||
} catch (error) {
|
||
logger.error('Error searching models:', error);
|
||
res.status(500).json({ error: 'Failed to search models' });
|
||
}
|
||
});
|
||
|
||
module.exports = router;
|