ваше сообщение коммита
This commit is contained in:
@@ -40,8 +40,8 @@ async function initServices() {
|
|||||||
|
|
||||||
// Запуск email-бота
|
// Запуск email-бота
|
||||||
console.log('Создаём экземпляр EmailBotService');
|
console.log('Создаём экземпляр EmailBotService');
|
||||||
// const emailBot = new EmailBotService();
|
const emailBot = new EmailBotService();
|
||||||
// await emailBot.start();
|
await emailBot.start();
|
||||||
|
|
||||||
// Добавляем graceful shutdown
|
// Добавляем graceful shutdown
|
||||||
process.once('SIGINT', async () => {
|
process.once('SIGINT', async () => {
|
||||||
|
|||||||
102
backend/services/admin-role.js
Normal file
102
backend/services/admin-role.js
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
const { ethers } = require('ethers');
|
||||||
|
const logger = require('../utils/logger');
|
||||||
|
const authTokenService = require('./authTokenService');
|
||||||
|
const rpcProviderService = require('./rpcProviderService');
|
||||||
|
|
||||||
|
// Минимальный ABI для ERC20
|
||||||
|
const ERC20_ABI = [
|
||||||
|
'function balanceOf(address owner) view returns (uint256)'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Основной метод проверки роли админа
|
||||||
|
* @param {string} address - Адрес кошелька
|
||||||
|
* @returns {Promise<boolean>} - Является ли пользователь админом
|
||||||
|
*/
|
||||||
|
async function checkAdminRole(address) {
|
||||||
|
if (!address) return false;
|
||||||
|
logger.info(`Checking admin role for address: ${address}`);
|
||||||
|
let foundTokens = false;
|
||||||
|
let errorCount = 0;
|
||||||
|
const balances = {};
|
||||||
|
// Получаем токены и RPC из базы
|
||||||
|
const tokens = await authTokenService.getAllAuthTokens();
|
||||||
|
const rpcProviders = await rpcProviderService.getAllRpcProviders();
|
||||||
|
const rpcMap = {};
|
||||||
|
for (const rpc of rpcProviders) {
|
||||||
|
rpcMap[rpc.network_id] = rpc.rpc_url;
|
||||||
|
}
|
||||||
|
const checkPromises = tokens.map(async (token) => {
|
||||||
|
try {
|
||||||
|
const rpcUrl = rpcMap[token.network];
|
||||||
|
if (!rpcUrl) {
|
||||||
|
logger.error(`No RPC URL for network ${token.network}`);
|
||||||
|
balances[token.network] = 'Error: No RPC URL';
|
||||||
|
errorCount++;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
||||||
|
// Проверяем доступность сети с таймаутом
|
||||||
|
try {
|
||||||
|
const networkCheckPromise = provider.getNetwork();
|
||||||
|
const timeoutPromise = new Promise((_, reject) =>
|
||||||
|
setTimeout(() => reject(new Error('Network check timeout')), 3000)
|
||||||
|
);
|
||||||
|
await Promise.race([networkCheckPromise, timeoutPromise]);
|
||||||
|
} catch (networkError) {
|
||||||
|
logger.error(`Provider for ${token.network} is not available: ${networkError.message}`);
|
||||||
|
balances[token.network] = 'Error: Network unavailable';
|
||||||
|
errorCount++;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const tokenContract = new ethers.Contract(token.address, ERC20_ABI, provider);
|
||||||
|
const balancePromise = tokenContract.balanceOf(address);
|
||||||
|
const timeoutPromise = new Promise((_, reject) =>
|
||||||
|
setTimeout(() => reject(new Error('Timeout')), 3000)
|
||||||
|
);
|
||||||
|
const balance = await Promise.race([balancePromise, timeoutPromise]);
|
||||||
|
const formattedBalance = ethers.formatUnits(balance, 18);
|
||||||
|
balances[token.network] = formattedBalance;
|
||||||
|
logger.info(`Token balance on ${token.network}:`, {
|
||||||
|
address,
|
||||||
|
contract: token.address,
|
||||||
|
balance: formattedBalance,
|
||||||
|
minBalance: token.min_balance,
|
||||||
|
hasTokens: parseFloat(formattedBalance) >= parseFloat(token.min_balance),
|
||||||
|
});
|
||||||
|
if (parseFloat(formattedBalance) >= parseFloat(token.min_balance)) {
|
||||||
|
logger.info(`Found admin tokens on ${token.network}`);
|
||||||
|
foundTokens = true;
|
||||||
|
}
|
||||||
|
return { network: token.network, balance: formattedBalance };
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Error checking balance in ${token.network}:`, {
|
||||||
|
address,
|
||||||
|
contract: token.address,
|
||||||
|
error: error.message || 'Unknown error',
|
||||||
|
});
|
||||||
|
balances[token.network] = 'Error';
|
||||||
|
errorCount++;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await Promise.all(checkPromises);
|
||||||
|
if (errorCount === tokens.length) {
|
||||||
|
logger.error(`All network checks for ${address} failed. Cannot verify admin status.`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (foundTokens) {
|
||||||
|
logger.info(`Admin role summary for ${address}:`, {
|
||||||
|
networks: Object.keys(balances).filter(
|
||||||
|
(net) => parseFloat(balances[net]) > 0 && balances[net] !== 'Error'
|
||||||
|
),
|
||||||
|
balances,
|
||||||
|
});
|
||||||
|
logger.info(`Admin role granted for ${address}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
logger.info(`Admin role denied - no tokens found for ${address}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { checkAdminRole };
|
||||||
@@ -8,6 +8,7 @@ const identityService = require('./identity-service'); // <-- ДОБАВЛЕН
|
|||||||
const authTokenService = require('./authTokenService');
|
const authTokenService = require('./authTokenService');
|
||||||
const rpcProviderService = require('./rpcProviderService');
|
const rpcProviderService = require('./rpcProviderService');
|
||||||
const { getLinkedWallet } = require('./wallet-service');
|
const { getLinkedWallet } = require('./wallet-service');
|
||||||
|
const { checkAdminRole } = require('./admin-role');
|
||||||
|
|
||||||
const ERC20_ABI = ['function balanceOf(address owner) view returns (uint256)'];
|
const ERC20_ABI = ['function balanceOf(address owner) view returns (uint256)'];
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ class AuthService {
|
|||||||
const user = userResult.rows[0];
|
const user = userResult.rows[0];
|
||||||
|
|
||||||
// Проверяем роль администратора при каждой аутентификации
|
// Проверяем роль администратора при каждой аутентификации
|
||||||
const isAdmin = await this.checkAdminRole(normalizedAddress);
|
const isAdmin = await checkAdminRole(normalizedAddress);
|
||||||
|
|
||||||
// Если статус админа изменился, обновляем роль в базе данных
|
// Если статус админа изменился, обновляем роль в базе данных
|
||||||
if (user.role === 'admin' && !isAdmin) {
|
if (user.role === 'admin' && !isAdmin) {
|
||||||
@@ -90,7 +91,7 @@ class AuthService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Проверяем, есть ли у пользователя роль админа
|
// Проверяем, есть ли у пользователя роль админа
|
||||||
const isAdmin = await this.checkAdminRole(normalizedAddress);
|
const isAdmin = await checkAdminRole(normalizedAddress);
|
||||||
logger.info(`New user ${userId} role check result: ${isAdmin ? 'admin' : 'user'}`);
|
logger.info(`New user ${userId} role check result: ${isAdmin ? 'admin' : 'user'}`);
|
||||||
|
|
||||||
// Если у пользователя есть админские токены, обновляем его роль
|
// Если у пользователя есть админские токены, обновляем его роль
|
||||||
@@ -108,97 +109,6 @@ class AuthService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Основной метод проверки роли админа
|
|
||||||
* @param {string} address - Адрес кошелька
|
|
||||||
* @returns {Promise<boolean>} - Является ли пользователь админом
|
|
||||||
*/
|
|
||||||
async checkAdminRole(address) {
|
|
||||||
if (!address) return false;
|
|
||||||
logger.info(`Checking admin role for address: ${address}`);
|
|
||||||
let foundTokens = false;
|
|
||||||
let errorCount = 0;
|
|
||||||
const balances = {};
|
|
||||||
// Получаем токены и RPC из базы
|
|
||||||
const tokens = await authTokenService.getAllAuthTokens();
|
|
||||||
const rpcProviders = await rpcProviderService.getAllRpcProviders();
|
|
||||||
const rpcMap = {};
|
|
||||||
for (const rpc of rpcProviders) {
|
|
||||||
rpcMap[rpc.network_id] = rpc.rpc_url;
|
|
||||||
}
|
|
||||||
const checkPromises = tokens.map(async (token) => {
|
|
||||||
try {
|
|
||||||
const rpcUrl = rpcMap[token.network];
|
|
||||||
if (!rpcUrl) {
|
|
||||||
logger.error(`No RPC URL for network ${token.network}`);
|
|
||||||
balances[token.network] = 'Error: No RPC URL';
|
|
||||||
errorCount++;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
|
||||||
// Проверяем доступность сети с таймаутом
|
|
||||||
try {
|
|
||||||
const networkCheckPromise = provider.getNetwork();
|
|
||||||
const timeoutPromise = new Promise((_, reject) =>
|
|
||||||
setTimeout(() => reject(new Error('Network check timeout')), 3000)
|
|
||||||
);
|
|
||||||
await Promise.race([networkCheckPromise, timeoutPromise]);
|
|
||||||
} catch (networkError) {
|
|
||||||
logger.error(`Provider for ${token.network} is not available: ${networkError.message}`);
|
|
||||||
balances[token.network] = 'Error: Network unavailable';
|
|
||||||
errorCount++;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const tokenContract = new ethers.Contract(token.address, ERC20_ABI, provider);
|
|
||||||
const balancePromise = tokenContract.balanceOf(address);
|
|
||||||
const timeoutPromise = new Promise((_, reject) =>
|
|
||||||
setTimeout(() => reject(new Error('Timeout')), 3000)
|
|
||||||
);
|
|
||||||
const balance = await Promise.race([balancePromise, timeoutPromise]);
|
|
||||||
const formattedBalance = ethers.formatUnits(balance, 18);
|
|
||||||
balances[token.network] = formattedBalance;
|
|
||||||
logger.info(`Token balance on ${token.network}:`, {
|
|
||||||
address,
|
|
||||||
contract: token.address,
|
|
||||||
balance: formattedBalance,
|
|
||||||
minBalance: token.min_balance,
|
|
||||||
hasTokens: parseFloat(formattedBalance) >= parseFloat(token.min_balance),
|
|
||||||
});
|
|
||||||
if (parseFloat(formattedBalance) >= parseFloat(token.min_balance)) {
|
|
||||||
logger.info(`Found admin tokens on ${token.network}`);
|
|
||||||
foundTokens = true;
|
|
||||||
}
|
|
||||||
return { network: token.network, balance: formattedBalance };
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(`Error checking balance in ${token.network}:`, {
|
|
||||||
address,
|
|
||||||
contract: token.address,
|
|
||||||
error: error.message || 'Unknown error',
|
|
||||||
});
|
|
||||||
balances[token.network] = 'Error';
|
|
||||||
errorCount++;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await Promise.all(checkPromises);
|
|
||||||
if (errorCount === tokens.length) {
|
|
||||||
logger.error(`All network checks for ${address} failed. Cannot verify admin status.`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (foundTokens) {
|
|
||||||
logger.info(`Admin role summary for ${address}:`, {
|
|
||||||
networks: Object.keys(balances).filter(
|
|
||||||
(net) => parseFloat(balances[net]) > 0 && balances[net] !== 'Error'
|
|
||||||
),
|
|
||||||
balances,
|
|
||||||
});
|
|
||||||
logger.info(`Admin role granted for ${address}`);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
logger.info(`Admin role denied - no tokens found for ${address}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Получение балансов токенов для адреса
|
* Получение балансов токенов для адреса
|
||||||
* @param {string} address - Адрес кошелька
|
* @param {string} address - Адрес кошелька
|
||||||
@@ -378,7 +288,7 @@ class AuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Если есть кошелек, проверяем админские токены
|
// Если есть кошелек, проверяем админские токены
|
||||||
const isAdmin = await this.checkAdminRole(wallet);
|
const isAdmin = await checkAdminRole(wallet);
|
||||||
logger.info(
|
logger.info(
|
||||||
`Role check for user ${userId} with wallet ${wallet}: ${isAdmin ? 'admin' : 'user'}`
|
`Role check for user ${userId} with wallet ${wallet}: ${isAdmin ? 'admin' : 'user'}`
|
||||||
);
|
);
|
||||||
@@ -415,7 +325,7 @@ class AuthService {
|
|||||||
|
|
||||||
if (wallet) {
|
if (wallet) {
|
||||||
// Если есть кошелек, проверяем баланс токенов
|
// Если есть кошелек, проверяем баланс токенов
|
||||||
const isAdmin = await this.checkAdminRole(wallet);
|
const isAdmin = await checkAdminRole(wallet);
|
||||||
role = isAdmin ? 'admin' : 'user';
|
role = isAdmin ? 'admin' : 'user';
|
||||||
logger.info(`User ${userId} has wallet ${wallet}, role set to ${role}`);
|
logger.info(`User ${userId} has wallet ${wallet}, role set to ${role}`);
|
||||||
} else {
|
} else {
|
||||||
@@ -530,7 +440,7 @@ class AuthService {
|
|||||||
logger.info(`Checking admin tokens for address: ${address}`);
|
logger.info(`Checking admin tokens for address: ${address}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const isAdmin = await this.checkAdminRole(address);
|
const isAdmin = await checkAdminRole(address);
|
||||||
|
|
||||||
// Обновляем роль пользователя в базе данных, если есть админские токены
|
// Обновляем роль пользователя в базе данных, если есть админские токены
|
||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
@@ -798,7 +708,7 @@ class AuthService {
|
|||||||
const linkedWallet = await getLinkedWallet(userId);
|
const linkedWallet = await getLinkedWallet(userId);
|
||||||
if (linkedWallet && linkedWallet.provider_id) {
|
if (linkedWallet && linkedWallet.provider_id) {
|
||||||
logger.info(`[handleEmailVerification] Found linked wallet ${linkedWallet.provider_id}. Checking role...`);
|
logger.info(`[handleEmailVerification] Found linked wallet ${linkedWallet.provider_id}. Checking role...`);
|
||||||
const isAdmin = await this.checkAdminRole(linkedWallet.provider_id);
|
const isAdmin = await checkAdminRole(linkedWallet.provider_id);
|
||||||
userRole = isAdmin ? 'admin' : 'user';
|
userRole = isAdmin ? 'admin' : 'user';
|
||||||
logger.info(`[handleEmailVerification] Role determined as: ${userRole}`);
|
logger.info(`[handleEmailVerification] Role determined as: ${userRole}`);
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const logger = require('../utils/logger');
|
|||||||
const EmailBotService = require('./emailBot.js');
|
const EmailBotService = require('./emailBot.js');
|
||||||
const db = require('../db');
|
const db = require('../db');
|
||||||
const authService = require('./auth-service');
|
const authService = require('./auth-service');
|
||||||
|
const { checkAdminRole } = require('./admin-role');
|
||||||
|
|
||||||
class EmailAuth {
|
class EmailAuth {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -167,7 +168,7 @@ class EmailAuth {
|
|||||||
const linkedWallet = await authService.getLinkedWallet(finalUserId);
|
const linkedWallet = await authService.getLinkedWallet(finalUserId);
|
||||||
if (linkedWallet) {
|
if (linkedWallet) {
|
||||||
logger.info(`[checkEmailVerification] Found linked wallet ${linkedWallet} for user ${finalUserId}. Checking admin role...`);
|
logger.info(`[checkEmailVerification] Found linked wallet ${linkedWallet} for user ${finalUserId}. Checking admin role...`);
|
||||||
const isAdmin = await authService.checkAdminRole(linkedWallet);
|
const isAdmin = await checkAdminRole(linkedWallet);
|
||||||
userRole = isAdmin ? 'admin' : 'user';
|
userRole = isAdmin ? 'admin' : 'user';
|
||||||
logger.info(`[checkEmailVerification] Role for user ${finalUserId} determined as: ${userRole}`);
|
logger.info(`[checkEmailVerification] Role for user ${finalUserId} determined as: ${userRole}`);
|
||||||
|
|
||||||
|
|||||||
@@ -236,6 +236,7 @@ class EmailBotService {
|
|||||||
logger.info('[EmailBot] IMAP config:', safeConfig);
|
logger.info('[EmailBot] IMAP config:', safeConfig);
|
||||||
let attempt = 0;
|
let attempt = 0;
|
||||||
const maxAttempts = 3;
|
const maxAttempts = 3;
|
||||||
|
this.isChecking = false;
|
||||||
const tryConnect = () => {
|
const tryConnect = () => {
|
||||||
attempt++;
|
attempt++;
|
||||||
logger.info(`[EmailBot] IMAP connect attempt ${attempt}`);
|
logger.info(`[EmailBot] IMAP connect attempt ${attempt}`);
|
||||||
@@ -253,6 +254,17 @@ class EmailBotService {
|
|||||||
// После успешного подключения — обычная логика
|
// После успешного подключения — обычная логика
|
||||||
this.checkEmails();
|
this.checkEmails();
|
||||||
logger.info('[EmailBot] Email bot started and IMAP connection initiated');
|
logger.info('[EmailBot] Email bot started and IMAP connection initiated');
|
||||||
|
// Периодическая проверка почты
|
||||||
|
setInterval(async () => {
|
||||||
|
if (this.isChecking) return;
|
||||||
|
this.isChecking = true;
|
||||||
|
try {
|
||||||
|
await this.checkEmails();
|
||||||
|
} catch (e) {
|
||||||
|
logger.error('[EmailBot] Error in periodic checkEmails:', e);
|
||||||
|
}
|
||||||
|
this.isChecking = false;
|
||||||
|
}, 60000); // 60 секунд
|
||||||
});
|
});
|
||||||
this.imap.once('error', (err) => {
|
this.imap.once('error', (err) => {
|
||||||
logger.error(`[EmailBot] IMAP connection error: ${err.message}`);
|
logger.error(`[EmailBot] IMAP connection error: ${err.message}`);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
const db = require('../db');
|
const db = require('../db');
|
||||||
const logger = require('../utils/logger');
|
const logger = require('../utils/logger');
|
||||||
const { getLinkedWallet } = require('./wallet-service');
|
const { getLinkedWallet } = require('./wallet-service');
|
||||||
|
const { checkAdminRole } = require('./admin-role');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Сервис для работы с идентификаторами пользователей
|
* Сервис для работы с идентификаторами пользователей
|
||||||
@@ -545,7 +546,7 @@ class IdentityService {
|
|||||||
const wallet = await getLinkedWallet(user.id);
|
const wallet = await getLinkedWallet(user.id);
|
||||||
let role = 'user';
|
let role = 'user';
|
||||||
if (wallet) {
|
if (wallet) {
|
||||||
const isAdmin = await authService.checkAdminRole(wallet);
|
const isAdmin = await checkAdminRole(wallet);
|
||||||
role = isAdmin ? 'admin' : 'user';
|
role = isAdmin ? 'admin' : 'user';
|
||||||
// Обновляем роль в users, если изменилась
|
// Обновляем роль в users, если изменилась
|
||||||
if (user.role !== role) {
|
if (user.role !== role) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const verificationService = require('./verification-service');
|
|||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const identityService = require('./identity-service');
|
const identityService = require('./identity-service');
|
||||||
const aiAssistant = require('./ai-assistant');
|
const aiAssistant = require('./ai-assistant');
|
||||||
|
const { checkAdminRole } = require('./admin-role');
|
||||||
|
|
||||||
let botInstance = null;
|
let botInstance = null;
|
||||||
let telegramSettingsCache = null;
|
let telegramSettingsCache = null;
|
||||||
@@ -158,7 +159,7 @@ async function getBot() {
|
|||||||
const linkedWallet = await authService.getLinkedWallet(userId);
|
const linkedWallet = await authService.getLinkedWallet(userId);
|
||||||
if (linkedWallet) {
|
if (linkedWallet) {
|
||||||
logger.info(`[TelegramBot] Found linked wallet ${linkedWallet} for user ${userId}. Checking role...`);
|
logger.info(`[TelegramBot] Found linked wallet ${linkedWallet} for user ${userId}. Checking role...`);
|
||||||
const isAdmin = await authService.checkAdminRole(linkedWallet);
|
const isAdmin = await checkAdminRole(linkedWallet);
|
||||||
userRole = isAdmin ? 'admin' : 'user';
|
userRole = isAdmin ? 'admin' : 'user';
|
||||||
logger.info(`[TelegramBot] Role for user ${userId} determined as: ${userRole}`);
|
logger.info(`[TelegramBot] Role for user ${userId} determined as: ${userRole}`);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user