feat: Implement CSRF protection and PostgreSQL support
- Added CSRF protection mechanism in the backend with utility functions for token management. - Introduced a new CSRF route to fetch the active CSRF token for SPA bootstrap flows. - Updated the auth routes to validate CSRF tokens on sensitive operations. - Configured PostgreSQL as a database option in the environment settings and Docker Compose. - Created a new SQLite configuration file for local development. - Enhanced the API client to automatically attach CSRF tokens to requests. - Updated various frontend components to utilize the new site origin utility for SEO purposes. - Modified Nginx configuration to improve redirection and SEO headers. - Added tests for CSRF token handling in the authentication routes.
This commit is contained in:
@@ -3,16 +3,18 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
import { Mail, Send, CheckCircle, AlertCircle, Loader2 } from 'lucide-react';
|
||||
import SEOHead from '@/components/seo/SEOHead';
|
||||
import { generateWebPage } from '@/utils/seo';
|
||||
import axios from 'axios';
|
||||
import { generateWebPage, getSiteOrigin } from '@/utils/seo';
|
||||
import { getApiClient } from '@/services/api';
|
||||
|
||||
const CONTACT_EMAIL = 'support@dociva.io';
|
||||
const API_BASE = import.meta.env.VITE_API_URL || '';
|
||||
const api = getApiClient();
|
||||
|
||||
type Category = 'general' | 'bug' | 'feature';
|
||||
|
||||
export default function ContactPage() {
|
||||
const { t } = useTranslation();
|
||||
const siteOrigin = getSiteOrigin(typeof window !== 'undefined' ? window.location.origin : '');
|
||||
const [category, setCategory] = useState<Category>('general');
|
||||
const [submitted, setSubmitted] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -29,7 +31,7 @@ export default function ContactPage() {
|
||||
const data = new FormData(form);
|
||||
|
||||
try {
|
||||
await axios.post(`${API_BASE}/api/contact/submit`, {
|
||||
await api.post(`${API_BASE}/contact/submit`, {
|
||||
name: data.get('name'),
|
||||
email: data.get('email'),
|
||||
category,
|
||||
@@ -38,10 +40,10 @@ export default function ContactPage() {
|
||||
});
|
||||
setSubmitted(true);
|
||||
} catch (err: unknown) {
|
||||
if (axios.isAxiosError(err) && err.response?.data?.error) {
|
||||
setError(err.response.data.error);
|
||||
if (err instanceof Error) {
|
||||
setError(err.message);
|
||||
} else {
|
||||
setError(t('pages.contact.errorMessage', 'Failed to send message. Please try again.'));
|
||||
setError(err.response.data.error);
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -79,7 +81,7 @@ export default function ContactPage() {
|
||||
jsonLd={generateWebPage({
|
||||
name: t('pages.contact.title'),
|
||||
description: t('pages.contact.metaDescription'),
|
||||
url: `${window.location.origin}/contact`,
|
||||
url: `${siteOrigin}/contact`,
|
||||
})}
|
||||
/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user