SEO optimization, site map generator for dynamic pages
This commit is contained in:
parent
36cb50e9db
commit
1129489750
3 changed files with 154 additions and 5 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -4,3 +4,4 @@ npm-debug.log
|
|||
yarn-error.log
|
||||
.DS_Store
|
||||
backend/public/uploads/*
|
||||
backend/public/*
|
||||
|
|
@ -12,6 +12,7 @@ const SystemSettings = require('./models/SystemSettings');
|
|||
const fs = require('fs');
|
||||
// services
|
||||
const notificationService = require('./services/notificationService');
|
||||
const siteMapService = require('./services/sitemapService');
|
||||
const emailService = require('./services/emailService');
|
||||
const storageService = require('./services/storageService');
|
||||
|
||||
|
|
@ -99,6 +100,9 @@ if (!fs.existsSync(blogImagesDir)) {
|
|||
// fileSize: 5 * 1024 * 1024 // 5MB limit
|
||||
// }
|
||||
// });
|
||||
|
||||
|
||||
|
||||
const upload = storageService.getUploadMiddleware();
|
||||
|
||||
pool.connect()
|
||||
|
|
@ -119,27 +123,47 @@ pool.connect()
|
|||
})
|
||||
.catch(err => console.error('Database connection error:', err))
|
||||
.finally((() => {
|
||||
setupRepeatedChecks(pool, query);
|
||||
}));
|
||||
|
||||
|
||||
async function setupRepeatedChecks(p, q){
|
||||
|
||||
let timeInterval = 60 * 1000;
|
||||
let siteGeneratorInterval = 60 * 1000;
|
||||
if (config.environment === 'prod') {
|
||||
console.log('Setting up scheduled tasks for production environment, 10 mins');
|
||||
console.log('Setting up scheduled tasks for production environment, 10 mins, 60 mins');
|
||||
timeInterval = 10 * 60 * 1000;
|
||||
siteGeneratorInterval = 60 * 60 * 1000;
|
||||
}else {
|
||||
console.log('Setting up scheduled tasks for development environment, 2 mins');
|
||||
console.log('Setting up scheduled tasks for development environment, 2 mins, 10 mins');
|
||||
timeInterval = 2 * 60 * 1000;
|
||||
siteGeneratorInterval = 10 * 60 * 1000;
|
||||
}
|
||||
// Process stock notifications every X minutes
|
||||
setInterval(async () => {
|
||||
try {
|
||||
console.log('Processing low stock notifications...');
|
||||
const processedCount = await notificationService.processLowStockNotifications(pool, query);
|
||||
const processedCount = await notificationService.processLowStockNotifications(p, q);
|
||||
console.log(`Processed ${processedCount} low stock notifications`);
|
||||
} catch (error) {
|
||||
console.error('Error processing low stock notifications:', error);
|
||||
}
|
||||
}, timeInterval);
|
||||
}));
|
||||
|
||||
console.log('Processing sitemap...');
|
||||
siteMapService.generateSitemap(p, q);
|
||||
console.log(`Sitemap generated`);
|
||||
setInterval(async () => {
|
||||
try {
|
||||
console.log('Processing sitemap...');
|
||||
await siteMapService.generateSitemap(p, q);
|
||||
console.log(`Sitemap generated`);
|
||||
} catch (error) {
|
||||
console.error('Error processing low stock notifications:', error);
|
||||
}
|
||||
}, timeInterval);
|
||||
}
|
||||
|
||||
// Handle SSL proxy headers
|
||||
app.use((req, res, next) => {
|
||||
|
|
|
|||
124
backend/src/services/sitemapService.js
Normal file
124
backend/src/services/sitemapService.js
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
const config = require('../config');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
/**
|
||||
* Service for updating site map for dynamic content
|
||||
*/
|
||||
const siteMapService = {
|
||||
/**
|
||||
* Process site and output SiteMap + robot.txt
|
||||
* @param {Object} pool - Database connection pool
|
||||
* @param {Function} query - Database query function
|
||||
*/
|
||||
async generateSitemap(pool, query) {
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query('BEGIN');
|
||||
console.log('Generating sitemap...');
|
||||
const siteDomain = config.site.domain;
|
||||
const protocol = config.site.protocol === 'prod' ? 'https' : 'http';
|
||||
const baseUrl = `${protocol}://${siteDomain}`;
|
||||
let sitemap = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<!-- Static pages -->
|
||||
<url>
|
||||
<loc>${baseUrl}/</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>${baseUrl}/products</loc>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>${baseUrl}/blog</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>`;
|
||||
|
||||
// Get all products
|
||||
const productsResult = await client.query(`SELECT id, updated_at FROM products WHERE is_active = true`);
|
||||
|
||||
// Add product URLs
|
||||
for (const product of productsResult.rows) {
|
||||
const lastmod = new Date(product.updated_at).toISOString().split('T')[0];
|
||||
sitemap += ` <url>
|
||||
<loc>${baseUrl}/products/${product.id}</loc>
|
||||
<lastmod>${lastmod}</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
`;
|
||||
}
|
||||
|
||||
// Get all published blog posts
|
||||
const blogResult = await client.query(`
|
||||
SELECT slug, updated_at FROM blog_posts WHERE status = 'published'
|
||||
`);
|
||||
|
||||
// Add blog post URLs
|
||||
for (const post of blogResult.rows) {
|
||||
const lastmod = new Date(post.updated_at).toISOString().split('T')[0];
|
||||
sitemap += ` <url>
|
||||
<loc>${baseUrl}/blog/${post.slug}</loc>
|
||||
<lastmod>${lastmod}</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.6</priority>
|
||||
</url>
|
||||
`;
|
||||
}
|
||||
|
||||
// Get product categories
|
||||
const categoriesResult = await client.query(`SELECT name FROM product_categories`);
|
||||
|
||||
// Add category URLs (assuming they use name as the identifier in the URL)
|
||||
for (const category of categoriesResult.rows) {
|
||||
sitemap += ` <url>
|
||||
<loc>${baseUrl}/products?category=${encodeURIComponent(category.name)}</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.5</priority>
|
||||
</url>`;
|
||||
}
|
||||
|
||||
sitemap += `</urlset>`;
|
||||
|
||||
// Write sitemap to file
|
||||
const publicDir = path.join(__dirname, '../../public');
|
||||
if (!fs.existsSync(publicDir)) {
|
||||
fs.mkdirSync(publicDir, { recursive: true });
|
||||
}
|
||||
|
||||
fs.writeFileSync(path.join(publicDir, 'sitemap.xml'), sitemap);
|
||||
console.log('Sitemap generated successfully at public/sitemap.xml');
|
||||
|
||||
// Create robots.txt if it doesn't exist
|
||||
const robotsTxtPath = path.join(publicDir, 'robots.txt');
|
||||
if (!fs.existsSync(robotsTxtPath)) {
|
||||
const robotsTxt = `# robots.txt for ${siteDomain}
|
||||
User-agent: *
|
||||
Disallow: /admin/
|
||||
Disallow: /auth/
|
||||
Disallow: /checkout/
|
||||
Disallow: /account/
|
||||
Disallow: /verify
|
||||
|
||||
# Allow sitemaps
|
||||
Sitemap: ${baseUrl}/sitemap.xml
|
||||
`;
|
||||
fs.writeFileSync(robotsTxtPath, robotsTxt);
|
||||
console.log('robots.txt created successfully');
|
||||
}
|
||||
await client.query('COMMIT');
|
||||
|
||||
} catch (error) {
|
||||
await client.query('ROLLBACK');
|
||||
console.error('Error generating sitemap:', error);
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = siteMapService;
|
||||
Loading…
Reference in a new issue