properly shared seo fiels between containers

This commit is contained in:
2ManyProjects 2025-05-07 22:37:15 -05:00
parent ce926cee4b
commit 77df5d6cd8
5 changed files with 5 additions and 149 deletions

View file

@ -85,7 +85,7 @@ const siteMapService = {
sitemap += `</urlset>`;
// Write sitemap to file
const publicDir = path.join(__dirname, '../../public');
const publicDir = path.join(__dirname, '../../public/seo-files');
if (!fs.existsSync(publicDir)) {
fs.mkdirSync(publicDir, { recursive: true });
}

View file

@ -11,6 +11,7 @@ services:
volumes:
- ./frontend:/app
- /app/node_modules
- seo-volume:/app/public
depends_on:
- backend
networks:
@ -28,7 +29,8 @@ services:
volumes:
- ./backend:/app
- /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:
db:
condition: service_healthy
@ -102,6 +104,7 @@ services:
volumes:
postgres_data:
redis_data:
seo-volume:
networks:
app-network:

View file

@ -8,7 +8,6 @@ import useBrandingSettings from '@hooks/brandingHooks';
import imageUtils from '@utils/imageUtils';
import Clarity from '@microsoft/clarity';
import CookieConsentPopup from '@components/CookieConsentPopup';
import SeoProxyRoutes from '@components/SeoProxyRoutes';
// Import layouts
import MainLayout from './layouts/MainLayout';
@ -133,9 +132,6 @@ function App() {
<Notifications />
<CookieConsentPopup />
{/* SEO Routes for sitemap.xml and robots.txt */}
<SeoProxyRoutes />
<Routes>
{/* Main routes with MainLayout */}
<Route path="/" element={<MainLayout />}>

View file

@ -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;

View file

@ -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;