185 lines
6.7 KiB
JavaScript
185 lines
6.7 KiB
JavaScript
const { SiweMessage } = require('siwe');
|
||
const { ethers } = require('ethers');
|
||
const AccessTokenABI = require('../artifacts/contracts/AccessToken.sol/AccessToken.json').abi;
|
||
require('dotenv').config();
|
||
const { pool } = require('../db');
|
||
|
||
// В ethers.js v6.x используется JsonRpcProvider напрямую
|
||
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
|
||
const accessToken = new ethers.Contract(process.env.ACCESS_TOKEN_ADDRESS, AccessTokenABI, provider);
|
||
|
||
// Проверяем наличие адреса контракта
|
||
if (!process.env.ACCESS_TOKEN_ADDRESS) {
|
||
console.error('ACCESS_TOKEN_ADDRESS не указан в .env файле');
|
||
}
|
||
|
||
/**
|
||
* Проверяет подпись сообщения
|
||
* @param {Object} message - Сообщение для проверки
|
||
* @param {string} signature - Подпись сообщения
|
||
* @param {string} address - Адрес кошелька
|
||
* @returns {Promise<boolean>} - Результат проверки
|
||
*/
|
||
async function verifySignature(message, signature, address) {
|
||
try {
|
||
// Формируем сообщение для проверки
|
||
const domain = message.domain || 'localhost';
|
||
const statement = message.statement || 'Sign in with Ethereum to the app.';
|
||
const uri = message.uri || 'http://localhost:8000';
|
||
const version = message.version || '1';
|
||
const chainId = message.chainId || '1';
|
||
const nonce = message.nonce;
|
||
|
||
const messageToVerify = `${domain} wants you to sign in with your Ethereum account:
|
||
${address}
|
||
|
||
${statement}
|
||
|
||
URI: ${uri}
|
||
Version: ${version}
|
||
Chain ID: ${chainId}
|
||
Nonce: ${nonce}
|
||
`;
|
||
|
||
// В ethers.js v6.x используется verifyMessage напрямую
|
||
const recoveredAddress = ethers.verifyMessage(messageToVerify, signature);
|
||
|
||
return recoveredAddress.toLowerCase() === address.toLowerCase();
|
||
} catch (error) {
|
||
console.error('Signature verification error:', error);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
async function checkAccess(address) {
|
||
// Временная заглушка
|
||
return { hasAccess: false, role: null };
|
||
}
|
||
|
||
async function verifyAndCheckAccess(message, signature, address) {
|
||
// Проверяем подпись
|
||
const verified = await verifySignature(message, signature, address);
|
||
if (!verified) {
|
||
return {
|
||
verified: false,
|
||
access: { hasAccess: false },
|
||
};
|
||
}
|
||
|
||
// Проверяем доступ
|
||
const access = await checkAccess(address);
|
||
|
||
return {
|
||
verified: true,
|
||
access,
|
||
};
|
||
}
|
||
|
||
// Функция для поиска или создания пользователя
|
||
async function findOrCreateUser(identifier, identityType = 'wallet') {
|
||
try {
|
||
// Проверяем, является ли адрес адресом администратора
|
||
const isAdmin = identityType === 'wallet' &&
|
||
identifier.toLowerCase() === process.env.ADMIN_WALLET_ADDRESS.toLowerCase();
|
||
|
||
console.log(`Проверка на администратора: ${identifier.toLowerCase()} === ${process.env.ADMIN_WALLET_ADDRESS.toLowerCase()} = ${isAdmin}`);
|
||
|
||
// Проверяем, существует ли пользователь с таким идентификатором
|
||
const identityResult = await pool.query(
|
||
'SELECT user_id FROM user_identities WHERE identity_type = $1 AND identity_value = $2',
|
||
[identityType, identifier.toLowerCase()]
|
||
);
|
||
|
||
let userId;
|
||
let isNewUser = false;
|
||
|
||
if (identityResult.rows.length > 0) {
|
||
// Пользователь найден
|
||
userId = identityResult.rows[0].user_id;
|
||
console.log(`Найден существующий пользователь с ID: ${userId}`);
|
||
|
||
// Обновляем статус администратора, если это необходимо
|
||
if (isAdmin) {
|
||
await pool.query(
|
||
'UPDATE users SET is_admin = true WHERE id = $1',
|
||
[userId]
|
||
);
|
||
console.log(`Обновлен статус администратора для пользователя ${userId}`);
|
||
}
|
||
} else {
|
||
// Создаем нового пользователя с явным указанием всех необходимых полей
|
||
const username = `user_${Date.now()}`;
|
||
|
||
// Проверяем существование роли USER или ADMIN
|
||
const roleName = isAdmin ? 'ADMIN' : 'USER';
|
||
const roleCheck = await pool.query('SELECT id FROM roles WHERE name = $1', [roleName]);
|
||
let roleId;
|
||
|
||
if (roleCheck.rows.length > 0) {
|
||
roleId = roleCheck.rows[0].id;
|
||
} else {
|
||
// Если роли нет, создаем её
|
||
const newRole = await pool.query(
|
||
'INSERT INTO roles (name, description) VALUES ($1, $2) RETURNING id',
|
||
[roleName, isAdmin ? 'Administrator role' : 'Regular user role']
|
||
);
|
||
roleId = newRole.rows[0].id;
|
||
}
|
||
|
||
// Создаем пользователя с обязательными полями
|
||
const userResult = await pool.query(
|
||
`INSERT INTO users (username, role_id, address, created_at, is_admin)
|
||
VALUES ($1, $2, $3, NOW(), $4)
|
||
RETURNING id`,
|
||
[username, roleId, identifier.toLowerCase(), isAdmin]
|
||
);
|
||
|
||
userId = userResult.rows[0].id;
|
||
isNewUser = true;
|
||
|
||
// Создаем запись в таблице идентификаторов
|
||
await pool.query(
|
||
'INSERT INTO user_identities (user_id, identity_type, identity_value, verified, created_at) VALUES ($1, $2, $3, true, NOW())',
|
||
[userId, identityType, identifier.toLowerCase()]
|
||
);
|
||
|
||
console.log(`Создан новый пользователь с ID: ${userId}, isAdmin: ${isAdmin}`);
|
||
}
|
||
|
||
// Получаем информацию о пользователе
|
||
try {
|
||
const userInfo = await pool.query(
|
||
`SELECT u.id, u.username, u.is_admin, r.name as role
|
||
FROM users u
|
||
JOIN roles r ON u.role_id = r.id
|
||
WHERE u.id = $1`,
|
||
[userId]
|
||
);
|
||
|
||
if (userInfo.rows.length > 0) {
|
||
return userInfo.rows[0];
|
||
}
|
||
} catch (err) {
|
||
console.error('Ошибка при получении информации о пользователе:', err);
|
||
}
|
||
|
||
// Если не удалось получить полную информацию, возвращаем базовую
|
||
return {
|
||
id: userId,
|
||
username: isNewUser ? `user_${Date.now()}` : 'unknown',
|
||
is_admin: isAdmin,
|
||
role: isAdmin ? 'ADMIN' : 'USER'
|
||
};
|
||
} catch (error) {
|
||
console.error('Ошибка при поиске/создании пользователя:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
module.exports = {
|
||
verifyAndCheckAccess,
|
||
verifySignature,
|
||
checkAccess,
|
||
findOrCreateUser
|
||
};
|