// Express middleware to inject dynamic SEO meta tags import fs from 'fs'; import path from 'path'; import axios from 'axios'; import { dirname } from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const seoMiddleware = async (req, res, next) => { // CRITICAL: Skip ALL static assets IMMEDIATELY - don't even process them const requestPath = req.path; // Skip API routes if (requestPath.startsWith('/api/')) { return next(); } // CRITICAL: Skip /src/ paths FIRST - these should NEVER be processed by SEO middleware // In production, /src/ files shouldn't be requested, but if they are, skip them if (requestPath.startsWith('/src/')) { return next(); } // Skip ALL static asset paths FIRST (before any other checks) if (requestPath.startsWith('/assets/') || requestPath.startsWith('/@') || requestPath.startsWith('/og/') || requestPath.startsWith('/static/') || requestPath.startsWith('/home/assets/')) { return next(); } // Skip files with extensions (except .html) - CRITICAL for JS/CSS files if (requestPath.match(/\.[a-z]{2,4}$/i) && !requestPath.endsWith('.html')) { return next(); } // Skip if path contains any file extension indicators (double check) if (requestPath.includes('.js') || requestPath.includes('.jsx') || requestPath.includes('.css') || requestPath.includes('.png') || requestPath.includes('.jpg') || requestPath.includes('.jpeg') || requestPath.includes('.gif') || requestPath.includes('.svg') || requestPath.includes('.woff') || requestPath.includes('.woff2') || requestPath.includes('.ttf') || requestPath.includes('.eot') || requestPath.includes('.ico') || requestPath.includes('.json') || requestPath.includes('.xml') || requestPath.includes('.webp')) { return next(); } // CRITICAL: Process ALL routes that could be HTML pages // This includes: /, /home, /home/blog, /home/pricing, etc. // Don't call next() - we will send the response ourselves console.log('🔍 SEO Middleware checking path:', req.path); console.log('🔍 Request method:', req.method); console.log('🔍 Full URL:', req.url); try { console.log('==== SEO Middleware triggered for:', req.path); console.log('🔍 Raw req.path:', req.path); const normalizedPath = normalizePath(mapToHomeNamespace(req.path)); console.log('Normalized path:', normalizedPath); console.log('🔍 After normalization:', normalizedPath); // Attempt dynamic SEO fetch by path/slug first, fallback to pageId map const routeToPageId = { '/': 1, '/signin': 2, '/signin/': 2, '/pricing': 3, '/pricing/': 3, '/contact-us': 4, '/contact-us/': 4, '/blog': 5, '/blog/': 5, '/about-us': 6, '/about-us/': 6, '/privacy-policy': 7, '/privacy-policy/': 7, '/cookie-policy': 8, '/cookie-policy/': 8, '/solutions': 9, '/solutions/': 9, '/case-studies': 10, '/case-studies/': 10, '/live-session': 11, '/live-session/': 11, '/book-demo': 12, '/book-demo/': 12, '/schedule-demo': 13, '/schedule-demo/': 13, '/solutions/retail-billing': 14, '/solutions/retail-billing/': 14, '/solutions/inventory-purchase': 15, '/solutions/inventory-purchase/': 15, '/solutions/weighing-scale-pos': 16, '/solutions/weighing-scale-pos/': 16, '/solutions/multi-store-erp': 17, '/solutions/multi-store-erp/': 17, '/solutions/gst-billing-e-invoice': 18, '/solutions/gst-billing-e-invoice/': 18, '/solutions/offline-billing': 19, '/solutions/offline-billing/': 19, '/solutions/healthcare-management': 20, '/solutions/healthcare-management/': 20, '/industries/bakery': 21, '/industries/bakery/': 21, '/industries/restaurant': 22, '/industries/restaurant/': 22, '/industries/salon': 23, '/industries/salon/': 23, '/industries/fashion': 24, '/industries/fashion/': 24, '/industries/electronics': 25, '/industries/electronics/': 25, '/industries/grocery': 26, '/industries/grocery/': 26, '/industries/healthcare': 27, '/industries/healthcare/': 27, '/industries/manufacturing': 28, '/industries/manufacturing/': 28 }; const pageId = routeToPageId[normalizedPath] || 1; console.log('Page ID matched:', pageId, 'for path:', normalizedPath); console.log('Available routes:', Object.keys(routeToPageId).filter(k => routeToPageId[k] === 5)); const fullUrl = buildFullUrl(req); const baseUrl = `${req.protocol || 'http'}://${req.headers.host || 'localhost:3000'}`; // Production URL for images - always use production domain for OG images const PRODUCTION_URL = process.env.PRODUCTION_URL || 'https://www.pozo.app'; // Helper function to ensure image URLs are absolute and use production URL const ensureAbsoluteImageUrl = (imagePath) => { if (!imagePath) return null; // If already absolute URL (starts with http/https), use as-is if (/^https?:\/\//i.test(imagePath)) { // If it's localhost, replace with production URL if (/^https?:\/\/localhost/i.test(imagePath)) { return imagePath.replace(/^https?:\/\/[^/]+/i, PRODUCTION_URL); } return imagePath; } // If relative path, make it absolute with production URL const cleanPath = imagePath.startsWith('/') ? imagePath : `/${imagePath}`; return `${PRODUCTION_URL}${cleanPath}`; }; const dynamicSeo = await fetchSEOByPathOrSlug(normalizedPath); let dbSeoData = dynamicSeo || (await fetchSEOFromDB(pageId)); console.log('SEO Data fetched:', dbSeoData ? 'from DB' : 'using fallback'); if (dbSeoData) { console.log('DB SEO Data:', { title: dbSeoData.MetaTitle?.substring(0, 50), image: dbSeoData.ImageUrl || dbSeoData.image, pageId: pageId }); } 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.', keywords: 'POS software, retail ERP, billing software, inventory management, GST billing, weighing scale POS, multi-store ERP', image: `${PRODUCTION_URL}/og/home.jpg`, url: fullUrl }, 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.', keywords: 'PozoApp login, sign in, business dashboard, retail management, POS login, ERP login', image: `${PRODUCTION_URL}/og/Signin-og.jpg`, url: fullUrl }, 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.', keywords: 'pricing, POS software pricing, retail ERP plans, billing software pricing, inventory management pricing, GST billing plans', image: `${PRODUCTION_URL}/og/pricing-og.jpg`, url: fullUrl }, 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.', keywords: 'contact PozoApp, support, sales, retail ERP support, POS software support', image: `${PRODUCTION_URL}/og/contact-og.jpg`, url: fullUrl }, 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.', keywords: 'POS billing, weighing scale integration, GST e-invoices, retail ERP, inventory management, grocery billing guides', image: `${PRODUCTION_URL}/og/blog-og.jpg`, url: fullUrl }, 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.', keywords: 'about POZO, retail ERP company, POS software company, Indian retail solutions, MSME digitization', image: `${PRODUCTION_URL}/og/about-og.jpg`, url: fullUrl }, 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.', keywords: 'privacy policy, data protection, business data security, POS privacy, ERP data protection', image: `${PRODUCTION_URL}/og/privacy-og.jpg`, url: fullUrl }, 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.', keywords: 'cookie policy, website cookies, user experience, POS software cookies', image: `${PRODUCTION_URL}/og/cookie-og.jpg`, url: fullUrl }, 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.', keywords: 'retail solutions, ERP software, POS solutions, billing software, inventory management, GST compliance', image: `${PRODUCTION_URL}/og/default-og.jpg`, url: fullUrl }, 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.', keywords: 'case studies, success stories, retail ERP results, POS software benefits, customer testimonials', image: `${PRODUCTION_URL}/og/case-studies-og.jpg`, url: fullUrl }, 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.', keywords: 'live demo, POS demo, ERP demonstration, free trial, retail software demo', image: `${PRODUCTION_URL}/og/live-session-og.jpg`, url: fullUrl }, 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.', keywords: 'book demo, free trial, POS trial, ERP demo booking, retail software trial', image: `${PRODUCTION_URL}/og/book-demo-og.jpg`, url: fullUrl }, 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.', keywords: 'schedule demo, consultation, personalized demo, expert guidance, business consultation', image: `${PRODUCTION_URL}/og/schedule-demo-og.jpg`, url: fullUrl }, 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.', keywords: 'retail billing, POS software, kirana billing, supermarket POS, barcode scanning, GST billing', image: `${PRODUCTION_URL}/og/retail-billing-og.jpg`, url: fullUrl }, 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.', keywords: 'inventory management, purchase management, stock control, supplier management, automated reordering', image: `${PRODUCTION_URL}/og/inventory-purchase-og.jpg`, url: fullUrl }, 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.', keywords: 'weighing scale POS, grocery POS, weight-based billing, fruit vendor POS, bulk retail billing', image: `${PRODUCTION_URL}/og/weighing-scale-pos-og.jpg`, url: fullUrl }, 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.', keywords: 'multi-store ERP, retail chain management, centralized inventory, unified reporting, inter-store transfers', image: `${PRODUCTION_URL}/og/multi-store-erp-og.jpg`, url: fullUrl }, 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.', keywords: 'GST billing, e-invoice, GST compliance, GSTR filing, tax calculations, government integration', image: `${PRODUCTION_URL}/og/gst-billing-og.jpg`, url: fullUrl }, 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.', keywords: 'offline billing, offline POS, no internet billing, automatic sync, reliable billing software', image: `${PRODUCTION_URL}/og/offline-billing-og.jpg`, url: fullUrl }, 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.', keywords: 'healthcare ERP, medical store software, clinic management, medicine inventory, prescription management', image: `${PRODUCTION_URL}/og/healthcare-og.jpg`, url: fullUrl }, 21: { title: 'Bakery POS & Management Software | POZO', description: 'Complete bakery management solution with POS billing, inventory tracking, recipe management, expiry alerts & GST compliance for bakeries in India.', keywords: 'bakery POS, bakery software, bakery management, recipe management, inventory tracking, expiry alerts, GST billing', image: `${PRODUCTION_URL}/og/default-og.jpg`, url: fullUrl } }; const chosenFallback = fallbackData[pageId] || fallbackData[1]; // Ensure image URL is always absolute and uses production URL // If database image URL is missing or points to wrong page (e.g., home.jpg for blog), use fallback const dbImageUrl = dbSeoData?.ImageUrl || dbSeoData?.image; let finalImageUrl = ensureAbsoluteImageUrl(dbImageUrl); // Validate image URL - if it's pointing to wrong page image, use correct fallback // For blog page (pageId 5), ensure it's not using home.jpg if (pageId === 5 && finalImageUrl && finalImageUrl.includes('/og/home.jpg')) { console.log('Warning: Blog page has home.jpg image, using blog-og.jpg instead'); finalImageUrl = chosenFallback.image; } // For home page (pageId 1), ensure it's not using blog-og.jpg if (pageId === 1 && finalImageUrl && finalImageUrl.includes('/og/blog-og.jpg')) { console.log('Warning: Home page has blog-og.jpg image, using home.jpg instead'); finalImageUrl = chosenFallback.image; } // If no valid image URL, use fallback if (!finalImageUrl) { finalImageUrl = chosenFallback.image; } // Ensure we always have a valid image - use default-og.jpg if specific image doesn't exist const industryImages = ['bakery-og.jpg', 'restaurant-og.jpg', 'salon-og.jpg', 'fashion-og.jpg', 'electronics-og.jpg', 'grocery-og.jpg', 'healthcare-og.jpg', 'manufacturing-og.jpg']; if (!finalImageUrl || finalImageUrl.includes('undefined') || industryImages.some(img => finalImageUrl.includes(img))) { finalImageUrl = `${PRODUCTION_URL}/og/default-og.jpg`; } // Validate URL - ensure it matches the current page path let finalUrl = dbSeoData?.CanonicalUrl || dbSeoData?.url || fullUrl; // If database URL doesn't match the current page path, construct production URL // Blog page (pageId 5) should have /blog in URL if (pageId === 5 && finalUrl && !finalUrl.includes('/blog')) { console.log('Warning: Blog page URL is incorrect, constructing correct path'); const blogPath = normalizedPath === '/' ? '/blog' : normalizedPath; finalUrl = `${PRODUCTION_URL.replace(/\/$/, '')}${blogPath}`; } // Home page (pageId 1) should be root or /home, not other paths if (pageId === 1 && finalUrl && finalUrl.includes('/blog')) { console.log('Warning: Home page URL is pointing to blog, using root'); finalUrl = `${PRODUCTION_URL.replace(/\/$/, '')}/`; } // Pricing page (pageId 3) should have /pricing in URL if (pageId === 3 && finalUrl && !finalUrl.includes('/pricing')) { console.log('Warning: Pricing page URL is incorrect, constructing correct path'); finalUrl = `${PRODUCTION_URL.replace(/\/$/, '')}/pricing`; } // CRITICAL: Replace localhost URLs with production URL if (finalUrl && /^https?:\/\/localhost/i.test(finalUrl)) { const pathPart = finalUrl.replace(/^https?:\/\/[^/]+/i, ''); finalUrl = `${PRODUCTION_URL.replace(/\/$/, '')}${pathPart}`; console.log('Replaced localhost URL with production:', finalUrl); } // Also ensure finalUrl doesn't have localhost at all if (finalUrl && finalUrl.includes('localhost')) { const pathPart = finalUrl.replace(/^https?:\/\/[^/]+/i, ''); finalUrl = `${PRODUCTION_URL.replace(/\/$/, '')}${pathPart}`; console.log('Force replaced any localhost in URL:', finalUrl); } // CRITICAL: Force correct image and URL based on pageId // Blog page (pageId 5) MUST use blog-og.jpg, not home.jpg // Home page (pageId 1) MUST use home.jpg, not blog-og.jpg if (pageId === 5) { // Blog page - force blog image and URL if (!finalImageUrl || finalImageUrl.includes('/og/home.jpg')) { console.log('FORCING blog-og.jpg for blog page (pageId 5)'); finalImageUrl = `${PRODUCTION_URL}/og/blog-og.jpg`; } if (!finalUrl || !finalUrl.includes('/blog')) { console.log('FORCING blog URL for blog page (pageId 5)'); // Construct production URL, don't use fullUrl (has localhost) const blogPath = normalizedPath === '/' ? '/blog' : normalizedPath; finalUrl = `${PRODUCTION_URL.replace(/\/$/, '')}${blogPath}`; } } else if (pageId === 1) { // Home page - force home image if (!finalImageUrl || finalImageUrl.includes('/og/blog-og.jpg')) { console.log('FORCING home.jpg for home page (pageId 1)'); finalImageUrl = `${PRODUCTION_URL}/og/home.jpg`; } } // Force correct data based on pageId - if database returns wrong page data, use fallback // This ensures blog page always gets blog data, not home data let seoData; if (dbSeoData) { // Use database data but with forced correct image/URL seoData = { title: dbSeoData.MetaTitle || dbSeoData.title || chosenFallback.title, description: dbSeoData.MetaDesc || dbSeoData.description || chosenFallback.description, keywords: dbSeoData.Keywords || dbSeoData.keywords || chosenFallback.keywords || '', image: finalImageUrl, // This is now forced to be correct url: finalUrl // This is now forced to be correct }; } else { // No database data, use fallback but replace URL with corrected finalUrl seoData = { ...chosenFallback, url: finalUrl // Use the corrected finalUrl, not fallback URL which has localhost }; } // CRITICAL: Final check - ensure seoData.url NEVER has localhost if (seoData.url && (seoData.url.includes('localhost') || /^https?:\/\/localhost/i.test(seoData.url))) { const pathPart = seoData.url.replace(/^https?:\/\/[^/]+/i, ''); seoData.url = `${PRODUCTION_URL.replace(/\/$/, '')}${pathPart}`; console.log('FINAL FIX: Replaced localhost in seoData.url:', seoData.url); } // One more safety check - if URL still has localhost, force replace if (seoData.url && seoData.url.includes('localhost')) { const pathPart = seoData.url.replace(/^https?:\/\/[^/]+/i, ''); seoData.url = `${PRODUCTION_URL.replace(/\/$/, '')}${pathPart}`; console.log('SAFETY CHECK: Force replaced localhost:', seoData.url); } console.log('Final SEO Data:', { title: seoData.title.substring(0, 50), image: seoData.image, url: seoData.url }); // Read HTML - try to read from specific path first, fallback to root index.html let htmlPath; const distPath = path.join(__dirname, '..', 'dist'); // Try to find HTML file for the specific route // For /home/blog, try dist/home/blog/index.html, then dist/index.html if (normalizedPath !== '/' && normalizedPath !== '/home' && normalizedPath !== '/home/') { const routeHtmlPath = path.join(distPath, normalizedPath, 'index.html'); if (fs.existsSync(routeHtmlPath)) { htmlPath = routeHtmlPath; console.log('Reading HTML from:', routeHtmlPath); } else { // Try without leading slash const routeHtmlPath2 = path.join(distPath, normalizedPath.replace(/^\//, ''), 'index.html'); if (fs.existsSync(routeHtmlPath2)) { htmlPath = routeHtmlPath2; console.log('Reading HTML from:', routeHtmlPath2); } else { htmlPath = path.join(distPath, 'index.html'); console.log('Using root index.html'); } } } else { htmlPath = path.join(distPath, 'index.html'); console.log('Using root index.html for home page'); } // Check if HTML file exists if (!fs.existsSync(htmlPath)) { console.error('❌ HTML file not found:', htmlPath); console.error('❌ Please build the project first using: npm run build or BUILD-WITH-SEO.bat'); return res.status(500).send(`
The dist/index.html file is missing.
Please build the project first:
npm run build or run BUILD-WITH-SEO.bat
Please rebuild the project.
`); } // ULTRA AGGRESSIVE: Remove ALL existing SEO tags (handles multiline tags) // Match from or > (handles multiline with [\s\S]*?) const metaTagPattern = /]*?\/?>/gs; // 's' flag makes . match newlines // Remove all OG tags (multiline aware) html = html.replace(//gi, ''); html = html.replace(//gi, ''); html = html.replace(//gi, ''); html = html.replace(//gi, ''); html = html.replace(//gi, ''); html = html.replace(//gi, ''); html = html.replace(//gi, ''); // Remove Twitter tags (multiline aware) html = html.replace(//gi, ''); html = html.replace(//gi, ''); html = html.replace(//gi, ''); html = html.replace(//gi, ''); html = html.replace(//gi, ''); html = html.replace(//gi, ''); // Remove existing description and keywords (will be re-added) html = html.replace(//gi, ''); html = html.replace(//gi, ''); // Remove empty SEO comment sections html = html.replace(/\s*[\r\n\s]*/gi, ''); html = html.replace(/\s*[\r\n\s]*/gi, ''); html = html.replace(/\s*[\r\n\s]*/gi, ''); // Remove all existing structured data scripts html = html.replace(/ `; // Generate structured data based on page const generateStructuredData = () => { const baseUrl = PRODUCTION_URL.replace(/\/$/, ''); // Use finalUrl (already has localhost replaced) or construct from normalizedPath let pageUrl = finalUrl; // If still has localhost, replace it if (pageUrl && /^https?:\/\/localhost/i.test(pageUrl)) { const pathPart = pageUrl.replace(/^https?:\/\/[^/]+/i, ''); pageUrl = `${baseUrl}${pathPart}`; } // If no URL, construct from normalizedPath if (!pageUrl || pageUrl === fullUrl) { pageUrl = `${baseUrl}${normalizedPath === '/' ? '' : normalizedPath}`; } // Base schemas (Organization + WebSite) - always included const baseSchemas = [ { "@type": "Organization", "name": "POZO", "url": baseUrl + "/", "logo": baseUrl + "/static/brand/logo.png", "foundingDate": "2019", "contactPoint": { "@type": "ContactPoint", "telephone": "+91-7324000011", "contactType": "customer service" }, "address": { "@type": "PostalAddress", "streetAddress": "No 51 Step Colony, Dharga", "addressLocality": "Hosur", "addressRegion": "Tamil Nadu", "postalCode": "635126", "addressCountry": "IN" }, "sameAs": [] }, { "@type": "WebSite", "name": "POZO", "url": baseUrl + "/", "potentialAction": { "@type": "SearchAction", "target": baseUrl + "/search?q={query}", "query-input": "required name=query" } } ]; // Page-specific schemas let pageSchemas = []; // Home page (pageId 1) - Organization + WebSite + WebPage only if (pageId === 1) { pageSchemas = [ { "@type": "WebPage", "url": pageUrl, "name": seoData.title, "isPartOf": { "@id": baseUrl + "/" }, "description": seoData.description } ]; } // Blog page (pageId 5) - CollectionPage instead of WebPage else if (pageId === 5) { pageSchemas = [ { "@type": "CollectionPage", "@id": pageUrl, "name": seoData.title, "isPartOf": { "@id": baseUrl + "/" }, "description": seoData.description } ]; } // Pricing page (pageId 3) - WebPage + SoftwareApplication else if (pageId === 3) { pageSchemas = [ { "@type": "WebPage", "@id": pageUrl, "name": seoData.title, "description": seoData.description }, { "@type": "SoftwareApplication", "name": "POZO", "applicationCategory": "BusinessApplication", "operatingSystem": "Web", "url": pageUrl, "offers": { "@type": "Offer", "priceCurrency": "INR", "availability": "https://schema.org/InStock" }, "featureList": [ "Fast POS billing", "Inventory & purchase", "GST e-invoice", "Weighing-scale integration", "WhatsApp e-bill", "Multi-store ERP" ] } ]; } // Other pages - default WebPage else { pageSchemas = [ { "@type": "WebPage", "url": pageUrl, "name": seoData.title, "isPartOf": { "@id": baseUrl + "/" }, "description": seoData.description } ]; } return { "@context": "https://schema.org", "@graph": [...baseSchemas, ...pageSchemas] }; }; const structuredDataScript = ``; // Add fresh SEO tags before // Include keywords if available const keywordsTag = seoData.keywords ? `` : ''; // Shortened OG description for home page (pageId 1) const ogDescription = pageId === 1 ? 'Fast billing, smart inventory, GST-ready POS for kirana & supermarkets.' : seoData.description; const seoTags = ` ${keywordsTag} ${trackingScripts} ${structuredDataScript}`; html = html.replace(/<\/head>/i, `${seoTags}\n`); // Remove ALL existing GTM noscript tags (old ones from index.html) html = html.replace(/[\s\S]*?/gi, ''); html = html.replace(/