Initial commit

This commit is contained in:
2025-02-18 21:56:51 +03:00
commit b56e6b5e46
19 changed files with 6998 additions and 0 deletions

211
backend/server.js Normal file
View File

@@ -0,0 +1,211 @@
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 - Выйти из системы');
});