properly shared seo fiels between containers
This commit is contained in:
parent
ce926cee4b
commit
77df5d6cd8
5 changed files with 5 additions and 149 deletions
|
|
@ -85,7 +85,7 @@ const siteMapService = {
|
||||||
sitemap += `</urlset>`;
|
sitemap += `</urlset>`;
|
||||||
|
|
||||||
// Write sitemap to file
|
// Write sitemap to file
|
||||||
const publicDir = path.join(__dirname, '../../public');
|
const publicDir = path.join(__dirname, '../../public/seo-files');
|
||||||
if (!fs.existsSync(publicDir)) {
|
if (!fs.existsSync(publicDir)) {
|
||||||
fs.mkdirSync(publicDir, { recursive: true });
|
fs.mkdirSync(publicDir, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- ./frontend:/app
|
- ./frontend:/app
|
||||||
- /app/node_modules
|
- /app/node_modules
|
||||||
|
- seo-volume:/app/public
|
||||||
depends_on:
|
depends_on:
|
||||||
- backend
|
- backend
|
||||||
networks:
|
networks:
|
||||||
|
|
@ -28,7 +29,8 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- ./backend:/app
|
- ./backend:/app
|
||||||
- /app/node_modules
|
- /app/node_modules
|
||||||
- ./backend/public/uploads:/app/public/uploads # Persist uploads
|
- ./backend/public/uploads:/app/public/uploads
|
||||||
|
- seo-volume:/app/public/seo-files
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
@ -102,6 +104,7 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
postgres_data:
|
postgres_data:
|
||||||
redis_data:
|
redis_data:
|
||||||
|
seo-volume:
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
app-network:
|
app-network:
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import useBrandingSettings from '@hooks/brandingHooks';
|
||||||
import imageUtils from '@utils/imageUtils';
|
import imageUtils from '@utils/imageUtils';
|
||||||
import Clarity from '@microsoft/clarity';
|
import Clarity from '@microsoft/clarity';
|
||||||
import CookieConsentPopup from '@components/CookieConsentPopup';
|
import CookieConsentPopup from '@components/CookieConsentPopup';
|
||||||
import SeoProxyRoutes from '@components/SeoProxyRoutes';
|
|
||||||
|
|
||||||
// Import layouts
|
// Import layouts
|
||||||
import MainLayout from './layouts/MainLayout';
|
import MainLayout from './layouts/MainLayout';
|
||||||
|
|
@ -133,9 +132,6 @@ function App() {
|
||||||
<Notifications />
|
<Notifications />
|
||||||
<CookieConsentPopup />
|
<CookieConsentPopup />
|
||||||
|
|
||||||
{/* SEO Routes for sitemap.xml and robots.txt */}
|
|
||||||
<SeoProxyRoutes />
|
|
||||||
|
|
||||||
<Routes>
|
<Routes>
|
||||||
{/* Main routes with MainLayout */}
|
{/* Main routes with MainLayout */}
|
||||||
<Route path="/" element={<MainLayout />}>
|
<Route path="/" element={<MainLayout />}>
|
||||||
|
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { Routes, Route } from 'react-router-dom';
|
|
||||||
import axiosClient from '@services/seoapi';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component to serve SEO files (sitemap.xml, robots.txt) directly from API
|
|
||||||
*/
|
|
||||||
const SeoFile = ({ filePath }) => {
|
|
||||||
const [content, setContent] = useState('');
|
|
||||||
const [contentType, setContentType] = useState('');
|
|
||||||
const [error, setError] = useState(null);
|
|
||||||
console.log(filePath)
|
|
||||||
useEffect(() => {
|
|
||||||
// Determine the content type based on the file extension
|
|
||||||
const fileExtension = filePath.split('.').pop();
|
|
||||||
const type = fileExtension === 'xml' ? 'application/xml' : 'text/plain';
|
|
||||||
setContentType(type);
|
|
||||||
|
|
||||||
// Fetch the file from the API
|
|
||||||
axiosClient.get(filePath, {
|
|
||||||
responseType: 'text',
|
|
||||||
headers: {
|
|
||||||
'Accept': type
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
setContent(response.data);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error(`Error fetching ${filePath}:`, err);
|
|
||||||
setError(`Error loading ${filePath}. ${err.message}`);
|
|
||||||
});
|
|
||||||
}, [filePath]);
|
|
||||||
|
|
||||||
// Set the content type and return the raw content
|
|
||||||
useEffect(() => {
|
|
||||||
if (content && contentType) {
|
|
||||||
// Clear existing document
|
|
||||||
document.open();
|
|
||||||
|
|
||||||
if (contentType.includes('xml')) {
|
|
||||||
// For XML sitemaps, just write the raw content directly
|
|
||||||
// This is critical - no HTML tags or wrappers for XML files
|
|
||||||
document.write(content);
|
|
||||||
} else {
|
|
||||||
// For robots.txt, use the pre tag to preserve formatting
|
|
||||||
document.write(`<pre>${content}</pre>`);
|
|
||||||
}
|
|
||||||
|
|
||||||
document.close();
|
|
||||||
|
|
||||||
// Set the correct content type meta tag
|
|
||||||
const meta = document.createElement('meta');
|
|
||||||
meta.httpEquiv = 'Content-Type';
|
|
||||||
meta.content = `${contentType}; charset=utf-8`;
|
|
||||||
document.head.appendChild(meta);
|
|
||||||
|
|
||||||
// Add minimal styling for robots.txt only
|
|
||||||
if (!contentType.includes('xml')) {
|
|
||||||
const style = document.createElement('style');
|
|
||||||
style.textContent = `
|
|
||||||
pre {
|
|
||||||
font-family: monospace;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.5;
|
|
||||||
margin: 0;
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
document.head.appendChild(style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [content, contentType]);
|
|
||||||
|
|
||||||
// If there was an error, show a simple error message
|
|
||||||
if (error) {
|
|
||||||
return <div>{error}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// During loading, return nothing (blank page)
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Routes component that handles SEO file requests
|
|
||||||
*/
|
|
||||||
const SeoProxyRoutes = () => {
|
|
||||||
return (
|
|
||||||
<Routes>
|
|
||||||
<Route path="/sitemap.xml" element={<SeoFile filePath="/sitemap.xml" />} />
|
|
||||||
<Route path="/robots.txt" element={<SeoFile filePath="/robots.txt" />} />
|
|
||||||
</Routes>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SeoProxyRoutes;
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
import axios from 'axios';
|
|
||||||
import { store } from '../store';
|
|
||||||
|
|
||||||
let url = "";
|
|
||||||
if(import.meta.env.VITE_ENVIRONMENT === "beta"){
|
|
||||||
url = import.meta.env.VITE_API_URL.split('/api')[0]
|
|
||||||
}else {
|
|
||||||
url = `https://${import.meta.env.VITE_API_PROD_URL}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const axiosClient = axios.create({
|
|
||||||
baseURL: url,
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// console.log(url, `https://${import.meta.env.VITE_API_PROD_URL}`)
|
|
||||||
axiosClient.interceptors.request.use(
|
|
||||||
(config) => {
|
|
||||||
const state = store.getState();
|
|
||||||
const apiKey = state.auth.apiKey;
|
|
||||||
|
|
||||||
if (apiKey) {
|
|
||||||
config.headers['X-API-Key'] = apiKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add response interceptor to handle common errors
|
|
||||||
axiosClient.interceptors.response.use(
|
|
||||||
(response) => response,
|
|
||||||
(error) => {
|
|
||||||
// Handle 401 unauthorized errors
|
|
||||||
if (error.response && error.response.status === 401) {
|
|
||||||
console.log("Missing Seo Files")
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export default axiosClient;
|
|
||||||
Loading…
Reference in a new issue