Files
DLE/backend/server.js
2025-02-18 21:56:51 +03:00

211 lines
6.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import express from 'express';
import cors from 'cors';
import session from 'express-session';
import { generateNonce, SiweMessage } from 'siwe';
import { createPublicClient, http, verifyMessage } from 'viem';
import { sepolia } from 'viem/chains';
const app = express();
// Создаем Viem клиент для Sepolia
const client = createPublicClient({
chain: sepolia,
transport: http()
});
// Конфигурация CORS для работы с frontend
app.use(cors({
origin: ['http://localhost:5174', 'http://127.0.0.1:5173', 'http://localhost:5173'],
credentials: true,
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type', 'Accept']
}));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Настройка сессий
app.use(session({
name: 'siwe-dapp',
secret: "siwe-dapp-secret",
resave: true,
saveUninitialized: true,
cookie: {
secure: false,
sameSite: 'lax',
maxAge: 24 * 60 * 60 * 1000 // 24 часа
}
}));
// Логирование запросов
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});
// Генерация nonce
app.get('/nonce', (_, res) => {
try {
const nonce = generateNonce();
res.setHeader('Content-Type', 'text/plain');
res.status(200).send(nonce);
} catch (error) {
console.error('Ошибка генерации nonce:', error);
res.status(500).send('Internal Server Error');
}
});
// Верификация сообщения
app.post('/verify', async (req, res) => {
try {
if (!req.body.message) {
return res.status(400).json({ error: 'SiweMessage is undefined' });
}
const { message, signature } = req.body;
console.log('Верификация сообщения:', { message, signature });
// Создаем и парсим SIWE сообщение
const siweMessage = new SiweMessage(message);
// Проверяем базовые параметры
if (siweMessage.chainId !== 11155111) { // Sepolia
throw new Error('Invalid chain ID. Only Sepolia is supported.');
}
if (siweMessage.domain !== '127.0.0.1:5173') {
throw new Error('Invalid domain');
}
// Проверяем время
const currentTime = new Date().getTime();
const messageTime = new Date(siweMessage.issuedAt).getTime();
const timeDiff = currentTime - messageTime;
// Временно отключаем проверку времени для разработки
console.log('Разница во времени:', {
currentTime: new Date(currentTime).toISOString(),
messageTime: new Date(messageTime).toISOString(),
diffMinutes: Math.abs(timeDiff) / (60 * 1000)
});
// Верифицируем сообщение
console.log('Начинаем валидацию SIWE сообщения...');
const fields = await siweMessage.validate(signature);
console.log('SIWE валидация успешна:', fields);
// Проверяем подпись через viem
console.log('Проверяем подпись через viem...');
const isValid = await client.verifyMessage({
address: fields.address,
message: message,
signature: signature
});
console.log('Результат проверки подписи:', isValid);
if (!isValid) {
throw new Error('Invalid signature');
}
console.log('Верификация успешна:', {
address: fields.address,
chainId: fields.chainId,
domain: fields.domain
});
// Сохраняем сессию
req.session.siwe = {
address: fields.address,
chainId: fields.chainId,
domain: fields.domain,
issuedAt: fields.issuedAt
};
req.session.save(() => {
res.status(200).json({
success: true,
address: fields.address,
chainId: fields.chainId,
domain: fields.domain
});
});
} catch (error) {
console.error('Ошибка верификации:', error);
req.session.siwe = null;
req.session.nonce = null;
req.session.save(() => {
res.status(400).json({
error: 'Verification failed',
message: error.message
});
});
}
});
// Получение сессии
app.get('/session', (req, res) => {
try {
res.json(req.session.siwe || null);
} catch (error) {
console.error('Ошибка получения сессии:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
});
// Выход
app.get('/signout', (req, res) => {
try {
req.session.destroy((err) => {
if (err) {
console.error('Ошибка при удалении сессии:', err);
return res.status(500).json({ error: 'Failed to destroy session' });
}
res.status(200).json({ success: true });
});
} catch (error) {
console.error('Ошибка выхода:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
});
// Базовый маршрут
app.get('/', (req, res) => {
res.json({
status: 'ok',
endpoints: {
nonce: 'GET /nonce',
verify: 'POST /verify',
session: 'GET /session',
signout: 'GET /signout'
}
});
});
// Обработка 404
app.use((req, res) => {
console.log(`404: ${req.method} ${req.url}`);
res.status(404).json({
error: 'Not Found',
message: `Endpoint ${req.method} ${req.url} не существует`
});
});
// Обработка ошибок
app.use((err, req, res, next) => {
console.error('Ошибка сервера:', err);
res.status(500).json({
error: 'Internal Server Error',
message: err.message
});
});
const PORT = 3000;
app.listen(PORT, () => {
console.log(`SIWE сервер запущен на порту ${PORT}`);
console.log('Доступные эндпоинты:');
console.log(' GET / - Информация о сервере');
console.log(' GET /nonce - Получить nonce');
console.log(' POST /verify - Верифицировать сообщение');
console.log(' GET /session - Получить текущую сессию');
console.log(' GET /signout - Выйти из системы');
});