ваше сообщение коммита

This commit is contained in:
2025-05-28 14:23:38 +03:00
parent 2fc496d5bb
commit 0c4eada515
7 changed files with 129 additions and 102 deletions

View File

@@ -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 () => {

View 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 };

View File

@@ -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}`);

View File

@@ -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}`);

View File

@@ -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}`);

View File

@@ -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) {

View File

@@ -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}`);