234 lines
12 KiB
JavaScript
234 lines
12 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';
|
||
|
|
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: "index.html", pageId: 1, url: `${mainDirectory}/`},
|
||
|
|
{ path: "signin/index.html", pageId: 2, url: `${mainDirectory}/signin` },
|
||
|
|
{ path: "pricing/index.html", pageId: 3, url: `${mainDirectory}/pricing` },
|
||
|
|
{ path: "contact-us/index.html", pageId: 4, url: `${mainDirectory}/contact-us` },
|
||
|
|
{ path: "blog/index.html", pageId: 5, url: `${mainDirectory}/blog` },
|
||
|
|
{ path: "about-us/index.html", pageId: 6, url: `${mainDirectory}/about-us` },
|
||
|
|
{ path: "privacy-policy/index.html", pageId: 7, url: `${mainDirectory}/privacy-policy` },
|
||
|
|
{ path: "cookie-policy/index.html", pageId: 8, url: `${mainDirectory}/cookie-policy` },
|
||
|
|
{ path: "solutions/index.html", pageId: 9, url: `${mainDirectory}/solutions` },
|
||
|
|
{ path: "case-studies/index.html", pageId: 10, url: `${mainDirectory}/case-studies` },
|
||
|
|
{ path: "live-session/index.html", pageId: 11, url: `${mainDirectory}/live-session` },
|
||
|
|
{ path: "book-demo/index.html", pageId: 12, url: `${mainDirectory}/book-demo` },
|
||
|
|
{ path: "schedule-demo/index.html", pageId: 13, url: `${mainDirectory}/schedule-demo` },
|
||
|
|
{ path: "solutions/retail-billing/index.html", pageId: 14, url: `${mainDirectory}/solutions/retail-billing` },
|
||
|
|
{ path: "solutions/inventory-purchase/index.html", pageId: 15, url: `${mainDirectory}/solutions/inventory-purchase` },
|
||
|
|
{ path: "solutions/weighing-scale-pos/index.html", pageId: 16, url: `${mainDirectory}/solutions/weighing-scale-pos` },
|
||
|
|
{ path: "solutions/multi-store-erp/index.html", pageId: 17, url: `${mainDirectory}/solutions/multi-store-erp` },
|
||
|
|
{ path: "solutions/gst-billing-e-invoice/index.html", pageId: 18, url: `${mainDirectory}/solutions/gst-billing-e-invoice` },
|
||
|
|
{ path: "solutions/offline-billing/index.html", pageId: 19, url: `${mainDirectory}/solutions/offline-billing` },
|
||
|
|
{ path: "solutions/healthcare-management/index.html", pageId: 20, url: `${mainDirectory}/solutions/healthcare-management` },
|
||
|
|
];
|
||
|
|
|
||
|
|
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 | Retail ERP & POS Login",
|
||
|
|
description: "Access your business dashboard. Sign in to POZO retail ERP & POS system for billing, inventory management, and business analytics.",
|
||
|
|
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 POZO | Retail ERP & POS Support",
|
||
|
|
description: "Get support and sales information for POZO retail ERP & POS solutions. Contact us for billing software, inventory management, and business automation.",
|
||
|
|
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`,
|
||
|
|
},
|
||
|
|
6: {
|
||
|
|
title: "About POZO | Retail ERP & POS Solutions for MSMEs",
|
||
|
|
description: "Learn about POZO's mission to digitize Indian retail businesses with affordable ERP & POS solutions, billing software, and inventory management.",
|
||
|
|
image: `${mainDirectory}/og/about-og.jpg`,
|
||
|
|
},
|
||
|
|
7: {
|
||
|
|
title: "Privacy Policy | POZO Retail ERP & POS",
|
||
|
|
description: "POZO's privacy policy for retail ERP & POS users. Learn how we protect your business data, billing information, and customer details.",
|
||
|
|
image: `${mainDirectory}/og/privacy-og.jpg`,
|
||
|
|
},
|
||
|
|
8: {
|
||
|
|
title: "Cookie Policy | POZO Retail ERP & POS",
|
||
|
|
description: "POZO's cookie policy explaining how we use cookies to improve your retail ERP & POS experience and website functionality.",
|
||
|
|
image: `${mainDirectory}/og/cookie-og.jpg`,
|
||
|
|
},
|
||
|
|
9: {
|
||
|
|
title: "Retail Solutions | ERP & POS Software for Every Business",
|
||
|
|
description: "Comprehensive retail solutions: billing software, inventory management, GST compliance, weighing scale integration, and multi-store ERP for Indian businesses.",
|
||
|
|
image: `${mainDirectory}/og/solutions-og.jpg`,
|
||
|
|
},
|
||
|
|
10: {
|
||
|
|
title: "Case Studies | POZO Retail ERP & POS Success Stories",
|
||
|
|
description: "Real success stories of Indian retailers using POZO ERP & POS. See how businesses improved billing speed, inventory control, and customer satisfaction.",
|
||
|
|
image: `${mainDirectory}/og/case-studies-og.jpg`,
|
||
|
|
},
|
||
|
|
11: {
|
||
|
|
title: "Live Demo Session | POZO Retail ERP & POS",
|
||
|
|
description: "Join free live demo sessions of POZO retail ERP & POS. See billing, inventory management, GST features, and weighing scale integration in action.",
|
||
|
|
image: `${mainDirectory}/og/live-session-og.jpg`,
|
||
|
|
},
|
||
|
|
12: {
|
||
|
|
title: "Book Demo | POZO Retail ERP & POS Free Trial",
|
||
|
|
description: "Book a free demo of POZO retail ERP & POS. Experience fast billing, smart inventory, GST compliance, and business automation for your store.",
|
||
|
|
image: `${mainDirectory}/og/book-demo-og.jpg`,
|
||
|
|
},
|
||
|
|
13: {
|
||
|
|
title: "Schedule Demo | POZO Retail ERP & POS Consultation",
|
||
|
|
description: "Schedule a personalized demo consultation for POZO retail ERP & POS. Get expert guidance on billing, inventory, and business digitization.",
|
||
|
|
image: `${mainDirectory}/og/schedule-demo-og.jpg`,
|
||
|
|
},
|
||
|
|
14: {
|
||
|
|
title: "Retail Billing Software | Fast POS for Kirana & Supermarkets",
|
||
|
|
description: "Lightning-fast retail billing software with barcode scanning, GST compliance, customer management, and real-time inventory updates for Indian retailers.",
|
||
|
|
image: `${mainDirectory}/og/retail-billing-og.jpg`,
|
||
|
|
},
|
||
|
|
15: {
|
||
|
|
title: "Inventory & Purchase Management | Smart Stock Control",
|
||
|
|
description: "Advanced inventory management with purchase orders, supplier management, stock alerts, expiry tracking, and automated reordering for retail businesses.",
|
||
|
|
image: `${mainDirectory}/og/inventory-purchase-og.jpg`,
|
||
|
|
},
|
||
|
|
16: {
|
||
|
|
title: "Weighing Scale POS | Integrated Billing for Grocery Stores",
|
||
|
|
description: "POS system with weighing scale integration for grocery stores, fruit vendors, and bulk retailers. Accurate billing with weight-based pricing.",
|
||
|
|
image: `${mainDirectory}/og/weighing-scale-pos-og.jpg`,
|
||
|
|
},
|
||
|
|
17: {
|
||
|
|
title: "Multi-Store ERP | Centralized Retail Chain Management",
|
||
|
|
description: "Manage multiple retail locations with centralized inventory, unified reporting, inter-store transfers, and consolidated business analytics.",
|
||
|
|
image: `${mainDirectory}/og/multi-store-erp-og.jpg`,
|
||
|
|
},
|
||
|
|
18: {
|
||
|
|
title: "GST Billing & E-Invoice | Compliant Retail Software",
|
||
|
|
description: "GST-compliant billing with automatic e-invoice generation, GSTR filing support, tax calculations, and government portal integration.",
|
||
|
|
image: `${mainDirectory}/og/gst-billing-og.jpg`,
|
||
|
|
},
|
||
|
|
19: {
|
||
|
|
title: "Offline Billing Software | Works Without Internet",
|
||
|
|
description: "Reliable offline billing software that works without internet. Automatic sync when online, ensuring uninterrupted business operations.",
|
||
|
|
image: `${mainDirectory}/og/offline-billing-og.jpg`,
|
||
|
|
},
|
||
|
|
20: {
|
||
|
|
title: "Healthcare Management System | Medical Store & Clinic ERP",
|
||
|
|
description: "Specialized ERP for medical stores, clinics, and healthcare providers with medicine inventory, prescription management, and patient records.",
|
||
|
|
image: `${mainDirectory}/og/healthcare-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;
|
||
|
|
|
||
|
|
// ALWAYS use the specific file path, create directory if needed
|
||
|
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||
|
|
|
||
|
|
if (fs.existsSync(filePath)) {
|
||
|
|
// If specific file exists, read it
|
||
|
|
html = fs.readFileSync(filePath, "utf8");
|
||
|
|
console.log(`Reading existing: ${route.path}`);
|
||
|
|
} else {
|
||
|
|
// Copy from base index.html and modify
|
||
|
|
const baseHtml = fs.readFileSync(
|
||
|
|
path.join(__dirname, "../dist/index.html"),
|
||
|
|
"utf8"
|
||
|
|
);
|
||
|
|
html = baseHtml;
|
||
|
|
console.log(`Creating new: ${route.path} from base`);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Replace SEO tags in the HTML
|
||
|
|
html = html
|
||
|
|
// Replace title
|
||
|
|
.replace(/<title>[^<]*<\/title>/g, `<title>${seoData.title}</title>`)
|
||
|
|
// Replace meta description (handles multiline)
|
||
|
|
.replace(/<meta name="description"[\s\S]*?>/g, `<meta name="description" content="${seoData.description}" />`)
|
||
|
|
// Replace OG tags
|
||
|
|
.replace(/<meta property="og:title" content="[^"]*">/g, `<meta property="og:title" content="${seoData.title}">`)
|
||
|
|
.replace(/<meta property="og:description"[\s\S]*?>/g, `<meta property="og:description" content="${seoData.description}" />`)
|
||
|
|
.replace(/<meta property="og:image" content="[^"]*">/g, `<meta property="og:image" content="${seoData.image}">`)
|
||
|
|
.replace(/<meta property="og:url" content="[^"]*">/g, `<meta property="og:url" content="${seoData.url}">`)
|
||
|
|
// Replace Twitter tags
|
||
|
|
.replace(/<meta property="twitter:title" content="[^"]*">/g, `<meta property="twitter:title" content="${seoData.title}">`)
|
||
|
|
.replace(/<meta property="twitter:description"[\s\S]*?>/g, `<meta property="twitter:description" content="${seoData.description}" />`)
|
||
|
|
.replace(/<meta property="twitter:image" content="[^"]*">/g, `<meta property="twitter:image" content="${seoData.image}">`)
|
||
|
|
.replace(/<meta property="twitter:url" content="[^"]*">/g, `<meta property="twitter:url" content="${seoData.url}">`)
|
||
|
|
// Replace canonical
|
||
|
|
.replace(/<link rel="canonical" href="[^"]*">/g, `<link rel="canonical" href="${seoData.url}">`);
|
||
|
|
|
||
|
|
// Write the updated HTML back to the file
|
||
|
|
fs.writeFileSync(filePath, html);
|
||
|
|
console.log(`✓ Generated ${route.path} with unique SEO data`);
|
||
|
|
console.log(` Title: ${seoData.title.substring(0, 50)}...`);
|
||
|
|
console.log(` URL: ${seoData.url}`);
|
||
|
|
console.log(` Image: ${seoData.image}`);
|
||
|
|
console.log('');
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log("\n✓ All SEO files generated successfully!");
|
||
|
|
}
|
||
|
|
|
||
|
|
generateSEOFiles().catch(console.error);
|