Я не писал бэкенд и у меня нет опыта работы с ним. Это был проект из двух человек, и теперь я пытаюсь получить этот опыт, развернув его.
У меня бэкенд развернут на Render, фронтенд на Vercel.
Все работало в разработке и в предварительном просмотре. Только после развертывания возникли проблемы с куками, что означает, что, чтобы протестировать код, мне приходится постоянно развертывать его заново, так как они не появляются при тестировании.
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const session = require('express-session');
const MongoDBStore = require('connect-mongodb-session')(session);
const socket = require('socket.io');
const multer = require('multer');
const cors = require('cors');
require('dotenv').config();
const User = require('./models/user');
const MONGODB_URI = process.env.MONGO_KEY;
const app = express();
const http = require('http');
const server = http.createServer(app);
const io = socket(server);
let users = [];
const addUser = (userId, socketId) => {
!users.some((user) => user.userId === userId) && users.push({ userId, socketId });
};
const removeUser = (socketId) => {
users = users.filter((user) => user.socketId !== socketId);
};
const getUser = (userId) => {
return users.find((user) => user.userId === userId);
};
io.on('connection', (socket) => {
// когда подключено
console.log('пользователь подключен.');
// взять userId и socketId от пользователя
socket.on('addUser', (userId) => {
addUser(userId, socket.id);
io.emit('getUsers', users);
});
// отправка и получение сообщений
socket.on('sendMessage', ({ senderId, receiverId, text }) => {
const user = getUser(receiverId);
io.to(user.socketId).emit('getMessage', {
senderId,
text,
});
});
// когда отключен
socket.on('disconnect', () => {
console.log('пользователь отключен!');
removeUser(socket.id);
io.emit('getUsers', users);
});
});
const store = new MongoDBStore({
uri: MONGODB_URI,
collection: 'sessions',
});
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'images');
},
filename: (req, file, cb) => {
cb(null, new Date().toISOString().replace(/:/g, '-') + '-' + file.originalname);
},
});
const fileFilter = (req, file, cb) => {
if (
file.mimetype === 'image/png' ||
file.mimetype === 'image/jpg' ||
file.mimetype === 'image/jpeg'
) {
cb(null, true);
} else {
cb(null, false);
}
};
app.set('views', 'views');
app.set('view engine', 'ejs');
const marketRoutes = require('./routes/market');
const adminRoutes = require('./routes/admin');
const authRoutes = require('./routes/auth');
const messageRoutes = require('./routes/message');
const chatRoutes = require('./routes/chat');
const user = require('./models/user');
app.use(
cors({
origin: process.env.CLIENT_URL, // настроено на прием учетных данных из фронтенда, чтобы куки работали
credentials: true,
})
);
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(multer({ storage: storage, fileFilter: fileFilter }).single('image'));
app.use(express.static(path.join(__dirname, '/public')));
app.use('/images', express.static(path.join(__dirname, 'images')));
app.use(
session({
secret: 'мой секрет',
resave: false,
saveUninitialized: false,
store: store,
cookie: {
maxAge: 30 * 24 * 60 * 60 * 1000,
httpOnly: true,
secure: true,
sameSite: 'None',
},
})
);
app.use((req, res, next) => {
res.locals.isAuthenticated = req.session.isLoggedIn;
next();
});
app.use((req, res, next) => {
if (!req.session.user) {
return next();
}
User.findById(req.session.user._id)
.then((user) => {
if (!user) {
return next();
}
req.user = user;
next();
})
.catch((err) => {
console.log(err);
});
});
app.use(marketRoutes);
app.use(adminRoutes);
app.use(authRoutes);
app.use(messageRoutes);
app.use(chatRoutes);
mongoose
.connect(MONGODB_URI)
.then((result) => {
server.listen(3000);
console.log('подключено!');
})
.catch((err) => {
console.log(err);
});
Так что после того, как я несколько раз обошелся с несколькими ИИ, они все время повторяют одно и то же: добавить эти строки, которых изначально не было:
httpOnly: true,
secure: true,
sameSite: 'None',
Это не сработало. Без этих строк приложение имело полноценную функциональность только в браузере Firefox. В любом другом браузере, который я тестировал, вы можете видеть данные БД, можете зарегистрироваться/войти (POST запросы), но не можете выполнять любые другие запросы, потому что контроллер будет проверять req.session.user практически в каждом контроллере. Это зависит от куки для правильной работы. Клиентская сторона будет использовать некоторые хуки для выполнения запросов, которые вызовут эту функцию
export interface Fetch {
params: string;
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
data?: object;
}
export const fetchData = async ({ params, method, data }: Fetch) => {
const isFormData = data instanceof FormData;
const body = data ? (isFormData ? data : JSON.stringify(data)) : null;
const response = await fetch(import.meta.env.VITE_SERVER_URL + params, {
method,
headers: isFormData ? undefined : { 'Content-Type': 'application/json' },
credentials: 'include', // отправить куки на бэкенд
body,
});
const resData = await response.json();
if (!response.ok) {
throw resData;
}
return resData;
};
Cors настроен для приема учетных данных. Учетные данные установлены в true на клиентской стороне. Все работает в Dev/Preview и Firefox с кодом приложения, как он есть:
app.use(
session({
secret: 'мой секрет',
resave: false,
saveUninitialized: false,
store: store,
cookie: {
maxAge: 30 * 24 * 60 * 60 * 1000,
},
})
);
Но куки не работают в любом браузере, кроме Firefox. Тогда добавление дополнительной конфигурации куки предотвращает ее работу в любой среде вообще.
Я пытался изменить конфигурацию куки и развернуть снова, но это только сделало его менее функциональным. Я бы подумал, что настройка правильная, но я не могу получить дальнейшую помощь по этому вопросу. В настоящее время я мог бы развернуть ее без конфигурации куки и просто оставить заметку, что это полностью функционирует только в Firefox.
Ответ
Проблема, с которой вы сталкиваетесь, связана с настройками cookie и CORS в вашем приложении. Вот несколько шагов, которые помогут устранить проблемы с cookies, особенно когда приложение развернуто на Render и Vercel.
1. Проверка настроек CORS
Убедитесь, что ваш сервер правильно настроен для работы с CORS. У вас уже стоит credentials: true
, что правильно. Вот пример, как это можно настроить:
app.use(
cors({
origin: process.env.CLIENT_URL, // ваш URL Vercel приложения
credentials: true,
})
);
2. Настройки cookie
Ваши настройки cookie должны учитывать использование secure, httpOnly и sameSite, особенно если ваше приложение должно быть доступно через HTTPS. Вам нужно убедиться, что secure: true
работает только тогда, когда ваше приложение развернуто на HTTPS. Попробуйте следующее:
app.use(
session({
secret: 'my secret',
resave: false,
saveUninitialized: false,
store: store,
cookie: {
maxAge: 30 * 24 * 60 * 60 * 1000,
httpOnly: true,
secure: process.env.NODE_ENV === 'production', // Убедитесь, что secure установлено в true только в продакшн
sameSite: 'None', // Браузеры, кроме Firefox, требуют это значение для ключевых функциональностей
},
})
);
3. Убедитесь, что вы используете правильные запросы
Убедитесь, что ваши запросы с клиента действительно отправляют необходимые cookies. Например, в вашем коде:
const response = await fetch(import.meta.env.VITE_SERVER_URL + params, {
method,
headers: isFormData ? undefined : { 'Content-Type': 'application/json' },
credentials: 'include', // Это важно для отправки cookies
body,
});
Это правильно, так как credentials: 'include'
позволяет отправлять cookies при запросах к вашему серверу.
4. Проверьте среду разработки
Иногда проблемы с cookie могут возникнуть из-за локальной среды разработки. Убедитесь, что:
- Ваши URL-адреса совпадают (например, если у вас есть
http://localhost:3000
на сервере иhttp://localhost:3001
на клиенте, это не сработает без дополнительных настроек). - Проверьте настройки вашего браузера, возможно, он блокирует cookies.
5. Логи и отладка
Добавьте дополнительные логи на серверную часть, чтобы понять, что происходит с вашей сессией и cookies. Это может быть полезно для диагностики:
app.use((req, res, next) => {
console.log('Session:', req.session);
console.log('Cookies:', req.cookies);
next();
});
6. Тестирование на разных браузерах
Некоторые браузеры могут по-разному обрабатывать cookies, особенно в более строгих настройках конфиденциальности. Проверьте:
- Google Chrome
- Safari
- Edge
Сравните, как cookies обрабатываются в каждом из них. Возможно, существует разница в использовании Cookies, связанная с конфиденциальностью или безопасностью.
Заключение
Если предыдущие шаги не помогли решить проблему, попробуйте каждый из изменений по отдельности и тестируйте развертывание, чтобы более точно определить, какое изменение влияет на функциональность.
Если у вас все еще возникают проблемы, пожалуйста, предоставьте больше информации о том, что именно не так—например, сообщения об ошибках или логи сервера—чтобы мы могли помочь еще больше.