172 lines
5.8 KiB
TypeScript
172 lines
5.8 KiB
TypeScript
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);
|
|
}
|