import axios from 'axios'; import dotenv from 'dotenv'; import { blogPosts } from '../lib/blog-data'; import { REDIRECTED_BLOG_SLUGS } from './content'; import { pillarMeta } from '../lib/pillar-data'; import { authors } from '../lib/author-data'; import { publishedUseCases } from '../lib/growth-pages'; import { useCasePagesDe } from '../lib/growth-pages-de'; import { industryPages } from '../lib/industry-pages'; import { publishedComparisonPages, publishedGuidePages } from '../lib/pseo-published-pages'; dotenv.config(); const INDEXNOW_ENDPOINT = 'https://api.indexnow.org/indexnow'; const HOST = 'www.qrmaster.net'; // You need to generate a key from https://www.bing.com/indexnow and place it in your public folder // Key must be set in .env as INDEXNOW_KEY const KEY = process.env.INDEXNOW_KEY!; const KEY_LOCATION = `https://${HOST}/${KEY}.txt`; export async function submitToIndexNow(urls: string[]) { try { const payload = { host: HOST, key: KEY, keyLocation: KEY_LOCATION, urlList: urls, }; console.log(`Submitting ${urls.length} URLs to IndexNow...`); const response = await axios.post(INDEXNOW_ENDPOINT, payload, { headers: { 'Content-Type': 'application/json', }, }); if (response.status === 200 || response.status === 202) { console.log('✅ Successfully submitted URLs to IndexNow.'); } else { console.error(`⚠️ IndexNow submission returned status: ${response.status}`); } } catch (error) { if (axios.isAxiosError(error)) { console.error('❌ Error submitting to IndexNow:', error.message); console.error('Response data:', error.response?.data); } else { console.error('❌ Unknown error submitting to IndexNow:', error); } } } // Function to gather all indexable URLs export function getAllIndexableUrls(): string[] { const baseUrl = `https://${HOST}`; // Free tools const freeTools = [ 'barcode-generator', // Added as per request 'url-qr-code', 'vcard-qr-code', 'text-qr-code', 'email-qr-code', 'sms-qr-code', 'wifi-qr-code', 'crypto-qr-code', 'event-qr-code', 'facebook-qr-code', 'instagram-qr-code', 'twitter-qr-code', 'youtube-qr-code', 'whatsapp-qr-code', 'tiktok-qr-code', 'geolocation-qr-code', 'call-qr-code-generator', 'paypal-qr-code', 'zoom-qr-code', 'teams-qr-code', 'google-review-qr-code', ].map(slug => `${baseUrl}/tools/${slug}`); // Use-case pages (English) and their German twins under /de const useCasePages = [ `${baseUrl}/use-cases`, ...publishedUseCases.map(uc => `${baseUrl}${uc.href}`), ]; const germanPages = [ `${baseUrl}/de/preise`, ...Object.values(useCasePagesDe).map(uc => `${baseUrl}${uc.href}`), ]; // Blog posts const blogPages = blogPosts .filter(post => !REDIRECTED_BLOG_SLUGS.has(post.slug)) .map(post => `${baseUrl}/blog/${post.slug}`); // Main pages (synced with sitemap.ts) const mainPages = [ baseUrl, `${baseUrl}/about`, `${baseUrl}/contact`, `${baseUrl}/press`, `${baseUrl}/testimonials`, `${baseUrl}/qr-code-erstellen`, `${baseUrl}/qr-code-tracking`, `${baseUrl}/reprint-calculator`, `${baseUrl}/dynamic-qr-code-generator`, // /dynamic-barcode-generator and /barcode-generator are 301'd to // /tools/barcode-generator in next.config.mjs - never submit them. `${baseUrl}/bulk-qr-code-generator`, `${baseUrl}/custom-qr-code-generator`, `${baseUrl}/manage-qr-codes`, `${baseUrl}/pricing`, `${baseUrl}/tools`, `${baseUrl}/features`, `${baseUrl}/faq`, `${baseUrl}/blog`, `${baseUrl}/privacy`, `${baseUrl}/newsletter`, `${baseUrl}/cookie-policy`, `${baseUrl}/terms`, `${baseUrl}/restaurants`, `${baseUrl}/qr-code-analytics`, `${baseUrl}/qr-code-print-size-guide`, `${baseUrl}/developers`, ]; // Alternatives & comparison hub pages const alternativesPages = [ `${baseUrl}/alternatives`, `${baseUrl}/alternatives/beaconstac`, `${baseUrl}/alternatives/bitly`, `${baseUrl}/alternatives/flowcode`, `${baseUrl}/alternatives/qr-code-generator`, `${baseUrl}/vs`, `${baseUrl}/vs/beaconstac`, ]; // pSEO comparison & guide pages (published subset) const pseoPages = [ ...publishedComparisonPages.map(page => `${baseUrl}${page.canonicalPath}`), ...publishedGuidePages.map(page => `${baseUrl}${page.canonicalPath}`), ]; // Learn hub pillars (the /guide/* URLs these replaced are 301'd in next.config.mjs // and must never be submitted to IndexNow - see redirects() there) const guidePages = [ `${baseUrl}/learn/developer`, `${baseUrl}/learn/basics`, `${baseUrl}/learn/tracking`, ]; // Industry landing pages const industryHubPages = [ `${baseUrl}/qr-code-for`, ...industryPages.map(industry => `${baseUrl}/qr-code-for/${industry.slug}`), ]; // Learn hub and pillar pages const learnPages = [ `${baseUrl}/learn`, ...pillarMeta.map(pillar => `${baseUrl}/learn/${pillar.key}`) ]; // Author pages const authorPages = authors.map(author => `${baseUrl}/authors/${author.slug}`); return [ ...mainPages, ...freeTools, ...useCasePages, ...germanPages, ...blogPages, ...learnPages, ...authorPages, ...alternativesPages, ...pseoPages, ...guidePages, ...industryHubPages, ]; } // If run directly if (require.main === module) { const urls = getAllIndexableUrls(); submitToIndexNow(urls); }