Описание изменений
This commit is contained in:
@@ -1,227 +0,0 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { Pool } = require('pg');
|
||||
const { requireAuth, requireAdmin } = require('../middleware/auth');
|
||||
const db = require('../db');
|
||||
const { ethers } = require('ethers');
|
||||
const authService = require('../services/auth-service');
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
// Проверка доступа
|
||||
router.get('/check', async (req, res) => {
|
||||
try {
|
||||
const { address } = req.query;
|
||||
|
||||
if (!address) {
|
||||
return res.status(400).json({ error: 'Address is required' });
|
||||
}
|
||||
|
||||
const isAdmin = await authService.checkAdminToken(address);
|
||||
|
||||
res.json({ isAdmin });
|
||||
} catch (error) {
|
||||
logger.error(`Error checking access: ${error.message}`);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Проверка прав администратора
|
||||
router.get('/admin-only', async (req, res) => {
|
||||
const walletAddress = req.headers['x-wallet-address'];
|
||||
|
||||
if (!walletAddress) {
|
||||
return res.status(400).json({ error: 'No wallet address provided' });
|
||||
}
|
||||
|
||||
try {
|
||||
// Временное решение: разрешаем доступ для всех
|
||||
console.log('Admin access requested by:', walletAddress);
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Admin check error:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Получение всех токенов доступа
|
||||
router.get('/tokens', async (req, res) => {
|
||||
try {
|
||||
const tokens = await authService.getAllTokens();
|
||||
res.json(tokens);
|
||||
} catch (error) {
|
||||
logger.error(`Error getting tokens: ${error.message}`);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Создание токена
|
||||
router.post('/tokens', async (req, res) => {
|
||||
const walletAddress = req.headers['x-wallet-address'];
|
||||
|
||||
if (!walletAddress) {
|
||||
return res.status(400).json({ error: 'No wallet address provided' });
|
||||
}
|
||||
|
||||
try {
|
||||
// Проверяем права администратора
|
||||
const isAdmin = await authService.checkAdminToken(walletAddress);
|
||||
|
||||
if (!isAdmin) {
|
||||
return res.status(403).json({ error: 'Access denied' });
|
||||
}
|
||||
|
||||
const { walletAddress: targetAddress, role, expiresInDays } = req.body;
|
||||
|
||||
if (!targetAddress || !role || !expiresInDays) {
|
||||
return res.status(400).json({ error: 'Missing required fields' });
|
||||
}
|
||||
|
||||
// Вычисляем дату истечения
|
||||
const expiresAt = new Date();
|
||||
expiresAt.setDate(expiresAt.getDate() + parseInt(expiresInDays));
|
||||
|
||||
// Создаем токен
|
||||
const result = await db.query(
|
||||
'INSERT INTO access_tokens (wallet_address, role, expires_at) VALUES ($1, $2, $3) RETURNING *',
|
||||
[targetAddress.toLowerCase(), role, expiresAt]
|
||||
);
|
||||
|
||||
res.json({
|
||||
id: result.rows[0].id,
|
||||
walletAddress: result.rows[0].wallet_address,
|
||||
role: result.rows[0].role,
|
||||
createdAt: result.rows[0].created_at,
|
||||
expiresAt: result.rows[0].expires_at,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Token creation error:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Отзыв токена
|
||||
router.delete('/tokens/:id', async (req, res) => {
|
||||
const walletAddress = req.headers['x-wallet-address'];
|
||||
|
||||
if (!walletAddress) {
|
||||
return res.status(400).json({ error: 'No wallet address provided' });
|
||||
}
|
||||
|
||||
try {
|
||||
// Проверяем права администратора
|
||||
const isAdmin = await authService.checkAdminToken(walletAddress);
|
||||
|
||||
if (!isAdmin) {
|
||||
return res.status(403).json({ error: 'Access denied' });
|
||||
}
|
||||
|
||||
const { id } = req.params;
|
||||
|
||||
// Удаляем токен
|
||||
await db.query('DELETE FROM access_tokens WHERE id = $1', [id]);
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Token revocation error:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Получение информации о роли текущего пользователя
|
||||
router.get('/role', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const role = await authService.getUserRole(req.user.id);
|
||||
return res.json({ role });
|
||||
} catch (error) {
|
||||
logger.error('Ошибка при получении роли:', error);
|
||||
return res.status(500).json({ error: 'Внутренняя ошибка сервера' });
|
||||
}
|
||||
});
|
||||
|
||||
// Получение списка всех пользователей (только для администраторов)
|
||||
router.get('/users', requireAuth, requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const result = await db.query(`
|
||||
SELECT u.id, ui.identity_value as wallet_address, r.name as role, u.created_at
|
||||
FROM users u
|
||||
JOIN roles r ON u.role_id = r.id
|
||||
LEFT JOIN user_identities ui ON u.id = ui.user_id AND ui.identity_type = 'wallet'
|
||||
`);
|
||||
|
||||
return res.json(result.rows);
|
||||
} catch (error) {
|
||||
logger.error('Ошибка при получении списка пользователей:', error);
|
||||
return res.status(500).json({ error: 'Внутренняя ошибка сервера' });
|
||||
}
|
||||
});
|
||||
|
||||
// Обновление роли пользователя
|
||||
router.post('/update-role', async (req, res) => {
|
||||
try {
|
||||
const { userId, role } = req.body;
|
||||
|
||||
if (!userId || !role) {
|
||||
return res.status(400).json({ error: 'User ID and role are required' });
|
||||
}
|
||||
|
||||
const success = await authService.updateUserRole(userId, role);
|
||||
|
||||
if (success) {
|
||||
res.json({ success: true });
|
||||
} else {
|
||||
res.status(400).json({ error: 'Failed to update role' });
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Error updating role: ${error.message}`);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Связывание нового идентификатора с аккаунтом
|
||||
router.post('/link-identity', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const { identityType, identityValue } = req.body;
|
||||
|
||||
if (!identityType || !identityValue) {
|
||||
return res.status(400).json({ error: 'Отсутствуют обязательные поля' });
|
||||
}
|
||||
|
||||
// Проверяем, не привязан ли уже этот идентификатор к другому пользователю
|
||||
const existingUserId = await authService.getUserIdByIdentity(identityType, identityValue);
|
||||
|
||||
if (existingUserId && existingUserId !== req.user.id) {
|
||||
return res.status(400).json({ error: 'Этот идентификатор уже привязан к другому аккаунту' });
|
||||
}
|
||||
|
||||
// Добавляем новый идентификатор
|
||||
if (!existingUserId) {
|
||||
await db.query(
|
||||
'INSERT INTO user_identities (user_id, identity_type, identity_value, created_at) VALUES ($1, $2, $3, NOW())',
|
||||
[req.user.id, identityType, identityValue]
|
||||
);
|
||||
}
|
||||
|
||||
// Если добавлен кошелек, проверяем токены
|
||||
if (identityType === 'wallet') {
|
||||
await authService.checkTokensAndUpdateRole(identityValue);
|
||||
}
|
||||
|
||||
// Получаем все идентификаторы пользователя
|
||||
const identities = await authService.getAllUserIdentities(req.user.id);
|
||||
|
||||
// Получаем текущую роль
|
||||
const isAdmin = await authService.isAdmin(req.user.id);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
identities,
|
||||
isAdmin
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(`Link identity error: ${error.message}`);
|
||||
res.status(500).json({ error: 'Ошибка сервера' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -1,15 +1,38 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const db = require('../db');
|
||||
const { requireAdmin } = require('../middleware/auth');
|
||||
const authService = require('../services/auth-service');
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
// Маршрут для получения списка пользователей
|
||||
router.get('/users', async (req, res) => {
|
||||
// Роли
|
||||
router.get('/roles', requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const result = await db.query('SELECT * FROM users');
|
||||
res.json(result.rows);
|
||||
const roles = await authService.getAllRoles();
|
||||
res.json({ success: true, roles });
|
||||
} catch (error) {
|
||||
console.error('Ошибка при получении списка пользователей:', error);
|
||||
logger.error('Error getting roles:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/roles', requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const { name, permissions } = req.body;
|
||||
const role = await authService.createRole(name, permissions);
|
||||
res.json({ success: true, role });
|
||||
} catch (error) {
|
||||
logger.error('Error creating role:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Админ функции
|
||||
router.get('/users', requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const users = await authService.getAllUsers();
|
||||
res.json({ success: true, users });
|
||||
} catch (error) {
|
||||
logger.error('Error getting users:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { ethers } = require('ethers');
|
||||
const crypto = require('crypto');
|
||||
const db = require('../db');
|
||||
const logger = require('../utils/logger');
|
||||
@@ -8,11 +7,11 @@ const helmet = require('helmet');
|
||||
const rateLimit = require('express-rate-limit');
|
||||
const { checkRole, requireAuth } = require('../middleware/auth');
|
||||
const { pool } = require('../db');
|
||||
const { verifySignature, checkAccess, findOrCreateUser } = require('../utils/auth');
|
||||
const authService = require('../services/auth-service');
|
||||
const { SiweMessage } = require('siwe');
|
||||
const { sendEmail } = require('../services/emailBot');
|
||||
const { verificationCodes } = require('../services/telegramBot');
|
||||
const { checkTokensAndUpdateRole } = require('../services/auth-service');
|
||||
|
||||
// Создайте лимитер для попыток аутентификации
|
||||
const authLimiter = rateLimit({
|
||||
@@ -87,85 +86,64 @@ async function checkUserRole(address, req) {
|
||||
router.post('/verify', async (req, res) => {
|
||||
try {
|
||||
const { address, signature, message } = req.body;
|
||||
console.log('Received verification request:', { address, message });
|
||||
console.log('Signature:', signature);
|
||||
|
||||
// Проверяем подпись через SIWE
|
||||
const siwe = new SiweMessage(message);
|
||||
console.log('Created SIWE message object');
|
||||
|
||||
const fields = await siwe.verify({ signature });
|
||||
console.log('SIWE validation result:', fields);
|
||||
|
||||
console.log('Verify request: address=' + address + ', signature=' + signature.substring(0, 10) + '...');
|
||||
|
||||
// Получаем nonce из базы данных
|
||||
const nonceResult = await db.query(`
|
||||
SELECT nonce FROM nonces
|
||||
WHERE identity_value = $1 AND expires_at > NOW() AND used = false
|
||||
`, [address.toLowerCase()]);
|
||||
|
||||
if (nonceResult.rows.length === 0) {
|
||||
console.error(`No valid nonce found for address ${address}`);
|
||||
return res.status(401).json({ error: 'Invalid or expired nonce' });
|
||||
}
|
||||
|
||||
const nonce = nonceResult.rows[0].nonce;
|
||||
console.log(`Found nonce ${nonce} for address ${address}`);
|
||||
|
||||
// Проверяем подпись
|
||||
const isValid = await verifySignature(nonce, signature, address);
|
||||
console.log('Signature verification result:', isValid);
|
||||
|
||||
if (!isValid) {
|
||||
if (!fields || !fields.success) {
|
||||
console.log('SIWE validation failed');
|
||||
return res.status(401).json({ error: 'Invalid signature' });
|
||||
}
|
||||
|
||||
// Помечаем nonce как использованный
|
||||
await db.query(`
|
||||
UPDATE nonces
|
||||
SET used = true
|
||||
WHERE identity_value = $1
|
||||
`, [address.toLowerCase()]);
|
||||
|
||||
// Находим или создаем пользователя
|
||||
console.log('Finding or creating user for address:', address);
|
||||
const { userId, isAdmin } = await findOrCreateUser(address);
|
||||
console.log('User found/created:', { userId, isAdmin });
|
||||
|
||||
// Сохраняем guestId перед обновлением сессии
|
||||
const currentGuestId = req.session.guestId;
|
||||
|
||||
// Устанавливаем пользователя в сессии
|
||||
req.session.userId = userId;
|
||||
req.session.address = address;
|
||||
req.session.isAdmin = isAdmin;
|
||||
req.session.authenticated = true;
|
||||
|
||||
// Сохраняем guestId в новой сессии
|
||||
if (currentGuestId) {
|
||||
req.session.guestId = currentGuestId;
|
||||
|
||||
// Проверяем nonce
|
||||
const nonceResult = await db.query(
|
||||
`SELECT nonce FROM nonces
|
||||
WHERE identity_value = $1
|
||||
AND expires_at > NOW()`,
|
||||
[address.toLowerCase()]
|
||||
);
|
||||
console.log('Nonce check result:', nonceResult.rows);
|
||||
|
||||
if (!nonceResult.rows.length) {
|
||||
console.log('Invalid or expired nonce');
|
||||
return res.status(401).json({ error: 'Invalid or expired nonce' });
|
||||
}
|
||||
|
||||
// Сохраняем сессию ПЕРЕД отправкой ответа
|
||||
await new Promise((resolve, reject) => {
|
||||
req.session.save(err => {
|
||||
if (err) {
|
||||
console.error('Error saving session:', err);
|
||||
reject(err);
|
||||
} else {
|
||||
console.log('Session saved successfully');
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
console.log('Authentication successful for user:', {
|
||||
userId,
|
||||
address,
|
||||
isAdmin,
|
||||
guestId: currentGuestId
|
||||
});
|
||||
console.log('Session after save:', req.session);
|
||||
|
||||
// Обрабатываем гостевые сообщения, если они есть
|
||||
if (currentGuestId) {
|
||||
console.log(`Processing guest messages for guestId: ${currentGuestId}`);
|
||||
await authService.processGuestMessages(userId, currentGuestId);
|
||||
|
||||
// Проверяем соответствие nonce
|
||||
if (nonceResult.rows[0].nonce !== fields.data.nonce) {
|
||||
console.log('Nonce mismatch');
|
||||
return res.status(401).json({ error: 'Invalid nonce' });
|
||||
}
|
||||
|
||||
return res.json({
|
||||
|
||||
// Получаем или создаем пользователя
|
||||
const userResult = await db.query(
|
||||
`INSERT INTO users (address, created_at, updated_at)
|
||||
VALUES ($1, NOW(), NOW())
|
||||
ON CONFLICT (address) DO UPDATE
|
||||
SET updated_at = NOW()
|
||||
RETURNING id, role = 'admin' as is_admin`,
|
||||
[address]
|
||||
);
|
||||
|
||||
const userId = userResult.rows[0].id;
|
||||
const isAdmin = userResult.rows[0].is_admin;
|
||||
|
||||
// Используем централизованный сервис
|
||||
await authService.createSession(req, {
|
||||
userId,
|
||||
address,
|
||||
isAdmin,
|
||||
authType: 'wallet',
|
||||
guestId: req.session.guestId
|
||||
});
|
||||
|
||||
res.json({
|
||||
authenticated: true,
|
||||
userId,
|
||||
address,
|
||||
@@ -173,7 +151,7 @@ router.post('/verify', async (req, res) => {
|
||||
authType: 'wallet'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error during wallet verification:', error);
|
||||
console.error('Error:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
@@ -818,31 +796,35 @@ router.post('/link-identity', requireAuth, async (req, res) => {
|
||||
});
|
||||
|
||||
// Добавляем маршрут для проверки прав доступа
|
||||
router.get('/check-access', requireAuth, (req, res) => {
|
||||
router.get('/check-access', requireAuth, async (req, res) => {
|
||||
try {
|
||||
// Получаем информацию о пользователе
|
||||
const userData = {
|
||||
address: req.session.address,
|
||||
isAdmin: req.session.isAdmin || false,
|
||||
roles: req.session.roles || [],
|
||||
authenticated: true,
|
||||
};
|
||||
const userId = req.session.userId;
|
||||
const address = req.session.address;
|
||||
|
||||
// Проверяем доступ к различным разделам
|
||||
const access = {
|
||||
dashboard: true, // Все аутентифицированные пользователи имеют доступ к панели управления
|
||||
admin: userData.isAdmin, // Только администраторы имеют доступ к админке
|
||||
contracts: userData.roles.includes('CONTRACT_MANAGER') || userData.isAdmin,
|
||||
users: userData.roles.includes('USER_MANAGER') || userData.isAdmin,
|
||||
};
|
||||
if (address) {
|
||||
const isAdmin = await checkTokensAndUpdateRole(address);
|
||||
|
||||
// Обновляем сессию
|
||||
req.session.isAdmin = isAdmin;
|
||||
|
||||
res.json({
|
||||
user: userData,
|
||||
access: access,
|
||||
return res.json({
|
||||
success: true,
|
||||
isAdmin,
|
||||
userId,
|
||||
address
|
||||
});
|
||||
}
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
isAdmin: false,
|
||||
userId,
|
||||
address: null
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Ошибка при проверке прав доступа:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
logger.error('Error checking access:', error);
|
||||
return res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { ChatOllama } = require('@langchain/ollama');
|
||||
const { getVectorStore } = require('../services/vectorStore');
|
||||
const aiAssistant = require('../services/ai-assistant');
|
||||
const db = require('../db');
|
||||
const { requireAuth, requireAdmin } = require('../middleware/auth');
|
||||
const logger = require('../utils/logger');
|
||||
@@ -105,7 +104,7 @@ async function processGuestMessages(userId, guestId) {
|
||||
|
||||
// Получаем ответ от AI
|
||||
console.log(`Getting AI response for message ${msg.id} in ${language}`);
|
||||
const aiResponse = await getAIResponse(msg.content, language);
|
||||
const aiResponse = await aiAssistant.getResponse(msg.content, language);
|
||||
|
||||
// Сохраняем ответ AI в ту же беседу
|
||||
await db.query(
|
||||
@@ -128,54 +127,44 @@ async function processGuestMessages(userId, guestId) {
|
||||
|
||||
// Обработчик для гостевых сообщений
|
||||
router.post('/guest-message', async (req, res) => {
|
||||
const { message, language } = req.body;
|
||||
|
||||
// Генерируем временный ID сессии, если его нет
|
||||
if (!req.session.guestId) {
|
||||
req.session.guestId = crypto.randomBytes(16).toString('hex');
|
||||
}
|
||||
|
||||
try {
|
||||
// Создаем запись в conversations для гостя
|
||||
const conversationResult = await db.query(
|
||||
`INSERT INTO conversations (created_at)
|
||||
VALUES (NOW())
|
||||
RETURNING id`
|
||||
);
|
||||
|
||||
const conversationId = conversationResult.rows[0].id;
|
||||
const { message, language } = req.body;
|
||||
const guestId = req.session.guestId || crypto.randomBytes(16).toString('hex');
|
||||
|
||||
// Создаем метаданные
|
||||
const metadata = {
|
||||
guest_id: req.session.guestId,
|
||||
language: language || 'en'
|
||||
};
|
||||
// Сохраняем ID гостя в сессии
|
||||
req.session.guestId = guestId;
|
||||
await req.session.save();
|
||||
|
||||
// Сохраняем только сообщение пользователя
|
||||
await db.query(
|
||||
`INSERT INTO messages
|
||||
(conversation_id, sender_type, content, channel, metadata, created_at)
|
||||
VALUES ($1, 'guest', $2, 'chat', $3, NOW())`,
|
||||
[
|
||||
conversationId,
|
||||
message,
|
||||
JSON.stringify(metadata)
|
||||
]
|
||||
console.log('Saving guest message:', { guestId, message });
|
||||
|
||||
// Сохраняем сообщение пользователя
|
||||
const result = await db.query(
|
||||
'INSERT INTO guest_messages (guest_id, content, language, is_ai) VALUES ($1, $2, $3, false) RETURNING id',
|
||||
[guestId, message, language]
|
||||
);
|
||||
|
||||
res.json({ success: true });
|
||||
console.log('Guest message saved:', result.rows[0]);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
messageId: result.rows[0].id
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error processing message:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
console.error('Error saving guest message:', error);
|
||||
res.status(500).json({ success: false, error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Маршрут для обычных сообщений (для аутентифицированных пользователей)
|
||||
router.post('/message', requireAuth, async (req, res) => {
|
||||
const { message, language } = req.body;
|
||||
const userId = req.session.userId;
|
||||
|
||||
try {
|
||||
const { message, language } = req.body;
|
||||
const userId = req.session.userId;
|
||||
|
||||
// Используем методы из aiAssistant вместо прямого обращения к vectorStore
|
||||
const similarDocs = await aiAssistant.findSimilarDocuments(message);
|
||||
const aiResponse = await aiAssistant.getResponse(message, language);
|
||||
|
||||
// Создаем новую беседу или получаем существующую
|
||||
const conversationResult = await db.query(
|
||||
`INSERT INTO conversations (user_id, created_at)
|
||||
@@ -189,20 +178,25 @@ router.post('/message', requireAuth, async (req, res) => {
|
||||
// Сохраняем сообщение пользователя
|
||||
await db.query(
|
||||
`INSERT INTO messages
|
||||
(conversation_id, sender_type, content, channel, created_at)
|
||||
VALUES ($1, 'user', $2, 'chat', NOW())`,
|
||||
[conversationId, message]
|
||||
(conversation_id, sender_type, content, channel, metadata, created_at)
|
||||
VALUES ($1, 'user', $2, 'chat', $3, NOW())`,
|
||||
[
|
||||
conversationId,
|
||||
message,
|
||||
JSON.stringify({ language: language || 'ru' })
|
||||
]
|
||||
);
|
||||
|
||||
// Получаем ответ от AI
|
||||
const aiResponse = await getAIResponse(message, language);
|
||||
|
||||
// Сохраняем ответ AI
|
||||
await db.query(
|
||||
`INSERT INTO messages
|
||||
(conversation_id, sender_type, content, channel, created_at)
|
||||
VALUES ($1, 'assistant', $2, 'chat', NOW())`,
|
||||
[conversationId, aiResponse]
|
||||
(conversation_id, sender_type, content, channel, metadata, created_at)
|
||||
VALUES ($1, 'assistant', $2, 'chat', $3, NOW())`,
|
||||
[
|
||||
conversationId,
|
||||
aiResponse,
|
||||
JSON.stringify({ language: language || 'ru' })
|
||||
]
|
||||
);
|
||||
|
||||
res.json({
|
||||
@@ -210,7 +204,7 @@ router.post('/message', requireAuth, async (req, res) => {
|
||||
message: aiResponse
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error processing message:', error);
|
||||
logger.error('Error processing message:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
@@ -233,47 +227,44 @@ router.get('/models', async (req, res) => {
|
||||
|
||||
// Получение истории сообщений
|
||||
router.get('/history', async (req, res) => {
|
||||
const limit = parseInt(req.query.limit) || 2; // По умолчанию только последнее сообщение и ответ
|
||||
const offset = parseInt(req.query.offset) || 0;
|
||||
|
||||
try {
|
||||
console.log('Session in history route:', {
|
||||
id: req.sessionID,
|
||||
userId: req.session.userId,
|
||||
address: req.session.address,
|
||||
authenticated: req.session.authenticated
|
||||
});
|
||||
|
||||
if (!req.session.authenticated || !req.session.userId) {
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
|
||||
// Получаем общее количество сообщений
|
||||
const countResult = await db.query(
|
||||
`SELECT COUNT(*) as total
|
||||
FROM messages m
|
||||
JOIN conversations c ON m.conversation_id = c.id
|
||||
WHERE c.user_id = $1
|
||||
OR (m.metadata->>'guest_id' = $2 AND m.metadata->>'processed' = 'true')`,
|
||||
[req.session.userId, req.session.guestId]
|
||||
);
|
||||
|
||||
const total = parseInt(countResult.rows[0].total);
|
||||
const limit = parseInt(req.query.limit) || 50;
|
||||
const offset = parseInt(req.query.offset) || 0;
|
||||
|
||||
// Получаем сообщения с пагинацией
|
||||
const result = await db.query(
|
||||
`SELECT m.id, m.content, m.sender_type as role, m.created_at,
|
||||
c.user_id, m.metadata
|
||||
`SELECT
|
||||
m.id,
|
||||
m.content,
|
||||
m.sender_type as role,
|
||||
m.created_at,
|
||||
c.user_id
|
||||
FROM messages m
|
||||
JOIN conversations c ON m.conversation_id = c.id
|
||||
WHERE c.user_id = $1
|
||||
OR (m.metadata->>'guest_id' = $2 AND m.metadata->>'processed' = 'true')
|
||||
WHERE c.user_id = $1
|
||||
ORDER BY m.created_at DESC
|
||||
LIMIT $3 OFFSET $4`,
|
||||
[req.session.userId, req.session.guestId, limit, offset]
|
||||
LIMIT $2 OFFSET $3`,
|
||||
[req.session.userId, limit, offset]
|
||||
);
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
messages: result.rows.reverse(),
|
||||
total
|
||||
messages: result.rows.reverse()
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error getting chat history:', error);
|
||||
logger.error('Error getting chat history:', error);
|
||||
return res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
@@ -314,75 +305,41 @@ router.get('/admin/history', requireAdmin, async (req, res) => {
|
||||
// Обработчик для связывания гостевых сообщений с пользователем
|
||||
router.post('/link-guest-messages', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const userId = req.session.userId;
|
||||
const { userId } = req.session;
|
||||
const guestId = req.session.guestId;
|
||||
|
||||
|
||||
console.log('Linking messages:', { userId, guestId });
|
||||
|
||||
if (!guestId) {
|
||||
console.log('No guestId in session');
|
||||
return res.json({ success: true, message: 'No guest messages to link' });
|
||||
}
|
||||
|
||||
// Связываем гостевые сообщения с пользователем
|
||||
await db.query(`
|
||||
INSERT INTO messages (user_id, content, role, created_at)
|
||||
SELECT $1, content, CASE WHEN is_ai THEN 'assistant' ELSE 'user' END, created_at
|
||||
FROM guest_messages
|
||||
WHERE guest_id = $2
|
||||
ORDER BY created_at
|
||||
`, [userId, guestId]);
|
||||
|
||||
// Удаляем гостевые сообщения
|
||||
await db.query(`
|
||||
DELETE FROM guest_messages
|
||||
WHERE guest_id = $1
|
||||
`, [guestId]);
|
||||
|
||||
// Удаляем временный ID из сессии
|
||||
delete req.session.guestId;
|
||||
|
||||
return res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Error linking guest messages:', error);
|
||||
return res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Обновляем маршрут верификации кошелька
|
||||
router.post('/verify', async (req, res) => {
|
||||
const { address, signature, message } = req.body;
|
||||
|
||||
try {
|
||||
// ... существующий код верификации ...
|
||||
// Проверяем наличие гостевых сообщений
|
||||
const guestMessages = await db.query(
|
||||
'SELECT EXISTS(SELECT 1 FROM guest_messages WHERE guest_id = $1)',
|
||||
[guestId]
|
||||
);
|
||||
|
||||
// После успешной верификации и создания пользователя
|
||||
if (req.session.guestId) {
|
||||
console.log('Found guest messages, processing...');
|
||||
await processGuestMessages(userId, req.session.guestId);
|
||||
console.log('Guest messages check:', guestMessages.rows[0]);
|
||||
|
||||
if (!guestMessages.rows[0].exists) {
|
||||
console.log('No guest messages found for guestId:', guestId);
|
||||
return res.json({ success: true, message: 'No guest messages to link' });
|
||||
}
|
||||
|
||||
// Сохраняем данные в сессии
|
||||
req.session.userId = userId;
|
||||
req.session.address = address;
|
||||
req.session.isAdmin = isAdmin;
|
||||
req.session.authenticated = true;
|
||||
|
||||
console.log('Authentication successful for user:', {
|
||||
userId,
|
||||
address,
|
||||
isAdmin,
|
||||
guestId: req.session.guestId
|
||||
});
|
||||
|
||||
res.json({
|
||||
authenticated: true,
|
||||
userId: userId,
|
||||
address: address,
|
||||
isAdmin: isAdmin,
|
||||
authType: 'wallet'
|
||||
});
|
||||
|
||||
// Связываем сообщения
|
||||
console.log('Calling link_guest_messages function');
|
||||
await db.query('SELECT link_guest_messages($1, $2)', [userId, guestId]);
|
||||
|
||||
// Очищаем guestId из сессии после связывания
|
||||
delete req.session.guestId;
|
||||
|
||||
console.log('Messages linked successfully');
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Error during wallet verification:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
console.error('Error linking guest messages:', error);
|
||||
res.status(500).json({ success: false, error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -406,4 +363,36 @@ router.post('/auth/telegram/verify', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Маршрут для удаления сообщений
|
||||
router.delete('/message/:id', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const messageId = req.params.id;
|
||||
const userId = req.session.userId;
|
||||
|
||||
// Проверяем права на удаление
|
||||
const messageCheck = await db.query(
|
||||
`SELECT m.id
|
||||
FROM messages m
|
||||
JOIN conversations c ON m.conversation_id = c.id
|
||||
WHERE m.id = $1 AND c.user_id = $2`,
|
||||
[messageId, userId]
|
||||
);
|
||||
|
||||
if (messageCheck.rows.length === 0) {
|
||||
return res.status(403).json({ error: 'Forbidden' });
|
||||
}
|
||||
|
||||
// Удаляем сообщение
|
||||
await db.query(
|
||||
'DELETE FROM messages WHERE id = $1',
|
||||
[messageId]
|
||||
);
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
logger.error('Error deleting message:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { requireRole } = require('../middleware/auth');
|
||||
|
||||
// Получение информации о контрактах
|
||||
router.get('/', (req, res) => {
|
||||
res.json({
|
||||
message: 'Contracts API endpoint',
|
||||
contracts: [
|
||||
// Удаляем AccessToken
|
||||
// {
|
||||
// name: 'AccessToken',
|
||||
// address: process.env.ACCESS_TOKEN_ADDRESS,
|
||||
// },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
// Защищенный эндпоинт для получения детальной информации о контрактах
|
||||
router.get('/details', requireRole('ADMIN'), (req, res) => {
|
||||
res.json({
|
||||
message: 'Contract details endpoint',
|
||||
contracts: [
|
||||
// Удаляем AccessToken
|
||||
// {
|
||||
// name: 'AccessToken',
|
||||
// address: process.env.ACCESS_TOKEN_ADDRESS,
|
||||
// network: process.env.ETHEREUM_NETWORK_URL.includes('sepolia') ? 'Sepolia' : 'Unknown',
|
||||
// },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const db = require('../db');
|
||||
|
||||
// Маршрут для проверки состояния сервера
|
||||
router.get('/status', (req, res) => {
|
||||
res.json({
|
||||
status: 'ok',
|
||||
uptime: process.uptime(),
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
});
|
||||
|
||||
// Маршрут для проверки сессии
|
||||
router.get('/session', (req, res) => {
|
||||
res.json({
|
||||
session: req.session,
|
||||
authenticated: req.session.authenticated || false,
|
||||
});
|
||||
});
|
||||
|
||||
// Маршрут для проверки содержимого таблицы session
|
||||
router.get('/sessions', async (req, res) => {
|
||||
try {
|
||||
const result = await db.query('SELECT * FROM session');
|
||||
res.json(result.rows);
|
||||
} catch (error) {
|
||||
console.error('Ошибка при получении данных из таблицы session:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -1,36 +0,0 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const db = require('../db');
|
||||
|
||||
router.get('/', async (req, res) => {
|
||||
try {
|
||||
// Проверка соединения с базой данных
|
||||
const dbResult = await db.query('SELECT NOW()');
|
||||
|
||||
// Проверка состояния сервера
|
||||
const memoryUsage = process.memoryUsage();
|
||||
const uptime = process.uptime();
|
||||
|
||||
res.json({
|
||||
status: 'ok',
|
||||
timestamp: new Date(),
|
||||
uptime: uptime,
|
||||
memory: {
|
||||
rss: Math.round(memoryUsage.rss / 1024 / 1024) + 'MB',
|
||||
heapTotal: Math.round(memoryUsage.heapTotal / 1024 / 1024) + 'MB',
|
||||
heapUsed: Math.round(memoryUsage.heapUsed / 1024 / 1024) + 'MB',
|
||||
},
|
||||
database: {
|
||||
connected: true,
|
||||
timestamp: dbResult.rows[0].now,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
status: 'error',
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -1,59 +1,47 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { linkIdentity, getUserIdentities } = require('../utils/identity-linker');
|
||||
const db = require('../db');
|
||||
const { requireAuth } = require('../middleware/auth');
|
||||
const authService = require('../services/auth-service');
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
// Получение связанных идентификаторов пользователя
|
||||
// Получение всех идентификаторов пользователя
|
||||
router.get('/', requireAuth, async (req, res) => {
|
||||
try {
|
||||
// Получаем ID пользователя по Ethereum-адресу
|
||||
const result = await db.query('SELECT id FROM users WHERE address = $1', [
|
||||
req.session.address,
|
||||
]);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
return res.status(404).json({ error: 'User not found' });
|
||||
}
|
||||
|
||||
const userId = result.rows[0].id;
|
||||
|
||||
// Получаем все идентификаторы пользователя
|
||||
const identities = await getUserIdentities(userId);
|
||||
|
||||
res.json({ identities });
|
||||
const userId = req.session.userId;
|
||||
const identities = await authService.getUserIdentities(userId);
|
||||
res.json({ success: true, identities });
|
||||
} catch (error) {
|
||||
console.error('Error getting user identities:', error);
|
||||
logger.error('Error getting identities:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Удаление связанного идентификатора
|
||||
router.delete('/:type/:value', requireAuth, async (req, res) => {
|
||||
// Связывание нового идентификатора
|
||||
router.post('/link', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const { type, value } = req.params;
|
||||
const { type, value } = req.body;
|
||||
const userId = req.session.userId;
|
||||
|
||||
// Получаем ID пользователя по Ethereum-адресу
|
||||
const result = await db.query('SELECT id FROM users WHERE address = $1', [
|
||||
req.session.address,
|
||||
]);
|
||||
await authService.linkIdentity(userId, type, value);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
return res.status(404).json({ error: 'User not found' });
|
||||
// Обновляем сессию
|
||||
if (type === 'wallet') {
|
||||
req.session.address = value;
|
||||
req.session.isAdmin = await authService.checkTokensAndUpdateRole(value);
|
||||
} else if (type === 'telegram') {
|
||||
req.session.telegramId = value;
|
||||
} else if (type === 'email') {
|
||||
req.session.email = value;
|
||||
}
|
||||
|
||||
const userId = result.rows[0].id;
|
||||
|
||||
// Удаляем идентификатор
|
||||
await db.query(
|
||||
'DELETE FROM user_identities WHERE user_id = $1 AND identity_type = $2 AND identity_value = $3',
|
||||
[userId, type, value]
|
||||
);
|
||||
|
||||
res.json({ success: true });
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Identity linked successfully',
|
||||
isAdmin: req.session.isAdmin
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error deleting user identity:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
logger.error('Error linking identity:', error);
|
||||
res.status(500).json({ error: error.message || 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,246 +0,0 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { pool } = require('../db');
|
||||
const { requireAuth } = require('../middleware/auth');
|
||||
const { processMessage, getUserInfo } = require('../services/ai-assistant');
|
||||
|
||||
// Получение списка диалогов пользователя
|
||||
router.get('/conversations', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const userId = req.session.userId;
|
||||
|
||||
const result = await pool.query(
|
||||
`SELECT * FROM conversation_view
|
||||
WHERE user_id = $1
|
||||
ORDER BY updated_at DESC`,
|
||||
[userId]
|
||||
);
|
||||
|
||||
res.json(result.rows);
|
||||
} catch (error) {
|
||||
console.error('Error fetching conversations:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Получение сообщений диалога
|
||||
router.get('/conversations/:id/messages', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const userId = req.session.userId;
|
||||
const conversationId = req.params.id;
|
||||
|
||||
// Проверка доступа к диалогу
|
||||
const conversationCheck = await pool.query(
|
||||
'SELECT id FROM conversations WHERE id = $1 AND user_id = $2',
|
||||
[conversationId, userId]
|
||||
);
|
||||
|
||||
if (conversationCheck.rows.length === 0) {
|
||||
return res.status(403).json({ error: 'Access denied' });
|
||||
}
|
||||
|
||||
const result = await pool.query(
|
||||
`SELECT * FROM message_view
|
||||
WHERE conversation_id = $1
|
||||
ORDER BY created_at ASC`,
|
||||
[conversationId]
|
||||
);
|
||||
|
||||
res.json(result.rows);
|
||||
} catch (error) {
|
||||
console.error('Error fetching messages:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Отправка сообщения
|
||||
router.post('/conversations/:id/messages', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const userId = req.session.userId;
|
||||
const conversationId = req.params.id;
|
||||
const { content } = req.body;
|
||||
|
||||
if (!content || content.trim() === '') {
|
||||
return res.status(400).json({ error: 'Message content is required' });
|
||||
}
|
||||
|
||||
// Проверка доступа к диалогу
|
||||
const conversationCheck = await pool.query(
|
||||
'SELECT id FROM conversations WHERE id = $1 AND user_id = $2',
|
||||
[conversationId, userId]
|
||||
);
|
||||
|
||||
if (conversationCheck.rows.length === 0) {
|
||||
return res.status(403).json({ error: 'Access denied' });
|
||||
}
|
||||
|
||||
// Обновление времени последней активности диалога
|
||||
await pool.query('UPDATE conversations SET updated_at = NOW() WHERE id = $1', [conversationId]);
|
||||
|
||||
// Сохранение сообщения пользователя
|
||||
const userMessageResult = await pool.query(
|
||||
`INSERT INTO messages
|
||||
(conversation_id, sender_type, sender_id, content, channel)
|
||||
VALUES ($1, 'user', $2, $3, 'web')
|
||||
RETURNING *`,
|
||||
[conversationId, userId, content]
|
||||
);
|
||||
|
||||
// Получение информации о пользователе для ИИ
|
||||
const userInfo = await getUserInfo(userId);
|
||||
|
||||
// Обработка сообщения ИИ-ассистентом
|
||||
const aiResponse = await processMessage(userId, content, userInfo.language || 'ru');
|
||||
|
||||
// Сохранение ответа ИИ
|
||||
const aiMessageResult = await pool.query(
|
||||
`INSERT INTO messages
|
||||
(conversation_id, sender_type, content, channel)
|
||||
VALUES ($1, 'ai', $2, 'web')
|
||||
RETURNING *`,
|
||||
[conversationId, aiResponse]
|
||||
);
|
||||
|
||||
res.json({
|
||||
userMessage: userMessageResult.rows[0],
|
||||
aiMessage: aiMessageResult.rows[0],
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error sending message:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Создание нового диалога
|
||||
router.post('/conversations', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const userId = req.session.userId;
|
||||
const { title } = req.body;
|
||||
|
||||
// Создание нового диалога
|
||||
const result = await pool.query(
|
||||
`INSERT INTO conversations (user_id, title)
|
||||
VALUES ($1, $2)
|
||||
RETURNING *`,
|
||||
[userId, title || 'Новый диалог']
|
||||
);
|
||||
|
||||
res.json(result.rows[0]);
|
||||
} catch (error) {
|
||||
console.error('Error creating conversation:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Обновление заголовка диалога
|
||||
router.put('/conversations/:id', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const userId = req.session.userId;
|
||||
const conversationId = req.params.id;
|
||||
const { title } = req.body;
|
||||
|
||||
if (!title || title.trim() === '') {
|
||||
return res.status(400).json({ error: 'Title is required' });
|
||||
}
|
||||
|
||||
// Проверка доступа к диалогу
|
||||
const conversationCheck = await pool.query(
|
||||
'SELECT id FROM conversations WHERE id = $1 AND user_id = $2',
|
||||
[conversationId, userId]
|
||||
);
|
||||
|
||||
if (conversationCheck.rows.length === 0) {
|
||||
return res.status(403).json({ error: 'Access denied' });
|
||||
}
|
||||
|
||||
// Обновление заголовка
|
||||
const result = await pool.query(
|
||||
'UPDATE conversations SET title = $1 WHERE id = $2 RETURNING *',
|
||||
[title, conversationId]
|
||||
);
|
||||
|
||||
res.json(result.rows[0]);
|
||||
} catch (error) {
|
||||
console.error('Error updating conversation:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Удаление диалога
|
||||
router.delete('/conversations/:id', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const userId = req.session.userId;
|
||||
const conversationId = req.params.id;
|
||||
|
||||
// Проверка доступа к диалогу
|
||||
const conversationCheck = await pool.query(
|
||||
'SELECT id FROM conversations WHERE id = $1 AND user_id = $2',
|
||||
[conversationId, userId]
|
||||
);
|
||||
|
||||
if (conversationCheck.rows.length === 0) {
|
||||
return res.status(403).json({ error: 'Access denied' });
|
||||
}
|
||||
|
||||
// Удаление диалога (каскадно удалит все сообщения)
|
||||
await pool.query('DELETE FROM conversations WHERE id = $1', [conversationId]);
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Error deleting conversation:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Маршруты для администраторов
|
||||
|
||||
// Получение всех диалогов (только для администраторов)
|
||||
router.get('/admin/conversations', requireAuth, async (req, res) => {
|
||||
try {
|
||||
// Проверка прав администратора
|
||||
if (!req.session.isAdmin) {
|
||||
return res.status(403).json({ error: 'Admin access required' });
|
||||
}
|
||||
|
||||
const result = await pool.query(
|
||||
`SELECT * FROM conversation_view
|
||||
ORDER BY updated_at DESC`
|
||||
);
|
||||
|
||||
res.json(result.rows);
|
||||
} catch (error) {
|
||||
console.error('Error fetching all conversations:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Получение статистики по каналам (только для администраторов)
|
||||
router.get('/admin/stats/channels', requireAuth, async (req, res) => {
|
||||
try {
|
||||
// Проверка прав администратора
|
||||
if (!req.session.isAdmin) {
|
||||
return res.status(403).json({ error: 'Admin access required' });
|
||||
}
|
||||
|
||||
const result = await pool.query(
|
||||
`SELECT
|
||||
channel,
|
||||
COUNT(*) AS message_count,
|
||||
COUNT(DISTINCT conversation_id) AS conversation_count,
|
||||
COUNT(DISTINCT sender_id) AS user_count,
|
||||
MIN(created_at) AS first_message,
|
||||
MAX(created_at) AS last_message
|
||||
FROM
|
||||
messages
|
||||
GROUP BY
|
||||
channel`
|
||||
);
|
||||
|
||||
res.json(result.rows);
|
||||
} catch (error) {
|
||||
console.error('Error fetching channel stats:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -1,166 +0,0 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const db = require('../db');
|
||||
const { requireAuth, requireAdmin } = require('../middleware/auth');
|
||||
const authService = require('../services/auth-service');
|
||||
const logger = require('../utils/logger');
|
||||
const { USER_ROLES } = require('../utils/constants');
|
||||
|
||||
// Получение всех ролей
|
||||
router.get('/', async (req, res) => {
|
||||
try {
|
||||
const result = await db.query('SELECT * FROM roles ORDER BY id');
|
||||
res.json(result.rows);
|
||||
} catch (error) {
|
||||
logger.error(`Error getting roles: ${error.message}`);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Получение роли текущего пользователя
|
||||
router.get('/me', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const userId = req.session.userId;
|
||||
|
||||
const result = await db.query(`
|
||||
SELECT r.name as role
|
||||
FROM users u
|
||||
JOIN roles r ON u.role_id = r.id
|
||||
WHERE u.id = $1
|
||||
`, [userId]);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
return res.status(404).json({ error: 'User not found' });
|
||||
}
|
||||
|
||||
res.json({ role: result.rows[0].role });
|
||||
} catch (error) {
|
||||
logger.error(`Error getting user role: ${error.message}`);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Создание новой роли (только для администраторов)
|
||||
router.post('/', requireAuth, requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const { name, description } = req.body;
|
||||
|
||||
if (!name) {
|
||||
return res.status(400).json({ error: 'Role name is required' });
|
||||
}
|
||||
|
||||
// Проверяем, существует ли уже такая роль
|
||||
const existingRole = await db.query('SELECT * FROM roles WHERE name = $1', [name]);
|
||||
|
||||
if (existingRole.rows.length > 0) {
|
||||
return res.status(400).json({ error: 'Role already exists' });
|
||||
}
|
||||
|
||||
// Создаем новую роль
|
||||
const result = await db.query(
|
||||
'INSERT INTO roles (name, description) VALUES ($1, $2) RETURNING *',
|
||||
[name, description || '']
|
||||
);
|
||||
|
||||
res.status(201).json(result.rows[0]);
|
||||
} catch (error) {
|
||||
logger.error(`Error creating role: ${error.message}`);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Обновление роли (только для администраторов)
|
||||
router.put('/:id', requireAuth, requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { name, description } = req.body;
|
||||
|
||||
if (!name) {
|
||||
return res.status(400).json({ error: 'Role name is required' });
|
||||
}
|
||||
|
||||
// Проверяем, существует ли роль
|
||||
const existingRole = await db.query('SELECT * FROM roles WHERE id = $1', [id]);
|
||||
|
||||
if (existingRole.rows.length === 0) {
|
||||
return res.status(404).json({ error: 'Role not found' });
|
||||
}
|
||||
|
||||
// Обновляем роль
|
||||
const result = await db.query(
|
||||
'UPDATE roles SET name = $1, description = $2 WHERE id = $3 RETURNING *',
|
||||
[name, description || '', id]
|
||||
);
|
||||
|
||||
res.json(result.rows[0]);
|
||||
} catch (error) {
|
||||
logger.error(`Error updating role: ${error.message}`);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Удаление роли (только для администраторов)
|
||||
router.delete('/:id', requireAuth, requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
// Проверяем, существует ли роль
|
||||
const existingRole = await db.query('SELECT * FROM roles WHERE id = $1', [id]);
|
||||
|
||||
if (existingRole.rows.length === 0) {
|
||||
return res.status(404).json({ error: 'Role not found' });
|
||||
}
|
||||
|
||||
// Проверяем, не используется ли роль
|
||||
const usersWithRole = await db.query('SELECT COUNT(*) FROM users WHERE role_id = $1', [id]);
|
||||
|
||||
if (parseInt(usersWithRole.rows[0].count) > 0) {
|
||||
return res.status(400).json({ error: 'Cannot delete role that is assigned to users' });
|
||||
}
|
||||
|
||||
// Удаляем роль
|
||||
await db.query('DELETE FROM roles WHERE id = $1', [id]);
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
logger.error(`Error deleting role: ${error.message}`);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Назначение роли пользователю (только для администраторов)
|
||||
router.post('/assign', requireAuth, requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const { userId, roleName } = req.body;
|
||||
|
||||
if (!userId || !roleName) {
|
||||
return res.status(400).json({ error: 'User ID and role name are required' });
|
||||
}
|
||||
|
||||
// Проверяем, существует ли роль
|
||||
const roleResult = await db.query('SELECT id FROM roles WHERE name = $1', [roleName]);
|
||||
|
||||
if (roleResult.rows.length === 0) {
|
||||
return res.status(404).json({ error: 'Role not found' });
|
||||
}
|
||||
|
||||
const roleId = roleResult.rows[0].id;
|
||||
|
||||
// Проверяем, существует ли пользователь
|
||||
const userResult = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
|
||||
|
||||
if (userResult.rows.length === 0) {
|
||||
return res.status(404).json({ error: 'User not found' });
|
||||
}
|
||||
|
||||
// Назначаем роль
|
||||
await db.query('UPDATE users SET role_id = $1 WHERE id = $2', [roleId, userId]);
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
logger.error(`Error assigning role: ${error.message}`);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user