175 lines
5.8 KiB
JavaScript
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);
|