Update sitemaps and improve language handling

- Updated last modification dates in static and tools sitemaps to 2026-04-01.
- Enhanced language switching in the Header component to ensure language resources are loaded before changing the language.
- Added language resource loading logic in i18n configuration to support dynamic loading of language files.
- Improved SEO route page to ensure correct language is set based on URL parameters.
- Adjusted global CSS for deferred sections to optimize rendering.
- Configured Nginx to enable Brotli compression for better performance.
This commit is contained in:
Your Name
2026-04-01 07:25:24 +02:00
parent eb8d6463c5
commit 568446697c
13 changed files with 378 additions and 273 deletions

View File

@@ -30,8 +30,6 @@
content="30+ free tools: merge, split, compress, convert PDFs, images, videos & text. No signup required." />
<meta name="twitter:image" content="/social-preview.svg" />
<meta name="twitter:image:alt" content="Dociva social preview" />
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3822257947737372"
crossorigin="anonymous"></script>
<script>
(function () {
try {
@@ -56,13 +54,17 @@
</script>
<link rel="dns-prefetch" href="https://fonts.googleapis.com" />
<link rel="dns-prefetch" href="https://fonts.gstatic.com" />
<link rel="dns-prefetch" href="https://www.googletagmanager.com" />
<link rel="dns-prefetch" href="https://www.google-analytics.com" />
<link rel="dns-prefetch" href="https://pagead2.googlesyndication.com" />
<link rel="dns-prefetch" href="https://plausible.io" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="preload" as="style"
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Tajawal:wght@400;500;700&display=swap" />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Tajawal:wght@400;500;700&display=swap"
rel="stylesheet" />
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Tajawal:wght@400;700&display=swap" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Tajawal:wght@400;700&display=swap" media="print" onload="this.media='all'" />
<noscript>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Tajawal:wght@400;700&display=swap" />
</noscript>
<title>Dociva — Free Online File Tools</title>
</head>

View File

@@ -3,6 +3,10 @@ server {
root /usr/share/nginx/html;
index index.html;
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript image/svg+xml;
gzip_min_length 1000;
# SPA fallback
location / {
try_files $uri $uri/ /index.html;

View File

@@ -2,18 +2,18 @@
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>https://dociva.io/sitemaps/static.xml</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
</sitemap>
<sitemap>
<loc>https://dociva.io/sitemaps/blog.xml</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
</sitemap>
<sitemap>
<loc>https://dociva.io/sitemaps/tools.xml</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
</sitemap>
<sitemap>
<loc>https://dociva.io/sitemaps/seo.xml</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
</sitemap>
</sitemapindex>

View File

@@ -2,31 +2,31 @@
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://dociva.io/blog/how-to-compress-pdf-online</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://dociva.io/blog/convert-images-without-losing-quality</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://dociva.io/blog/ocr-extract-text-from-images</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://dociva.io/blog/merge-split-pdf-files</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://dociva.io/blog/ai-chat-with-pdf-documents</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>

File diff suppressed because it is too large Load Diff

View File

@@ -2,55 +2,55 @@
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://dociva.io/</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://dociva.io/tools</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/about</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.4</priority>
</url>
<url>
<loc>https://dociva.io/contact</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.4</priority>
</url>
<url>
<loc>https://dociva.io/privacy</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>yearly</changefreq>
<priority>0.3</priority>
</url>
<url>
<loc>https://dociva.io/terms</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>yearly</changefreq>
<priority>0.3</priority>
</url>
<url>
<loc>https://dociva.io/pricing</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://dociva.io/blog</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://dociva.io/developers</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
</url>

View File

@@ -2,265 +2,265 @@
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://dociva.io/tools/pdf-to-word</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://dociva.io/tools/word-to-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://dociva.io/tools/compress-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://dociva.io/tools/merge-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://dociva.io/tools/split-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/rotate-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://dociva.io/tools/pdf-to-images</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/images-to-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/watermark-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://dociva.io/tools/protect-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/unlock-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/page-numbers</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://dociva.io/tools/pdf-editor</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/pdf-flowchart</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://dociva.io/tools/pdf-to-excel</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/remove-watermark-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://dociva.io/tools/reorder-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://dociva.io/tools/extract-pages</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://dociva.io/tools/image-converter</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/image-resize</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/compress-image</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/ocr</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/remove-background</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/image-to-svg</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/html-to-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://dociva.io/tools/chat-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/summarize-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/translate-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/extract-tables</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/qr-code</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://dociva.io/tools/video-to-gif</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://dociva.io/tools/word-counter</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://dociva.io/tools/text-cleaner</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://dociva.io/tools/pdf-to-pptx</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/excel-to-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/pptx-to-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/sign-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://dociva.io/tools/crop-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://dociva.io/tools/flatten-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://dociva.io/tools/repair-pdf</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://dociva.io/tools/pdf-metadata</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://dociva.io/tools/image-crop</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://dociva.io/tools/image-rotate-flip</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://dociva.io/tools/barcode-generator</loc>
<lastmod>2026-03-30</lastmod>
<lastmod>2026-04-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>

View File

@@ -10,6 +10,8 @@ import { useDirection } from '@/hooks/useDirection';
import { initAnalytics, trackPageView } from '@/services/analytics';
import { useAuthStore } from '@/stores/authStore';
let clarityInitialized = false;
// Pages
const HomePage = lazy(() => import('@/pages/HomePage'));
const AboutPage = lazy(() => import('@/pages/AboutPage'));
@@ -99,10 +101,38 @@ export default function App() {
// Microsoft Clarity: Run only in production and browser
useEffect(() => {
if (import.meta.env.PROD && typeof window !== 'undefined') {
// ضع هنا رقم مشروع Clarity الخاص بك بدلاً من 'YOUR_CLARITY_PROJECT_ID'
Clarity.init(import.meta.env.VITE_CLARITY_PROJECT_ID);
}
if (!import.meta.env.PROD || typeof window === 'undefined') return;
const projectId = (import.meta.env.VITE_CLARITY_PROJECT_ID || '').trim();
if (!projectId) return;
const tryInitClarity = () => {
if (clarityInitialized) return;
try {
const rawConsent = localStorage.getItem('cookie_consent');
const parsed = rawConsent ? JSON.parse(rawConsent) : null;
const hasConsent = parsed?.state === 'accepted';
if (hasConsent) {
Clarity.init(projectId);
clarityInitialized = true;
}
} catch {
// Ignore malformed consent payloads.
}
};
tryInitClarity();
const onConsent = (event: Event) => {
const customEvent = event as CustomEvent<{ accepted: boolean }>;
if (customEvent.detail?.accepted && !clarityInitialized) {
Clarity.init(projectId);
clarityInitialized = true;
}
};
window.addEventListener('cookie-consent', onConsent as EventListener);
return () => window.removeEventListener('cookie-consent', onConsent as EventListener);
}, []);
useEffect(() => {

View File

@@ -3,6 +3,7 @@ import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { FileText, Moon, Sun, Menu, X, ChevronDown, UserRound } from 'lucide-react';
import { useAuthStore } from '@/stores/authStore';
import { ensureLanguageResources } from '@/i18n';
interface LangOption {
code: string;
label: string;
@@ -58,8 +59,9 @@ export default function Header() {
return () => document.removeEventListener('mousedown', handleClick);
}, []);
const switchLang = (code: string) => {
i18n.changeLanguage(code);
const switchLang = async (code: string) => {
const resolved = await ensureLanguageResources(code);
void i18n.changeLanguage(resolved);
setLangOpen(false);
};
@@ -140,7 +142,7 @@ export default function Header() {
{languages.map((lang) => (
<button
key={lang.code}
onClick={() => switchLang(lang.code)}
onClick={() => void switchLang(lang.code)}
className={`flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-colors ${
lang.code === i18n.language
? 'bg-primary-50 text-primary-700 dark:bg-primary-900/30 dark:text-primary-400'

View File

@@ -3,8 +3,57 @@ import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import en from './en.json';
import ar from './ar.json';
import fr from './fr.json';
type SupportedLanguage = 'en' | 'ar' | 'fr';
const loadedLanguages = new Set<SupportedLanguage>(['en']);
const languageLoaders: Record<Exclude<SupportedLanguage, 'en'>, () => Promise<{ default: Record<string, unknown> }>> = {
ar: () => import('./ar.json'),
fr: () => import('./fr.json'),
};
function normalizeLanguage(language?: string): SupportedLanguage {
const base = (language || '').split('-')[0];
return base === 'ar' || base === 'fr' ? base : 'en';
}
function getInitialLanguage(): SupportedLanguage {
if (typeof window === 'undefined') {
return 'en';
}
const htmlLanguage = normalizeLanguage(document.documentElement.lang);
if (htmlLanguage !== 'en') {
return htmlLanguage;
}
try {
const stored = localStorage.getItem('i18nextLng');
const fromStorage = normalizeLanguage(stored || undefined);
if (fromStorage !== 'en') {
return fromStorage;
}
} catch {
// no-op
}
return normalizeLanguage(navigator.language);
}
export async function ensureLanguageResources(language: string) {
const normalized = normalizeLanguage(language);
if (normalized === 'en' || loadedLanguages.has(normalized)) {
return normalized;
}
const module = await languageLoaders[normalized]();
i18n.addResourceBundle(normalized, 'translation', module.default, true, true);
loadedLanguages.add(normalized);
return normalized;
}
const initialLanguage = getInitialLanguage();
i18n
.use(LanguageDetector)
@@ -12,11 +61,11 @@ i18n
.init({
resources: {
en: { translation: en },
ar: { translation: ar },
fr: { translation: fr },
},
lng: initialLanguage,
fallbackLng: 'en',
supportedLngs: ['en', 'ar', 'fr'],
load: 'languageOnly',
interpolation: {
escapeValue: false,
},
@@ -26,4 +75,12 @@ i18n
},
});
if (initialLanguage !== 'en') {
void ensureLanguageResources(initialLanguage).then((resolved) => {
if (i18n.language !== resolved) {
void i18n.changeLanguage(resolved);
}
});
}
export default i18n;

View File

@@ -1,6 +1,7 @@
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import { ensureLanguageResources } from '@/i18n';
import { getProgrammaticToolPage, getSeoCollectionPage } from '@/config/seoPages';
import NotFoundPage from '@/pages/NotFoundPage';
import SeoCollectionPage from '@/pages/SeoCollectionPage';
@@ -17,9 +18,12 @@ export default function SeoRoutePage() {
const resolvedLocale = locale === 'ar' ? 'ar' : 'en';
useEffect(() => {
if (i18n.language !== resolvedLocale) {
void i18n.changeLanguage(resolvedLocale);
}
if (i18n.language === resolvedLocale) return;
void (async () => {
const resolved = await ensureLanguageResources(resolvedLocale);
await i18n.changeLanguage(resolved);
})();
}, [i18n, resolvedLocale]);
if (!slug) {
@@ -35,4 +39,4 @@ export default function SeoRoutePage() {
}
return <NotFoundPage />;
}
}

View File

@@ -157,5 +157,6 @@
}
.deferred-section {
content-visibility: visible;
content-visibility: auto;
contain-intrinsic-size: 1px 2000px;
}

View File

@@ -65,6 +65,11 @@ server {
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript image/svg+xml;
gzip_min_length 1000;
# Brotli (if module is available)
brotli on;
brotli_comp_level 5;
brotli_types text/plain text/css application/json application/javascript text/xml application/xml image/svg+xml;
# SSE streaming for assistant chat
location /api/assistant/chat/stream {
proxy_pass http://$backend_upstream;