Update files
This commit is contained in:
parent
e446691b3d
commit
d6ed268c4a
|
|
@ -28,7 +28,7 @@ const signAuthTokens = (user) => ({
|
|||
|
||||
const getCookieBaseOptions = () => ({
|
||||
httpOnly: true,
|
||||
secure: config.isProduction(),
|
||||
secure: config.isProduction(), // HTTPS только в production
|
||||
sameSite: config.isProduction() ? 'lax' : 'lax',
|
||||
path: '/'
|
||||
});
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ EMAIL_FROM=noreply@nakama.guru
|
|||
# Модерация
|
||||
MODERATION_PORT=3001
|
||||
MODERATION_CORS_ORIGIN=https://moderation.nkm.guru
|
||||
VITE_MODERATION_API_URL=https://moderation.nkm.guru/api
|
||||
VITE_MODERATION_API_URL=https://moderation.nkm.guru/api # ВАЖНО: обязательно HTTPS!
|
||||
|
||||
# Email для кодов подтверждения админа
|
||||
OWNER_EMAIL=aaem9848@gmail.com
|
||||
|
|
|
|||
|
|
@ -29,10 +29,9 @@ const { generalLimiter } = require('../../backend/middleware/rateLimiter');
|
|||
const app = express();
|
||||
const server = http.createServer(app);
|
||||
|
||||
// Trust proxy для правильного IP
|
||||
if (config.isProduction()) {
|
||||
app.set('trust proxy', 1);
|
||||
}
|
||||
// Trust proxy для правильного IP и HTTPS
|
||||
// В production доверяем прокси (nginx), чтобы правильно определять HTTPS
|
||||
app.set('trust proxy', config.isProduction() ? 1 : false);
|
||||
|
||||
// Security headers
|
||||
app.use(helmetConfig);
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ export default function App() {
|
|||
const [chatInput, setChatInput] = useState('');
|
||||
const chatSocketRef = useRef(null);
|
||||
const chatListRef = useRef(null);
|
||||
const telegramWidgetRef = useRef(null);
|
||||
|
||||
// Comments modal
|
||||
const [commentsModal, setCommentsModal] = useState(null); // { postId, comments: [] }
|
||||
|
|
@ -139,65 +140,6 @@ export default function App() {
|
|||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
// Инициализация Telegram Login Widget для обычного браузера
|
||||
const initTelegramWidget = () => {
|
||||
// Глобальная функция для обработки авторизации через виджет
|
||||
window.onTelegramAuth = async (userData) => {
|
||||
console.log('Telegram Login Widget данные:', userData);
|
||||
|
||||
try {
|
||||
setAuthLoading(true);
|
||||
|
||||
// Отправить данные виджета на сервер для создания сессии
|
||||
const API_URL = getApiUrl();
|
||||
const response = await fetch(`${API_URL}/moderation-auth/telegram-widget`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(userData)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.error || 'Ошибка авторизации');
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.accessToken) {
|
||||
localStorage.setItem('moderation_jwt_token', result.accessToken);
|
||||
}
|
||||
|
||||
if (result?.user) {
|
||||
setUser(result.user);
|
||||
setError(null);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Ошибка авторизации через виджет:', err);
|
||||
setError(err.message || 'Ошибка авторизации через Telegram');
|
||||
} finally {
|
||||
setAuthLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Загрузить виджет скрипт если его нет и есть контейнер
|
||||
if (!document.querySelector('script[src*="telegram-widget"]')) {
|
||||
setTimeout(() => {
|
||||
const widgetContainer = telegramWidgetRef.current;
|
||||
if (!widgetContainer) return;
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.async = true;
|
||||
script.src = 'https://telegram.org/js/telegram-widget.js?22';
|
||||
script.setAttribute('data-telegram-login', 'NakamaSpaceBot');
|
||||
script.setAttribute('data-size', 'large');
|
||||
script.setAttribute('data-request-access', 'write');
|
||||
script.setAttribute('data-onauth', 'onTelegramAuth');
|
||||
|
||||
widgetContainer.appendChild(script);
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
|
||||
const init = async () => {
|
||||
try {
|
||||
|
|
@ -221,10 +163,7 @@ export default function App() {
|
|||
}
|
||||
}
|
||||
|
||||
// Инициализировать виджет для обычного браузера
|
||||
if (!telegramApp?.initData) {
|
||||
initTelegramWidget();
|
||||
}
|
||||
// Инициализация виджета будет в отдельном useEffect после монтирования компонента
|
||||
|
||||
// Проверить JWT токен
|
||||
const jwtToken = localStorage.getItem('moderation_jwt_token');
|
||||
|
|
@ -266,6 +205,93 @@ export default function App() {
|
|||
};
|
||||
}, []);
|
||||
|
||||
// Отдельный useEffect для инициализации виджета после монтирования
|
||||
useEffect(() => {
|
||||
const telegramApp = window.Telegram?.WebApp;
|
||||
const showLoginForm = error === 'login_required' || (!user && !loading);
|
||||
|
||||
// Инициализировать виджет только если нет WebApp initData и показана форма входа
|
||||
if (!telegramApp?.initData && showLoginForm) {
|
||||
// Глобальная функция для обработки авторизации через виджет
|
||||
window.onTelegramAuth = async (userData) => {
|
||||
console.log('Telegram Login Widget данные:', userData);
|
||||
|
||||
try {
|
||||
setAuthLoading(true);
|
||||
|
||||
// Отправить данные виджета на сервер для создания сессии
|
||||
const API_URL = getApiUrl();
|
||||
// В production используем относительный путь (HTTPS через nginx)
|
||||
const fullApiUrl = API_URL.startsWith('http') ? API_URL : `${window.location.origin}${API_URL}`;
|
||||
const response = await fetch(`${fullApiUrl}/moderation-auth/telegram-widget`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(userData)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.error || 'Ошибка авторизации');
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.accessToken) {
|
||||
localStorage.setItem('moderation_jwt_token', result.accessToken);
|
||||
}
|
||||
|
||||
if (result?.user) {
|
||||
setUser(result.user);
|
||||
setError(null);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Ошибка авторизации через виджет:', err);
|
||||
setError(err.message || 'Ошибка авторизации через Telegram');
|
||||
} finally {
|
||||
setAuthLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const initWidget = () => {
|
||||
// Проверить не загружен ли уже виджет
|
||||
if (document.querySelector('script[src*="telegram-widget"]')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const widgetContainer = telegramWidgetRef.current;
|
||||
if (!widgetContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.async = true;
|
||||
script.src = 'https://telegram.org/js/telegram-widget.js?22';
|
||||
script.setAttribute('data-telegram-login', 'NakamaSpaceBot');
|
||||
script.setAttribute('data-size', 'large');
|
||||
script.setAttribute('data-request-access', 'write');
|
||||
script.setAttribute('data-onauth', 'onTelegramAuth');
|
||||
|
||||
widgetContainer.appendChild(script);
|
||||
};
|
||||
|
||||
// Подождать немного чтобы контейнер был готов
|
||||
const timer = setTimeout(initWidget, 500);
|
||||
return () => {
|
||||
clearTimeout(timer);
|
||||
if (window.onTelegramAuth) {
|
||||
delete window.onTelegramAuth;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (window.onTelegramAuth) {
|
||||
delete window.onTelegramAuth;
|
||||
}
|
||||
};
|
||||
}, [error, user, loading]);
|
||||
|
||||
useEffect(() => {
|
||||
if (tab === 'users') {
|
||||
loadUsers();
|
||||
|
|
@ -403,16 +429,27 @@ export default function App() {
|
|||
if (import.meta.env.VITE_API_URL) {
|
||||
return import.meta.env.VITE_API_URL;
|
||||
}
|
||||
// В production используем относительный путь (HTTPS)
|
||||
if (import.meta.env.PROD) {
|
||||
return '/api';
|
||||
}
|
||||
// В development используем localhost (только для dev)
|
||||
return 'http://localhost:3001/api';
|
||||
};
|
||||
|
||||
const API_URL = getApiUrl();
|
||||
|
||||
// Для WebSocket убираем "/api" из base URL, т.к. socket.io слушает на корне
|
||||
const socketBase = API_URL.replace(/\/?api\/?$/, '');
|
||||
// В production используем текущий origin через wss:// (HTTPS)
|
||||
let socketBase = API_URL.replace(/\/?api\/?$/, '');
|
||||
if (!socketBase || socketBase === '/api') {
|
||||
// В production используем текущий origin (HTTPS)
|
||||
socketBase = window.location.origin;
|
||||
}
|
||||
// Заменить http:// на https:// для безопасности (если не localhost)
|
||||
if (socketBase.startsWith('http://') && !socketBase.includes('localhost')) {
|
||||
socketBase = socketBase.replace('http://', 'https://');
|
||||
}
|
||||
|
||||
console.log('[Chat] Инициализация чата');
|
||||
console.log('[Chat] WS base URL:', socketBase);
|
||||
|
|
@ -910,9 +947,11 @@ export default function App() {
|
|||
if (import.meta.env.VITE_API_URL) {
|
||||
return import.meta.env.VITE_API_URL.replace('/api', '');
|
||||
}
|
||||
// В production используем текущий origin (HTTPS через nginx)
|
||||
if (import.meta.env.PROD) {
|
||||
return window.location.origin;
|
||||
}
|
||||
// Только для development
|
||||
return 'http://localhost:3000';
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ export const getApiUrl = () => {
|
|||
if (import.meta.env.VITE_API_URL) {
|
||||
return import.meta.env.VITE_API_URL;
|
||||
}
|
||||
// В production используем относительный путь
|
||||
// В production используем относительный путь (HTTPS через nginx)
|
||||
if (import.meta.env.PROD) {
|
||||
return '/api';
|
||||
}
|
||||
// В development используем порт модерации
|
||||
// В development используем порт модерации (только для dev)
|
||||
return 'http://localhost:3001/api';
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ server {
|
|||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
|
||||
# Логи
|
||||
access_log /var/log/nginx/moderation-access.log;
|
||||
|
|
@ -44,6 +45,10 @@ server {
|
|||
# Максимальный размер загружаемых файлов
|
||||
client_max_body_size 20M;
|
||||
|
||||
# Корневая директория фронтенда
|
||||
root /var/www/nakama/moderation/frontend/dist;
|
||||
index index.html;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
|
|
@ -56,15 +61,17 @@ server {
|
|||
application/vnd.ms-fontobject image/svg+xml;
|
||||
|
||||
# Проксирование API запросов к бэкенду модерации
|
||||
# ИЗМЕНИТЕ localhost:3001 на ваш реальный адрес бэкенда
|
||||
location /api {
|
||||
proxy_pass http://nakama-moderation-backend:3001;
|
||||
proxy_pass http://127.0.0.1:3001;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto https; # Всегда HTTPS
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port 443;
|
||||
|
||||
# WebSocket поддержка
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
|
|
@ -74,11 +81,14 @@ server {
|
|||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
|
||||
# Отключить буферизацию для реального времени
|
||||
proxy_buffering off;
|
||||
}
|
||||
|
||||
# WebSocket для чата модераторов
|
||||
location /mod-chat {
|
||||
proxy_pass http://nakama-moderation-backend:3001;
|
||||
proxy_pass http://127.0.0.1:3001;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
|
|
@ -86,27 +96,33 @@ server {
|
|||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto https; # Всегда HTTPS
|
||||
proxy_set_header X-Forwarded-Port 443;
|
||||
|
||||
# Таймауты для WebSocket
|
||||
proxy_connect_timeout 7d;
|
||||
proxy_send_timeout 7d;
|
||||
proxy_read_timeout 7d;
|
||||
|
||||
proxy_buffering off;
|
||||
}
|
||||
|
||||
# Статические файлы фронтенда (из Docker контейнера)
|
||||
# Статические файлы фронтенда
|
||||
location / {
|
||||
proxy_pass http://nakama-moderation-frontend:80;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# Кэширование статических файлов
|
||||
proxy_cache_valid 200 1y;
|
||||
proxy_cache_valid 404 1h;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# Кэширование статических файлов
|
||||
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot|map)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# Без кэша для index.html (для обновлений)
|
||||
location = /index.html {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
add_header Expires "0";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue