PozoAppOptimandSEO/scripts/generate-seo.js

175 lines
5.8 KiB
JavaScript

import fs from "fs";
import path from "path";
import axios from "axios";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// For production deployment, always use the production URL
// When running convert-jsx-to-html, always use production URLs for SEO
const mainDirectory = (import.meta?.env?.ENV_MAIN_BASE_URL)
|| process.env.ENV_MAIN_BASE_URL
|| 'https://www.pozo.app/home';
const rawApiUrl = (import.meta?.env?.ENV_API_URL) || process.env.ENV_API_URL || '';
function normalizeApiBase(url) {
if (!url) return '';
// If full http/https, use as-is
if (/^https?:\/\//i.test(url)) return url;
// If protocol-relative (//domain), prefix https:
if (/^\/\//.test(url)) return `https:${url}`;
// Otherwise, assume domain/path and prefix https://
return `https://${url}`;
}
const CommonApiUrl = normalizeApiBase(rawApiUrl).replace(/\/$/, '');
const routes = [
{ path: "home/index.html", pageId: 1, url: `${mainDirectory}/`},
{
path: "home/signin/index.html",
pageId: 2,
url: `${mainDirectory}/signin`,
},
{
path: "home/pricing-pozoapp/index.html",
pageId: 3,
url: `${mainDirectory}/pricing-pozoapp`,
},
{
path: "home/contact-us/index.html",
pageId: 4,
url: `${mainDirectory}/contact-us`,
},
{ path: "home/blog/index.html", pageId: 5, url: `${mainDirectory}/blog` },
];
const fallbackData = {
1: {
title: "Retail ERP & POS for Indian MSMEs | POZO",
description: "Fast billing, smart inventory, GST-ready POS. POZO helps kirana, mini-supermarkets & retail chains speed checkout, connect weighing scales, and manage multi-store ops.",
image: `${mainDirectory}/og/home.jpg`,
},
2: {
title: "Sign In to PozoApp",
description: "Access your business dashboard",
image: `${mainDirectory}/og/Signin-og.jpg`,
},
3: {
title: "Pricing - Retail ERP & POS Plans | POZO",
description: "Simple plans for MSMEs. Fast billing, inventory, GST e-invoice, weighing-scale integration, WhatsApp e-bills & multi-store controls. Book a demo.",
image: `${mainDirectory}/og/pricing-og.jpg`,
},
4: {
title: "Contact PozoApp",
description: "Get support and sales information",
image: `${mainDirectory}/og/contact-og.jpg`,
},
5: {
title: "POZO Blog — Retail ERP, POS & Grocery Billing Guides",
description: "Practical guides on POS billing, weighing-scale integration, GST e-invoices, multi-store ERP & inventory control for Indian retailers.",
image: `${mainDirectory}/og/blog-og.jpg`,
},
};
async function fetchSEO(pageId) {
try {
const response = await axios.get(
`${CommonApiUrl}/Seo`,
{ params: { pageId } }
);
if (response.data?.statusCode === 1 && response.data?.data?.length > 0) {
return response.data.data[0];
}
} catch (error) {
console.log(`Using fallback for PageId ${pageId}`);
}
return null;
}
async function generateSEOFiles() {
for (const route of routes) {
const dbData = await fetchSEO(route.pageId);
const fallback = fallbackData[route.pageId];
const seoData = {
title: dbData?.MetaTitle || fallback.title,
description: dbData?.MetaDesc || fallback.description,
image: dbData?.ImageUrl || fallback.image,
url: route.url,
};
// Read the prerendered HTML file for this specific route
const filePath = path.join(__dirname, "../dist", route.path);
let html;
if (fs.existsSync(filePath)) {
// If prerendered file exists, read it
html = fs.readFileSync(filePath, "utf8");
} else {
// Fallback to base index.html if prerendered file doesn't exist
html = fs.readFileSync(
path.join(__dirname, "../dist/index.html"),
"utf8"
);
}
// Replace SEO tags in the HTML
html = html
// First uncomment commented title tags
.replace(/<!--\s*<title>[^<]*<\/title>\s*-->/, `<title>${seoData.title}</title>`)
// Then replace any existing title tags
.replace(/<title>[^<]*<\/title>/, `<title>${seoData.title}</title>`)
.replace(
/<meta property="og:title" content="[^"]*">/,
`<meta property="og:title" content="${seoData.title}">`
)
.replace(
/<meta property="og:description" content="[^"]*">/,
`<meta property="og:description" content="${seoData.description}">`
)
.replace(
/<meta property="og:image" content="[^"]*">/,
`<meta property="og:image" content="${seoData.image}">`
)
.replace(
/<meta property="og:url" content="[^"]*">/,
`<meta property="og:url" content="${seoData.url}">`
)
.replace(
/<meta name="description" content="[^"]*">/,
`<meta name="description" content="${seoData.description}">`
)
.replace(
/<meta property="twitter:title" content="[^"]*">/,
`<meta property="twitter:title" content="${seoData.title}">`
)
.replace(
/<meta property="twitter:description" content="[^"]*">/,
`<meta property="twitter:description" content="${seoData.description}">`
)
.replace(
/<meta property="twitter:image" content="[^"]*">/,
`<meta property="twitter:image" content="${seoData.image}">`
)
.replace(
/<meta property="twitter:url" content="[^"]*">/,
`<meta property="twitter:url" content="${seoData.url}">`
)
.replace(
/<link rel="canonical" href="[^"]*">/,
`<link rel="canonical" href="${seoData.url}">`
);
// Write the updated HTML back to the file
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, html);
console.log(`✓ Generated ${route.path}`);
}
console.log("\n✓ All SEO files generated successfully!");
}
generateSEOFiles().catch(console.error);