fix: production CSRF, ProxyFix, and SSE streaming issues

This commit is contained in:
Your Name
2026-03-18 11:21:42 +02:00
parent 88cc92c252
commit aed02e36e5
10 changed files with 773 additions and 22 deletions

View File

@@ -2,8 +2,9 @@ import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Helmet } from 'react-helmet-async';
import { Mail } from 'lucide-react';
import { getApiClient } from '../services/api';
const API_BASE = import.meta.env.VITE_API_URL || '';
const api = getApiClient();
export default function ForgotPasswordPage() {
const { t } = useTranslation();
@@ -18,13 +19,7 @@ export default function ForgotPasswordPage() {
setLoading(true);
try {
const res = await fetch(`${API_BASE}/api/auth/forgot-password`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ email }),
});
if (!res.ok) throw new Error('Request failed');
await api.post('/auth/forgot-password', { email });
setSubmitted(true);
} catch {
setError(t('auth.forgotPassword.error'));

View File

@@ -3,8 +3,9 @@ import { useTranslation } from 'react-i18next';
import { useSearchParams, useNavigate } from 'react-router-dom';
import { Helmet } from 'react-helmet-async';
import { KeyRound } from 'lucide-react';
import { getApiClient } from '../services/api';
const API_BASE = import.meta.env.VITE_API_URL || '';
const api = getApiClient();
export default function ResetPasswordPage() {
const { t } = useTranslation();
@@ -33,14 +34,7 @@ export default function ResetPasswordPage() {
setLoading(true);
try {
const res = await fetch(`${API_BASE}/api/auth/reset-password`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ token, password }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error || 'Reset failed');
await api.post('/auth/reset-password', { token, password });
setSuccess(true);
setTimeout(() => navigate('/account'), 3000);
} catch (err) {

View File

@@ -412,13 +412,25 @@ export async function streamAssistantChat(
payload: AssistantChatRequest,
handlers: AssistantStreamHandlers = {}
): Promise<AssistantChatResponse> {
// Ensure a CSRF token cookie exists before streaming
let csrfToken = getCookieValue(CSRF_COOKIE_NAME);
if (!csrfToken) {
await csrfBootstrapClient.get('/auth/csrf');
csrfToken = getCookieValue(CSRF_COOKIE_NAME);
}
const streamHeaders: Record<string, string> = {
'Content-Type': 'application/json',
Accept: 'text/event-stream',
};
if (csrfToken) {
streamHeaders[CSRF_HEADER_NAME] = csrfToken;
}
const response = await fetch('/api/assistant/chat/stream', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
Accept: 'text/event-stream',
},
headers: streamHeaders,
body: JSON.stringify(payload),
});