// server.js
const express = require('express');
const axios = require('axios');
const cheerio = require('cheerio');
const PORT = process.env.PORT || 3000;
const TARGET = 'https://www.chawrimart.co.in/'; // target site
const app = express();
// Basic CORS for your frontend
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
next();
});
app.get('/api/kpi', async (req, res) => {
const start = Date.now();
try {
// Fetch the homepage
const response = await axios.get(TARGET, { timeout: 12000, headers: { 'User-Agent': 'KPI-Scraper/1.0' }});
const html = response.data;
const fetchTime = Date.now() - start;
// Load into cheerio
const $ = cheerio.load(html);
// SITE TITLE (try multiple selectors)
let siteTitle = $('meta[property="og:site_name"]').attr('content') || $('title').first().text().trim();
if(!siteTitle) siteTitle = $('h1').first().text().trim() || $('h2').first().text().trim();
// TOTAL POSTS (approx - count of post snippets on homepage)
// Blogger/templated sites usually use article/post selectors; we attempt several fallbacks
let postElems = $('article, .post, .post-preview, .recent-posts, .post-list, .post-title');
if (postElems.length === 0) {
// fallback: look for headings that look like posts
postElems = $('h2, h3').filter((i, el) => $(el).text().length > 20);
}
const totalPosts = postElems.length;
// CATEGORIES count - count category links from nav / list
const catElems = $('a').filter((i, el) => {
const txt = $(el).text().toLowerCase();
// heuristics: category links often short and present in category list
return txt.includes('stainless') || txt.includes('category') || txt.includes('steel') || $(el).closest('.categories, .category-list, .widget').length > 0;
});
const categoriesCount = catElems.length; // heuristic, may include duplicates
// latest post — try first post link
let latestPostTitle = null;
let latestPostUrl = null;
const firstPostLink = $('article a[href]').first();
if (firstPostLink && firstPostLink.length) {
latestPostTitle = firstPostLink.text().trim() || $('article h2').first().text().trim();
latestPostUrl = firstPostLink.attr('href');
if (latestPostUrl && latestPostUrl.startsWith('/')) latestPostUrl = new URL(latestPostUrl, TARGET).href;
} else {
// fallback search for the first large link
const alt = $('a').filter((i, el) => $(el).text().length > 20).first();
if (alt && alt.length) {
latestPostTitle = alt.text().trim();
latestPostUrl = alt.attr('href');
if (latestPostUrl && latestPostUrl.startsWith('/')) latestPostUrl = new URL(latestPostUrl, TARGET).href;
}
}
// Prepare response
const payload = {
siteTitle: siteTitle || 'ChawriMart',
totalPosts,
categoriesCount,
latestPostTitle: latestPostTitle || null,
latestPostUrl: latestPostUrl || null,
avgResponseMs: fetchTime,
fetchedAt: new Date().toISOString(),
source: TARGET
};
res.json(payload);
} catch (err) {
res.status(500).json({ error: 'Failed to fetch site', details: err.message });
}
});
app.listen(PORT, () => {
console.log(`KPI API running on port ${PORT}`);
});