Files
DLE/backend/server.js

261 lines
7.4 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.

const express = require('express');
const cors = require('cors');
const session = require('express-session');
const { SiweMessage, generateNonce } = require('siwe');
const path = require('path');
const apiRoutes = require('./routes/api.js');
const FileStore = require('session-file-store')(session);
const app = express();
// 1. Парсинг JSON
app.use(express.json());
// 2. CORS
app.use(cors({
origin: [
'http://127.0.0.1:5173',
'http://127.0.0.1:5174'
],
credentials: true,
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-SIWE-Nonce', 'X-Requested-With', 'Accept'],
exposedHeaders: ['Set-Cookie']
}));
// 3. Сессии
app.use(session({
name: 'siwe-dapp',
secret: "siwe-dapp-secret",
resave: true,
saveUninitialized: false,
store: new FileStore({
path: './sessions',
ttl: 86400,
retries: 0,
logFn: function(){},
reapInterval: 86400,
reapAsync: true,
reapSyncCheck: true,
retryTimeout: 100
}),
cookie: {
httpOnly: true,
secure: false,
sameSite: 'lax',
path: '/',
domain: '127.0.0.1',
maxAge: 30 * 24 * 60 * 60 * 1000
}
}));
// Middleware для сохранения сессии
app.use((req, res, next) => {
const oldEnd = res.end;
res.end = function (chunk, encoding) {
if (req.session && req.session.save) {
req.session.save((err) => {
if (err) console.error('Session save error:', err);
oldEnd.apply(res, arguments);
});
} else {
oldEnd.apply(res, arguments);
}
};
next();
});
// Генерация nonce
app.get('/nonce', (req, res) => {
try {
if (!req.session) {
throw new Error('No session available');
}
req.session.nonce = generateNonce();
console.log('Сгенерирован новый nonce:', req.session.nonce);
res.json({ nonce: req.session.nonce });
} catch (error) {
console.error('Ошибка генерации nonce:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
});
// Функция для проверки формата адреса EIP-55
function isValidEIP55Address(address) {
return /^0x[0-9A-F]{40}$/.test(address);
}
// Верификация сообщения
app.post('/verify', async (req, res) => {
try {
const clientNonce = req.headers['x-siwe-nonce'];
const { signature, message } = req.body;
console.log('Received message:', message);
if (!req.session) {
throw new Error('No session available');
}
// Создаем и проверяем SIWE сообщение
let siweMessage;
try {
siweMessage = new SiweMessage(message);
console.log('SIWE message parsed:', siweMessage);
// Проверяем nonce
if (siweMessage.nonce !== clientNonce) {
throw new Error('Invalid nonce');
}
// Проверяем подпись
const fields = await siweMessage.verify({
signature: signature,
domain: siweMessage.domain,
nonce: clientNonce
});
console.log('SIWE validation successful:', fields);
console.log('Сообщение успешно верифицировано');
// Сохраняем данные в сессии
req.session.siwe = fields.data;
req.session.authenticated = true;
req.session.nonce = null;
// Принудительно сохраняем сессию
await new Promise((resolve, reject) => {
req.session.save((err) => {
if (err) reject(err);
else resolve();
});
});
res.json({
success: true,
address: fields.data.address
});
} catch (error) {
console.error('Ошибка валидации сообщения:', error);
req.session.authenticated = false;
req.session.siwe = null;
req.session.nonce = null;
throw error;
}
} catch (error) {
console.error('Ошибка верификации:', error);
res.status(400).json({
success: false,
error: error.message,
details: error.stack
});
}
});
// Получение сессии
app.get('/session', (req, res) => {
try {
if (!req.session) {
return res.status(401).json({
authenticated: false,
error: 'No session'
});
}
res.json({
authenticated: !!req.session.authenticated,
address: req.session.siwe?.address
});
} 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',
shutdown: 'POST /shutdown'
}
});
});
// Эндпоинт для остановки сервера
app.post('/shutdown', (req, res) => {
res.json({ message: 'Сервер останавливается...' });
console.log('Получен запрос на остановку сервера');
setTimeout(() => {
process.exit(0);
}, 1000);
});
// 4. API роуты
app.use('/api', apiRoutes);
// Обработка 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 = process.env.PORT || 3000;
const server = app.listen(PORT, '127.0.0.1', () => {
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 - Выйти из системы');
console.log(' POST /shutdown - Остановить сервер');
});
// Обработка ошибки занятого порта
server.on('error', (error) => {
if (error.code === 'EADDRINUSE') {
console.log(`Порт ${PORT} занят. Останавливаем предыдущий процесс...`);
require('child_process').exec(`lsof -i :${PORT} | grep LISTEN | awk '{print $2}' | xargs kill -9`, (err) => {
if (err) {
console.error('Ошибка при остановке процесса:', err);
process.exit(1);
}
console.log('Предыдущий процесс остановлен. Перезапускаем сервер...');
server.listen(PORT);
});
}
});